Skip to content

EventSourcedBehavior PostStop handler not invoked during snapshot replay phase #3250

Description

@He-Pin

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions