fix: async delete_all race condition corrupts entity store linked_memory_ids (#5553)
This commit is contained in:
@@ -18,6 +18,7 @@ def make_async_memory():
|
||||
memory = AsyncMemory.__new__(AsyncMemory)
|
||||
memory.vector_store = MagicMock()
|
||||
memory._delete_memory = AsyncMock()
|
||||
memory._entity_store = None
|
||||
return memory
|
||||
|
||||
|
||||
|
||||
@@ -1256,3 +1256,54 @@ class TestPreserveCustomMetadata:
|
||||
assert payload["source"] == "chat"
|
||||
assert payload["data"] == "I love swimming"
|
||||
assert payload["user_id"] == "user_1"
|
||||
|
||||
|
||||
class TestAsyncDeleteAllEntityRace:
|
||||
"""Tests for async delete_all entity store race condition fix."""
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@patch('mem0.utils.factory.EmbedderFactory.create')
|
||||
@patch('mem0.utils.factory.VectorStoreFactory.create')
|
||||
@patch('mem0.utils.factory.LlmFactory.create')
|
||||
@patch('mem0.memory.storage.SQLiteManager')
|
||||
async def test_async_delete_all_bulk_clears_entity_store(self, mock_sqlite, mock_llm_factory, mock_vector_factory, mock_embedder_factory):
|
||||
"""
|
||||
Verify that async delete_all bulk-clears entity records after
|
||||
concurrent memory deletes complete, preventing both the
|
||||
read-modify-write race and entity orphaning on partial failures.
|
||||
"""
|
||||
mock_embedder_factory.return_value = MagicMock()
|
||||
mock_llm_factory.return_value = MagicMock()
|
||||
mock_sqlite.return_value = MagicMock()
|
||||
|
||||
mock_vector_store = MagicMock()
|
||||
mem_a = MagicMock()
|
||||
mem_a.id = "mem-a"
|
||||
mem_a.payload = {"data": "Alice likes Python", "user_id": "alice"}
|
||||
mem_b = MagicMock()
|
||||
mem_b.id = "mem-b"
|
||||
mem_b.payload = {"data": "Alice works at Acme", "user_id": "alice"}
|
||||
mock_vector_store.list.return_value = ([mem_a, mem_b],)
|
||||
mock_vector_store.get.side_effect = lambda vector_id: {"mem-a": mem_a, "mem-b": mem_b}[vector_id]
|
||||
mock_vector_factory.return_value = mock_vector_store
|
||||
|
||||
mock_entity_store = MagicMock()
|
||||
entity_row = MagicMock()
|
||||
entity_row.id = "entity-alice"
|
||||
entity_row.payload = {
|
||||
"data": "alice",
|
||||
"user_id": "alice",
|
||||
"linked_memory_ids": ["mem-a", "mem-b"],
|
||||
}
|
||||
mock_entity_store.list.return_value = ([entity_row],)
|
||||
|
||||
from mem0.memory.main import AsyncMemory
|
||||
config = MemoryConfig()
|
||||
memory = AsyncMemory(config)
|
||||
memory._entity_store = mock_entity_store
|
||||
|
||||
await memory.delete_all(user_id="alice")
|
||||
|
||||
mock_entity_store.delete.assert_called_once_with(vector_id="entity-alice")
|
||||
|
||||
assert mock_vector_store.delete.call_count == 2
|
||||
|
||||
Reference in New Issue
Block a user