COG-5975 fix(cache): unique constraint + upsert for cache_session_context so retried writes can't poison a session - #4229
Closed
Vasilije1990 wants to merge 1 commit into
Closed
Conversation
…t poison a session COG-5975 / fixes #4226 cache_session_context had no unique constraint on (user_id, session_id, entry_id), so a client retry of a timed-out write inserted a duplicate row, after which scalar_one_or_none() in update_session_context_entry raised on every write for that session — a permanent 503 with no self-healing path. - Add unique index uq_cache_session_context_entry (as a unique Index so pre-existing tables can be upgraded in place) - Make create_session_context_entry an ON CONFLICT DO UPDATE upsert on Postgres and SQLite; retried writes now replace the payload in place - Migrate legacy tables on init: collapse duplicate groups to the newest row, then CREATE UNIQUE INDEX IF NOT EXISTS; failure is non-fatal and falls back to plain inserts - Self-heal in update_session_context_entry: merge into the newest row and prune older duplicates instead of erroring until manual cleanup Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Collaborator
|
@Vasilije1990 added a PR with the fix here: Fix should be handled with an alembic migration of the relational DB That also resolves the issue for existing users who had it |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
Fixes #4226 —
cache_session_contexthad no uniqueness on(user_id, session_id, entry_id)(unlikecache_qa_entries, which hasuq_cache_qa_entry). The session-context write path is update-then-create across two transactions, so an ordinary client retry after a timeout — or two concurrent writers — inserted a duplicate row. Once duplicates existed,scalar_one_or_none()inSqlCacheAdapter.update_session_context_entryraised on every subsequent write for that session: a permanent 503 recoverable only by manual row deletion (the reporter hit 77 copies of one bookkeeping entry and ~36 failed writes/hour for 6.5 hours).All four parts of the fix suggested in the issue:
uq_cache_session_context_entryon(user_id, session_id, entry_id)— declared as a uniqueIndexrather than aUniqueConstraintso tables created before it existed can be upgraded in place.create_session_context_entrynow usesON CONFLICT DO UPDATE(Postgres + SQLite dialects, same pattern asset_value), so a retried write replaces the payload in place. Id-less entries keep their synthetic UUID and are unaffected._ensure_initializedcollapses each duplicate group to its newest row, then runsCREATE UNIQUE INDEX IF NOT EXISTS. Migration failure is non-fatal: the adapter logs a warning and falls back to plain inserts soON CONFLICTcan never error against a missing index.update_session_context_entryfetches all rows for the key ordered byseq, merges into the newest, and prunes older duplicates in the same transaction, so even an un-migrated database recovers on the next write instead of 503ing forever.The Redis and FS adapters don't share the crash (they update the first list match), so this fix is scoped to the SQL adapter. Follow-up noted in COG-5975: their
create_session_context_entrystill appends duplicates on retried creates (no crash, but duplicated context) — the adapter-parametrized contract tests from #4129 would be the natural home for cross-adapter idempotency coverage.Testing
New regression tests in
test_sql_adapter_crud.py(sqlite+aiosqlite):pytest cognee/tests/unit/infrastructure/databases/cache/— 139 passed;cognee/tests/unit/infrastructure/session/— 181 passed. Ruff lint + format clean.Related
🤖 Generated with Claude Code