@@ -1183,6 +1183,12 @@ impl<Ctx: WorkerCtx> DurableWorkerCtx<Ctx> {
11831183 & mut self ,
11841184 function_type : & DurableFunctionType ,
11851185 ) -> Result < OplogIndex , WorkerExecutorError > {
1186+ if self . state . snapshotting_mode . is_some ( ) {
1187+ let begin_index = self . state . current_oplog_index ( ) . await ;
1188+ self . state . current_retry_point = begin_index;
1189+ return Ok ( begin_index) ;
1190+ }
1191+
11861192 if ( * function_type == DurableFunctionType :: WriteRemote && !self . state . assume_idempotence )
11871193 || matches ! (
11881194 * function_type,
@@ -1325,6 +1331,10 @@ impl<Ctx: WorkerCtx> DurableWorkerCtx<Ctx> {
13251331 function_type : & DurableFunctionType ,
13261332 begin_index : OplogIndex ,
13271333 ) -> Result < ( ) , WorkerExecutorError > {
1334+ if self . state . snapshotting_mode . is_some ( ) {
1335+ return Ok ( ( ) ) ;
1336+ }
1337+
13281338 if ( * function_type == DurableFunctionType :: WriteRemote && !self . state . assume_idempotence )
13291339 || matches ! (
13301340 * function_type,
@@ -1717,11 +1727,6 @@ impl<Ctx: WorkerCtx> DurableWorkerCtx<Ctx> {
17171727 return Ok ( Some ( RetryDecision :: Immediate ) ) ;
17181728 }
17191729
1720- store
1721- . as_context_mut ( )
1722- . data_mut ( )
1723- . begin_call_snapshotting_function ( ) ;
1724-
17251730 let load_result = invoke_observed_and_traced (
17261731 lowered,
17271732 store,
@@ -1733,7 +1738,8 @@ impl<Ctx: WorkerCtx> DurableWorkerCtx<Ctx> {
17331738 store
17341739 . as_context_mut ( )
17351740 . data_mut ( )
1736- . end_call_snapshotting_function ( ) ;
1741+ . durable_ctx_mut ( )
1742+ . end_call_snapshotting_function_if_active ( ) ;
17371743
17381744 for span_id in local_span_ids {
17391745 let _ = store
@@ -1877,9 +1883,11 @@ impl<Ctx: WorkerCtx> DurableWorkerCtx<Ctx> {
18771883 ..
18781884 } => ( payload, mime_type) ,
18791885 _ => {
1880- warn ! (
1886+ let error = format ! (
18811887 "Expected Snapshot entry at oplog index {snapshot_index}, found different entry; falling back to full replay"
18821888 ) ;
1889+ warn ! ( "{error}" ) ;
1890+ Self :: emit_snapshot_recovery_event ( store, snapshot_index, false , Some ( error) ) ;
18831891 if let Err ( err) = store
18841892 . as_context_mut ( )
18851893 . data_mut ( )
@@ -1906,7 +1914,11 @@ impl<Ctx: WorkerCtx> DurableWorkerCtx<Ctx> {
19061914 {
19071915 Ok ( data) => data,
19081916 Err ( err) => {
1909- warn ! ( "Failed to download snapshot payload: {err}; falling back to full replay" ) ;
1917+ let error = format ! (
1918+ "Failed to download snapshot payload: {err}; falling back to full replay"
1919+ ) ;
1920+ warn ! ( "{error}" ) ;
1921+ Self :: emit_snapshot_recovery_event ( store, snapshot_index, false , Some ( error) ) ;
19101922 if let Err ( err) = store
19111923 . as_context_mut ( )
19121924 . data_mut ( )
@@ -1950,7 +1962,10 @@ impl<Ctx: WorkerCtx> DurableWorkerCtx<Ctx> {
19501962 ) {
19511963 Ok ( lowered) => lowered,
19521964 Err ( err) => {
1953- warn ! ( "Snapshot recovery failed to lower load-snapshot invocation: {err}" ) ;
1965+ let error =
1966+ format ! ( "Snapshot recovery failed to lower load-snapshot invocation: {err}" ) ;
1967+ warn ! ( "{error}" ) ;
1968+ Self :: emit_snapshot_recovery_event ( store, snapshot_index, false , Some ( error) ) ;
19541969 return SnapshotRecoveryResult :: Failed ;
19551970 }
19561971 } ;
@@ -1964,13 +1979,16 @@ impl<Ctx: WorkerCtx> DurableWorkerCtx<Ctx> {
19641979 . set_current_invocation_context ( invocation_context)
19651980 . await
19661981 {
1967- warn ! ( "Snapshot recovery failed to install invocation context: {err}" ) ;
1982+ let error = format ! ( "Snapshot recovery failed to install invocation context: {err}" ) ;
1983+ warn ! ( "{error}" ) ;
1984+ Self :: emit_snapshot_recovery_event ( store, snapshot_index, false , Some ( error) ) ;
19681985 return SnapshotRecoveryResult :: Failed ;
19691986 }
19701987
19711988 store
19721989 . as_context_mut ( )
19731990 . data_mut ( )
1991+ . durable_ctx_mut ( )
19741992 . begin_call_snapshotting_function ( ) ;
19751993
19761994 let load_result =
@@ -1979,7 +1997,8 @@ impl<Ctx: WorkerCtx> DurableWorkerCtx<Ctx> {
19791997 store
19801998 . as_context_mut ( )
19811999 . data_mut ( )
1982- . end_call_snapshotting_function ( ) ;
2000+ . durable_ctx_mut ( )
2001+ . end_call_snapshotting_function_if_active ( ) ;
19832002
19842003 for span_id in local_span_ids {
19852004 let _ = store
@@ -2024,12 +2043,38 @@ impl<Ctx: WorkerCtx> DurableWorkerCtx<Ctx> {
20242043
20252044 if let Some ( error) = failed {
20262045 warn ! ( "{error}; re-creating instance for full replay" ) ;
2046+ Self :: emit_snapshot_recovery_event ( store, snapshot_index, false , Some ( error) ) ;
20272047 SnapshotRecoveryResult :: Failed
20282048 } else {
20292049 debug ! ( "Snapshot loaded successfully from oplog index {snapshot_index}" ) ;
2050+ Self :: emit_snapshot_recovery_event ( store, snapshot_index, true , None ) ;
20302051 SnapshotRecoveryResult :: Success
20312052 }
20322053 }
2054+
2055+ fn emit_snapshot_recovery_event (
2056+ store : & mut ( impl AsContextMut < Data = Ctx > + Send ) ,
2057+ snapshot_index : OplogIndex ,
2058+ succeeded : bool ,
2059+ error : Option < String > ,
2060+ ) {
2061+ store
2062+ . as_context_mut ( )
2063+ . data_mut ( )
2064+ . get_public_state ( )
2065+ . event_service ( )
2066+ . emit_event (
2067+ if succeeded {
2068+ InternalWorkerEvent :: snapshot_recovery_succeeded ( snapshot_index)
2069+ } else {
2070+ InternalWorkerEvent :: snapshot_recovery_failed (
2071+ snapshot_index,
2072+ error. unwrap_or_else ( || "unknown" . to_string ( ) ) ,
2073+ )
2074+ } ,
2075+ true ,
2076+ ) ;
2077+ }
20332078}
20342079
20352080enum SnapshotRecoveryResult {
@@ -2039,6 +2084,12 @@ enum SnapshotRecoveryResult {
20392084}
20402085
20412086impl < Ctx : WorkerCtx > DurableWorkerCtx < Ctx > {
2087+ pub ( crate ) fn end_call_snapshotting_function_if_active ( & mut self ) {
2088+ if self . state . snapshotting_mode . is_some ( ) {
2089+ self . end_call_snapshotting_function ( ) ;
2090+ }
2091+ }
2092+
20422093 pub ( crate ) fn register_open_websocket (
20432094 & mut self ,
20442095 rep : u32 ,
@@ -2601,6 +2652,14 @@ impl<Ctx: WorkerCtx> UpdateManagement for DurableWorkerCtx<Ctx> {
26012652 // While calling a snapshotting function (load/save), we completely turn off persistence
26022653 // In addition to the user-controllable persistence level we also skip writing the
26032654 // oplog entries marking the exported function call.
2655+ if self . state . snapshotting_mode . is_some ( ) {
2656+ warn ! (
2657+ "begin_call_snapshotting_function called while snapshotting is already active; \
2658+ leaving persistence level unchanged"
2659+ ) ;
2660+ return ;
2661+ }
2662+
26042663 let previous_level = self . state . persistence_level ;
26052664 self . state . snapshotting_mode = Some ( previous_level) ;
26062665 self . state . persistence_level = PersistenceLevel :: PersistNothing ;
0 commit comments