feat: alert when an audit sidecar stops delivering - #227
Open
scotwells wants to merge 3 commits into
Open
Conversation
Production lost ~75% of its audit events for over three hours with no alert. Two of three audit sidecars wedged while the third kept delivering, which held every aggregate above zero and left the pipeline looking healthy. Nothing covered the sidecar to NATS leg, and three of the four rules meant to cover this pipeline could never fire. Key changes: - Add VectorSidecarNATSPublishStalled (critical): a sidecar still transforming events while its NATS sink consumes none. Evaluated per pod, so one stalled replica pages even when its siblings are healthy - Add VectorSidecarBufferBacklog (warning): >25k events on the sink's disk buffer, an independent backstop with ~8x headroom over the healthy peak of 3,208 - Repoint ActivityDataPipelineStalled at vector_component_sent_events_total; vector_events_out_total has no series since the upstream rename - Repoint the NATS backlog rules at nats_consumer_num_pending with stream_name/consumer_name; nats_jetstream_consumer_num_pending is not exported - Rebase ActivityGenerationStalled on events evaluated rather than activities generated, aggregating both sides so their label sets match. Generation is policy-driven and legitimately zero for hours; evaluation is not - Make VectorClickHouseWritesStopped per pod so a single stalled aggregator replica is caught rather than only a total stop Every expression was validated against production: the sidecar rules went true 5-6 minutes after each wedge and produced zero firings across six days of healthy baseline. Related to #226 Claude-Session: https://claude.ai/code/session_01C4BSNeKuaKVu44MVkLCnS6
scotwells
force-pushed
the
feat/sidecar-audit-alerting
branch
from
August 24, 2026 22:06
0766082 to
b8ca31a
Compare
The sidecar runbooks reached for the nats CLI and df, neither of which exists in the clusters these alerts fire in. natsBox.enabled is false, no nats-box workload is deployed, and the nats and vector images ship no shell. An on-call responder following these steps hit a dead end at the point they most needed the answer. Key changes: - Read stream state and max_payload from the NATS monitoring endpoint (/jsz, /varz) instead of the absent nats CLI - Note that the reloader sidecar applies a max_payload change on its own, leaving the manual signal as a fallback - Take buffer size from vector_buffer_byte_size, since the vector image has neither a shell nor df Every replacement was run against production before being written down. Claude-Session: https://claude.ai/code/session_01C4BSNeKuaKVu44MVkLCnS6
ActivityPipelineBacklogCritical fired at 500,000 pending events. This stream carries roughly 100 events/s, so that is about 83 minutes of a completely stopped consumer before a critical alert reaches anyone. The threshold was written against a metric name that did not exist, so it was never once evaluated against real throughput. Repairing the name made the rule run; it did not make the number right. 50,000 is roughly 8 minutes of a stopped consumer, one order above the existing 5k warning and 25,000x the highest backlog observed in 30 days, so the change buys earlier warning without risking a false page. Claude-Session: https://claude.ai/code/session_01C4BSNeKuaKVu44MVkLCnS6
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
Production lost about 75% of its audit events for three hours today and nothing paged. Two of the three audit collectors stalled, the third kept working, and every alert averaged across all three — so the pipeline looked healthy the whole time. It was spotted by eye on a dashboard.
After this, a single stalled collector pages within about five minutes, and whoever answers gets a runbook that walks them from the alert to the fix.
While checking why nothing fired, four existing rules turned out to be incapable of firing at all — three referencing metrics that no longer exist, one comparing two things that can never match. A fifth was set to page only after roughly 83 minutes of a completely stopped consumer, a number chosen against a metric that never existed and so never checked against real traffic.
Note
The backlog alert fires the moment this merges — a collector is still working through events left over from today's incident. That is correct, and it clears itself as the backlog drains.
Root cause of the outage is fixed separately. This is detection and response only; no application code changes.
Related to #226
Validation
Worth being precise about, since the bug being fixed is rules that quietly never fire — proving a rule stays quiet proves very little on its own.
Confirmed to fire on real failure data:
VectorSidecarNATSPublishStalled— went true at 18:50 and 18:52 UTC during today's outage, 5–6 minutes after each collector stalledVectorSidecarBufferBacklog— firing now on genuine leftover backlogStructurally verified only. The repaired rules cover conditions that have not occurred in 30 days, so there is no real failure to test them against. For each, the metric, labels, and thresholds were checked to be live and reachable — but none has been observed firing:
ActivityDataPipelineStalled,ActivityGenerationStalled— the quantities they watch have never touched zero (11.75/s and 11.24/s minimums over 30 days), so the rules are sound but unexercisedVectorClickHouseWritesStopped— label sets intersect on the three aggregator pods; collectors drop out on their own, having no ClickHouse stageActivityPipelineBacklogCritical,NATSConsumerLagHigh— consumer names match a real consumer, and the new 50k threshold sits 25,000x above the highest backlog seen in 30 daysEvery new and repaired rule produces zero firings across 30 days of history.
Test plan
kustomize build config/components/observabilityis cleanWarning
Two of these dead expressions also exist in the Jsonnet mixin. #155 needs to fix them there before it moves alert authoring into Jsonnet, or they will come back.