- idempotency built, reviewed, then reverted WHOLE to the tag archive/porch-idempotency. Not a design failure: it passed its gates. It provokes a C-runtime SIGSEGV in wo_arena_alloc/wo_str_new under concurrent call()-parked callers - the evidence for that attribution: over ten gate runs every failure was an idempotency leg and none was the limiter's, which drives the same pool through the same call/park machinery. The begin arm has 5x the allocation sites inside receive and moves a whole Req plus a Handler through the mailbox - before the split the suite reported 0 to 6 failures run to run; after it, five consecutive runs at 56 checks, 0 failures - PoolMsg loses digest/req/handler, and NullHandler/dummy_req/fresh_req go with them — every rate-limit count used to allocate a throwaway Req it never read - IdempotencyKey is KEPT and commented: the schema is settled and the digest-as-column decision cost a review round to get right - the limiter's saturation 503 has no leg of its own now (§19 drove Idempotent). Stated in the README rather than papered over — a deterministic leg needs a slow actor, and only the reverted arm was - new: porch 9 (idempotency, on hold) and language 41 (the arena crash, with the reproduction harness and the evidence that localises it) Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> (cherry picked from commit 79e6da4465133dc555e913c960d544ef1c7bedd8)
47 lines
No EOL
2.2 KiB
Text
47 lines
No EOL
2.2 KiB
Text
-- porch/middleware/store.wo — store-backed middleware tables.
|
|
-- Two purpose-shaped @table classes for rate limiting and idempotency.
|
|
-- Iteration 1 of the porch track.
|
|
|
|
-- Rate limiter: fixed-window counter.
|
|
-- Key format: "ip:192.168.1.1" or "principal:alice"
|
|
-- Window = start of current window in time.ticks (µs monotonic)
|
|
@table(name: "rate_limit_counters", index: [key])
|
|
class RateLimitCounter {
|
|
key: Text @unique
|
|
count: Int
|
|
window: Int
|
|
}
|
|
|
|
-- ============================================================================
|
|
-- KEPT DELIBERATELY, UNUSED TODAY.
|
|
--
|
|
-- Nothing in porch reads or writes this table right now. The idempotency
|
|
-- middleware that did was reverted on 2026-08-30 — not because the design was
|
|
-- wrong (it was built, reviewed and works) but because it provoked a C-runtime
|
|
-- crash: a SIGSEGV in wo_arena_alloc / wo_str_new under concurrent
|
|
-- call()-parked callers allocating heavily inside an actor's receive. Over ten
|
|
-- gate runs every failure belonged to an idempotency leg and none to the rate
|
|
-- limiter's, which drives the same pool through the same machinery but
|
|
-- allocates a fifth as much.
|
|
--
|
|
-- The table stays because the schema is settled and re-adding it would be
|
|
-- churn, not design: `digest` as its own column (never folded into the key, or
|
|
-- "same key, different body" becomes undetectable) is the one decision that
|
|
-- cost a review round to get right. The middleware, its actor arm and its gate
|
|
-- legs are whole in the tag `archive/porch-idempotency`, which is also the
|
|
-- reproduction harness for the runtime bug.
|
|
--
|
|
-- If the runtime bug is fixed and idempotency is NOT resumed, delete this.
|
|
-- ============================================================================
|
|
-- Idempotency: stored response for replay.
|
|
-- Key format: "idem:keyheader" or "idem:keyheader:sha256(method|path|body)"
|
|
-- Response = JSON-encoded Resp {status, headers, body} (headers allowlist:
|
|
-- content-type, location, etag, cache-control)
|
|
-- created_at = time.ticks when stored (µs monotonic) for lazy expiry
|
|
@table(name: "idempotency_keys", index: [key])
|
|
class IdempotencyKey {
|
|
key: Text @unique
|
|
response: Text
|
|
created_at: Int
|
|
digest: Text
|
|
} |