mirror of
https://github.com/Significant-Gravitas/AutoGPT.git
synced 2026-04-08 03:00:28 -04:00
fix(backend/copilot): prevent title update from overwriting session messages (#12302)
### Changes 🏗️ Fixes a race condition in `update_session_title()` where the background title generation task could overwrite the Redis session cache with a stale snapshot, causing the copilot to "forget" its previous turns. **Root cause:** `update_session_title()` performs a read-modify-write on the Redis cache (read full session → set title → write back). Meanwhile, `upsert_chat_session()` writes a newer version with more messages during streaming. If the title task reads early (e.g., 34 messages) and writes late (after streaming persisted 101 messages), the stale 34-message version overwrites the 101-message version. When the next message lands on a different pod, it loads the stale session from Redis. **Fix:** Replace the read-modify-write with a simple cache invalidation (`invalidate_session_cache`). The title is already updated in the DB; the next access just reloads from DB with the correct title and messages. No locks, no deserialization of the full session blob, no risk of stale overwrites. **Evidence from prod logs (session `41a3814c`):** - Pod `tm2jb` persisted session with 101 messages - Pod `phflm` loaded session from Redis cache with only 35 messages (66 messages lost) - The title background task ran between these events, overwriting the cache ### Checklist 📋 #### For code changes: - [x] I have clearly listed my changes in the PR description - [x] I have made a test plan - [x] I have tested my changes according to the test plan: - [x] `poetry run pytest backend/copilot/model_test.py` — 15/15 pass - [x] All pre-commit hooks pass (ruff, black, isort, pyright) - [ ] After deploy: verify long sessions no longer lose context on multi-pod setups
This commit is contained in:
@@ -705,19 +705,10 @@ async def update_session_title(session_id: str, title: str) -> bool:
|
||||
logger.warning(f"Session {session_id} not found for title update")
|
||||
return False
|
||||
|
||||
# Update title in cache if it exists (instead of invalidating).
|
||||
# This prevents race conditions where cache invalidation causes
|
||||
# the frontend to see stale DB data while streaming is still in progress.
|
||||
try:
|
||||
cached = await _get_session_from_cache(session_id)
|
||||
if cached:
|
||||
cached.title = title
|
||||
await cache_chat_session(cached)
|
||||
except Exception as e:
|
||||
# Not critical - title will be correct on next full cache refresh
|
||||
logger.warning(
|
||||
f"Failed to update title in cache for session {session_id}: {e}"
|
||||
)
|
||||
# Invalidate the cache so the next access reloads from DB with the
|
||||
# updated title. This avoids a read-modify-write on the full session
|
||||
# blob, which could overwrite concurrent message updates.
|
||||
await invalidate_session_cache(session_id)
|
||||
|
||||
return True
|
||||
except Exception as e:
|
||||
|
||||
Reference in New Issue
Block a user