Skip to content

perf(console): skip the feed-poll re-render on unchanged ticks — client-side 304 (v0.292.1) - #532

Merged
vikasprogrammer merged 1 commit into
mainfrom
feat/sessions-delta
Aug 3, 2026
Merged

perf(console): skip the feed-poll re-render on unchanged ticks — client-side 304 (v0.292.1)#532
vikasprogrammer merged 1 commit into
mainfrom
feat/sessions-delta

Conversation

@vikasprogrammer

@vikasprogrammer vikasprogrammer commented Aug 3, 2026

Copy link
Copy Markdown
Owner

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/sessions list. 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/messagesFeedcallFeed, 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 any derived change (a run going 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 explicit If-None-Match is authoritative; gzip still applies on the ticks that do change (accept-encoding is independent of cache mode).
  • Server is unchangedperf(console): gzip + ETag + immutable asset caching — 6.5 MB → 1.4 MB per load (v0.291.6) #530's shared sendBody already tags + 304s every response.

Verification

In-process round-trip against the #530 sendBody server: explicit If-None-Match304 empty, stale ETag → 200 + body, a new session flips the weak ETag. Byte-stability across unchanged ticks confirmed. Typecheck + web build + npm run test:governance green.

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

…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 vikasprogrammer changed the title perf(console): conditional ETag/304 on the sessions + messages feed poll (v0.291.5) perf(console): skip the feed-poll re-render on unchanged ticks — client-side 304 (v0.292.1) Aug 3, 2026
@vikasprogrammer
vikasprogrammer merged commit d8f9d1f into main Aug 3, 2026
1 check passed
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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant