Fix #85: Duplicate chat message submissions produce inconsistent AI-corrected content #119
Loading…
Reference in New Issue
Block a user
No description provided.
Delete Branch "typosaurus/ticket-85"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Resolves #85.
Plan
Implementation Plan: Cross-Worker Dedup for Chat Messages
1. Add Database-Level Unique Index
File:
devplacepy/database/schema.pyAction: After the existing table creation, add a migration function that creates a unique composite index on
(sender_uid, receiver_uid, content)in themessagestable. Use raw SQL via thedatasetconnection to ensure the index exists. The index name:idx_messages_unique_sender_receiver_content.If the table already has rows with duplicates, the migration must either:
INSERT OR IGNOREonly going forward (safer, no data loss).Given the ticket, assume no production data loss is acceptable. Strategy: Add the index with
CREATE UNIQUE INDEX IF NOT EXISTS ...– if duplicates exist, the migration will fail. To avoid this, first delete duplicates with a SQL query keeping the oldestidper group. This is safe because duplicates were unintended.Call this function after the schema is initialised.
2. Modify
persist_messageto Use DB ConstraintFile:
devplacepy/persist.pyAction: In the
persist_messagefunction (around lines 88–106), replace the in-memory dedup check with a try/except that attempts an insert and falls back to selecting the existing row onIntegrityError.dataset(which uses SQLAlchemy). If anIntegrityError(SQLite raisessqlite3.IntegrityError) is caught, query for the existing row with samesender_uid, receiver_uid, contentand return that row's UID.Implementation sketch:
Important: After catching the IntegrityError, we must not call
schedule_correctionorschedule_modificationfor the duplicate. The correction was already scheduled for the first insert. So move those calls outside the try block, only in the non-duplicate path.3. Update
_content_cacheUsageThe existing in-memory cache can remain as an optimisation, but it should not be relied upon for correctness. Ensure that the DB constraint is always the final check. If
_content_cachereturns a hit, still proceed to the DB insert to confirm. This adds minimal overhead and guarantees correctness across workers.4. Adjust
schedule_correctionCall LocationCurrently
schedule_correctionis called after insert regardless. Move it inside the successful insert branch only.5. Migrate Existing Duplicate Rows
Implement a migration step (as described in step 1) that deduplicates existing data before adding the unique index. This ensures the migration doesn't fail.
6. Review and Update Existing Test
File:
tests/api/messages/send.py– testtest_duplicate_message_returns_same_uidalready expects same UID. It should still pass because the DB constraint will cause the second insert to return the existing row with the same UID. No change needed unless the test uses a single worker; it will now work across workers too.Add a new test (optional but recommended):
test_duplicate_message_different_workers– simulate two concurrent requests via threads with separate sessions. This is not mandatory for the fix but would improve coverage.7. Verify No Side Effects
Run the full test suite and ensure no regressions, especially the previously failing tests noted in the lessons:
tests/unit/services/game/store.py::test_advance_quests_increments_and_capstests/unit/services/deepsearch/chat.py::test_answer_is_grounded_and_citedThese failures were likely caused by previous incorrect changes; this plan should not affect them.
Definition of Done
schema.pythat removes duplicate(sender_uid, receiver_uid, content)rows (keeping the oldest) and creates a UNIQUE index on those columns.persist_messageinpersist.pycatchesIntegrityErroron insert, fetches the existing row, returns its UID without scheduling correction/modification a second time.schedule_correctionandschedule_modificationare called only for new inserts, not for duplicates._content_cacheis retained as a best-effort optimisation but a DB insert is always attempted.test_duplicate_message_returns_same_uidpasses (unchanged expectations).pip install -e '.[dev]' -q && python -m pytestruff .or equivalent linter and fix any new issues).Opened automatically by Typosaurus.
Checkout
From your project repository, check out a new branch and test the changes.