Skip to content

Discover scrolling and serving performance

This change fixes demonstrated waiting and rendering overhead without changing ranking weights, candidate reachability, account interests, or experiment assignment.

Baseline

Read-only production analytics queried on September 23 UTC (September 22 Pacific), covering the previous six hours:

Cursor requestRequestsMedianp95
Initial4,0431,315 ms2,841 ms
Continuation2,158890 ms1,974 ms

These are hub_serve.duration_ms: serving plus canonical measurement, excluding preceding authentication/filter work, network transfer and painting. They are not browser-perceived latency or an experiment result. One continuation had unavailable measurement; the others recorded normally.

A bounded guest HTTP probe requested six pages at each of 10 and 24 cards: 204 distinct cards, full pages, continuation available throughout, and private/no-store responses. Local request durations ranged from 659 to 1,318 ms. This is a smoke sample, not a load test or population percentile.

Changes

  • Prefetch uses the actual scrolling panel on desktop and the document on mobile. A window-root observer cannot extend its margin through an overflowing ancestor, so the previous 800px margin did not provide the intended desktop head start.
  • Prefetch distance is three viewport heights, bounded to 1,200–3,200 pixels to avoid downloading the whole catalog. Resize and scroll-owner changes reconnect the observer; appends retain the existing cursor and scroll position.
  • A stable virtualizer key callback keeps unchanged row measurements cached while scrolling. Existing cards retain their world object; favorite badges update separately, allowing memoized cards to skip unrelated rerenders.
  • Canonical exposure persistence batches the page and opportunities in one insert and uses a narrow RETURNING identity projection. A fresh measured page removes two database round trips and no longer rereads large feature payloads. Conflicting/replayed rows still use a separate, fresh statement to retrieve committed identities. Erasure locks, atomic commit, immutable snapshots and receipt validation remain in place.
  • Server-Timing exposes fixed numeric discover, rank and measure stages. discover includes route authentication/admission/filtering; rank includes cursor storage, eligibility, ranking and hydration, not just model inference. hub_serve adds the same serving/measurement split, authenticated status, deployment and region.
  • hub_feed_load records one accepted request outcome with initial/append/refresh mode, HTTP status, duration and batch size. It includes fetch/body decoding but excludes paint. It contains no cards, chosen interests, cursors or user-entered content. Stale/aborted requests do not report completion. It is performance telemetry, not a training signal or evidence of dislike.

Training and quality

Live configuration confirms personalization enabled at 50%, cursor serving at 100%, and measurement enabled. The current story representation is premise-evidence-v1. The trainer's latest deployed image is ready for its scheduled run; image deployment success alone is not evidence that the cron executed. The preceding verified native training runs completed in roughly 20–21 seconds.

The snapshot value-model pipeline is still warming up. At the September 22 cutoff, its training and validation windows precede continuous history capture. The live history marker is September 20 at 07:47 UTC. No new snapshot value model has been published. Waiting for qualified mature data preserves the existing evaluation contract; this performance release does not relax promotion gates or claim an engagement uplift.

Read-only canonical aggregates locate the first target-cohort page proxies on September 21 evening. Under the existing split windows these first enter the training window on October 1, conditional on complete validation and sufficient support; this is not a promotion date. Current warehouse completeness flags remain false, so recent event timestamps alone cannot establish complete capture.

Prefetch also changes how much delivered inventory may remain unseen. The value target measures observed attributed reading per delivered opportunity; it does not require a recorded impression, so an unviewed, mature opportunity can carry zero time. This is not an explicit dislike. Treat this release as an exposure-policy boundary for before/after offline comparisons, retain actor denominators in the live randomized comparison, and inspect serve-to-impression coverage separately. Do not interpret extra zero-outcome slates as evidence that readers disliked those stories.

Use canonical treatment/shadow assignment for the 50/50 comparison. hub_serve.variant names the older ranking experiment and is not sufficient to identify personalization treatment. Do not compute ordinary percentiles from server_request: all responses over one second are captured, but only 1% of faster responses are sampled.

Verification and limits

Regression tests cover the real desktop/document observer ownership, resize, unchanged-card rendering, virtualized long feeds, stable cursor retries, full catalog traversal, transactional exposure persistence, immutable retries and erasure safeguards. Database operation-count tests failed against the previous implementation before the fix. No local browser navigation was performed, following repository instructions; DOM tests cannot establish real-device frame pacing.

Local verification: server suite 3,919 passed / 15 skipped; app suite 1,548 passed; monorepo build 5/5 tasks and typecheck 8/8 tasks. The new telemetry import required the interest-flow test harness to mock analytics, matching the other component fixtures. The PostgreSQL-driver union required a narrow, table-derived type for projected RETURNING; real PGlite tests exercise the SQL behavior.

In the 1,725-card DOM fixture, one scroll dropped from 505 to 160 key reads. Existing-card renders during loading/append/parent updates dropped from 72 to zero, and redundant renders of retained cards during virtual scrolling dropped from 30 to zero. These are component-work counts, not device frame-rate measurements. Independent server and app reviews found no remaining blockers. The generated OSS tree passed typecheck and discovery/edition boundary checks.

Keep first load, subsequent load and explicit refresh separate when checking post-deploy client timings. Compare equivalent deployment, device, authentication and network cohorts. Speed and recommendation usefulness are separate acceptance criteria: lower latency cannot establish that readers find better stories. Real-user frame pacing and adequately powered reader-outcome comparisons remain measurements to collect, not claims established by this release.

Reference patterns: Reddit's feed engineering describes paginated networking and avoiding expensive layout work during scrolling; Google's long-list guidance explains bounded rendering and overscan. These inform the approach, not a claim of performance parity with those products.