fix(state): batch large session cleanup queries below SQLite's variable limit
prune_sessions(), delete_empty_sessions() and prune_empty_ghost_sessions() built one IN (?, ..., ?) clause containing every selected session id (the parent-orphaning UPDATE), so cleaning more than SQLITE_MAX_VARIABLE_NUMBER sessions failed atomically with "too many SQL variables". The per-row DELETE loops that followed are folded into the same 900-id batches. Same single _execute_write() transaction; only the binding is split. Hand-ported from PR #100658 (targeted the pre-decomposition hermes_state.py god file; the methods now live in hermes_state_maintenance.py / hermes_state_sessions.py). The one-pass transcript-directory sweep from that PR is not ported (out of scope for the variable-limit bug). Authored by @Mi55ed; ported under --author.
This commit is contained in:
@@ -8,7 +8,7 @@ from pathlib import Path
|
||||
from typing import Any, Dict, List, Optional, Tuple
|
||||
|
||||
from hermes_state_common import (
|
||||
AUTO_VACUUM_MIN_FREELIST_RATIO, _placeholders, _sql_session_last_active, escape_like as _escape_like
|
||||
AUTO_VACUUM_MIN_FREELIST_RATIO, _id_chunks, _placeholders, _sql_session_last_active, escape_like as _escape_like
|
||||
)
|
||||
|
||||
# caplog tests pin the "hermes_state" logger name.
|
||||
@@ -95,8 +95,9 @@ class SessionMaintenanceMixin:
|
||||
SELECT 1 FROM messages WHERE messages.session_id = sessions.id
|
||||
)
|
||||
""", (cutoff,)).fetchall()]
|
||||
for chunk in _id_chunks(ids):
|
||||
conn.execute(f"DELETE FROM sessions WHERE id IN ({_placeholders(chunk)})", chunk)
|
||||
if ids:
|
||||
conn.execute(f"DELETE FROM sessions WHERE id IN ({_placeholders(ids)})", ids)
|
||||
self._delete_unreferenced_system_prompts(conn)
|
||||
return ids
|
||||
removed_ids = self._execute_write(_do) or []
|
||||
@@ -282,12 +283,13 @@ class SessionMaintenanceMixin:
|
||||
if self._write_guards_reject(conn, sid, allow_closed_compression_parent=True)}
|
||||
if not session_ids:
|
||||
return 0
|
||||
conn.execute(f"UPDATE sessions SET parent_session_id = NULL "
|
||||
f"WHERE parent_session_id IN ({_placeholders(session_ids)})", list(session_ids))
|
||||
for sid in session_ids:
|
||||
conn.execute("DELETE FROM messages WHERE session_id = ?", (sid,))
|
||||
conn.execute("DELETE FROM sessions WHERE id = ?", (sid,))
|
||||
removed_ids.append(sid)
|
||||
# Batched: a cron-heavy store prunes tens of thousands of ids in one call.
|
||||
for chunk in _id_chunks(session_ids):
|
||||
ph = _placeholders(chunk)
|
||||
conn.execute(f"UPDATE sessions SET parent_session_id = NULL WHERE parent_session_id IN ({ph})", chunk)
|
||||
conn.execute(f"DELETE FROM messages WHERE session_id IN ({ph})", chunk)
|
||||
conn.execute(f"DELETE FROM sessions WHERE id IN ({ph})", chunk)
|
||||
removed_ids.extend(chunk)
|
||||
self._delete_unreferenced_system_prompts(conn)
|
||||
return len(session_ids)
|
||||
count = self._execute_write(_do)
|
||||
|
||||
@@ -1573,16 +1573,14 @@ class SessionSessionsMixin:
|
||||
).fetchall()}
|
||||
if not session_ids:
|
||||
return 0
|
||||
conn.execute(
|
||||
"UPDATE sessions SET parent_session_id = NULL "
|
||||
f"WHERE parent_session_id IN ({_session_ids_placeholders(session_ids)})", list(session_ids),
|
||||
)
|
||||
for sid in session_ids:
|
||||
for chunk in _id_chunks(session_ids):
|
||||
ph = _session_ids_placeholders(chunk)
|
||||
conn.execute(f"UPDATE sessions SET parent_session_id = NULL WHERE parent_session_id IN ({ph})", chunk)
|
||||
# DELETE FROM messages: a row inserted between the SELECT and here
|
||||
# would otherwise dangle (clean FK state).
|
||||
conn.execute("DELETE FROM messages WHERE session_id = ?", (sid,))
|
||||
conn.execute("DELETE FROM sessions WHERE id = ?", (sid,))
|
||||
removed_ids.append(sid)
|
||||
conn.execute(f"DELETE FROM messages WHERE session_id IN ({ph})", chunk)
|
||||
conn.execute(f"DELETE FROM sessions WHERE id IN ({ph})", chunk)
|
||||
removed_ids.extend(chunk)
|
||||
self._delete_unreferenced_system_prompts(conn)
|
||||
return len(session_ids)
|
||||
count = self._execute_write(_do)
|
||||
|
||||
Reference in New Issue
Block a user