You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Phase 3 of the horizontal scale-out plan (spec/scale-out.md §15): bring the Tier 2 (actor) cohort — solid-pod, activitypub, remotestorage, webauthn, atproto-pds — up in central storage mode, with the Durable Object guarantees enforced across replicas. Builds on #428/#430 (LibsqlKv) and #431 (central mode + Tier 1); fleet pollers/cron/drain are phase 4.
What
Per spec/scale-out.md §6, adopting @dwk/deno-host's implemented per-request-lease model unchanged (v1):
DO namespace over the coordination store: compose @dwk/deno-host's createDurableObjectNamespace with LibsqlKv — per-event lease acquire/release (["dwk_lease", className, idHex], CAS + expireIn crash safety net), process-local per-id chain within a replica, KV lease across replicas. LeaseContendedError maps to 503 + Retry-After at the host.
getStorageClient(idHex) → per-object libSQL database via embedded replica: the libsql npm package's sync client against storage.libsql.replicaDir scratch files (a rebuildable cache; ephemeral disk suffices). This is the first hard dependency on the libsql native module loading on Node in this repo — verify early.
The sync-before-serve rule (§6.2 — load-bearing): after acquiring an id's lease and before the event runs, the embedded replica MUST sync from the primary — a different replica may have written since this one last held the id; skipping the sync serves stale reads and is a correctness bug. Note: createDurableObjectNamespace currently calls getStorageClient once per id and caches the instance, with no per-dispatch hook — this likely needs a small, contract-reviewed extension to @dwk/deno-host (e.g. an onLeaseAcquired/sync callback in DurableObjectNamespaceOptions), amended in the same PR per the host-contract §8 growth discipline's spirit.
Alarms: the KV-indexed due/by-id schedule works as-is over LibsqlKv (proven in feat(server): add LibsqlKv, the DenoKvLike coordination store over libSQL #430's tests); wire pollAlarms to a per-replica jittered interval timer (~1 s default — real timers, better than platform-cron granularity), claim-based so concurrent pollers are safe. Sweep LibsqlKv.sweepExpired from the same tick.
WebSockets (§6.4, v1 stance): sockets stay pinned to the terminating replica via the existing web-socket-upgrade.ts bridging; document the lease/socket window and the LB-affinity recommendation rather than architecting around it (v2 residency is the eventual fix — see the phase 6 issue).
Testing (spec §14 items 2–3)
Two DwkServer instances sharing one LibsqlKv + SQL backing + S3 fake:
Racing same-pod writes from both replicas serialize — one wins the lease, the other queues or 503s; never interleaved state.
Sync-before-serve regression: write a pod resource via replica A, immediately read via replica B, assert B sees it (the test that fails if the sync rule is skipped).
An alarm scheduled via A fires exactly once though both replicas poll (activitypub Accept-delivery retry as the lifecycle).
Kill replica A mid-request (simulate: never release) → B recovers the id after leaseTtlMs.
Tier 2 lifecycles across replicas, modeled on the existing phase5-*.integration.test.ts suite.
Phase 3 of the horizontal scale-out plan (spec/scale-out.md §15): bring the Tier 2 (actor) cohort —
solid-pod,activitypub,remotestorage,webauthn,atproto-pds— up incentralstorage mode, with the Durable Object guarantees enforced across replicas. Builds on #428/#430 (LibsqlKv) and #431 (central mode + Tier 1); fleet pollers/cron/drain are phase 4.What
Per spec/scale-out.md §6, adopting
@dwk/deno-host's implemented per-request-lease model unchanged (v1):@dwk/deno-host'screateDurableObjectNamespacewithLibsqlKv— per-event lease acquire/release (["dwk_lease", className, idHex], CAS +expireIncrash safety net), process-local per-id chain within a replica, KV lease across replicas.LeaseContendedErrormaps to503+Retry-Afterat the host.getStorageClient(idHex)→ per-object libSQL database via embedded replica: thelibsqlnpm package's sync client againststorage.libsql.replicaDirscratch files (a rebuildable cache; ephemeral disk suffices). This is the first hard dependency on thelibsqlnative module loading on Node in this repo — verify early.createDurableObjectNamespacecurrently callsgetStorageClientonce per id and caches the instance, with no per-dispatch hook — this likely needs a small, contract-reviewed extension to@dwk/deno-host(e.g. anonLeaseAcquired/sync callback inDurableObjectNamespaceOptions), amended in the same PR per the host-contract §8 growth discipline's spirit.LibsqlKv(proven in feat(server): add LibsqlKv, the DenoKvLike coordination store over libSQL #430's tests); wirepollAlarmsto a per-replica jittered interval timer (~1 s default — real timers, better than platform-cron granularity), claim-based so concurrent pollers are safe. SweepLibsqlKv.sweepExpiredfrom the same tick.web-socket-upgrade.tsbridging; document the lease/socket window and the LB-affinity recommendation rather than architecting around it (v2 residency is the eventual fix — see the phase 6 issue).Testing (spec §14 items 2–3)
Two
DwkServerinstances sharing oneLibsqlKv+ SQL backing + S3 fake:activitypubAccept-delivery retry as the lifecycle).leaseTtlMs.phase5-*.integration.test.tssuite.Out of scope
References