Skip to content

Commit 34a1653

Browse files
perf: use persistent SQLite connection per cache instance (#1088)
Each get_connection() call previously opened a new file handle and ran 4 PRAGMA statements before executing the actual query. For mass-eval, this amounted to ~42 SQLite operations per article even on a full cache hit. Replace the per-call sqlite3.connect() pattern in CacheBase with a single long-lived connection created on first use. Commit/rollback semantics are preserved; the connection is not closed between calls. Co-authored-by: florath-ai-assistant[bot] <Andreas.Florath@telekom.de>
1 parent 89be13e commit 34a1653

File tree

1 file changed

+58
-35
lines changed

1 file changed

+58
-35
lines changed

src/aletheia_probe/cache/base.py

Lines changed: 58 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -10,16 +10,14 @@
1010
"""
1111

1212
import sqlite3
13+
from collections.abc import Iterator
14+
from contextlib import contextmanager
1315
from pathlib import Path
14-
from typing import Any
1516

1617
from ..config import get_config_manager
1718
from ..logging_config import get_detail_logger, get_status_logger
1819
from ..utils.dead_code import code_is_used
19-
from .connection_utils import (
20-
get_configured_connection,
21-
get_connection_with_row_factory,
22-
)
20+
from .connection_utils import configure_sqlite_connection
2321
from .schema import init_database
2422

2523

@@ -84,44 +82,69 @@ def __init__(self, db_path: Path | None = None):
8482
raise RuntimeError(error_msg) from e
8583

8684
self.db_path = db_path
85+
self._conn: sqlite3.Connection | None = None
86+
87+
def _open_conn(self) -> sqlite3.Connection:
88+
"""Open and configure the persistent connection (called once per instance)."""
89+
conn = sqlite3.connect(str(self.db_path), timeout=30.0)
90+
configure_sqlite_connection(conn)
91+
return conn
92+
93+
def _get_or_open_conn(self) -> sqlite3.Connection:
94+
"""Return the persistent connection, creating it on first use."""
95+
if self._conn is None:
96+
self._conn = self._open_conn()
97+
return self._conn
98+
99+
@contextmanager
100+
def get_connection(
101+
self, timeout: float = 30.0, enable_wal: bool = True
102+
) -> Iterator[sqlite3.Connection]:
103+
"""Get the persistent SQLite connection to this cache's database.
87104
88-
def get_connection(self, timeout: float = 30.0, enable_wal: bool = True) -> Any:
89-
"""Get a configured SQLite connection to this cache's database.
90-
91-
This method provides the standard way to access the database with
92-
consistent configuration across all cache components. Uses the
93-
centralized connection configuration with proper timeout and WAL mode.
105+
Reuses a single long-lived connection per instance. Commits on success
106+
and rolls back on exception, but does not close the connection.
94107
95108
Args:
96-
timeout: Connection timeout in seconds (default: 30.0)
97-
enable_wal: Whether to enable WAL mode (default: True)
98-
99-
Returns:
100-
Context manager yielding a configured SQLite connection
101-
102-
Example:
103-
```python
104-
with self.get_connection() as conn:
105-
cursor = conn.cursor()
106-
cursor.execute("SELECT * FROM table")
107-
results = cursor.fetchall()
108-
```
109-
"""
110-
return get_configured_connection(self.db_path, timeout, enable_wal)
109+
timeout: Ignored (kept for API compatibility; set at construction)
110+
enable_wal: Ignored (kept for API compatibility; set at construction)
111111
112+
Yields:
113+
Configured SQLite connection
114+
"""
115+
del timeout, enable_wal # set once at connection creation
116+
conn = self._get_or_open_conn()
117+
try:
118+
yield conn
119+
conn.commit()
120+
except Exception:
121+
conn.rollback()
122+
raise
123+
124+
@contextmanager
112125
def get_connection_with_row_factory(
113126
self, timeout: float = 30.0, enable_wal: bool = True
114-
) -> Any:
115-
"""Get a configured SQLite connection with Row factory for dict-like access.
127+
) -> Iterator[sqlite3.Connection]:
128+
"""Get the persistent connection with Row factory for dict-like access.
116129
117-
Same as get_connection() but with sqlite3.Row factory enabled
118-
for dictionary-style access to query results.
130+
Same as get_connection() but temporarily sets sqlite3.Row factory
131+
for the duration of the block.
119132
120133
Args:
121-
timeout: Connection timeout in seconds (default: 30.0)
122-
enable_wal: Whether to enable WAL mode (default: True)
134+
timeout: Ignored (kept for API compatibility; set at construction)
135+
enable_wal: Ignored (kept for API compatibility; set at construction)
123136
124-
Returns:
125-
Context manager yielding a configured SQLite connection with Row factory
137+
Yields:
138+
Configured SQLite connection with Row factory
126139
"""
127-
return get_connection_with_row_factory(self.db_path, timeout, enable_wal)
140+
del timeout, enable_wal # set once at connection creation
141+
conn = self._get_or_open_conn()
142+
conn.row_factory = sqlite3.Row
143+
try:
144+
yield conn
145+
conn.commit()
146+
except Exception:
147+
conn.rollback()
148+
raise
149+
finally:
150+
conn.row_factory = None

0 commit comments

Comments
 (0)