@@ -43,8 +43,13 @@ type DisconnectPredicateTsfn =
4343type RunRestartHook = Arc < dyn Fn ( Option < ( i64 , u64 ) > ) -> anyhow:: Result < ( ) > + Send + Sync + ' static > ;
4444pub ( crate ) type RegisteredTask = Pin < Box < dyn Future < Output = ( ) > + Send + ' static > > ;
4545
46- static ACTOR_CONTEXT_SHARED : LazyLock < SccHashMap < String , Weak < ActorContextShared > > > =
47- LazyLock :: new ( SccHashMap :: new) ;
46+ // Keyed by generation so a newer generation of the same actor never shares, resets, or clears the
47+ // JS runtime state of an older generation that is still shutting down on this host.
48+ static ACTOR_CONTEXT_SHARED : LazyLock <
49+ SccHashMap < ActorContextSharedKey , Weak < ActorContextShared > > ,
50+ > = LazyLock :: new ( SccHashMap :: new) ;
51+
52+ type ActorContextSharedKey = ( String , Option < u32 > ) ;
4853
4954/// N-API wrapper around `rivetkit-core::ActorContext`.
5055#[ derive( Clone ) ]
@@ -193,10 +198,12 @@ struct DisconnectPredicatePayload {
193198impl ActorContext {
194199 pub ( crate ) fn new ( inner : CoreActorContext ) -> Self {
195200 let actor_id = inner. actor_id ( ) . to_owned ( ) ;
196- let shared = actor_context_shared ( & actor_id) ;
201+ let generation = inner. generation ( ) ;
202+ let shared = actor_context_shared ( & actor_id, generation) ;
197203 tracing:: debug!(
198204 class = "ActorContext" ,
199205 %actor_id,
206+ ?generation,
200207 shared_strong_count = Arc :: strong_count( & shared) ,
201208 "constructed napi class"
202209 ) ;
@@ -994,14 +1001,15 @@ impl Drop for ActorContextShared {
9941001 }
9951002}
9961003
997- fn actor_context_shared ( actor_id : & str ) -> Arc < ActorContextShared > {
1004+ fn actor_context_shared ( actor_id : & str , generation : Option < u32 > ) -> Arc < ActorContextShared > {
9981005 ACTOR_CONTEXT_SHARED . retain_sync ( |_, shared| shared. strong_count ( ) > 0 ) ;
9991006
1000- match ACTOR_CONTEXT_SHARED . entry_sync ( actor_id. to_owned ( ) ) {
1007+ match ACTOR_CONTEXT_SHARED . entry_sync ( ( actor_id. to_owned ( ) , generation ) ) {
10011008 scc:: hash_map:: Entry :: Occupied ( mut entry) => {
10021009 if let Some ( shared) = entry. get ( ) . upgrade ( ) {
10031010 tracing:: debug!(
10041011 %actor_id,
1012+ ?generation,
10051013 outcome = "hit" ,
10061014 strong_count = Arc :: strong_count( & shared) ,
10071015 "actor context shared-state cache lookup"
@@ -1013,6 +1021,7 @@ fn actor_context_shared(actor_id: &str) -> Arc<ActorContextShared> {
10131021 * entry. get_mut ( ) = Arc :: downgrade ( & shared) ;
10141022 tracing:: debug!(
10151023 %actor_id,
1024+ ?generation,
10161025 outcome = "stale" ,
10171026 "actor context shared-state cache lookup"
10181027 ) ;
@@ -1023,6 +1032,7 @@ fn actor_context_shared(actor_id: &str) -> Arc<ActorContextShared> {
10231032 entry. insert_entry ( Arc :: downgrade ( & shared) ) ;
10241033 tracing:: debug!(
10251034 %actor_id,
1035+ ?generation,
10261036 outcome = "miss" ,
10271037 "actor context shared-state cache lookup"
10281038 ) ;
0 commit comments