refactor(hermes_state): reuse utils.safe_json_loads, one tolerant topic reader, one registry path resolver
This commit is contained in:
@@ -75,6 +75,17 @@ def _teardown(db: "SessionDB") -> None:
|
||||
logger.debug("Error closing shared SessionDB", exc_info=True)
|
||||
|
||||
|
||||
def _db_path_of(db: "SessionDB") -> Optional[Path]:
|
||||
"""``Path(db.db_path)`` or None when absent/unconvertible."""
|
||||
path = getattr(db, "db_path", None)
|
||||
if path is None:
|
||||
return None
|
||||
try:
|
||||
return Path(path)
|
||||
except (TypeError, ValueError):
|
||||
return None
|
||||
|
||||
|
||||
def _finish_opening(path: Path, opening: threading.Event) -> None:
|
||||
"""Drop the per-path construction marker and wake waiters (caller holds _lock)."""
|
||||
if _opening.get(path) is opening:
|
||||
@@ -162,13 +173,9 @@ def release(db: "SessionDB") -> bool:
|
||||
with _lock:
|
||||
generation = _retired.get(key)
|
||||
if generation is None:
|
||||
path = getattr(db, "db_path", None)
|
||||
path = _db_path_of(db)
|
||||
if path is None:
|
||||
return False
|
||||
try:
|
||||
path = Path(path)
|
||||
except (TypeError, ValueError):
|
||||
return False
|
||||
generation = _generations.get(path)
|
||||
if generation is None or generation.db is not db:
|
||||
# Not shared (bare SessionDB()); the caller owns close().
|
||||
@@ -179,12 +186,9 @@ def release(db: "SessionDB") -> bool:
|
||||
if generation.retired:
|
||||
_retired.pop(key, None)
|
||||
else:
|
||||
path = getattr(db, "db_path", None)
|
||||
path = _db_path_of(db)
|
||||
if path is not None:
|
||||
try:
|
||||
_generations.pop(Path(path), None)
|
||||
except (TypeError, ValueError):
|
||||
pass
|
||||
_generations.pop(path, None)
|
||||
# Teardown OUTSIDE the lock: stopping the token writer, WAL checkpoint and read-pool
|
||||
# drain must not block acquisition for every other state.db.
|
||||
if needs_teardown:
|
||||
|
||||
@@ -18,6 +18,7 @@ from typing import Dict, List, Optional, Sequence
|
||||
|
||||
from hermes_constants import get_hermes_home
|
||||
from hermes_startup_watchdog import report_startup_progress
|
||||
from utils import safe_json_loads
|
||||
from hermes_state_common import (
|
||||
DEFERRED_INDEX_SQL, FTS_CJK_STALE_KEY, FTS_REBUILD_DEFERRAL_KEY, FTS_STALE_KEY, FTS_SQL,
|
||||
FTS_STORAGE_VERSION, FTS_TRIGRAM_SQL, LEGACY_FTS_SQL, LEGACY_FTS_TRIGRAM_SQL, SCHEMA_SQL,
|
||||
@@ -356,12 +357,12 @@ class SessionSchemaMixin:
|
||||
row = cursor.execute(
|
||||
"SELECT value FROM state_meta WHERE key = ? LIMIT 1", (FTS_REBUILD_DEFERRAL_KEY,),
|
||||
).fetchone()
|
||||
if row:
|
||||
parsed = json.loads(row[0])
|
||||
if isinstance(parsed, dict):
|
||||
record = parsed
|
||||
except (sqlite3.Error, TypeError, ValueError, json.JSONDecodeError):
|
||||
record = {}
|
||||
except sqlite3.Error:
|
||||
row = None
|
||||
if row:
|
||||
parsed = safe_json_loads(row[0])
|
||||
if isinstance(parsed, dict):
|
||||
record = parsed
|
||||
try:
|
||||
first_seen = float(record.get("first_seen", now))
|
||||
attempts = int(record.get("attempts", 0)) + 1
|
||||
@@ -570,17 +571,11 @@ class SessionSchemaMixin:
|
||||
from hermes_constants import get_hermes_home as _home
|
||||
cache_path = _home() / "cache" / "schema_columns.json"
|
||||
blob = json.loads(cache_path.read_text(encoding="utf-8"))
|
||||
if (
|
||||
isinstance(blob, dict)
|
||||
and blob.get("schema_hash") == schema_hash
|
||||
and isinstance(blob.get("tables"), dict)
|
||||
tables = blob.get("tables") if isinstance(blob, dict) and blob.get("schema_hash") == schema_hash else None
|
||||
if isinstance(tables, dict) and all(
|
||||
isinstance(cols, dict) and all(isinstance(v, str) for v in cols.values()) for cols in tables.values()
|
||||
):
|
||||
tables = blob["tables"]
|
||||
if all(
|
||||
isinstance(cols, dict) and all(isinstance(v, str) for v in cols.values())
|
||||
for cols in tables.values()
|
||||
):
|
||||
return tables
|
||||
return tables
|
||||
except Exception:
|
||||
pass # missing/corrupt cache → recompute below
|
||||
|
||||
|
||||
@@ -103,17 +103,18 @@ class SessionTelegramTopicsMixin:
|
||||
|
||||
def _topic_read_one(self, sql: str, params, default=None):
|
||||
"""``fetchone`` that treats an unmigrated table as *default*."""
|
||||
try:
|
||||
return self._read_one(sql, params)
|
||||
except sqlite3.OperationalError:
|
||||
return default
|
||||
return self._topic_read(self._read_one, sql, params, default)
|
||||
|
||||
def _topic_read_all(self, sql: str, params) -> list:
|
||||
"""``fetchall`` that treats an unmigrated table as no rows."""
|
||||
return self._topic_read(self._read_all, sql, params, [])
|
||||
|
||||
@staticmethod
|
||||
def _topic_read(reader, sql: str, params, default):
|
||||
try:
|
||||
return self._read_all(sql, params)
|
||||
return reader(sql, params)
|
||||
except sqlite3.OperationalError:
|
||||
return []
|
||||
return default
|
||||
|
||||
def apply_telegram_topic_migration(self) -> None:
|
||||
"""Create Telegram DM topic-mode tables on explicit /topic opt-in. Deliberately NOT
|
||||
|
||||
Reference in New Issue
Block a user