Fix uptime bars display bug - #28
Conversation
* Caught this by running `pnpm check` as prescribed in the PR template
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
kilemensi
left a comment
There was a problem hiding this comment.
Way too early for all that maths! But, unless the bots are sleepy also, they seem to think there is at least a labelling and test issues/concerns that needs fixing/clarifying.
The core production fix is correct and should remain. The shifted query bounds make the final in-window evaluation an interior query point, and the existing index mapping correctly maps:
start + step → first bucket
end → final bucket
end + step → outside the grid, discarded
The focused tests in lib/synthetics.test.ts meaningfully exercise this path and would fail against the old implementation. I do not see a correctness problem in the query or sample mapping itself.
One user-visible regression should be fixed before merging.
Blocking: make the displayed history interval explicit
The PR changes UptimeBucket.t on the live path from the bucket's ending edge to its starting edge:
t: barPlan.startSec + i * barPlan.stepSecThat aligns live data with mockSiteHistory(), which is good. However, the detail page derives the displayed history range from the first and last bar timestamps:
bars[0].t … bars.at(-1).t
For a plan covering [startSec, endSec], the last bar begins at:
endSec - stepSec
The page therefore reports:
startSec … endSec - stepSec
even though the final bar contains data through endSec.
This is most visible on 1y, where one step is roughly four days. The status page can appear to have stopped updating several days ago while its final bar actually represents current data.
This is not fundamentally a page-formatting problem. It exposes an ambiguous domain contract: a single UptimeBucket.t cannot also authoritatively describe the complete chart interval.
I recommend returning explicit interval metadata:
interface SiteHistory {
// existing fields
rangeStart: number;
rangeEnd: number;
}Both data paths already know these values, including under retention clamping may reduce stepSec and therefore move startSec, while endSec remains the current plan boundary:
// fetchSiteHistory
rangeStart: barPlan.startSec,
rangeEnd: barPlan.endSec,
// mockSiteHistory
rangeStart: plan.startSec,
rangeEnd: plan.endSec,The page should render the label from those fields. This preserves bucket-start timestamps, keeps mock and live semantics identical, and avoids forcing future consumers to infer an interval from adjacent points.
Adding one step to the final timestamp in the page is an acceptable minimal fix, but explicit range metadata is the more durable solution and is still small enough for this PR.
Please add focused coverage asserting that:
rangeEndequalsplan.endSec;rangeStartequalsplan.startSec;rangeEnd - rangeStartequalsplan.stepSec * plan.count;- a retained-window plan ends at the current plan boundary and does not span more than the selected window.
Should fix: describe the E2E test honestly
The new Playwright test runs under MOCK=1. Consequently, getSiteHistory() returns mockSiteHistory() and never executes the Prometheus query or bucket mapping changed by this PR. Because mock history already fills every bucket, the test passes against both the old and new production implementations.
The test is still useful as a UI smoke test. I suggest renaming it along the lines of:
renders populated uptime bars for long mock windows
The PR description should likewise distinguish:
- unit tests: regression coverage for query bounds and bucket mapping;
- E2E test: rendering coverage for populated long-window bars.
A fake Prometheus service would provide true integration coverage, but it is not warranted for this change. The focused unit tests provide an appropriate guard.
Non-blocking observations
The response-time query still depends on the query-end boundary. Its impact is materially smaller because response points are rendered directly rather than mapped onto a fixed grid: an omitted endpoint slightly shortens the line instead of creating an explicit empty bucket. Keeping it outside this fix is reasonable.
The .pnpm-store ignore rule and Biome schema update are legitimate housekeeping changes and do not raise concerns.
* In the future, maybe we should drop mocking and use real data?
|
@kilemensi Your bots sure had a lot to say but I agree with them and I've made the necessary adjustments. |
kilemensi
left a comment
There was a problem hiding this comment.
🚀
--
Yeah, these bots are chatty! One would think they were trained to tokenmaxxing.
What & why
Fixes an off-by-one bucket query issue where the final uptime bar could show as empty for
14d,30d, and1ysite detail windows. Uptime range queries now request samples from the first bucket edge through one extra step, then map returned samples back onto bucket-start timestamps so the final in-window bucket is populated.Adds focused unit coverage for long-window query bounds, bucket-edge mapping, ignored out-of-grid samples, and retained-window behaviour. Adds an e2e regression test that verifies the final uptime bar is populated for
14d,30d, and1ymock site detail views.Type of change
Checklist
AGENTS.mdandCONTRIBUTING.mdpnpm checkpassespnpm testpassespnpm test:typespassespnpm buildsucceedsdocs/) if behaviour or configuration changedNEXT_PUBLIC_prefixNotes for reviewers
gitignorefile andbiome.jsonmight seem unrelated but they were both necessitated naturally as I worked on this fix. The.pnpm-storedirectory was created when I pulled the main branch and the mismatched Biome version was flagged when I ranpnpm checkas part of the PR checklist.