From 2de42ba6901240eaefc256798db700866a7739ae Mon Sep 17 00:00:00 2001 From: Hermes Date: Sat, 21 Mar 2026 10:15:06 +0000 Subject: [PATCH] fix(state): add missing thread lock to session_count() and message_count() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Both methods accessed self._conn without self._lock, breaking the thread-safety contract documented on SessionDB (line 111). All 22 other DB methods use with self._lock — these two were the only exceptions. In the gateway's multi-threaded environment (multiple platform reader threads + single writer) this could cause cursor interleaving, sqlite3.ProgrammingError, or inconsistent COUNT results. Closes #2130 --- hermes_state.py | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/hermes_state.py b/hermes_state.py index 34b553dc6..c8a59060c 100644 --- a/hermes_state.py +++ b/hermes_state.py @@ -855,23 +855,25 @@ class SessionDB: def session_count(self, source: str = None) -> int: """Count sessions, optionally filtered by source.""" - if source: - cursor = self._conn.execute( - "SELECT COUNT(*) FROM sessions WHERE source = ?", (source,) - ) - else: - cursor = self._conn.execute("SELECT COUNT(*) FROM sessions") - return cursor.fetchone()[0] + with self._lock: + if source: + cursor = self._conn.execute( + "SELECT COUNT(*) FROM sessions WHERE source = ?", (source,) + ) + else: + cursor = self._conn.execute("SELECT COUNT(*) FROM sessions") + return cursor.fetchone()[0] def message_count(self, session_id: str = None) -> int: """Count messages, optionally for a specific session.""" - if session_id: - cursor = self._conn.execute( - "SELECT COUNT(*) FROM messages WHERE session_id = ?", (session_id,) - ) - else: - cursor = self._conn.execute("SELECT COUNT(*) FROM messages") - return cursor.fetchone()[0] + with self._lock: + if session_id: + cursor = self._conn.execute( + "SELECT COUNT(*) FROM messages WHERE session_id = ?", (session_id,) + ) + else: + cursor = self._conn.execute("SELECT COUNT(*) FROM messages") + return cursor.fetchone()[0] # ========================================================================= # Export and cleanup