Blocker (public-release): the trigger watermark can silently halt AE forever
adaptive_export's kubescape trigger gates on a strict forward-only high-water-mark of the content
event_time (internal/trigger/clickhouse.go: WHERE event_time >= watermark, watermark advances to
maxSeen). Any anomaly with event_time < watermark is dropped and never re-queried. This is the
production "writes succeed, then stop, data still on Pixie" failure.
Evidence (real PG runs — loadtest E8, FINDINGS_AND_BACKLOG.md F8)
- Watermark poisoned at a leftover nanos value
1781559619170395824; 25/25 ticks of fresh seconds
anomalies → n_anomalies stayed 0 (writes stopped; Pixie + data-plane fine). Evidence e8_steady.csv.
- Recovery required a manual
ALTER TABLE trigger_watermark DELETE WHERE 1=1 + AE restart; then
n_anomalies grew 1→2→3→4, delta=1/tick. Evidence e8_recov.csv.
Three failure modes (all reproduce the halt)
- Unit heterogeneity (catastrophic): one nanos/millis row →
watermark ≈ 1.78e18; every later seconds
row (~1.78e9) is < watermark → dropped forever.
- Clock skew / out-of-order: a late/earlier-stamped anomaly after a newer one → dropped.
- Restart re-scan: on reboot the persisted/max-existing watermark buries anomalies stamped below it.
A partial mitigation already landed (normalizeEventTimeNanos, trigger/clickhouse.go:205,285) — it
reduces the unit-poison odds but does NOT fix skew/out-of-order/restart drops, and there is no metric
to even see a halt. This issue is the durable AE-9 fix.
Targets (acceptance criteria)
- T1 — no permanent halt: after the worst-case poison (a
1.78e18 row) or an out-of-order/skewed row,
the trigger self-recovers without any manual ALTER TABLE … DELETE + restart. Re-run E8: fresh
seconds anomalies must flow within one lookback window.
- T2 — late arrivals captured exactly once: an anomaly arriving with
event_time within a bounded
lookback of the watermark is processed exactly once (no drop, no duplicate).
- T3 — observability: new metrics
ae_trigger_watermark_ns (gauge), ae_trigger_below_watermark_total
(rows seen below the prior HWM — i.e. would-have-been-dropped, now captured), ae_trigger_event_time_rejected_total
(poison clamps). A halt is visible + alertable.
- T4 — no happy-path regression: the monotonic-exact path stays exact (E1: 71/71 then 20/20, std=0).
Planned change (stacks on PR #92 branch fix/ae-protocol-export-pxexport)
- Bounded lookback + content-dedup (
internal/trigger/clickhouse.go): query
event_time >= max(0, watermark - LOOKBACK_NS) instead of >= watermark; extend the existing
boundary-fingerprint set into a bounded LRU covering [watermark-LOOKBACK, watermark] so re-seen rows
are deduped. New env ADAPTIVE_TRIGGER_LOOKBACK_SEC (default 300; 0 = legacy strict HWM). Catches
skew/out-of-order/restart drops (failure modes 2+3).
- Wall-clock poison clamp (failure mode 1): after
normalizeEventTimeNanos, reject any event_time
> now + MaxSkew (default 1h) from advancing the watermark; count it in ..._rejected_total. A single
1.78e18 row can no longer jump the cursor past all real data.
- Metrics (T3) wired in the poll loop.
- Keep
normalizeEventTimeNanos (AE-2 partial) as the first line; this stacks on it. AE-2 (remove the
auto-detect, enforce one unit) tracked separately.
Trade-offs: each poll re-scans the lookback window (bounded by PollLimit + window); the dedup set is a
bounded LRU. Both configurable; LOOKBACK=0 preserves today's behavior.
Test plan
- Unit (
trigger/clickhouse_internal_test.go, watermark_test.go): late-arrival-in-lookback captured once;
below-lookback dropped (documented bound); 1.78e18 poison row rejected + watermark stays wall-clock-bounded
- subsequent seconds rows still processed; each metric increments.
- Rig re-run of E8 on a live PG: poison the watermark, fire seconds anomalies → with the fix, anomalies
flow with NO manual reset (T1). Capture e8_recov equivalent.
Non-goals
- Switching the cursor to an ingest-order column (
inserted_at DEFAULT now64()) — a heavier schema change,
deferred; bounded-lookback+clamp achieves the targets without a kubescape_logs schema change.
- AE-2 (unit standardization / remove auto-detect) — separate issue.
Blocker (public-release): the trigger watermark can silently halt AE forever
adaptive_export's kubescape trigger gates on a strict forward-only high-water-mark of the contentevent_time(internal/trigger/clickhouse.go:WHERE event_time >= watermark, watermark advances tomaxSeen). Any anomaly withevent_time < watermarkis dropped and never re-queried. This is theproduction "writes succeed, then stop, data still on Pixie" failure.
Evidence (real PG runs — loadtest E8,
FINDINGS_AND_BACKLOG.mdF8)1781559619170395824; 25/25 ticks of fresh secondsanomalies →
n_anomaliesstayed 0 (writes stopped; Pixie + data-plane fine). Evidencee8_steady.csv.ALTER TABLE trigger_watermark DELETE WHERE 1=1+ AE restart; thenn_anomaliesgrew 1→2→3→4, delta=1/tick. Evidencee8_recov.csv.Three failure modes (all reproduce the halt)
watermark ≈ 1.78e18; every later secondsrow (
~1.78e9) is< watermark→ dropped forever.A partial mitigation already landed (
normalizeEventTimeNanos,trigger/clickhouse.go:205,285) — itreduces the unit-poison odds but does NOT fix skew/out-of-order/restart drops, and there is no metric
to even see a halt. This issue is the durable AE-9 fix.
Targets (acceptance criteria)
1.78e18row) or an out-of-order/skewed row,the trigger self-recovers without any manual
ALTER TABLE … DELETE+ restart. Re-run E8: freshseconds anomalies must flow within one lookback window.
event_timewithin a boundedlookback of the watermark is processed exactly once (no drop, no duplicate).
ae_trigger_watermark_ns(gauge),ae_trigger_below_watermark_total(rows seen below the prior HWM — i.e. would-have-been-dropped, now captured),
ae_trigger_event_time_rejected_total(poison clamps). A halt is visible + alertable.
Planned change (stacks on PR #92 branch
fix/ae-protocol-export-pxexport)internal/trigger/clickhouse.go): queryevent_time >= max(0, watermark - LOOKBACK_NS)instead of>= watermark; extend the existingboundary-fingerprint set into a bounded LRU covering
[watermark-LOOKBACK, watermark]so re-seen rowsare deduped. New env
ADAPTIVE_TRIGGER_LOOKBACK_SEC(default 300;0= legacy strict HWM). Catchesskew/out-of-order/restart drops (failure modes 2+3).
normalizeEventTimeNanos, reject anyevent_time> now + MaxSkew(default 1h) from advancing the watermark; count it in..._rejected_total. A single1.78e18row can no longer jump the cursor past all real data.normalizeEventTimeNanos(AE-2 partial) as the first line; this stacks on it. AE-2 (remove theauto-detect, enforce one unit) tracked separately.
Trade-offs: each poll re-scans the lookback window (bounded by
PollLimit+ window); the dedup set is abounded LRU. Both configurable;
LOOKBACK=0preserves today's behavior.Test plan
trigger/clickhouse_internal_test.go,watermark_test.go): late-arrival-in-lookback captured once;below-lookback dropped (documented bound);
1.78e18poison row rejected + watermark stays wall-clock-boundedflow with NO manual reset (T1). Capture
e8_recovequivalent.Non-goals
inserted_at DEFAULT now64()) — a heavier schema change,deferred; bounded-lookback+clamp achieves the targets without a
kubescape_logsschema change.