docs(porch-store): call replies are scalars — response goes via the table
- WO-E226: call's reply must be a copyable scalar, and every receive program-wide must declare the same return type. Verified by fixture: "call's reply type `Out` is not a copyable scalar" - the spec had the actor return the response object, which cannot cross the mailbox. Corrected: the actor stores the response and returns an outcome code; the middleware reads the row and builds the Resp - owner and duplicate now read the SAME durable row, so byte-identical replay is structural rather than careful copying - blocking, exactly-once execution and the mailbox queue are unchanged Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> (cherry picked from commit 77e06c1690b92d456a9bc53503695fdaa2b4b44e)
This commit is contained in:
parent
4be826f9ab
commit
88b61f7522
2 changed files with 26 additions and 10 deletions
|
|
@ -216,11 +216,18 @@ from Task 1.
|
||||||
- [ ] **Step 3 — the digest.** Compute a digest of method, path and body when
|
- [ ] **Step 3 — the digest.** Compute a digest of method, path and body when
|
||||||
`include_body` is set, and pass it alongside the bare key. Keep `use
|
`include_body` is set, and pass it alongside the bare key. Keep `use
|
||||||
json` — the file did not typecheck without it.
|
json` — the file did not typecheck without it.
|
||||||
- [ ] **Step 4 — the actor arm for the begin message.** Look the bare key up.
|
- [ ] **Step 4 — the actor arm for the begin message.** `call`'s reply must be
|
||||||
A hit whose digest matches returns the stored response. A hit whose
|
a copyable scalar (WO-E226 — a response object cannot cross the mailbox),
|
||||||
digest differs returns a refusal, answered as **422**. A miss runs the
|
so the arm returns an **outcome code** and the response travels through
|
||||||
handler, stores status, body and `content-type` with the digest and the
|
the table. Look the bare key up. A hit whose digest matches returns
|
||||||
current tick, and returns the response.
|
"replayed" without running the handler. A hit whose digest differs
|
||||||
|
returns "mismatch". A miss runs the handler, stores status, body and
|
||||||
|
`content-type` with the digest and the current tick, and returns
|
||||||
|
"executed". The middleware then reads the stored row and builds the
|
||||||
|
response from it — so owner and duplicate return the same durable row,
|
||||||
|
and byte-identical replay is structural rather than careful.
|
||||||
|
**Keep the return type identical to the one Task 2's count arm uses**;
|
||||||
|
WO-E226 also requires every `receive` program-wide to agree.
|
||||||
- [ ] **Step 5 — the replay allowlist.** Store and replay `content-type` only.
|
- [ ] **Step 5 — the replay allowlist.** Store and replay `content-type` only.
|
||||||
Never `Set-Cookie`, never `Date`. Cookies arrive in porch 2; this is
|
Never `Set-Cookie`, never `Date`. Cookies arrive in porch 2; this is
|
||||||
cheap now and expensive to retrofit after.
|
cheap now and expensive to retrofit after.
|
||||||
|
|
|
||||||
|
|
@ -123,11 +123,20 @@ History. A keyed request does not run its own handler; it calls its pool actor,
|
||||||
passing the request and the route's `Handler`, and the actor invokes the
|
passing the request and the route's `Handler`, and the actor invokes the
|
||||||
handler inside its own `receive`.
|
handler inside its own `receive`.
|
||||||
|
|
||||||
| Actor state for that key | What `receive` returns |
|
**The reply is a scalar, so the response travels through the table.** `call`'s
|
||||||
| --- | --- |
|
reply must be a copyable scalar (WO-E226), and every `receive` program-wide
|
||||||
| A stored response exists, digest matches | the stored response; the handler never runs |
|
must declare the same return type. A response object cannot cross the mailbox.
|
||||||
| A stored response exists, digest differs | a refusal verdict, answered as 422 |
|
So the actor stores the response in the `@table` and returns an outcome code;
|
||||||
| No record | run the handler here, store the response, return it |
|
the middleware reads the stored row and builds the response from it. Owner and
|
||||||
|
duplicate therefore read the *same durable row* — which is what the store is
|
||||||
|
for, and it means the replay is byte-identical by construction rather than by
|
||||||
|
careful copying.
|
||||||
|
|
||||||
|
| Actor state for that key | Actor does | Returns |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| A stored response exists, digest matches | nothing; the handler never runs | "replayed" |
|
||||||
|
| A stored response exists, digest differs | nothing | "mismatch", answered as 422 |
|
||||||
|
| No record | runs the handler here and stores the response | "executed" |
|
||||||
|
|
||||||
There is no fourth row, and that is the point. **A duplicate arriving while the
|
There is no fourth row, and that is the point. **A duplicate arriving while the
|
||||||
owner's handler runs waits in the mailbox**, because an actor processes one
|
owner's handler runs waits in the mailbox**, because an actor processes one
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue