fix(sessions): share the write-guard filter and trim repeated walks

prune_sessions hand-rolled the same "guarded by a live lease/lock"
comprehension as the new _guarded_ids helper, so the two could drift on
the next guard change. Move _guarded_ids next to _write_guards_reject in
the maintenance mixin and have prune call it.

delete_session walked the delegate tree up to three times in one write
transaction; compute the target ids once for both the guard check and the
expected-ids fence. delete_sessions ran a per-root guard walk for every
selected id; do one batched check over all roots and their children first
and only attribute per root when something is actually guarded.

The CLI export --delete message repeated "session 'X'" because the
exception text already names the session.
This commit is contained in:
kshitijk4poor
2026-09-27 16:34:22 +05:30
committed by kshitij
parent e78e7ccdeb
commit 230f89b47b
3 changed files with 21 additions and 19 deletions

View File

@@ -5,7 +5,7 @@ from __future__ import annotations
import logging
import time
from pathlib import Path
from typing import Any, Dict, List, Optional, Tuple
from typing import Any, Dict, Iterable, List, Optional, Tuple
from hermes_state_common import (
AUTO_VACUUM_MIN_FREELIST_RATIO, _id_chunks, _non_continuation_child_sql, _placeholders, _sql_session_last_active,
@@ -118,6 +118,11 @@ class SessionMaintenanceMixin:
self._remove_session_files(sessions_dir, sid)
return len(removed_ids)
def _guarded_ids(self, conn, ids: Iterable[str]) -> set:
"""Ids in *ids* protected by a live turn lease / compression lock. Idle compression-ended
parents are closed, not live, so they are not guarded (prune and delete share this)."""
return {sid for sid in ids if self._write_guards_reject(conn, sid, allow_closed_compression_parent=True)}
def _write_guards_reject(self, conn, sid: str, **kwargs) -> bool:
"""True when a live turn lease / compression lock protects ``sid``; expired or
dead-holder guards are reclaimed and fenced as a side effect."""
@@ -308,8 +313,7 @@ class SessionMaintenanceMixin:
cursor = conn.execute(f"SELECT s.id FROM sessions s WHERE {where}", where_params)
session_ids = {row["id"] for row in cursor.fetchall()}
if exclude_active_write_guards:
session_ids -= {sid for sid in session_ids
if self._write_guards_reject(conn, sid, allow_closed_compression_parent=True)}
session_ids -= self._guarded_ids(conn, session_ids)
if not session_ids:
return 0
# Batched: a cron-heavy store prunes tens of thousands of ids in one call.