Skip to content

Commit ce4e2c3

Browse files
authored
Merge pull request #38 from devqubit-labs/storage-query-optimization
perf(storage): optimize registry queries for large datasets
2 parents efb8bf7 + 16cf6e1 commit ce4e2c3

6 files changed

Lines changed: 1329 additions & 618 deletions

File tree

changelog.d/38.changed.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Optimized storage backends for scale (S3/GCS local index, SQLite connection pooling, GC pagination).

packages/devqubit-engine/src/devqubit_engine/storage/backends/local.py

Lines changed: 43 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@
2121
import re
2222
import sqlite3
2323
import tempfile
24+
import threading
25+
from contextlib import contextmanager
2426
from pathlib import Path
2527
from typing import Any, Iterator
2628

@@ -356,7 +358,8 @@ class LocalRegistry:
356358
SQLite-backed run registry.
357359
358360
Provides efficient storage and querying of run metadata with
359-
indexed fields for common query patterns.
361+
indexed fields for common query patterns. Uses thread-local
362+
connection pooling for better performance.
360363
361364
Parameters
362365
----------
@@ -371,17 +374,18 @@ def __init__(self, root: Path, timeout: float = 30.0) -> None:
371374
self.root.mkdir(parents=True, exist_ok=True)
372375
self.db_path = self.root / "registry.db"
373376
self._timeout = timeout
377+
self._local = threading.local()
374378
self._init_db()
375379
logger.debug("LocalRegistry initialized at %s", self.db_path)
376380

377-
def _get_connection(self) -> sqlite3.Connection:
381+
def _create_connection(self) -> sqlite3.Connection:
378382
"""
379-
Get a database connection with standard settings.
383+
Create a new database connection with standard settings.
380384
381385
Returns
382386
-------
383387
sqlite3.Connection
384-
Connection with row_factory set to sqlite3.Row.
388+
New connection with row_factory set to sqlite3.Row.
385389
"""
386390
conn = sqlite3.connect(self.db_path, timeout=self._timeout)
387391
conn.row_factory = sqlite3.Row
@@ -390,6 +394,28 @@ def _get_connection(self) -> sqlite3.Connection:
390394
conn.execute("PRAGMA busy_timeout=30000")
391395
return conn
392396

397+
@contextmanager
398+
def _get_connection(self):
399+
"""
400+
Get a thread-local database connection.
401+
402+
Uses connection pooling per thread for better performance.
403+
Connections are reused within the same thread.
404+
405+
Yields
406+
------
407+
sqlite3.Connection
408+
Database connection.
409+
"""
410+
if not hasattr(self._local, "conn") or self._local.conn is None:
411+
self._local.conn = self._create_connection()
412+
try:
413+
yield self._local.conn
414+
self._local.conn.commit()
415+
except Exception:
416+
self._local.conn.rollback()
417+
raise
418+
393419
def _init_db(self) -> None:
394420
"""Initialize database schema."""
395421
with self._get_connection() as conn:
@@ -1144,6 +1170,17 @@ def clear_baseline(self, project: str) -> bool:
11441170
return True
11451171
return False
11461172

1173+
def close(self) -> None:
1174+
"""
1175+
Close the thread-local database connection if open.
1176+
1177+
This is optional - connections are automatically closed when
1178+
the thread terminates.
1179+
"""
1180+
if hasattr(self._local, "conn") and self._local.conn is not None:
1181+
self._local.conn.close()
1182+
self._local.conn = None
1183+
11471184

11481185
class LocalWorkspace:
11491186
"""
@@ -1196,8 +1233,8 @@ def registry(self) -> LocalRegistry:
11961233
return self._registry
11971234

11981235
def close(self) -> None:
1199-
"""Close the workspace (no-op for local storage)."""
1200-
pass
1236+
"""Close the workspace and release resources."""
1237+
self._registry.close()
12011238

12021239
def __enter__(self) -> LocalWorkspace:
12031240
"""Enter context manager."""

0 commit comments

Comments
 (0)