From 38d06d3104f355337455238cf73d5c24aac7fa1a Mon Sep 17 00:00:00 2001 From: James Brunton Date: Sat, 11 Jul 2026 12:47:29 +0100 Subject: [PATCH] Make sqlite backend more resilient when using multiple runners (#6971) --- engine/src/stirling/documents/sqlite_vec_store.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/engine/src/stirling/documents/sqlite_vec_store.py b/engine/src/stirling/documents/sqlite_vec_store.py index 9346623314..5f909a4bcd 100644 --- a/engine/src/stirling/documents/sqlite_vec_store.py +++ b/engine/src/stirling/documents/sqlite_vec_store.py @@ -15,6 +15,10 @@ from stirling.documents.store import Document, DocumentStore, SearchResult, Stor from stirling.models import OwnerId, PrincipalId _READ_PERMISSION = "read" +# SQLite defaults to failing immediately (busy_timeout=0) when it can't grab the +# write lock. With multiple worker processes opening the same file, they collide on +# startup schema-init and get "database is locked". Wait for the lock instead. +_BUSY_TIMEOUT_MS = 5000 # sqlite stores TIMESTAMP as TEXT. We normalise to UTC ISO 8601 ``YYYY-MM-DD HH:MM:SS`` # so lexicographic comparison against ``datetime('now')`` matches chronological order. _SQLITE_DATETIME_FMT = "%Y-%m-%d %H:%M:%S" @@ -52,6 +56,8 @@ class SqliteVecStore(DocumentStore): # Required so cascade deletes from documents_meta clean up child tables. conn.execute("PRAGMA foreign_keys=ON") if self._db_path is not None: + # Set before the WAL switch below: that pragma also takes the lock. + conn.execute(f"PRAGMA busy_timeout={_BUSY_TIMEOUT_MS}") conn.execute("PRAGMA journal_mode=WAL") self._conn = conn