Skip to content

Commit 63bec6d

Browse files
cardmagicclaude
andcommitted
docs: describe the result lookup
The branch added an API and documented none of it. The changelog, the architecture guide, the pruning note, and the roadmap entry the issue quotes now say what exists, including that a result is stored for sync delivery only and that a pruned message still answers nil. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
1 parent acc7e03 commit 63bec6d

4 files changed

Lines changed: 35 additions & 2 deletions

File tree

‎CHANGELOG.md‎

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,22 @@
22

33
## Unreleased
44

5+
- Find a message whose reference a caller lost.
6+
`SolidObjects.client.find_by(request_id:)` answers a request id, which is
7+
unique across the table, and `reference.find_by(idempotency_key:)` answers a
8+
key, which is unique per instance, so the receiver supplies the scope the key
9+
needs. Naming neither key, naming both, or naming an idempotency key without a
10+
reference raises `ArgumentError`.
11+
- Authorize every lookup with the hook the original call ran, against the stored
12+
operation and arguments, because a request id is not a capability. An absent
13+
row, an actor this process no longer registers, and a caller the policy
14+
refuses all return `nil`, so a lookup cannot be used to ask whether a request
15+
id exists.
16+
- Add `MessageReference#outcome`, which reports the status, the result, the
17+
persisted error, the rejection, and the attempt count, so a terminal failure
18+
answers as well as a success. A result is stored for `sync` delivery only, so
19+
an asynchronous message reports its status and error and no result.
20+
521
- Retry a dead effect or broadcast. `SolidObjects.dead_letters` keeps its
622
message meaning and answers `effects` and `broadcasts`, so the kind rides on
723
the receiver. `retry` returns a dead row to pending with a zero attempt count

‎docs/architecture.md‎

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -424,6 +424,16 @@ outer commit, and callers timing out on work they indirectly block.
424424
waiting and immediately returns a `MessageReference`. Runtime workers process
425425
it normally.
426426

427+
A caller that loses that reference rebuilds one. `SolidObjects.client.find_by`
428+
answers a request id, which is unique across the table, and
429+
`reference.find_by` answers an idempotency key, which is unique per instance.
430+
Each lookup runs the authorization hook the original call ran, against the
431+
stored operation and arguments, and answers `nil` for an absent row, an
432+
unregistered actor, and a refused caller alike, so it cannot be used to ask
433+
whether a request id exists. `MessageReference#outcome` reports the status, the
434+
result, the persisted error, the rejection, and the attempt count. A result is
435+
stored for `sync` delivery only.
436+
427437
An executing caller receives an inline after-commit callback error even though
428438
the turn committed. An independently waiting caller observes the durable
429439
result and may return before that callback raises in the worker. Completed
@@ -867,7 +877,7 @@ All backends use unique identity and sequence constraints, short transactions, a
867877
12. **How are leases renewed?** Conditional database update by instance, owner, generation, and unexpired lease.
868878
13. **How does graceful shutdown work?** Stop claims, finish current turn within timeout, release cached leases, stop heartbeat, mark process stopped.
869879
14. **How does synchronous invocation work across processes?** The caller first tries to claim and execute the actor locally. If another process owns it, a wake-up adapter prompts a durable result query and bounded polling remains the fallback.
870-
15. **What happens after caller timeout?** A committed message continues and its eventual result can be recovered with the timeout's authorized message reference. An enqueue timeout leaves no message. Running Ruby code is not preempted.
880+
15. **What happens after caller timeout?** A committed message continues and its eventual result can be recovered with the timeout's authorized message reference, or with `find_by` from the request id or the idempotency key when that reference is gone. An enqueue timeout leaves no message. Running Ruby code is not preempted.
871881
16. **How are results cleaned up?** `prune_messages` deletes eligible terminal history in bounded batches after global or per-actor retention. It previews by default and preserves live work, dead letters, retry links, and unfinished outboxes.
872882
17. **How are large mailboxes managed?** The implemented controls are the per-actor mailbox cap, payload caps, and fair activation yields; rate and global admission controls remain roadmap work.
873883
18. **How are completed messages pruned?** Operators schedule the dry-run-reviewed `prune_messages --execute` command. Solid Objects does not run deletion automatically.

‎docs/operations.md‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -543,6 +543,11 @@ broadcasts, and other message-owned rows. Choose a cutoff longer than every
543543
`sync` timeout because a caller whose result row disappears can no longer
544544
observe it.
545545

546+
`find_by` reads the same rows, so a lookup answers only while the message it
547+
names survives retention. A pruned message and one that never existed both
548+
answer `nil` today, which is why a cutoff longer than the window in which a
549+
caller may retry matters.
550+
546551
Actor expiration is disabled by default. `prune_instances` considers only
547552
actor types listed in `instance_retention_by_actor_type`, excludes active or
548553
paused actors, and preserves ready/claimed mailbox work, scheduled reminders,

‎docs/roadmap.md‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -197,7 +197,9 @@
197197

198198
## Next milestones
199199

200-
1. Add result lookup by request ID and broader deadlock retry classification.
200+
1. Broaden deadlock retry classification. Result lookup by request ID and by
201+
idempotency key is implemented; what remains is telling a pruned message
202+
from one that never existed.
201203
2. Add Turbo append intents.
202204
3. Add distributed rate limits, global admission hooks, and cache-capacity
203205
eviction.

0 commit comments

Comments
 (0)