perf: have the next screenshot ready before the reviewer asks for it - #402
Merged
nGervasyuk merged 1 commit intoSep 1, 2026
Conversation
nGervasyuk
force-pushed
the
perf/details-dialog-image-prefetch
branch
from
August 31, 2026 11:35
d670d17 to
9db1d66
Compare
nGervasyuk
force-pushed
the
perf/details-dialog-image-prefetch
branch
from
September 1, 2026 08:07
3ee37b6 to
9db1d66
Compare
Stepping through a build's runs with the arrows left the pane blank under a "Loading..." for a second or more per step: each screenshot is several megapixels, and nothing was fetched until the reviewer had already moved. The dialog now warms the browser cache for the runs on either side of the one on screen — two ahead, one behind, since review moves forward, nearest first so the run the arrow lands on is requested first. Names already asked for are not asked for again. Prefetching only pays if the browser is allowed to keep what it fetched, so nginx now serves the image directory with a year of immutable caching: an image name is unique per upload and the bytes behind it are never rewritten. That needed its own location block, which also stops a missing image falling through to index.html the way the SPA route does — the app's HTML answered under an image's name and then cached for a year would have been a far worse bug than the 404 it now returns.
nGervasyuk
force-pushed
the
perf/details-dialog-image-prefetch
branch
from
September 1, 2026 14:03
9db1d66 to
94f229c
Compare
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.



What
Stepping through a build's runs with the arrows left the pane blank under a
Loading...for a second or more per step. Each screenshot is several megapixels (1284×2778, 2048×2732), and nothing was fetched until the reviewer had already moved.How
Prefetch. The details dialog warms the browser cache for the runs on either side of the one on screen — two ahead, one behind, since review moves forward — nearest first, so the run the arrow lands on is requested first. All three pictures of a neighbour (baseline, checkpoint, diff). Names already requested are not requested again, carried in a ref across renders.
Caching. Prefetching only pays if the browser is allowed to keep what it fetched, and nginx was serving the image directory with no cache headers at all, so every revisit re-fetched. Images now get a year of
immutable: an image name is unique per upload and the bytes behind it are never rewritten.That needed its own
locationblock rather than a header on the existing one — and it turns out that was necessary for correctness, not just tidiness. The SPA route ends intry_files ... /index.html =404, so a missing image was answered with the app's HTML. Combined with a year of immutable caching, the browser would have cachedindex.htmlunder an image's name and never asked again. The new block ends intry_files $uri =404.Verified on a built image:
Not done, on purpose
Keeping the previous run's image on screen while the next one loads. The header would read
Step 5over step 4's pixels — worse than a spinner. The prefetch removes the pause itself.Tests
imagePrefetch.helper.test.ts— the neighbour window (order, both ends, the current run excluded, missing names, dedup) and the request side (URLs come fromstaticService.getImage, nothing asked for twice), withwindow.Imagestubbed.jest 36/36, Playwright 87/87 (matched against 87/87 on
master),tscunchanged at the 30 pre-existing errors, eslint and prettier clean on touched files,nginx -tpasses in the built image.Deploy after backend #373
On a deployment that serves images from S3, this prefetch is worse than useless until backend#373 is out.
There,
REACT_APP_STATIC_URLis empty, sogetImagereturns an API URL that 302s to a pre-signed S3 URL. Today that signature is minted per request, so the URL differs every time and the redirect itself is not cacheable. The prefetch downloads the neighbour's screenshot, and when the reviewer actually opens that screen the browser asks the API again, gets a different URL, and downloads the same bytes a second time — double the traffic and the S3 bill, no gain.backend#373 makes the signed URL stable for a window, has S3 advertise a lifetime on the object, and makes the redirect cacheable. With it in place the prefetch lands in the browser cache and opening the screen is free.
On HDD deployments (
REACT_APP_STATIC_URLset, the nginx change in this PR) the prefetch is already effective on its own.