From 1de6d73e75ea6ac133ab2b8d11bcec20b977a02f Mon Sep 17 00:00:00 2001 From: timmy Date: Thu, 13 Aug 2026 02:22:29 +0000 Subject: [PATCH] perf: keep planning reads available during writes (Closes #699) --- src/later_store.py | 12 ++++++++++-- src/today_store.py | 12 ++++++++++-- tests/test_later_store.py | 17 +++++++++++++++++ tests/test_today_store.py | 14 ++++++++++++++ 4 files changed, 51 insertions(+), 4 deletions(-) diff --git a/src/later_store.py b/src/later_store.py index 2db64d5..d48ce39 100644 --- a/src/later_store.py +++ b/src/later_store.py @@ -22,10 +22,14 @@ class LaterStore: self.operation_limit = operation_limit self.operation_retention_seconds = operation_retention_seconds self.clock = clock + self._initialize() - def _connect(self) -> sqlite3.Connection: + def _initialize(self) -> None: self.path.parent.mkdir(parents=True, exist_ok=True) connection = sqlite3.connect(self.path, timeout=self.timeout) + if connection.execute("PRAGMA user_version").fetchone()[0] >= 1: + connection.close() + return connection.execute("PRAGMA journal_mode=WAL") connection.execute( """ @@ -63,8 +67,12 @@ class LaterStore: "UPDATE later_operations SET created_at = ? WHERE created_at IS NULL", (self.clock(),), ) + connection.execute("PRAGMA user_version = 1") connection.commit() - return connection + connection.close() + + def _connect(self) -> sqlite3.Connection: + return sqlite3.connect(self.path, timeout=self.timeout) def _record_operation(self, connection: sqlite3.Connection, login: str, operation_id: str) -> None: now = self.clock() diff --git a/src/today_store.py b/src/today_store.py index d1285a7..2736e95 100644 --- a/src/today_store.py +++ b/src/today_store.py @@ -30,10 +30,14 @@ class TodayStore: self.operation_retention_seconds = operation_retention_seconds self.recap_limit = recap_limit self.clock = clock + self._initialize() - def _connect(self) -> sqlite3.Connection: + def _initialize(self) -> None: self.path.parent.mkdir(parents=True, exist_ok=True) connection = sqlite3.connect(self.path, timeout=self.timeout) + if connection.execute("PRAGMA user_version").fetchone()[0] >= 1: + connection.close() + return connection.execute("PRAGMA journal_mode=WAL") connection.execute( """ @@ -101,8 +105,12 @@ class TodayStore: ) """ ) + connection.execute("PRAGMA user_version = 1") connection.commit() - return connection + connection.close() + + def _connect(self) -> sqlite3.Connection: + return sqlite3.connect(self.path, timeout=self.timeout) def _record_operation(self, connection: sqlite3.Connection, login: str, operation_id: str) -> None: now = self.clock() diff --git a/tests/test_later_store.py b/tests/test_later_store.py index adb2384..f93c887 100644 --- a/tests/test_later_store.py +++ b/tests/test_later_store.py @@ -65,6 +65,23 @@ def test_deferrals_are_durable_revisioned_idempotent_and_account_scoped(tmp_path ) == {"revision": 2, "records": {}} +def test_initialized_later_reads_remain_available_during_a_planning_write(tmp_path): + path = tmp_path / "later.sqlite3" + store = LaterStore(path, timeout=0.05) + expected = store.apply( + "timmy", "seed", "defer", "issue:r:1:", + wake_at="2026-08-10T09:00:00.000Z", + ) + + writer = sqlite3.connect(path) + writer.execute("BEGIN IMMEDIATE") + try: + assert LaterStore(path, timeout=0.05).get("timmy") == expected + finally: + writer.rollback() + writer.close() + + def test_batch_applies_in_one_ordered_idempotent_unit(tmp_path): store = LaterStore(tmp_path / "later.sqlite3") operations = [ diff --git a/tests/test_today_store.py b/tests/test_today_store.py index ff92735..e958dbc 100644 --- a/tests/test_today_store.py +++ b/tests/test_today_store.py @@ -67,6 +67,20 @@ def test_operations_are_durable_ordered_idempotent_and_account_scoped(tmp_path): } +def test_initialized_today_reads_remain_available_during_a_planning_write(tmp_path): + path = tmp_path / "today.sqlite3" + store = TodayStore(path, timeout=0.05) + expected = store.apply("timmy", "seed", "add", "issue:r:1:") + + writer = sqlite3.connect(path) + writer.execute("BEGIN IMMEDIATE") + try: + assert TodayStore(path, timeout=0.05).get("timmy") == expected + finally: + writer.rollback() + writer.close() + + def test_capacity_and_estimates_are_durable_account_scoped_and_follow_item_identity(tmp_path): path = tmp_path / "today.sqlite3" store = TodayStore(path, limit=3)