He-Pin opened a new issue, #3232:
URL: https://github.com/apache/pekko/issues/3232
### Motivation
When an EventSourcedBehavior actor is stopped during the snapshot loading
phase of recovery, the user-registered `PostStop` signal handler is never
invoked. This is inconsistent with every other recovery phase
(`RequestingRecoveryPermit`, `ReplayingEvents`, `Running`), all of which
forward `PostStop` to the user's signal handler. Users relying on `PostStop`
for cleanup (releasing resources, notifying other actors, etc.) will see their
handler silently skipped if the actor stops during snapshot loading.
### Current behavior
In `ReplayingSnapshot.scala`, the signal handler is composed using `.orElse`:
```scala
.receiveSignal(returnPermitOnStop.orElse {
case (_, PoisonPill) =>
stay(receivedPoisonPill = true)
case (_, signal) =>
if (setup.onSignal(setup.emptyState, signal, catchAndLog = true))
Behaviors.same
else Behaviors.unhandled
})
```
`returnPermitOnStop` (defined in `ExternalInteractions.scala`) matches
`PostStop` and `PreRestart` first:
```scala
protected def returnPermitOnStop = {
case (_, PostStop) =>
tryReturnRecoveryPermit("PostStop")
Behaviors.stopped
case (_, PreRestart) =>
tryReturnRecoveryPermit("PreRestart")
Behaviors.stopped
}
```
Since `returnPermitOnStop` is tried first via `.orElse`, it intercepts
`PostStop` (and `PreRestart`) and returns `Behaviors.stopped` without ever
calling `setup.onSignal`. The user's signal handler is never reached.
### Expected behavior
`PostStop` and `PreRestart` signals should be forwarded to the user's signal
handler via `setup.onSignal` during the snapshot loading phase, consistent with
all other recovery phases. The recovery permit should still be returned, but
only after the user handler has been invoked.
### Code evidence
-
`persistence-typed/src/main/scala/org/apache/pekko/persistence/typed/internal/ReplayingSnapshot.scala`
(line 88): `returnPermitOnStop.orElse` intercepts `PostStop` before the user
handler.
-
`persistence-typed/src/main/scala/org/apache/pekko/persistence/typed/internal/ExternalInteractions.scala`
(lines 148-156): `returnPermitOnStop` definition that catches
`PostStop`/`PreRestart` without calling `setup.onSignal`.
-
`persistence-typed/src/main/scala/org/apache/pekko/persistence/typed/internal/RequestingRecoveryPermit.scala`
(lines 70-76): Does NOT use `returnPermitOnStop`; forwards `PostStop` to
`setup.onSignal` correctly.
-
`persistence-typed/src/main/scala/org/apache/pekko/persistence/typed/internal/ReplayingEvents.scala`
(lines 122-129): Does NOT use `returnPermitOnStop`; forwards `PostStop` to
`setup.onSignal` correctly.
### Reproduction
1. Create an `EventSourcedBehavior` with a `receiveSignal` handler that
handles `PostStop`.
2. Ensure the actor has a snapshot available in the snapshot store.
3. Stop the actor while it is in the snapshot loading phase (e.g., by
sending `PoisonPill` immediately after spawn, or by having the snapshot store
respond slowly while the actor is stopped).
4. Observe that the `PostStop` handler is never invoked.
Suggested test: Add a test in the persistence-typed test suite that spawns
an `EventSourcedBehavior` with a `PostStop` signal handler, uses a
slow/controllable snapshot store, and verifies the signal handler is called
when the actor is stopped during snapshot loading.
### Impact
- Module: `pekko-persistence-typed`
- Affects any `EventSourcedBehavior` that registers a `PostStop` or
`PreRestart` signal handler and may be stopped during the snapshot loading
phase.
- The `PreRestart` signal is also affected by the same issue (intercepted
without user handler invocation).
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]