mirror of
https://github.com/hanzo-apps/dataroom.git
synced 2026-10-30 14:47:36 +00:00
Three RED-blessed non-blocking follow-ups on the KV-optional work. 1. Drop the non-atomic getdel() shim in lib/redis.ts. It shadowed BOTH backends' native atomic GETDEL with a get->del pipeline (other commands interleave between GET and DEL), reintroducing the one-time login-code double-use race that fetchAndDeleteLoginCodeData relies on GETDEL to prevent. ioredis maps getdel to the single-round-trip Redis GETDEL command; MemoryKV.getdel now reads+deletes in one un-yielded step (was `await this.get()` then delete — the await yielded the event loop, letting two racers both observe the value). 2. Bound MemoryKV growth. Lazy on-read eviction never fires for write-once- never-read keys (rl:<ip> rate-limit counters, one-shot tokens), so the map grew unbounded until the pod OOMKilled. Add sweepExpired() (one O(n) active- expire pass) run on an unref'd 60s timer, started by lib/redis.ts for the server singleton only (edge-gated; no test imports that module, so unit runs stay timer-free). MemoryKV stays pure (zero imports). 3. Document in .env.example that re-enabling multi-replica REQUIRES KV_URL (single-replica-by-construction otherwise: SQLite-on-RWO + in-process KV). The one-time non-silent in-process warning already exists in lib/kv/select.ts; no metrics surface exists, so log + doc is sufficient. Tests: +3 cases (getdel atomicity under concurrent race; 100k-key sweep reclaims to 0; sweep retains live/no-TTL keys). 28 pass, 3 skip (select.ts skips without ioredis in a bare checkout), 0 fail. Co-authored-by: Hanzo Dev <[email protected]>