Skip to content

sessions: relation refresh rereads the whole session per appended message #2607

Closed

Description

@ScriptedAlchemy

Summary

With the git-convergence and derived-anchor full scans from #2521 removed, the largest remaining reads under a steady ingest stream come from session-temporal relation projection. It re-reads every occurrence of the refreshed session generation together with its observations.observation_json and retrieval_anchors.anchor_json (the SELECT occurrence.occurrence_id, occurrence.retrieval_anchor_id, anchor.anchor_json, occurrence.message_id, occurrence.agent_id, occurrence.projection_output_ordinal, occurrence.knowledge_at, occurrence.valid_time_json, observation.observation_json ... page in crates/tracedecay-session-temporal-store). Each appended message therefore costs O(size of its session), not O(new bytes).

Evidence (isolated profile, synthetic corpus: 31k project messages, 8 live transcripts receiving one message every 0.5 s)

  • 183 runs in a ~3 minute window read 507 MB: 2.8 MB average and 9.7 MB maximum per run, the top statement by bytes.
  • The largest live transcripts in the stream hold about 100–400 messages. Operator sessions reach thousands, so the per-message cost grows with session length.

Expected

A refresh that admits k new messages reads O(k) occurrence and observation rows plus bounded generation metadata. Carry forward the settled prefix instead of rehydrating it. Found while root-causing #2521.

Activity

  1. ScriptedAlchemy commented on Sep 29, 2026

    @ScriptedAlchemy
    OwnerAuthor

    Status after #2639 (fd451f3) and #2646 (9606347): not fixed. This is now the largest store-scaled read on the stream path.

    Root cause (source). Every refresh completion works on the whole session generation:

    • It builds a new candidate generation by copying every row of the active one (GENERATION_COPY_STATEMENTS: occurrences, turns, derived evidence, FTS).
    • It rebuilds relations from every occurrence plus its observation_json and anchor_json (rebuild_candidate_session_relations → reconstruct_session_relation_projection → reconstruct_occurrences).
    • It recomputes the final projection receipt digests over every generation table inside the completion transaction (validate_final_projection_receipt, copy_identity_coverage via reconstruct_logical_copy_relations).

    So each refresh is O(session) in reads and writes by construction. "Carry the settled prefix forward" needs a change to the generation model, not a query fix:

    • Append the batch to a generation, or share unchanged rows copy-on-write, instead of copying the session.
    • Fold new occurrences into the active generation's already-applied relation projection. It needs a lightweight message/anchor → occurrence index from session_occurrences columns, not observation_json.
    • Chain the receipt digest (previous digest + batch digest) instead of re-hashing every row.

    Evidence (isolated profile, 1,200-transcript synthetic corpus, 8 live transcripts at one message per 0.5 s, per-statement SQLite profile). Two consecutive 300 s windows on the same store; each window adds about 75 messages to each live session:

    window streamed msgs generation path (copy + relation rebuild + receipt digests) share of attributed reads per streamed msg
    1 449 1.45 GB 43% 3.23 MB
    2 452 3.27 GB 50% 7.23 MB

    The new work per message is the same in both windows. Per-message cost doubled because the sessions got longer. The relation reconstruct query alone went from 662 MB (87 runs) to 1.23 GB (108 runs). Hotpath session_temporal.txn.complete_refresh went from avg 434 ms to 674 ms, and session_temporal.rebuild.relations from 102 ms to 151 ms.

    Leaving this open for that design; #2521 stays open on it.

  2. added a commit that references this issue on Oct 1, 2026
    9d91e64
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions