-
Notifications
You must be signed in to change notification settings - Fork 52.3k
perf(db): cache SQLite schema init and optimize descendant resolution #39140
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
8ed5e54
196c7dc
a18427b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -4478,7 +4478,17 @@ def row_get(row, key, index): | |
| rows = [] | ||
| if conn is not None: | ||
| raw_rows = conn.execute( | ||
| "SELECT id, parent_session_id, started_at FROM sessions" | ||
| """ | ||
| WITH RECURSIVE descendants(id, parent_session_id, started_at) AS ( | ||
| SELECT id, parent_session_id, started_at FROM sessions WHERE id = ? | ||
| UNION ALL | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
| SELECT s.id, s.parent_session_id, s.started_at | ||
| FROM sessions s | ||
| JOIN descendants d ON s.parent_session_id = d.id | ||
| ) | ||
| SELECT id, parent_session_id, started_at FROM descendants WHERE id != ? | ||
| """, | ||
| (sid, sid) | ||
| ).fetchall() | ||
| for row in raw_rows: | ||
| rows.append({ | ||
|
|
@@ -7000,10 +7010,11 @@ async def get_models_analytics(days: int = 30): | |
| # the dashboard (sessions, jobs, metrics, config editor) still loads and the | ||
| # /api/pty endpoint cleanly refuses with a WSL-suggested message. | ||
| try: | ||
| from hermes_cli.pty_bridge import PtyBridge, PtyUnavailableError | ||
| from hermes_cli.pty_bridge import PtyBridge, RustPtyBridge, PtyUnavailableError | ||
| _PTY_BRIDGE_AVAILABLE = True | ||
| except ImportError as _pty_import_err: # pragma: no cover - Windows-only path | ||
| PtyBridge = None # type: ignore[assignment] | ||
| RustPtyBridge = None | ||
| _PTY_BRIDGE_AVAILABLE = False | ||
|
|
||
| class PtyUnavailableError(RuntimeError): # type: ignore[no-redef] | ||
|
|
@@ -7464,7 +7475,12 @@ async def pty_ws(ws: WebSocket) -> None: | |
|
|
||
|
|
||
| try: | ||
| bridge = PtyBridge.spawn(argv, cwd=cwd, env=env) | ||
| if RustPtyBridge is not None and RustPtyBridge.is_available(): | ||
| bridge = await RustPtyBridge.spawn(argv, cwd=cwd, env=env) | ||
| is_rust = True | ||
| else: | ||
| bridge = PtyBridge.spawn(argv, cwd=cwd, env=env) | ||
| is_rust = False | ||
| except PtyUnavailableError as exc: | ||
| await ws.send_text(f"\r\n\x1b[31mChat unavailable: {exc}\x1b[0m\r\n") | ||
| await ws.close(code=1011) | ||
|
|
@@ -7479,9 +7495,12 @@ async def pty_ws(ws: WebSocket) -> None: | |
| # --- reader task: PTY master → WebSocket ---------------------------- | ||
| async def pump_pty_to_ws() -> None: | ||
| while True: | ||
| chunk = await loop.run_in_executor( | ||
| None, bridge.read, _PTY_READ_CHUNK_TIMEOUT | ||
| ) | ||
| if is_rust: | ||
| chunk = await bridge.read_async() | ||
| else: | ||
| chunk = await loop.run_in_executor( | ||
| None, bridge.read, _PTY_READ_CHUNK_TIMEOUT | ||
| ) | ||
| if chunk is None: # EOF | ||
| return | ||
| if not chunk: # no data this tick; yield control and retry | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -19,6 +19,7 @@ | |
| import random | ||
| import re | ||
| import sqlite3 | ||
| import sys | ||
| import threading | ||
| import time | ||
| from pathlib import Path | ||
|
|
@@ -72,6 +73,9 @@ | |
| _wal_fallback_warned_paths: set[str] = set() | ||
| _wal_fallback_warned_lock = threading.Lock() | ||
|
|
||
| _initialized_dbs: Dict[str, Dict[str, Any]] = {} | ||
| _db_init_lock = threading.Lock() | ||
|
|
||
| _FTS_TRIGGERS = ( | ||
| "messages_fts_insert", | ||
| "messages_fts_delete", | ||
|
|
@@ -421,7 +425,16 @@ def __init__(self, db_path: Path = None): | |
| apply_wal_with_fallback(self._conn, db_label="state.db") | ||
| self._conn.execute("PRAGMA foreign_keys=ON") | ||
|
|
||
| self._init_schema() | ||
| db_path_str = str(self.db_path.resolve()) | ||
| is_testing = "pytest" in sys.modules or "unittest" in sys.modules | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Disabling the cache whenever pytest or unittest is imported prevents the new branch from being covered by normal tests. Prefer explicit cache reset/test fixtures and add multi-connection tests for path scoping and invalidation behavior. |
||
| with _db_init_lock: | ||
| if not is_testing and db_path_str in _initialized_dbs: | ||
| self._fts_enabled = _initialized_dbs[db_path_str]["fts_enabled"] | ||
| else: | ||
| self._init_schema() | ||
| _initialized_dbs[db_path_str] = { | ||
| "fts_enabled": self._fts_enabled | ||
| } | ||
| except Exception as exc: | ||
| # Capture the cause so /resume and friends can surface WHY the | ||
| # session DB is unavailable instead of a bare "Session database | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This checks an ignored
/bin/hermes-pty-refactorartifact, but this PR contains no package, CI, Docker, or install step that builds or copies it there. Standard installs will therefore always take the Python fallback; add a supported distribution path before selecting this implementation.