fix: zero-fill known status codes absent from a returned minute - #48
Draft
shreekarshetty wants to merge 2 commits into
Draft
fix: zero-fill known status codes absent from a returned minute#48shreekarshetty wants to merge 2 commits into
shreekarshetty wants to merge 2 commits into
Conversation
Cloudflare's Adaptive Groups API only returns a (zone, status) row for a minute when that status had at least one request. Engine.Ingest only re-emits a counter when an Observation actually arrives, so a status code that's gone quiet for a minute just stops getting pushed - the series ages past VictoriaMetrics' staleness window and drops out of any sum()/rate()/delta() over the zone's total, producing a phantom drop that "recovers" the instant the status code reappears. This is what's been driving the CloudflareZone5xxZscoreWarn noise. A minute row that comes back at all is authoritative for what it lists: absence of a known status inside it is Cloudflare's own way of saying zero, not an ambiguous gap. flattenHTTPAdaptiveGroups now tracks, per zone, every status code ever seen and zero-fills any that a given minute's response doesn't mention. This can only ever fire for a (zone, minute) a fetch actually returned data for - if a chunk fetch fails outright, this never runs for that zone, and its keys fall through to real staleness exactly as today, so CloudflareZoneRequestsMetricMissing still catches a genuine sustained outage at its normal speed. ⚡ Built with Claude Code
Every real Fetch call spans the full Lookback/BackfillChunk window (10 minutes) at once, not a single bucket. Adds coverage for a status seen only in the newest minute of a batch still correctly zero-filling into the earlier minutes of that same batch. ⚡ Built with Claude Code
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.
Summary
Root-caused the CloudflareZone5xxZscoreWarn noise to idle status-code series silently dropping out of aggregate queries. Zero-fills them using Cloudflare's own response as the source of truth, instead of guessing with a timer.
Claude details
Root cause
Cloudflare's Adaptive Groups API only returns a
(zone, status)row in a minute when that status had at least one request.Engine.Ingestonly re-emits a counter when anObservationfor that exact(key, bucket)arrives, so a rare 5xx code (501, 507, 520, 523, 525, 555 on supabase.co; 504, 530 on snapcloud.dev) that goes quiet for a few minutes just stops getting pushed. Once the gap outlives VictoriaMetrics' staleness window, the series drops out ofsum by (zone) (...)entirely, making the zone's total 5xx count appear to fall - then jump back the instant the code reappears. That swing is what tripscloudflare:zone_5xx_zscore.Verified against the three real alerts (08:45, 09:15, 09:25 UTC, 2026-08-14):
supabase.co: status555gap 09:14:30-09:32 UTC (17.5min),507gap 09:24-09:36 UTC (12min), plus shorter gaps on501/520/525snapcloud.dev: status504gaps of 12-14min,530appears for a single 30s sample then vanishesFix
A superseded earlier version of this fix (#47, closed) used a timer: re-push every known key's last value every tick unless it's been idle past a cap, to bound (but not eliminate) the risk of masking a genuine sustained outage. That was solving with a guess something Cloudflare already answers directly: a minute row that comes back at all is a complete, authoritative list of every status that occurred - a known status missing from it is a confirmed zero, not an ambiguous gap.
flattenHTTPAdaptiveGroupsnow tracks, per zone, every status code ever seen and zero-fills any that a given minute's response doesn't mention, feeding a realValue: 0Observation through the existing (already-tested) counter-chain/tracker pipeline - no new Engine API surface. Because zero-fill only ever fires for a(zone, minute)a fetch actually, successfully returned data for, it can't mask a real outage the way the timer could: if a chunk fetch fails outright, this never runs for that zone, and its keys fall through to real staleness exactly as today -CloudflareZoneRequestsMetricMissingstill catches a genuine sustained failure at its normal ~10 minute speed, no grace period needed.Testing
cfetch/zerofill_test.go- TDD: tests written first (confirmed failing to compile without theknownStatusesfield / method), then implemented. Covers: zero-fill of a previously-seen-now-absent status, no zero-fill before a status has ever been seen, zero-fill knowledge isolated per zone, and the existingenabledmetric filter still works. Full suite (go test ./...),go vet ./..., andgofmtall clean.Scope
Left
cfgql.FetchZones's silent per-chunk error swallowing alone (real defect - no error propagation, no metric - but this fix's safety doesn't depend on it, since zero-fill inherently can't fire off a failed fetch). Also leftflattenHTTP1mGroups's equivalent non-v2cloudflare_zone_requests_statusbreakdown untouched - same latent issue, but nothing currently alerts on that metric, so it's out of scope here.⚡ Built with Claude Code