perf(console): skip the feed-poll re-render on unchanged ticks — client-side 304 (v0.292.1) - #532
Merged
Merged
Conversation
…t-side 304) (v0.292.1) #530 gave every response a wire-level ETag + gzip, so an idle tab already stops re-downloading the ~1.65 MB sessions list. But browser auto-revalidation still hands the cached body back to JS, so the console kept re-parsing + re-rendering the whole list every 1.5s regardless. The global feed poll now sends its last ETag explicitly (sessionsFeed/messagesFeed → callFeed, cache:'no-store') and, on a 304, resolves notModified so the poll SKIPS setState — no re-parse, no React re-render. The tag covers the fully-computed response, so a derived change (blocked/crashed/cost/new card) still flips it and the badge/bells never go stale. Complements #530 (which keeps the transfer + gzip on the ticks that DO change). Server side is unchanged — the shared sendBody already tags + 304s. Verified: in-process round-trip against the #530 sendBody server — explicit If-None-Match → 304 empty, stale → 200, a new session flips the weak ETag. Interop confirmed. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
vikasprogrammer
force-pushed
the
feat/sessions-delta
branch
from
August 3, 2026 07:29
0ddca86 to
ad20d3f
Compare
vikasprogrammer
added a commit
that referenced
this pull request
Aug 3, 2026
GET /api/sessions has always shipped `task` clipped to 240 chars, but the server still SELECT *'d every session's FULL prompt out of SQLite — up to 53 KB/row on instawp, 2.1 MB materialised per 1.5s poll — only for server.ts to throw it away. listSessions/listArchivedSessions now take an optional taskClip; the list endpoint passes LIST_CLIP and the SELECT projects substr(task,1,241) AS task (schema-derived column list), so SQLite stops materialising the overflow text. Measured on a live instawp snapshot (950 rows): task bytes 2.10 MB → 201 KB, raw query 5.23 → 3.53 ms (-33%), full listSessions(owner) 13.3 → 11.1 ms (-17%) per poll, plus ~1.9 MB less string allocation each tick. Output is byte-identical — clipText still runs as the ellipsis-preserving finisher on the ≤241-char string (verified across all 950 rows, 746 of them >240). Internal callers that read the whole prompt (sessionsForAgent, Cockpit context) pass no clip and keep the full SELECT *. Follow-on to #530/#532/#533; pagination (the structural fix) still open. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
vikasprogrammer
added a commit
that referenced
this pull request
Aug 3, 2026
…#535) GET /api/sessions has always shipped `task` clipped to 240 chars, but the server still SELECT *'d every session's FULL prompt out of SQLite — up to 53 KB/row on instawp, 2.1 MB materialised per 1.5s poll — only for server.ts to throw it away. listSessions/listArchivedSessions now take an optional taskClip; the list endpoint passes LIST_CLIP and the SELECT projects substr(task,1,241) AS task (schema-derived column list), so SQLite stops materialising the overflow text. Measured on a live instawp snapshot (950 rows): task bytes 2.10 MB → 201 KB, raw query 5.23 → 3.53 ms (-33%), full listSessions(owner) 13.3 → 11.1 ms (-17%) per poll, plus ~1.9 MB less string allocation each tick. Output is byte-identical — clipText still runs as the ellipsis-preserving finisher on the ≤241-char string (verified across all 950 rows, 746 of them >240). Internal callers that read the whole prompt (sessionsForAgent, Cockpit context) pass no clip and keep the full SELECT *. Follow-on to #530/#532/#533; pagination (the structural fix) still open. Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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.
Context
Follow-on to #530. #530 gave every response a wire-level ETag + gzip, so an idle console tab already stops re-downloading the ~1.65 MB
/api/sessionslist. But it relies on browser auto-revalidation, which still hands the cached body back to JS — so the console kept re-parsing and re-rendering the whole list every 1.5 s regardless. On a large-list tenant (instawp, ~946 sessions) that's a real recurring main-thread cost.Change (client-only)
The global 1.5 s feed poll now sends its last ETag explicitly (
sessionsFeed/messagesFeed→callFeed,cache: 'no-store') and, on a 304, resolvesnotModifiedso the poll skipssetState— no re-parse, no React re-render.blocked/crashed, a cost backfill, a new inbox card) still flips it — the tab-title badge and per-session waiting bells never go stale.cache: 'no-store'keeps the browser cache out of the loop so our explicitIf-None-Matchis authoritative; gzip still applies on the ticks that do change (accept-encoding is independent of cache mode).sendBodyalready tags + 304s every response.Verification
In-process round-trip against the #530
sendBodyserver: explicitIf-None-Match→ 304 empty, stale ETag → 200 + body, a new session flips the weak ETag. Byte-stability across unchanged ticks confirmed. Typecheck + web build +npm run test:governancegreen.Remaining (separate follow-ups)
Neither #530 nor this cuts the server-side query cost — the sessions list is still built every tick (
SELECT *incl. full task text). The instawp server pain (memory + event-loop) needs: (1)substr(task,…)in the query, (2) reap idle resident sessions.🤖 Generated with Claude Code