fix: async delete_all aborts on first error, leaving partial deletion (#5529)

This commit is contained in:
Hrushikesh Yadav
2026-06-16 11:59:33 +05:30
committed by GitHub
parent 30d172e826
commit a2f01a8fcc
2 changed files with 57 additions and 2 deletions
+8 -2
View File
@@ -3235,9 +3235,15 @@ class AsyncMemory(MemoryBase):
for memory in memories[0]:
delete_tasks.append(self._delete_memory(memory.id))
await asyncio.gather(*delete_tasks)
results = await asyncio.gather(*delete_tasks, return_exceptions=True)
logger.info(f"Deleted {len(memories[0])} memories")
errors = [r for r in results if isinstance(r, BaseException)]
if errors:
logger.warning("Failed to delete %d out of %d memories", len(errors), len(results))
for err in errors:
logger.warning("Delete error: %s", err)
logger.info(f"Deleted {len(results) - len(errors)} memories")
decay_usage_notice = detect_decay_usage_from_delete_all(len(memories[0]))
if decay_usage_notice:
+49
View File
@@ -762,6 +762,55 @@ async def test_async_delete_memory_history_has_timestamps(mock_sqlite, mock_llm_
datetime.fromisoformat(call_kwargs["updated_at"]) # verify valid ISO timestamp
@pytest.mark.asyncio
@patch('mem0.utils.factory.EmbedderFactory.create')
@patch('mem0.utils.factory.VectorStoreFactory.create')
@patch('mem0.utils.factory.LlmFactory.create')
@patch('mem0.memory.main.SQLiteManager')
async def test_async_delete_all_continues_on_partial_failure(mock_sqlite, mock_llm_factory, mock_vector_factory, mock_embedder_factory):
"""async delete_all must not abort when a single memory fails to delete.
Without return_exceptions=True, asyncio.gather raises on the first error
and cancels remaining tasks, leaving a partial deletion.
"""
mock_embedder_factory.return_value = MagicMock()
mock_vector_store = MagicMock()
mock_vector_factory.return_value = mock_vector_store
mock_llm_factory.return_value = MagicMock()
mock_sqlite.return_value = MagicMock()
from mem0.memory.main import AsyncMemory
config = MemoryConfig()
memory = AsyncMemory(config)
mem1 = MagicMock()
mem1.id = "mem-1"
mem1.payload = {"data": "one", "created_at": "2024-01-01T00:00:00+00:00", "actor_id": None, "role": None}
mem2 = MagicMock()
mem2.id = "mem-2"
mem2.payload = {"data": "two", "created_at": "2024-01-01T00:00:00+00:00", "actor_id": None, "role": None}
mem3 = MagicMock()
mem3.id = "mem-3"
mem3.payload = {"data": "three", "created_at": "2024-01-01T00:00:00+00:00", "actor_id": None, "role": None}
mock_vector_store.list.return_value = ([mem1, mem2, mem3],)
def _get_side_effect(vector_id):
if vector_id == "mem-2":
raise RuntimeError("simulated store failure")
return {
"mem-1": mem1,
"mem-3": mem3,
}.get(vector_id)
mock_vector_store.get.side_effect = _get_side_effect
result = await memory.delete_all(user_id="test-user")
assert result == {"message": "Memories deleted successfully!"}
assert mock_vector_store.delete.call_count == 2
@patch('mem0.utils.factory.EmbedderFactory.create')
@patch('mem0.utils.factory.VectorStoreFactory.create')
@patch('mem0.utils.factory.LlmFactory.create')