Found by adversarial audit @ 261d00b, survived refutation (conf 0.88).
Mechanism: timeoutMiddleware (src/middleware/timeout.ts:28-41) resolves Promise.race, returns 504, and attaches only a log-and-swallow .catch — the handler keeps running with no cancellation. Multi-step mutating routes have no generation guard between steps: DELETE /delete-namespace (namespaces.ts:37-40: dropIndex → deleteKeysByPattern → SREM×2), POST /reset (reset.ts:82-86), /rename-namespace (namespaces.ts:88-113). deleteKeysByPattern (keys.ts:91-112) is a serial SCAN(100)+DEL loop capped at 10k iterations — >30s realistic at ~10^5+ keys or during any Redis stall (enableOfflineQueue:true redis.ts:27 also queues commands across reconnects).
Data-loss interleave: caller gets 504 on delete-namespace of large N → assumes failure → re-upserts into N (recreates idx:N, SADDs registry) → zombie's open SCAN cursor DELs the fresh v:N:* keys and its trailing SREM unregisters N again. Client saw 200 for the upsert; data silently gone. Upserts are idempotent-safe; delete/reset/rename are not.
Fix direction: cancellation/generation token checked between steps of multi-step routes, or abort work when the response has been sent; optionally serialize destructive namespace operations.
Found by adversarial audit @ 261d00b, survived refutation (conf 0.88).
Mechanism:
timeoutMiddleware(src/middleware/timeout.ts:28-41) resolvesPromise.race, returns 504, and attaches only a log-and-swallow.catch— the handler keeps running with no cancellation. Multi-step mutating routes have no generation guard between steps: DELETE /delete-namespace (namespaces.ts:37-40: dropIndex → deleteKeysByPattern → SREM×2), POST /reset (reset.ts:82-86), /rename-namespace (namespaces.ts:88-113).deleteKeysByPattern(keys.ts:91-112) is a serial SCAN(100)+DEL loop capped at 10k iterations — >30s realistic at ~10^5+ keys or during any Redis stall (enableOfflineQueue:trueredis.ts:27 also queues commands across reconnects).Data-loss interleave: caller gets 504 on delete-namespace of large N → assumes failure → re-upserts into N (recreates idx:N, SADDs registry) → zombie's open SCAN cursor DELs the fresh v:N:* keys and its trailing SREM unregisters N again. Client saw 200 for the upsert; data silently gone. Upserts are idempotent-safe; delete/reset/rename are not.
Fix direction: cancellation/generation token checked between steps of multi-step routes, or abort work when the response has been sent; optionally serialize destructive namespace operations.