Skip to content

feat(console): net origin offload, and catch up to plugin 0.64.1; v0.12.0 - #152

Merged
harper-joseph merged 3 commits into
mainfrom
feat/console-net-offload
Sep 4, 2026
Merged

feat(console): net origin offload, and catch up to plugin 0.64.1; v0.12.0#152
harper-joseph merged 3 commits into
mainfrom
feat/console-net-offload

Conversation

@harper-joseph

@harper-joseph harper-joseph commented Sep 4, 2026

Copy link
Copy Markdown
Contributor

What

Origin offload, gross and net. The offload tile the rollout is judged on counts only crawler requests proxied to the origin live. It ignores every origin request this system makes — renders, change probes, sitemap fetches — so a deployment can post a 95% gross offload while sending the origin more requests than the crawlers would have. This adds the net figure:

net = 1 − (proxied serves + renders + change probes + sitemap fetches) ÷ crawler requests arrived

All four terms come from series already in the one analytics scan (bot_serve source=origin, render/outcome — exactly one row per posted result, probe_probed and sitemap_sitemaps read by value). originLoad() in charts.js owns the arithmetic so Traffic and the Overview cannot disagree. Traffic gains a "What the origin actually saw" panel (each term over time + a bar per cause); the Overview tile carries the net figure under the gross one.

Stated on the panel rather than assumed:

  • a render counts as one origin request (the document; the page's subresources reach the origin only if the CDN doesn't cache them for the renderer);
  • the probe/sitemap counters land in the bucket where a pass finished, so short ranges lump them — quote the 24h figure.

The term nothing here can count is stated, and it is missing from both sides. The requests a page's own scripts make when a rendering crawler (Googlebot, Bingbot, Applebot) runs it — the XHR/API calls no CDN caches — never pass through the plugin (the CDN forwards the document to us and sends subrequests straight to the origin). Without this system every such page-view costs the origin 1 + k, so the "crawlers asked for" baseline understates what the origin was spared; with it, a snapshot served without scripts triggers none of the k (a saving not credited), while a snapshot that keeps scripts, a proxied origin page, and every one of our own renders still trigger them (a cost not charged). The figure is documents-only on both sides and says so; the net tile reads "before crawler follow-up requests"; the panel reports the exposure as every page handed to a crawler — for every crawler, not a guessed list of bots, and never multiplied by a guessed factor. Where snapshots ship with scripts stripped, the true net for rendering crawlers is higher than shown.

Catch-up to plugin 0.61 → 0.64.1 (console was current to 0.60.0):

  • verified cache status (v0.63.0) was invisible in the worst way: outside every cache-served sum (the hit rate read low) and in the "other" non-hit family. One shared CACHE_SERVED set now drives the freshness chart, the per-route table and the staleness sums; verified joins invalidated in one Invalidation family — one population, split into rescued and refused.
  • Invalidations gains a third panel: refused vs rescued serves, page_verification writes (faults on the tile), every invalidation_reenqueue outcome including v0.64.0's forwarded / forward-failed (forwarded is never added to lowered — the owner counts it), and invalidation_error with lkg-expired as the failed-OPEN alarm. Reads the same cached 1h window the Overview holds. Two catalog-guard waivers removed — the guard's page_verification failure on current main is what started this.
  • Change probe (v0.62.0): stateAvailable: false is named per node as "unknown, not idle" (the old shape made an unreadable state look like the probe being off); a running sweep shows its heartbeat count on the header, the sweep card and the node table.

Verification

  • cd packages/console && node --test — 257 pass (was 227/1 fail on main: the guard's page_verification catch).
  • npm run lint && npm run format:check clean.
  • New: test/invalidationsView.test.js; extended traffic/overview/probe/aggregate suites pin the arithmetic (value vs count for pass counters, bot_request as denominator, negative net, all-bots tag under a filter, the two-sided caveat, verified cache-served, forwardedlowered).

No plugin change; console-only release train (prerender-console-v0.12.0).

Follow-up (not in this PR): measure k

The renderer already runs a cache policy on every same-origin response (renderer.ts response hook). Counting uncacheable same-origin subrequests per render gives k per page; stored on the cached page and applied — by what the registry says each crawler runs — to the baseline, the proxied serves, the cache-served pages (zero where scripts are stripped) and our own renders, it turns the exposure into a counted term on both sides. Browser → plugin → console train; happy to open the issue.

🤖 Generated with Claude Code

…12.0

Origin offload is now stated twice. The existing figure — crawler requests not
proxied live — is gross: it counts what the origin was spared and none of what
this system asks of the origin in exchange. Net offload subtracts every origin
request the deployment itself made, all from series already in the one analytics
scan: proxied serves (bot_serve source=origin), renders (render/outcome, one row
per posted result), change probes (probe_probed, a pass counter read by value)
and sitemap fetches (sitemap_sitemaps). `originLoad` in charts.js carries the
arithmetic so Traffic and the Overview cannot disagree; Traffic gains a "What
the origin actually saw" panel with each term over time, the Overview tile
carries the net figure under the gross one.

Two things the number cannot know are stated on the panel, not assumed: a
render counts as ONE origin request (the document), and the probe/sitemap
counters land where a PASS FINISHED, so short ranges lump them — quote 24h.

One term the plugin cannot see at all is exposed rather than omitted: whatever
a crawler fetches from the origin after we hand it a page (a rendering
crawler's scripts and the page's own API calls above all) never passes through
this plugin. The net tile says "before crawler follow-up requests" and the
panel reports every page handed to a crawler as the exposure — a count, never
multiplied by a guessed factor, and for every crawler rather than a guessed
subset of bots. The render fleet can measure the per-page factor; that is the
follow-up.

Catch-up to plugin 0.61–0.64.1:
- `verified` (v0.63.0) is a cache serve. It sat outside every cache-served sum
  and fell into the "other" non-hit family; now one shared CACHE_SERVED set
  drives the freshness chart, the per-route table and the staleness sums, and
  it joins `invalidated` in one "Invalidation" family (one population: rescued
  and refused).
- Invalidations gains a "what the invalidations are doing" panel reading
  page_verification, invalidation_reenqueue (incl. v0.64.0's forwarded /
  forward-failed — never added to lowered, the owner counts those) and
  invalidation_error (lkg-expired = failed OPEN). Two catalog-guard waivers
  removed; the guard's page_verification failure is what started this.
- Change probe (v0.62.0): `stateAvailable: false` is named per node as
  "unknown, not idle"; a running sweep shows its heartbeat count.

Co-Authored-By: Claude Code <noreply@anthropic.com>

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request updates the prerender console to version 0.12.0, introducing a new 'net offload' metric alongside the gross offload figure, and adding a new panel to the Invalidations view to show what invalidations are doing (including page verification and demand-driven heals). It also adds support for displaying probe sweep progress and handling unreadable probe state rows. The changes are accompanied by comprehensive unit tests. The review feedback suggests using the ratioOf helper function in charts.js to safely calculate the net offload ratio and handle potential division issues.

Comment thread packages/console/src/admin/charts.js Outdated
total,
arrived,
// Null, never 0 or 100%: an empty window has no offload to report.
net: arrived > 0 ? (arrived - total) / arrived : null,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

When calculating ratios or dividing metrics, use the ratioOf helper function to safely handle division and potential null/NaN values. This serves as an explicit guard at the call site and avoids relying on implicit null comparisons.

Suggested change
net: arrived > 0 ? (arrived - total) / arrived : null,
net: ratioOf(arrived - total, arrived),
References
  1. When calculating ratios or dividing metrics, use a helper function (such as ratioOf) to safely handle division and potential null/NaN values. This serves as an explicit guard at the call site, even if the inputs are currently guaranteed to be numbers.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Taken — ratioOf(arrived - total, arrived) in edd9af1. Same result (both inputs are finite sums), but it is the module's convention for every division and the guard should not be re-derived at one call site.

harper-joseph and others added 2 commits September 4, 2026 13:48
…f the offload ledger

The panel said omitting a rendering crawler's follow-up requests flattered the
net figure. It does not, or not only: without this system every rendering
crawler's page-view costs the origin the document PLUS the page's own API calls
(the uncacheable ones), so the "crawlers asked for" baseline understates what
the origin was spared — and a snapshot served without scripts triggers none of
those calls, a saving the figure does not credit. Proxied pages, snapshots that
keep their scripts, and our own renders still trigger them, a cost it does not
charge. Documents-only on both sides, and the copy now says which way each case
cuts; where snapshots ship without scripts the true net for rendering crawlers
is HIGHER than shown.

Co-Authored-By: Claude Code <noreply@anthropic.com>
…in charts.js (review)

Co-Authored-By: Claude Code <noreply@anthropic.com>
@harper-joseph

Copy link
Copy Markdown
Contributor Author

Addressed the one inline finding (ratioOf for the net ratio). 257 tests, lint and format clean locally — this repo has no PR CI.

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