host: durable lazy sessions — logins survive a restart
Sessions were in-memory, so a restart logged everyone out (same class as the relation wipe). Move them to the durable store, but LAZILY so anonymous/crawler traffic doesn't spam it: session/create mints a sid with no row; the row appears on the first session/set (a login). A per-boot epoch (one durable write at startup, host/session-init!) keeps sids unique across restarts without a write per request. - lib/host/session.sx: lazy backend (create = no row, set = create row, exists = row written) + epoch/in-memory-counter sid generation. - serve.sh: point the session store at the durable backend + host/session-init!. - blog.sx: host/current-principal is now a durable read, so host/auth-footer (home + post footers) had to move OUT of the quasiquote into let bindings — a perform during page-tree build raises VmSuspended (the whole site 500'd for a beat). Principal computed once per page. - 2 session tests: create writes no row, set creates the row. 249/249. Verified live: site renders (anon + authed), login + footer survive a container force-recreate. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -20,26 +20,37 @@
|
||||
|
||||
;; ── keys ────────────────────────────────────────────────────────────
|
||||
(define host/-sess-key (fn (sid) (str "session:" sid)))
|
||||
(define host/-sess-counter-key "session:-counter")
|
||||
(define host/-sess-epoch-key "session:-epoch")
|
||||
|
||||
;; mint the next sid from a persisted counter (signature guards guessability)
|
||||
;; sid generation: a per-BOOT epoch (one durable write at startup) + an in-memory
|
||||
;; counter. The epoch keeps sids unique across restarts WITHOUT a write per
|
||||
;; request, so anonymous traffic costs no disk. host/session-init! bumps the epoch
|
||||
;; on boot (serve.sh); without it (e.g. tests) epoch 0 is fine within one process.
|
||||
(define host/session-epoch 0)
|
||||
(define host/session-ctr 0)
|
||||
(define host/session-init!
|
||||
(fn ()
|
||||
(let ((e (+ 1 (or (persist/backend-kv-get host/session-store host/-sess-epoch-key) 0))))
|
||||
(begin
|
||||
(persist/backend-kv-put host/session-store host/-sess-epoch-key e)
|
||||
(set! host/session-epoch e)
|
||||
(set! host/session-ctr 0)))))
|
||||
(define host/-sess-next-sid
|
||||
(fn ()
|
||||
(let ((n (+ 1 (or (persist/backend-kv-get host/session-store host/-sess-counter-key) 0))))
|
||||
(begin
|
||||
(persist/backend-kv-put host/session-store host/-sess-counter-key n)
|
||||
(str "s" n)))))
|
||||
(begin
|
||||
(set! host/session-ctr (+ host/session-ctr 1))
|
||||
(str "s" host/session-epoch "-" host/session-ctr))))
|
||||
|
||||
;; ── backend io fn: dispatch session/* ops onto the persist KV ───────
|
||||
;; LAZY: session/create mints a sid but writes NO row, so an anonymous request
|
||||
;; (which never sets a field) leaves no durable trace — the store isn't spammed by
|
||||
;; crawlers. The row appears on the first session/set (i.e. login), so a logged-in
|
||||
;; session persists and survives a restart; session/exists is "has a written row".
|
||||
(define host/session-backend
|
||||
(fn (op)
|
||||
(let ((kind (get op :op)))
|
||||
(cond
|
||||
((= kind "session/create")
|
||||
(let ((sid (host/-sess-next-sid)))
|
||||
(begin
|
||||
(persist/backend-kv-put host/session-store (host/-sess-key sid) {})
|
||||
sid)))
|
||||
((= kind "session/create") (host/-sess-next-sid))
|
||||
((= kind "session/exists")
|
||||
(persist/backend-kv-has? host/session-store (host/-sess-key (get op :sid))))
|
||||
((= kind "session/get")
|
||||
|
||||
Reference in New Issue
Block a user