Skip to content

fix(memory): prevent race condition in MemoryBlock._ensure_loaded() (#102) - #109

Open
chiragjangiir wants to merge 1 commit into
knight22-21:mainfrom
chiragjangiir:fix/memory-ensure-loaded-race
Open

chiragjangiir wants to merge 1 commit into
knight22-21:mainfrom
chiragjangiir:fix/memory-ensure-loaded-race

Conversation

@chiragjangiir

Copy link
Copy Markdown
Contributor

Closes #102

Root Cause

MemoryBlock._ensure_loaded() in session/memory.py used an unprotected check-then-act pattern:

if not self._loaded:
    self._cache = store.get_memory(...)
    ...
    self._loaded = True

When multiple worker threads call get(), all(), or as_prompt_block() concurrently on the same MemoryBlock instance (such as in multi-worker sessions sharing memory blocks), both threads could enter the branch before self._loaded was set to True. This resulted in duplicate SQLite queries, duplicate ProjectMemory.load() calls, and redundant upserts.

Changes

  1. Added self._load_lock = threading.Lock() in MemoryBlock.__init__.
  2. Wrapped _ensure_loaded() using double-checked locking:
    • Early return if self._loaded is already True (zero lock contention on subsequent reads).
    • Acquire self._load_lock and verify not self._loaded before loading and populating self._cache.
  3. Added test_ensure_loaded_concurrent_single_load in tests/test_phase12.py to verify that concurrent calls to _ensure_loaded() across multiple threads only call store.get_memory and pm.load exactly once.

Verification

  • Ran test suite:
    python -m pytest tests/test_phase12.py tests/test_project_memory_concurrency.py
    All 32 tests passed.
  • Ran linter:
    ruff check devagent/session/memory.py tests/test_phase12.py
    Passed with 0 violations.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

MemoryBlock._ensure_loaded() has a race condition under concurrent access

1 participant