From b6ea07d2de6489388e8efb96e403db2bef165a00 Mon Sep 17 00:00:00 2001 From: Yiorgis Gozadinos Date: Thu, 23 Apr 2026 15:42:10 +0300 Subject: [PATCH] Track all in-flight background vacuum tasks, not just the last one --- haiku_rag_slim/haiku/rag/client.py | 33 +++++--- ...t_aexit_awaits_all_background_vacuums.yaml | 82 +++++++++++++++++++ tests/test_versioning.py | 52 +++++++++++- 3 files changed, 152 insertions(+), 15 deletions(-) create mode 100644 tests/cassettes/test_versioning/test_aexit_awaits_all_background_vacuums.yaml diff --git a/haiku_rag_slim/haiku/rag/client.py b/haiku_rag_slim/haiku/rag/client.py index 41fe2648..244d430c 100644 --- a/haiku_rag_slim/haiku/rag/client.py +++ b/haiku_rag_slim/haiku/rag/client.py @@ -91,7 +91,7 @@ class HaikuRAG: self._create = create self._read_only = read_only self._before = before - self._vacuum_task: asyncio.Task | None = None + self._vacuum_tasks: set[asyncio.Task] = set() @property def is_read_only(self) -> bool: @@ -116,12 +116,26 @@ class HaikuRAG: async def __aexit__(self, exc_type, exc_val, exc_tb): # noqa: ARG002 """Async context manager exit.""" - # Wait for any pending background vacuum to complete before closing - if self._vacuum_task is not None and not self._vacuum_task.done(): - await self._vacuum_task + await self._await_vacuum_tasks() self.close() return False + async def _await_vacuum_tasks(self) -> None: + """Wait for all in-flight background vacuum tasks to complete. + + Each create_document / update_document can schedule its own vacuum task; + all must be awaited before tearing down the connection, not just the + most recently scheduled one. + """ + if self._vacuum_tasks: + await asyncio.gather(*self._vacuum_tasks, return_exceptions=True) + + def _schedule_vacuum(self) -> None: + """Schedule a background vacuum and track the task for later awaiting.""" + task = asyncio.create_task(self.store.vacuum()) + self._vacuum_tasks.add(task) + task.add_done_callback(self._vacuum_tasks.discard) + # ========================================================================= # Processing Primitives # ========================================================================= @@ -376,8 +390,6 @@ class HaikuRAG: Returns: The created Document instance with ID set. """ - import asyncio - # Ensure all chunks have embeddings before storing chunks = await self._ensure_chunks_embedded(chunks) @@ -405,7 +417,7 @@ class HaikuRAG: # Vacuum old versions in background (non-blocking) if auto_vacuum enabled if self._config.storage.auto_vacuum: - self._vacuum_task = asyncio.create_task(self.store.vacuum()) + self._schedule_vacuum() return created_doc except Exception: @@ -432,8 +444,6 @@ class HaikuRAG: Returns: The updated Document instance. """ - import asyncio - assert document.id is not None, "Document ID is required for update" # Ensure all chunks have embeddings before storing @@ -468,7 +478,7 @@ class HaikuRAG: # Vacuum old versions in background (non-blocking) if auto_vacuum enabled if self._config.storage.auto_vacuum: - self._vacuum_task = asyncio.create_task(self.store.vacuum()) + self._schedule_vacuum() return updated_doc except Exception: @@ -1403,8 +1413,7 @@ class HaikuRAG: The ID of the document currently being processed. """ # Wait for any background vacuum before destructive table operations - if self._vacuum_task is not None and not self._vacuum_task.done(): - await self._vacuum_task + await self._await_vacuum_tasks() # Update settings to current config settings_repo = SettingsRepository(self.store) diff --git a/tests/cassettes/test_versioning/test_aexit_awaits_all_background_vacuums.yaml b/tests/cassettes/test_versioning/test_aexit_awaits_all_background_vacuums.yaml new file mode 100644 index 00000000..c0f6b401 --- /dev/null +++ b/tests/cassettes/test_versioning/test_aexit_awaits_all_background_vacuums.yaml @@ -0,0 +1,82 @@ +interactions: +- request: + headers: + accept: + - application/json + accept-encoding: + - gzip, deflate, zstd + connection: + - keep-alive + content-length: + - '91' + content-type: + - application/json + host: + - localhost:11434 + method: POST + parsed_body: + encoding_format: base64 + input: + - triggers first vacuum + model: qwen3-embedding:4b + uri: http://localhost:11434/v1/embeddings + response: + headers: + content-type: + - application/json + transfer-encoding: + - chunked + parsed_body: + data: + - embedding: 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 + index: 0 + object: embedding + model: qwen3-embedding:4b + object: list + usage: + prompt_tokens: 5 + total_tokens: 5 + status: + code: 200 + message: OK +- request: + headers: + accept: + - application/json + accept-encoding: + - gzip, deflate, zstd + connection: + - keep-alive + content-length: + - '92' + content-type: + - application/json + host: + - localhost:11434 + method: POST + parsed_body: + encoding_format: base64 + input: + - triggers second vacuum + model: qwen3-embedding:4b + uri: http://localhost:11434/v1/embeddings + response: + headers: + content-type: + - application/json + transfer-encoding: + - chunked + parsed_body: + data: + - embedding: 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 + index: 0 + object: embedding + model: qwen3-embedding:4b + object: list + usage: + prompt_tokens: 5 + total_tokens: 5 + status: + code: 200 + message: OK +version: 1 diff --git a/tests/test_versioning.py b/tests/test_versioning.py index a8008163..5d0a0198 100644 --- a/tests/test_versioning.py +++ b/tests/test_versioning.py @@ -101,9 +101,8 @@ async def test_existing_database_checks_migrations(monkeypatch, temp_db_path): async def _wait_for_background_vacuum(client): - """Wait for any background vacuum task to complete.""" - if client._vacuum_task is not None and not client._vacuum_task.done(): - await client._vacuum_task + """Wait for any in-flight background vacuum tasks to complete.""" + await client._await_vacuum_tasks() @pytest.mark.vcr() @@ -226,6 +225,53 @@ async def test_aexit_awaits_background_vacuum(temp_db_path, monkeypatch): assert vacuum_completed.is_set(), "__aexit__ exited before vacuum finished" +@pytest.mark.vcr() +async def test_aexit_awaits_all_background_vacuums(temp_db_path, monkeypatch): + """Multiple create_document calls schedule multiple vacuum tasks; __aexit__ + must await all of them, not just the last-scheduled one. + + Scenario: Task A acquires the vacuum lock and is slow. Task B is scheduled + while Task A still holds the lock — Task B sees the lock held and returns + immediately. If the client only tracks the most recently scheduled task, + __aexit__ awaits the fast no-op B and closes the connection while Task A + is still running. + """ + from haiku.rag.config import Config + + monkeypatch.setattr(Config.storage, "auto_vacuum", True) + + first_vacuum_completed = asyncio.Event() + + async with HaikuRAG(db_path=temp_db_path, create=True) as client: + call_count = 0 + + async def slow_vacuum(*_args, **_kwargs): + nonlocal call_count + call_count += 1 + my_num = call_count + # Mimic the real vacuum's skip-if-running behavior. + if client.store._vacuum_lock.locked(): + return + async with client.store._vacuum_lock: + if my_num == 1: + # Hold the lock longer than any other operation in the + # test so Task A cannot finish incidentally. __aexit__ + # must explicitly wait for this task. + await asyncio.sleep(2.0) + first_vacuum_completed.set() + + client.store.vacuum = slow_vacuum + + await client.create_document(content="triggers first vacuum") + # Let Task A start and acquire the vacuum lock before scheduling B. + await asyncio.sleep(0.02) + await client.create_document(content="triggers second vacuum") + + assert first_vacuum_completed.is_set(), ( + "__aexit__ returned before the first vacuum task finished" + ) + + @pytest.mark.vcr() async def test_auto_vacuum_disabled_skips_vacuum(temp_db_path, monkeypatch): """Test that auto_vacuum=False prevents automatic vacuum after operations."""