examples: make the at-least-once duplicate observable at a sink - #26
Conversation
Implements #23. The delivery contract promises at-least-once and the docs tell external systems to hold stable idempotency keys, but no artifact showed the duplicate arriving anywhere with deduplication deliberately off. A contract clause nobody can observe firing is decoration; this demo lets anyone watch the clause fire and watch the documented remedy absorb it. examples/at-least-once stages one actor turn whose effect writes to an external sink file. The first effect worker crashes between the sink write and the acknowledgement; a second worker reclaims the stale effect after the liveness threshold and delivers again. With deduplication off the sink reads 2, both deliveries carrying the same stable effect id at attempts 1 and 2. With a guard on that id the sink reads 1. The actor state commits exactly once in both runs, which is the sharpest line of the proof: the state machine kept its exactly-once story while the outside world saw two. pnpm run test:at-least-once runs it; CI runs it beside the recovery demo in both the quality and floor jobs; docs/correctness.md links it from the at-least-once limitation it makes observable. The sink module carries unit coverage for both guard modes.
Greptile SummaryThis PR adds an executable demonstration of at-least-once effect delivery and sink-side deduplication.
Confidence Score: 5/5The PR appears safe to merge. No blocking failure remains. Important Files Changed
Sequence DiagramsequenceDiagram
participant A as Actor worker
participant DB as Durable database
participant W1 as Crashing effect worker
participant S as External sink
participant W2 as Recovery effect worker
A->>DB: Commit actor state and effect
W1->>DB: Claim effect (attempt 1)
W1->>S: Record delivery
W1--xDB: Crash before acknowledgement
W2->>DB: Reclaim stale effect (attempt 2)
W2->>S: Record or deduplicate by effect ID
W2->>DB: Complete effect
Reviews (2): Last reviewed commit: "fix: stop the demo sink from hiding its ..." | Re-trigger Greptile |
|
Ruby counterpart is open: cardmagic/solid-objects-ruby#51. Same proof against the gem: |
Greptile flagged two ways the at-least-once proof could report success without proving anything. readSink turned every read failure into an empty sink, so a damaged or unreadable file looked identical to a first run. The deduplication phase would then forget the effect id it needs, append the replay as if it were the first delivery, and still satisfy the assertion that the sink holds one delivery. Only ENOENT means an empty sink now; every other error propagates. Two tests cover it: a truncated JSON file and a path that is a directory. The effect worker exited 0 when its 200 claim attempts all came back empty, and the parent reads that exit status as proof that the delivery completed and was acknowledged. It now fails when nothing was processed, so the recovery assertion means what it claims. The Ruby counterpart already had both properties, so this brings the two runtimes back to the same shape rather than moving them apart.
|
Both findings were valid and are fixed in af82d86. Preserve non-missing sink errors. Recovery can exit without processing. The effect worker now throws Checked the Ruby counterpart in cardmagic/solid-objects-ruby#51 for the same two defects. Neither is present:
|
|
@greptileai review |
Closes #23.
This PR makes the at-least-once clause observable, and the documented remedy observable absorbing it.
The demo
examples/at-least-once(run withpnpm run test:at-least-once) stages one actor turn whose effect writes to an external sink file, then runs the crash choreography twice:process.exitbetween the external write andcompleteEffect). A second worker reclaims the stale effect past the liveness threshold and delivers again. The sink reads 2 — and both deliveries carry the same stable effect id, at attempts 1 and 2, which is exactly the handle the remedy needs.In both runs the actor state commits exactly once — the sharpest line of the proof: the state machine keeps its exactly-once story while the outside world sees two, unless the sink holds the key.
Demo output:
{ "duplicate": { "stateCommits": 1, "sinkDeliveries": 2, "sameEffectId": true, "attempts": [1, 2] }, "remedy": { "stateCommits": 1, "sinkDeliveries": 1 } }Wiring
pnpm run test:at-least-oncescript; CI runs it besidetest:recoveryin both the quality and floor jobs, so the clause is proven on every push and on the Node floor.docs/correctness.mdlinks the demo from the at-least-once limitation it makes observable.