Repository navigation
fix(conversations): bound reconnect walk + memoize turn hashes — 10-130s pre-routing stall on long agent histories - #10800
Merged
diegosouzapw merged 1 commit intoAug 20, 2026
Conversation
…souzapw#7847) resolveConversationId runs once per chat request in the validate phase, before routing (src/sse/handlers/chat.ts). Its reconnect walk evaluates every (start turn x duplicate anchor) pair and walks the chain forward, computing two HMACs per step with the turn FULL text re-hashed at every step. On long coding-agent histories (1000+ turns, heavily duplicated tool outputs) that is O(starts x anchors x walkLength) re-hashing of multi-KB content, synchronous on the request path. Measured on a production deployment routing a real 1500-message / 92-tool agent session: POST -> ROUTING p50 12.6s (n=132, max 130s) for requests >= 200 messages, while <200-message requests on the same instance routed in 0.2s p50. A local repro of the exact duplicate-heavy shape spent 17.2s inside resolveConversationId before this fix. - Memoize hashTurnContent per request: each turn is hashed once; every walk step then hashes ~130 fixed-size bytes (parentId + turnHash). - Bound the walk with a shared step budget (DEFAULT_RECONNECT_MAX_STEPS = 150000) across all fingerprint candidates, starts and anchors. Budget exhaustion degrades exactly like a no-match (a new conversation is minted downstream), never an unverified attachment. - Export findReconnectMatch with { turnHashes, maxSteps, budget } so the bound and memoization are directly unit-testable. Tests: tests/unit/conversationTracker-reconnect-7847.test.ts pins the budget cap, zero-budget no-match degradation, and a wall-clock bound on a duplicate-heavy 800-turn resolve. All existing conversationTracker semantics tests pass unchanged.
This was referenced Aug 20, 2026
Merged
muhamadgalihsaputra
pushed a commit
to niyatna/NiyatnaRoute
that referenced
this pull request
Sep 27, 2026
…souzapw#7847) (diegosouzapw#10800) Obrigado — bug de produção real e muito bem raiz-causado: resolveConversationId travava a request path por 10-130s em históricos longos de agente (medido em produção: p50 12.6s / max 130.2s em requests com ≥200 mensagens), por re-hashear o texto completo de cada turno a cada passo do walk de reconexão (O(starts × anchors × walkLength) HMACs síncronos). Fix cirúrgico: memoiza o hash de cada turno por request + budget de passos compartilhado entre candidatos (degrada como no-match, nunca como attach não verificado ou latência ilimitada). Resultado medido: 17.2s → 0.3s no repro. Validação (worktree combinado a partir de origin/release/v3.8.50, 0 conflitos): - typecheck:core limpo, complexity/cognitive-complexity dentro do baseline - tests/unit/conversationTracker-reconnect-7847.test.ts (novo) — cobre o cap de budget, degradação com budget zero, e guarda de regressão de wall-clock (falha em 17.2s pré-fix) - 17 testes de semântica pré-existentes + conversationTurnContent (5) — passando sem alteração
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
resolveConversationId(the agentic conversation tracker,open-sse/services/conversationTracker.ts) blocks the chat request path for 10–130 s on long coding-agent histories. On a production deployment routing a real 1500-message / 92-tool agent session, measuredPOST → ROUTINGlatency was:The stall grows as the conversation grows (early turns 0.2 s → late turns 130 s), and its synchronous work also starves unrelated endpoints — a trivial
GET /healthzon the same instance spiked to 11.1 s during large-request processing. This is the request-path face of the long-context cost family tracked in #7847.Root cause
resolveConversationIdruns once per chat request in the validate phase, before routing (src/sse/handlers/chat.ts,resolveConversationIdcall). To detect continuation it must find where the incoming turn list reconnects to a recorded chain.findReconnectMatch:chainNodeIdper step — twocreateHmac("sha256")calls per step, with the turn's full (often multi-KB) text re-hashed at every step (no memoization).That is
O(starts × anchors × walkLength)full-content HMACs, synchronous on the request path. A local repro of the exact duplicate-heavy shape (800 turns, alternating identical"ok"tool outputs and 40 KB file-content turns, second request editing every large turn) spent 17.2 s insideresolveConversationIdon otherwise-idle hardware.Fix (two surgical changes in
conversationTracker.ts)turnHashes); every walk step then hashes ~130 fixed-size bytes (parentId + turnHash) instead of re-hashing the turn's full text twice.buildNewNodesreuses the same memoized hashes.DEFAULT_RECONNECT_MAX_STEPS = 150_000), charged one step per anchor claim and per forward step, shared across all fingerprint candidates. Budget exhaustion degrades exactly like a no-match — the request mints a new conversation — never an unverified attachment and never unbounded latency.findReconnectMatchis now exported with{ turnHashes, maxSteps, budget }options so both properties are directly unit-testable.Result on the repro above: 17.2 s → 0.3 s (suite runs in ~1.2 s including DB setup). Selection semantics are unchanged: longest verified run still wins, ties still break toward the childless anchor, edits still mint a new conversation, compaction-window reconnection still works.
Tests
tests/unit/conversationTracker-reconnect-7847.test.ts(new):maxSteps: 50reportsstepsUsed ≤ 50;maxSteps: 0;DATA_DIRand close the DB handle intest.after.All 17 existing
conversationTracker.test.tssemantics tests pass unchanged, plusconversationTurnContent.test.ts(5).npm run typecheck:coreclean; scoped eslint + prettier clean; full husky pre-commit gates passed.Notes