Skip to content

Fix uptime bars display bug - #28

Merged
maquchizi merged 7 commits into
mainfrom
fix-uptime-bars-display-bug
Aug 6, 2026
Merged

Fix uptime bars display bug#28
maquchizi merged 7 commits into
mainfrom
fix-uptime-bars-display-bug

Conversation

@maquchizi

Copy link
Copy Markdown
Collaborator

What & why

Fixes an off-by-one bucket query issue where the final uptime bar could show as empty for 14d, 30d, and 1y site 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, and 1y mock site detail views.

Type of change

  • Bug fix
  • New feature
  • Documentation
  • Refactor / chore

Checklist

  • I read AGENTS.md and CONTRIBUTING.md
  • pnpm check passes
  • pnpm test passes
  • pnpm test:types passes
  • pnpm build succeeds
  • Added/updated tests for new logic
  • Updated docs (README / docs/) if behaviour or configuration changed
  • No secrets committed and no Grafana credential given a NEXT_PUBLIC_ prefix

Notes for reviewers

  • Open the detail page of any site on the live site. Compare the uptime bars with those shown in this preview environment. The bars on this environment for windows >= 14d should all be populated.
  • The changes to the gitignore file and biome.json might seem unrelated but they were both necessitated naturally as I worked on this fix. The .pnpm-store directory was created when I pulled the main branch and the mismatched Biome version was flagged when I ran pnpm check as part of the PR checklist.

@vercel

vercel Bot commented Aug 3, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
probeview Ready Ready Preview Aug 6, 2026 9:31am

Request Review

@maquchizi
maquchizi requested a review from a team August 3, 2026 15:04

@kilemensi kilemensi left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

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.stepSec

That 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:

  • rangeEnd equals plan.endSec;
  • rangeStart equals plan.startSec;
  • rangeEnd - rangeStart equals plan.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.

@kilemensi kilemensi added the bug Something isn't working label Aug 4, 2026
* In the future, maybe we should drop mocking and use real data?
@maquchizi

Copy link
Copy Markdown
Collaborator Author

@kilemensi Your bots sure had a lot to say but I agree with them and I've made the necessary adjustments.

@kilemensi kilemensi left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

🚀

--

Yeah, these bots are chatty! One would think they were trained to tokenmaxxing.

@maquchizi
maquchizi merged commit 0e1edd7 into main Aug 6, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants