Skip to content

perf: fetch only what is shown, pause while hidden, stop drawing what nobody sees - #28

Merged
mikim merged 1 commit into
mainfrom
claude/network-cpu-diet
Sep 22, 2026
Merged

mikim merged 1 commit into
mainfrom
claude/network-cpu-diet

Conversation

@mikim

@mikim mikim commented Sep 21, 2026

Copy link
Copy Markdown
Contributor

Stacked on #27 (which is stacked on #26) — merge in order. This PR's diff is only its own commit.

Network: ~186 MB an hour per open tab → ~3.4 MB

Every 15 s the monitor re-downloaded AO's debate list and plans, its ideas, and Algora's issues:

Read Per 15 s (gzip) What the UI used
/ao-api/debates?limit=10 470 kB (2.2 MB raw) a topic and 100 characters of context
/ao-api/plans?limit=20 237 kB .length, in a fallback, when /ao-api/status already has plans_created
/ao-api/ideas?limit=30 92 kB the idea bubbles
/algora-api/issues?limit=30 9 kB nothing (last reader removed in def839a)

AO's debate list changes about four times a day (a debate starts every 6 h). It was fetched 5,760 times a day, with no ETag or Cache-Control to spare the bytes. A hidden tab kept exactly the same schedule.

Now each read has its own schedule, on a small PollChain (src/services/poll-chain.ts). It chains runs and never overlaps them, has a real in-flight guard, supports pause and resume, and re-arms even after a throw.

  • Signals and stats: every 15 s. These are what LIVE, the queue and the sidebar stand on. They are unchanged, with a 10 s timeout.
  • AO ideas, AO project total, Bridge outcomes and trust: every 5 min. 30 s timeout, retried after 1 min on failure.
  • The three latest AO debates: every 10 min (limit=3, 141 kB gzip).
  • Issues and plans: no longer fetched. This follows a860750, which dropped the Bridge proposal list for the same reason.
  • Hidden tab: polling pauses on visibilitychange. On show, whatever fell due runs at once and the rest wait out their remaining time. The first load always completes, so a tab opened in the background has something to show.

Measured through a byte-for-byte production-like proxy (CDP encodedDataLength):

Before After
Transferred per hour (visible) 185.7 MB ~3.4 MB
Decoded JSON per hour 806 MB ~47 MB
Requests while hidden (40 s, real hidden tab) 26 0

The biggest remaining item is /algora-api/signals?limit=30, at ~150–190 kB decoded per 15 s, half of it served from the HTTP cache. It stays on the 15 s tier as a possible follow-up.

Totals that were fetch sizes

When stats were missing, the AO funnel fell back to ideaCache.length and planCache.length. Those are 30 and 20, our page sizes, shown as AO's totals. The funnel now shows AO's own figures: ideas_generated, plans_created, and the project list envelope's total (fetched with limit=1). A missing figure shows . Bridge's ?? 0 fallbacks become as well. A missing value is not a measured zero.

CPU: ~40 ms → ~0.55 ms of main-thread time per Map frame

Phaser rebuilds a Graphics object's triangles on every render, turns every arc into ~100 points whatever its radius, and culls nothing by bounds. So:

  • Stars. 520 stars of 1–4 px were each a 100-point polygon, re-triangulated every frame (~20 ms of the frame). They are now squares of the same area (h = size·√π/2). At that size the two look the same, and brightness is kept.
  • Belt zones on the map. They sat under the map's opaque backdrop and outside the camera, and were still tessellated every frame (~14 ms). They are now hidden while the map is shown and shown as soon as a belt tab is picked. Their update() keeps running, so Algora still drains the signal queue and each Graphics is current on its first visible frame.

The numbers are from headless Chrome with SwiftShader, so read the ratio rather than the absolute figure. At 375×812 the main thread was saturated before (99.8% busy). Screenshots of the map and every belt tab look the same as before at 1440×900 and 375×812.

Also: the sidebar skips innerHTML rewrites when the markup is unchanged, which stops rebuilding the Service I/O panel every 500 ms. The CentralMonitor strip reads its cadence from the constants (Polling: 15s / 5–10m).

What is verified, and what is not

  • ✅ typecheck, 119 tests, build, and check-health-json
  • ✅ New tests: PollChain (chaining, in-flight guard, pause and resume timing, never two chains), DataBridge cadences and timeouts, a resume during init, and funnel/gauge totals. A test pins the constants to the values the README states.
  • ✅ Browser checks with real data: the Algora belt fills after 60 s on the map; AO shows a real debate and live totals; Bridge figures render; switching map → belt → map drops no graphics for a frame; the console is clean
  • ✅ A real hidden tab (a second target activated) makes no requests, and one reachability cycle fires on show
  • ⚠️ Needs AO server work to shrink further: a list view of debates without ideas_generated and final_plan would cut the remaining ~141 kB per 10 min

🤖 Generated with Claude Code

… nobody sees

Every 15 s the monitor re-downloaded AO's debates (2.2 MB for 10 rows, to show
a topic and 100 characters), plans (read only as a length), ideas, and
Algora's issues (read by nothing) — ~186 MB an hour per open tab, and a hidden
tab kept the same schedule. Each read now has its own schedule on a small
PollChain: signals and stats every 15 s (they carry LIVE and the sidebar),
ideas / project total / Bridge outcomes and trust every 5 min, the three
latest debates every 10 min, each with its own timeout and a 1 min retry.
Issues and plans are no longer fetched. Polling pauses on visibilitychange
and resumes with whatever fell due. Measured: ~186 MB/h -> ~3.4 MB/h.

The AO funnel showed the fetch sizes (30 ideas, 20 plans, 20 projects) as
totals when stats were missing; it now shows AO's own totals or a dash, and
Bridge's `?? 0` fallbacks become dashes too.

Rendering: 520 stars of 1-4 px were drawn as ~100-point polygons each and
re-triangulated every frame, and the belt zones were drawn under the map's
opaque backdrop. Stars are area-matched squares, and belt Graphics are hidden
while the map is shown (their update() still runs, so the signal queue keeps
draining). Main-thread time per Map frame: ~40 ms -> ~0.55 ms in software GL.

The sidebar skips identical innerHTML rewrites, and the CentralMonitor strip
reads its cadence from the constants.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@mikim
mikim changed the base branch from claude/service-correctness-tests to main September 22, 2026 03:23
@mikim
mikim merged commit c88b3f8 into main Sep 22, 2026
1 check passed
@mikim
mikim deleted the claude/network-cpu-diet branch September 22, 2026 03:25
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