Hold a chart's last bin back until all of its data arrived

The newest metric bucket is upserted every flush while its minute runs, and
both rollup endpoints summed it in — so every curve on Home ended on a fall
that was only the clock. The timeseries now stops at the last closed bin, and
the flow rollups' window ends on the last closed minute, so all sixty slices
are whole ones.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-08-27 15:53:55 +02:00
co-authored by Claude Opus 5
parent 37a7df9d24
commit 168aadb217
3 changed files with 70 additions and 6 deletions
@@ -195,6 +195,47 @@ def test_the_timeseries_folds_into_the_requested_bucket(
assert points[0]["ts"] % 900 == 0
def test_the_bucket_still_filling_is_held_back(
client: TestClient, superuser_token_headers: dict[str, str], db: Session
) -> None:
"""The minute in progress is not drawn.
A bucket is upserted every flush while its minute runs, so the newest one
only ever holds part of a minute. Charted, it reads as a fall that never
happened.
"""
flow = "open-bucket-test"
minute = int(datetime.now(UTC).timestamp()) // 60 * 60
for offset, executions in ((-60, 3), (0, 1)):
db.add(
MetricBucket(
flow=flow,
node=f"{flow}.calc",
bucket=datetime.fromtimestamp(minute + offset, UTC),
executions=executions,
)
)
db.commit()
points = client.get(
f"{PREFIX}/timeseries",
headers=superuser_token_headers,
params={"flow": flow, "hours": 1},
).json()
flows = client.get(
f"{PREFIX}/flows", headers=superuser_token_headers, params={"hours": 1}
).json()
row = next(entry for entry in flows if entry["flow"] == flow)
# Unless that minute closed while the requests were in flight — then both
# buckets are complete and counting both is right.
if int(datetime.now(UTC).timestamp()) // 60 * 60 == minute:
assert [int(point["ts"]) for point in points] == [minute - 60]
# The window ends on the last closed minute, so the final slice is a
# whole one rather than however much of this minute has arrived.
assert (row["executions"], row["spark"][-1]) == (3, 3)
def test_a_zero_hour_window_is_still_an_hour(
client: TestClient, superuser_token_headers: dict[str, str]
) -> None: