Use staged persistence effect APIs to restrict unsafe chaining
I maintainer di solito rispondono entro 1 giorno
Nessuno ha ancora preso questa issue.
Valutazione
- Difficoltà
- 5/5
- Tempo stimato
- Più di una settimana
- Idoneità per principianti
- 25/100
- Tipo di issue
- Funzionalità
- Chiarezza
- Abbastanza chiara
- Stato di attività
- Attiva
- Ambito
- backend-api-design
Direzione di ricerca
Start from the Pekko persistence effect sources: the ReplyEffect, Effect and ReplyEffectWithStop traits in Scala and Java EffectBuilder. Read the #3682 review and the isolated prototype to learn the staged hierarchy. 'Done' means legal chains like reply().thenStop() compile, illegal stash().thenStop() reject, and Scala/Java tests with @since 2.0.0 pass in CI.
Scritto dal modello di indicizzazione a partire dal testo della issue.
Descrizione
Motivation
Adding thenStop directly to ReplyEffect also exposes Effect.stash().thenStop() in both persistence models and both DSLs. This stashes the current command and immediately stops the actor, discarding that command without a reply. Enforced replies do not reject it because stashing already returns a ReplyEffect.
This was identified in the review of #3682. Instead of accepting the combination or throwing when the effect is constructed, we can express the permitted operations through stages in the public API.
Proposed staged API
Use a session-type-inspired staged API (also commonly described as typestate): each returned type exposes only the operations supported at that stage. This is a restriction on effect construction, not a full session type system for actor communication.
For thenStop, introduce ReplyEffectWithStop as a subtype of ReplyEffect. The name describes the added stop capability:
trait ReplyEffect[+Event, State] extends Effect[Event, State] {
def thenUnstashAll(): ReplyEffect[Event, State]
}
trait ReplyEffectOps[
+Event, State, +Self <: ReplyEffectOps[Event, State, Self]]
extends ReplyEffect[Event, State] {
override def thenUnstashAll(): Self
}
trait ReplyEffectWithStop[+Event, State]
extends ReplyEffectOps[Event, State, ReplyEffectWithStop[Event, State]] {
def thenStop(): ReplyEffectWithStop[Event, State]
}
| Operation | Returned stage |
|---|---|
reply, thenReply |
ReplyEffectWithStop |
noReply, thenNoReply |
ReplyEffectWithStop |
stash |
ReplyEffect |
Shared operations use an F-bounded Self, following the Netty AbstractBootstrap pattern. Each stage binds Self once, so it does not need to redeclare every common chaining method. Generic helpers can return Self and preserve the stage.
ReplyEffectOps extends ReplyEffect, retaining both the broad command-handler contract and the specific Self stage in generic helpers. Declaration-only Java Effect, ReplyEffect, and ReplyEffectWithStop do not require an EffectImpl self-type constraint. The existing Java EffectBuilder keeps its constraint because its concrete callback/reply method bodies rely on that implementation type. Scala and Java generic-helper probes pass with this hierarchy.
The names distinguish responsibilities: ReplyEffect is the general reply decision, ReplyEffectOps defines shared stage-preserving operations, and ReplyEffectWithStop adds the stop capability.
The base ReplyEffect must not declare thenStop: a subtrait cannot hide an inherited operation. Scala and Java, and event sourcing and durable state, should follow the same rules. Public traits should contain declarations; concrete effect implementations implement the methods.
Expected behavior and boundaries
reply(...).thenStop()andpersist(...).thenReply(...).thenStop()compile.reply(...).thenUnstashAll().thenStop()retains the stop capability.stash().thenStop()andstash().thenUnstashAll().thenStop()do not compile.- A helper that may stash returns
ReplyEffect, so its caller cannot appendthenStop. A helper whose callers need that capability must explicitly returnReplyEffectWithStop. noReplydeliberately opts out of replying and can still be followed by stopping.- This only constrains the current effect chain. Stopping can still discard commands stashed by earlier commands; it cannot prove that the actor's stash is empty or that a recipient has processed a reply.
Compatibility and validation
An isolated prototype using the real effect sources compiled on Scala 2.13 and Java, accepted legal chains and rejected illegal ones. Directly narrowing existing factory return types changes JVM descriptors: a Java client compiled against the old EffectFactories.noReply(): ReplyEffect failed with NoSuchMethodError on that prototype. A separate one-method bridge experiment preserved that call, but introduced compatibility machinery.
For the proposed 2.0.0 change, prefer the direct staged API and explicitly document the intentional binary incompatibility, rather than adding bridge machinery. Any MiMa exceptions must identify the exact changed signatures and the 2.0.0 rationale. Full repository validation remains required; the isolated prototype is not a substitute for CI.
#3682 is being revised as the first concrete application. Once it is merged, a separate thenRun proposal should follow the same stage design, including whether callbacks are supported after stashing and how capability is preserved through callback chaining. Avoid introducing thenRun into #3682.
Acceptance includes positive and negative compilation coverage, runtime reply/stop ordering, Scala/Java parity, documentation, @since 2.0.0, and remote CI including binary compatibility checks with only the agreed intentional changes excluded.
Prior art
jOOQ's DSL API uses an interface hierarchy to express legal construction sequences. Its guidance on step types also highlights the composition and compatibility costs of exposing those types in helper signatures. Those tradeoffs should be explicit in Pekko's design.
Refs #3682.
- Lingua principale
- Scala
- Stelle
- 1.7k
- Fork
- 212
- Merge medio
- 21h 21m
- PR unite (30g)
- 99
Preparare l'ambiente
- Nessun Dockerfile né file Docker Compose
- Nessun modello di pull request
- Leggi la guida per i contributori
Come iniziare
- Leggi tutta la issue e poi la guida ai contributi del progetto.
- Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
- Fai un fork del repository e lavora su un branch.
- Apri una pull request che faccia riferimento al numero della issue.
Altre issue di apache/pekko
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 85/100
I maintainer di solito rispondono entro 1 giorno
-
documentation
Difficoltà 2/5 1-3 ore Idoneità per principianti 68/100
apache/pekko#1840 · 8 commenti ·
I maintainer di solito rispondono entro 1 giorno
-
Add wildcard bounds to Java DSL parameters that are needlessly invariantForse già presa Una pull request collegata a questa issue è aperta o già unita. Aperta
Difficoltà 4/5 3-5 giorni Idoneità per principianti 52/100
apache/pekko#3712 · 4 commenti ·
I maintainer di solito rispondono entro 1 giorno
-
Coordinator singleton lease loss permanently stops the local ShardRegion while the node is still UpForse già presa @He-Pin l’ha presa 3 giorni fa. Apertabug cluster
Difficoltà 4/5 3-5 giorni Idoneità per principianti 25/100
I maintainer di solito rispondono entro 1 giorno
-
enhancement flaky make-tests-easier-to-run
Difficoltà 4/5 3-5 giorni Idoneità per principianti 45/100
I maintainer di solito rispondono entro 1 giorno
Tutte le issue di apache/pekko
Issue simili
-
Difficoltà 1/5 Meno di un'ora Idoneità per principianti 73/100
lichess-org/lila#22298 ·
I maintainer di solito rispondono entro 1 giorno
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 82/100
I maintainer di solito rispondono entro 1 giorno
-
bug 🐞 Untriaged user issue
Difficoltà 2/5 1-3 ore Idoneità per principianti 78/100
valkey-io/valkey-glide#7443 ·
I maintainer di solito rispondono entro 1 giorno
-
OpenAPI: PowSolutions requires pk, w and d, but a V2 solution needs only nForse già presa Una pull request collegata a questa issue è aperta o già unita. ApertaA-api C-bug S-ready-for-human
Difficoltà 2/5 1-3 ore Idoneità per principianti 75/100
ergoplatform/ergo#2851 ·
I maintainer di solito rispondono entro 2 giorni
-
? - Needs Triage audit_4.2.0 feature request test
Difficoltà 2/5 1-3 ore Idoneità per principianti 87/100
NVIDIA/cudf-spark#16614 ·
I maintainer di solito rispondono entro 1 giorno