Summary
Only the ingest worker ever calls Cache.Invalidate. Admin mutations (DELETE/UPDATE/TRUNCATE/ALTER via /v1/admin/query — the sanctioned mutation path) bump nothing, so /v1/query keeps serving pre-mutation cached rows until TTL expiry.
Detail
internal/ingest/worker.go:488 is the sole Cache.Invalidate caller in the tree.
internal/api/query.go (admin raw-SQL proxy) holds no cache reference.
QueryTimeToTTL allows up to 1h (floor 10s).
Scenario: admin performs a GDPR erasure DELETE FROM users WHERE id = ... via POST /v1/admin/query; POST /v1/query continues returning the deleted rows from LocalCache for up to an hour, with no operator recourse short of restart.
Fix direction
Give the admin-query handler a cache handle and invalidate the affected table(s) after a successful mutation (best-effort table extraction, mirroring the ingest path), or bypass/short-TTL the cache for tables recently mutated.
Found in a repo-wide audit; verified by code trace. Distinct from #382 (ingest-path version race) and #362 (MV writes vs SSE — same "only ingest drives the reactive layer" root cause, different surface).
Summary
Only the ingest worker ever calls
Cache.Invalidate. Admin mutations (DELETE/UPDATE/TRUNCATE/ALTERvia/v1/admin/query— the sanctioned mutation path) bump nothing, so/v1/querykeeps serving pre-mutation cached rows until TTL expiry.Detail
internal/ingest/worker.go:488is the soleCache.Invalidatecaller in the tree.internal/api/query.go(admin raw-SQL proxy) holds no cache reference.QueryTimeToTTLallows up to 1h (floor 10s).Scenario: admin performs a GDPR erasure
DELETE FROM users WHERE id = ...viaPOST /v1/admin/query;POST /v1/querycontinues returning the deleted rows fromLocalCachefor up to an hour, with no operator recourse short of restart.Fix direction
Give the admin-query handler a cache handle and invalidate the affected table(s) after a successful mutation (best-effort table extraction, mirroring the ingest path), or bypass/short-TTL the cache for tables recently mutated.
Found in a repo-wide audit; verified by code trace. Distinct from #382 (ingest-path version race) and #362 (MV writes vs SSE — same "only ingest drives the reactive layer" root cause, different surface).