Motivation
When a typed EventSourcedBehavior is stopped during the snapshot loading phase (ReplayingSnapshot), the user's PostStop signal handler is never invoked. This is inconsistent with the event replay phase (ReplayingEvents), which correctly calls the user handler. The same issue affects PreRestart.
Current Behavior
In ReplayingSnapshot.scala:88, the signal handler is composed as:
.receiveSignal(returnPermitOnStop.orElse {
case (_, signal) =>
if (setup.onSignal(setup.emptyState, signal, catchAndLog = true)) Behaviors.same
else Behaviors.unhandled
})
returnPermitOnStop (defined in ExternalInteractions.scala:148-156) intercepts PostStop and PreRestart:
protected def returnPermitOnStop = {
case (_, PostStop) =>
tryReturnRecoveryPermit("PostStop")
Behaviors.stopped // stops without calling setup.onSignal
}
Due to PartialFunction.orElse semantics, when returnPermitOnStop matches (which it does for PostStop/PreRestart), the fallback branch that calls setup.onSignal is never evaluated. The user's signal handler is silently skipped.
Comparison with ReplayingEvents
ReplayingEvents.scala:88-95, 217-223 correctly handles PostStop:
case (_, PostStop) =>
onPostStopInEffect(state) // calls both tryReturnRecoveryPermit AND setup.onSignal
Behaviors.stopped
Expected Behavior
ReplayingSnapshot should call setup.onSignal(state, PostStop, catchAndLog = true) before returning Behaviors.stopped, matching the behavior of ReplayingEvents.
Impact
Users relying on PostStop cleanup logic (resource release, metric reporting, child actor stopping) will have that logic silently skipped if the actor is stopped during snapshot loading.
Environment
- Pekko Persistence Typed (any version)
ReplayingSnapshot.scala:88, ExternalInteractions.scala:148-156
References
None
Motivation
When a typed
EventSourcedBehavioris stopped during the snapshot loading phase (ReplayingSnapshot), the user'sPostStopsignal handler is never invoked. This is inconsistent with the event replay phase (ReplayingEvents), which correctly calls the user handler. The same issue affectsPreRestart.Current Behavior
In
ReplayingSnapshot.scala:88, the signal handler is composed as:.receiveSignal(returnPermitOnStop.orElse { case (_, signal) => if (setup.onSignal(setup.emptyState, signal, catchAndLog = true)) Behaviors.same else Behaviors.unhandled })returnPermitOnStop(defined inExternalInteractions.scala:148-156) interceptsPostStopandPreRestart:Due to
PartialFunction.orElsesemantics, whenreturnPermitOnStopmatches (which it does forPostStop/PreRestart), the fallback branch that callssetup.onSignalis never evaluated. The user's signal handler is silently skipped.Comparison with ReplayingEvents
ReplayingEvents.scala:88-95, 217-223correctly handles PostStop:Expected Behavior
ReplayingSnapshotshould callsetup.onSignal(state, PostStop, catchAndLog = true)before returningBehaviors.stopped, matching the behavior ofReplayingEvents.Impact
Users relying on
PostStopcleanup logic (resource release, metric reporting, child actor stopping) will have that logic silently skipped if the actor is stopped during snapshot loading.Environment
ReplayingSnapshot.scala:88,ExternalInteractions.scala:148-156References
None