fix: discover sessions from persisted messages (#5938)

* fix(session): discover sessions from persisted messages

Use indexed chat-row existence instead of stale derived message_count metadata during startup discovery, then repair the bounded in-memory counts so lazy hydration remains correct. Keep truly empty sessions excluded and cover stale-low and stale-high counts with real SQLite.

* test(session): isolate discovery database

* test(session): use manager database metadata
This commit is contained in:
RaresKeY
2026-08-16 23:34:27 +01:00
committed by GitHub
parent db05175e3e
commit 0728b994d8
2 changed files with 79 additions and 1 deletions
+17 -1
View File
@@ -14,6 +14,8 @@ import logging
from datetime import datetime, timezone, timedelta from datetime import datetime, timezone, timedelta
from typing import Dict, Optional from typing import Dict, Optional
from sqlalchemy import func
from .database import Session as DbSession, ChatMessage as DbChatMessage, Document as DbDocument, SessionLocal, utcnow_naive from .database import Session as DbSession, ChatMessage as DbChatMessage, Document as DbDocument, SessionLocal, utcnow_naive
from .models import Session, ChatMessage from .models import Session, ChatMessage
from src.attachment_refs import persistable_message_content from src.attachment_refs import persistable_message_content
@@ -92,14 +94,28 @@ class SessionManager:
try: try:
db_sessions = db.query(DbSession).filter( db_sessions = db.query(DbSession).filter(
DbSession.archived == False, DbSession.archived == False,
DbSession.message_count > 0, DbSession.messages.any(),
).order_by(DbSession.last_accessed.desc()).limit(100).all() ).order_by(DbSession.last_accessed.desc()).limit(100).all()
# message_count is derived metadata and can drift after interrupted
# or legacy writes. Count only the bounded discovery set so startup
# remains metadata-only while lazy hydration sees an authoritative
# positive count for every discovered non-empty session.
message_counts = {}
if db_sessions:
message_counts = dict(
db.query(DbChatMessage.session_id, func.count(DbChatMessage.id))
.filter(DbChatMessage.session_id.in_([row.id for row in db_sessions]))
.group_by(DbChatMessage.session_id)
.all()
)
loaded_count = 0 loaded_count = 0
for db_session in db_sessions: for db_session in db_sessions:
try: try:
session = self._db_to_session_meta(db_session) session = self._db_to_session_meta(db_session)
if session is not None: if session is not None:
session.message_count = message_counts[db_session.id]
self.sessions[db_session.id] = session self.sessions[db_session.id] = session
loaded_count += 1 loaded_count += 1
except Exception as e: except Exception as e:
@@ -0,0 +1,62 @@
"""Real-SQLite regressions for session discovery with stale derived counts."""
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from sqlalchemy.pool import NullPool
import core.session_manager as session_manager
def _make_manager(db_path, monkeypatch):
engine = create_engine(
f"sqlite:///{db_path}",
connect_args={"check_same_thread": False},
poolclass=NullPool,
)
session_manager.DbSession.metadata.create_all(bind=engine)
session_local = sessionmaker(
bind=engine,
autoflush=False,
autocommit=False,
)
monkeypatch.setattr(session_manager, "SessionLocal", session_local)
return session_manager.SessionManager(), session_manager
def test_discovery_uses_persisted_rows_and_repairs_cached_count(tmp_path, monkeypatch):
from core.models import ChatMessage
manager, session_manager = _make_manager(
tmp_path / "session-discovery.db", monkeypatch
)
for session_id in ("stale-low", "empty", "stale-high"):
manager.create_session(
session_id=session_id,
name=session_id,
endpoint_url="http://example.invalid",
model="test-model",
rag=False,
owner="tester",
)
manager.add_message("stale-low", ChatMessage("user", "persisted message"))
db = session_manager.SessionLocal()
try:
db.query(session_manager.DbSession).filter(
session_manager.DbSession.id == "stale-low"
).update({"message_count": 0})
db.query(session_manager.DbSession).filter(
session_manager.DbSession.id == "stale-high"
).update({"message_count": 7})
db.commit()
finally:
db.close()
restarted = session_manager.SessionManager()
assert set(restarted.sessions) == {"stale-low"}
assert restarted.sessions["stale-low"].history == []
assert restarted.sessions["stale-low"].message_count == 1
hydrated = restarted.get_session("stale-low")
assert [message.content for message in hydrated.history] == ["persisted message"]