diff --git a/api/agent_sessions.py b/api/agent_sessions.py index 677bb096..31e32fd9 100644 --- a/api/agent_sessions.py +++ b/api/agent_sessions.py @@ -19,6 +19,7 @@ MESSAGING_SOURCES = { CLI_MIN_UNTITLED_MESSAGE_COUNT = 6 CLI_MIN_UNTITLED_USER_MESSAGE_COUNT = 2 +_CRON_PREAGGREGATE_CANDIDATE_ORDER_MIN_MESSAGES = 100000 SOURCE_LABELS = { 'api_server': 'API', @@ -505,6 +506,8 @@ def read_importable_agent_session_rows( # self-heals an affected db in milliseconds. Best-effort: degrade # silently on a read-only db or any error so the listing never fails # because of the prime. + messages_index_ready = False + messages_rowid_upper_bound = None if messages_has_session_id and messages_has_timestamp: try: cur.execute( @@ -512,8 +515,15 @@ def read_importable_agent_session_rows( "ON messages(session_id, timestamp)" ) conn.commit() + messages_index_ready = True except sqlite3.Error: pass # read-only db / locked / older schema — degrade gracefully + try: + cur.execute("SELECT MAX(rowid) FROM messages") + row = cur.fetchone() + messages_rowid_upper_bound = int(row[0] or 0) if row else 0 + except sqlite3.Error: + messages_rowid_upper_bound = None if use_messages_join: actual_count_expr = f"COUNT(m.{count_col})" @@ -554,7 +564,13 @@ def read_importable_agent_session_rows( params.extend(excluded) use_preaggregated_candidate_order = ( - use_messages_join and messages_has_timestamp and included == ("cron",) + use_messages_join + and messages_has_timestamp + and included == ("cron",) + and ( + not messages_index_ready + or (messages_rowid_upper_bound or 0) >= _CRON_PREAGGREGATE_CANDIDATE_ORDER_MIN_MESSAGES + ) ) if use_preaggregated_candidate_order: order_by_clause = "ORDER BY COALESCE(MAX(m.timestamp), s.started_at) DESC" diff --git a/tests/test_issue2628_cli_sessions_perf.py b/tests/test_issue2628_cli_sessions_perf.py index 6f08d77e..608d9242 100644 --- a/tests/test_issue2628_cli_sessions_perf.py +++ b/tests/test_issue2628_cli_sessions_perf.py @@ -201,6 +201,7 @@ def test_importable_agent_rows_push_sidebar_limit_into_sql(tmp_path): assert "JOIN candidates c ON c.id = s.id" in src assert "latest_messages AS" in src assert 'included == ("cron",)' in src + assert "_CRON_PREAGGREGATE_CANDIDATE_ORDER_MIN_MESSAGES" in src assert "MAX(mx.timestamp) FROM messages mx WHERE mx.session_id = s.id" in src assert "candidate_limit = max(result_limit * 8, result_limit)" in src @@ -211,8 +212,8 @@ def test_importable_agent_rows_candidate_ordering_respects_progress_budget(tmp_p _make_state_db( db, sessions=120, - messages_per_session=120, - create_messages_index=False, + messages_per_session=900, + create_messages_index=True, source="cron", session_source="cron", )