Skip to content

feat(browser): Add bfcacheMetricsIntegration#22397

Draft
logaretm wants to merge 8 commits into
developfrom
awad/bfcache-integration
Draft

feat(browser): Add bfcacheMetricsIntegration#22397
logaretm wants to merge 8 commits into
developfrom
awad/bfcache-integration

Conversation

@logaretm

@logaretm logaretm commented Jul 20, 2026

Copy link
Copy Markdown
Member

Adds an opt-in bfcacheMetricsIntegration that reports back/forward-cache health as metrics: a hit/miss navigation counter, the (Chromium-only) notRestoredReasons behind a miss, and the fallback reload cost on a miss.

Metrics are the source of truth rather than spans, because a bfcache hit restores an existing page and never creates a pageload span, so span-only instrumentation would drop every successful restore from the dataset.

New Metrics

Three metrics, all under the browser.bfcache.* namespace.

metric type emitted key attributes answers
browser.bfcache.navigation counter 1 per back/forward navigation browser.bfcache.outcome (hit/miss), browser.bfcache.not_restored_reason_count (miss only) hit/miss rate (total = ungrouped sum; hit% = outcome:hit / total)
browser.bfcache.not_restored counter 1 per reason (a miss can emit several) browser.bfcache.reason (verbatim), browser.bfcache.frame (top/child) which blockers, and where (top document vs a child/embed)
browser.bfcache.reload.duration distribution (ms) 1 per miss the cost of a miss

Conventions PR is here getsentry/sentry-conventions#513

An example of a dashboard that uses bfcache metrics would look like this:

CleanShot 2026-07-20 at 14 12 41@2x

We can pre-ship a sentry-provided dashboard to onboard users to this feature.

Resources

logaretm added 4 commits July 20, 2026 10:47
Add an opt-in browser integration that tracks back/forward cache health
as metrics:

- browser.bfcache.navigation: hit/miss counter by navigation type
- browser.bfcache.not_restored: Chromium notRestoredReasons per miss
- browser.bfcache.miss.reload.duration: fallback reload cost on a miss

Hits are detected via pageshow.persisted; misses via a back_forward
navigation entry. Includes unit tests for the notRestoredReasons parser
and a Playwright suite for hit/miss detection.
Adds a dedicated `browser-bfcache` e2e app that exercises
`bfcacheMetricsIntegration` against a genuine back/forward cache: real
cross-document navigation served by `vite preview`, restored via
renderer-initiated `history.back()`, under the full Chrome-for-Testing
binary (`channel: 'chromium'`) with the disable flag stripped and
`BackForwardCache` enabled.

Covers hits, misses with real `notRestoredReasons`, and deliberately
bfcache-ineligible pages via `?botch=`: an `unload` listener (stable
blocker) and an open WebSocket (blocks only before Chrome 149, so that
assertion is version-gated). `Cache-Control: no-store` no longer blocks.

Removes the simulated browser-integration bfcache suite, which faked the
restore with a synthetic `pageshow` and a manual flush; it is superseded
by the real assertions here. The reason-parser unit test stays.
The unload miss deterministically also yields a privacy-masked reason.
Assert the integration reports it as a `masked` frame, giving real-browser
coverage of the masked-frame classification path (previously only unit-tested).
A plain open IndexedDB connection (and even an in-flight transaction) no
longer blocks bfcache in current Chrome, contrary to web.dev's list. The
condition that still blocks is a connection holding up a version upgrade,
which yields an `idbversionchangeevent` reason. Add a deterministic e2e
case for it and document how real Chrome diverges from the article.
@logaretm logaretm changed the title awad/bfcache integration feat(browser): Add bfcacheMetricsIntegration Jul 20, 2026
@logaretm
logaretm force-pushed the awad/bfcache-integration branch from 59903b5 to f7104d6 Compare July 20, 2026 18:20
A no-store (CCNS) page is only evicted from bfcache once a cookie changes;
neither the header nor a cookie change blocks on its own. Add a `?botch=nostore`
case that serves the document with `Cache-Control: no-store` (via a preview
middleware, since a static server can't set per-request headers) and mutates a
cookie, asserting the `response-cache-control-no-store` reason.

Also trim the version-specific blocker details from the README so they don't
drift; the botch cases and their assertions are the source of truth.
@logaretm
logaretm force-pushed the awad/bfcache-integration branch from f7104d6 to fc820d3 Compare July 20, 2026 18:30
`browser.bfcache.navigation_type` was 1:1 with `browser.bfcache.outcome`
(hit <-> back-forward-cache, miss <-> back-forward), so it added a second
attribute encoding the same bit. Keep only `outcome`; navigation-type
vocabulary belongs on web-vital samples (future soft-navigation work), not
on bfcache counters. Also drops an unnecessary PageTransitionEvent cast.
logaretm added 2 commits July 20, 2026 15:02
Adds two real-browser e2e cases: an embedded bfcache-eligible iframe keeps
the top page restorable (hit), and an iframe with an unload listener makes
the top page ineligible with the reason reported from the child frame. This
gives the `frame: 'child'` classification real coverage (previously unit-only).
`frame` no longer special-cases the `masked` reason value; it now reflects
only the frame's position in the notRestoredReasons tree (`top`/`child`).
The `masked` value already lives on the `reason` attribute, so tagging the
frame as `masked` too was redundant. The integration no longer inspects any
reason string - reasons pass through verbatim.
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