You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
[Bug]: Improve Asynchronous Summary Processing in ShortTermMemory.add_episodes #909
This issue reports a performance bottleneck in the ShortTermMemory.add_episodes method, specifically a serialization lock that limits throughput during asynchronous summary creation triggered by eviction. When high write concurrency occurs (e.g., during load testing via Locust), callers block if an eviction triggers and a previous summary is still running.
Locking: Both episode addition and eviction are protected by an asyncio.Lock, preventing concurrent modifications; however, async summary creation still serializes callers until the summary task is freed.
Cause of Bottleneck
The add_episodes flow waits for the previous summary task to complete—in the critical section protected by the lock.
This means multiple callers trying to add episodes will be serialized during evictions, and if the summary task is slow, they will block until it finishes.
The lock is held for the entire duration of waiting for the previous async summary, preventing concurrency and causing throughput degradation under load.
Suggested Workaround/Solution
A. Reduce Critical Section
Only hold the lock for truly critical operations: The lock is meant to protect memory state changes (episode, count, length). Waiting for the summary task's completion does not need to be inside the critical section.
Proposed changes:
Release the lock before asynchronously waiting for the summary_task:
# in _do_evict# Instead of:ifself._summary_taskisnotNone:
awaitself._summary_task# Do this:prev_task=self._summary_taskself._summary_task=asyncio.create_task(self._create_summary(result))
# Release lock, then await prev_task outside lock!# Example (pseudocode):asyncwithself._lock:
# ... prepare for summary ...prev_task=self._summary_taskself._summary_task=asyncio.create_task(self._create_summary(result))
ifprev_taskisnotNone:
awaitprev_task
Minimize Lock Holding
Update only the necessary state inside async with self._lock.
Spawn summary tasks outside of the lock if safe.
If memory consistency relies on summary completion, queue episode additions or use a separate awaitable for summary completion, but do not block all writers with the lock.
B. Advanced: Use Task Queue/FIFO Pool
Consider a background summary worker that triggers summaries in a separate asyncio Task, or use an asyncio condition or event to signal when summary is available, allowing episode additions to proceed and just mark that the memory is "evicting/evicted".
C. Short-term: Document for Users
Warn clients and testers: This bottleneck can be encountered any time rapid eviction (due to high episode volume) overlaps summary generation.
D. Long-term Improvement
Summary generation should be completely decoupled from episode addition; perhaps implement an eviction queue, and let new episodes proceed even if summary is pending, so long as memory capacity allows.
Summary:
The bottleneck is caused by holding the serialization lock while waiting for async summary completion in add_episodes. You should release the lock before awaiting summary completion. Only protect the minimal memory state with the lock, and let summary processing happen outside the critical section. For maximal throughput, consider backgrounding summary eviction or creating an async queue for eviction summaries.
Activity
sscargal commentedon Jan 7, 2026
This issue reports a performance bottleneck in the
ShortTermMemory.add_episodesmethod, specifically a serialization lock that limits throughput during asynchronous summary creation triggered by eviction. When high write concurrency occurs (e.g., during load testing via Locust), callers block if an eviction triggers and a previous summary is still running.Code Review
ShortTermMemory.add_episodes_do_evict, after preparing for a summary, if a previous summary is still running, it waits:asyncio.Lock, preventing concurrent modifications; however, async summary creation still serializes callers until the summary task is freed.Cause of Bottleneck
add_episodesflow waits for the previous summary task to complete—in the critical section protected by the lock.Suggested Workaround/Solution
A. Reduce Critical Section
Proposed changes:
Release the lock before asynchronously waiting for the
summary_task:Minimize Lock Holding
async with self._lock.B. Advanced: Use Task Queue/FIFO Pool
C. Short-term: Document for Users
D. Long-term Improvement
References
Summary:
The bottleneck is caused by holding the serialization lock while waiting for async summary completion in
add_episodes. You should release the lock before awaiting summary completion. Only protect the minimal memory state with the lock, and let summary processing happen outside the critical section. For maximal throughput, consider backgrounding summary eviction or creating an async queue for eviction summaries.[GH-909] Improve async summary performance.
[GH-909] Improve async summary performance.
[GH-909] Improve async summary performance.
[GH-909] Improve async summary performance.
[GH-909] Improve async summary performance.
[GH-909] Improve async summary performance. (#949)
[MemMachineGH-909] Improve async summary performance. (MemMachine#949)