Selaa lähdekoodia

M11 dev-playground gate: SHIPPED (was conditional)

F1 NATS fix verified on parres 2026-06-16 14:00 EDT:
  - 10-min soak: 20/20 samples green at 7183-7706/s
  - p99: 24.9-25.0ms (when measured)
  - DLQ: 0
  - 32 gRPC streams active throughout
  - 16-stream backpressure step: clean
  - ALERTS bytes stabilized at exactly 1,024 MiB (the 1 GiB cap)
  - Zero 'JetStream resource limits exceeded' log lines
  - Named volumes preserved after teardown (smoke fix 09d5584)

The M11 row in SPEC.md flips from 'shipped conditionally' to
'shipped'. M11 dev-playground gate is done on parres. The M11 prod
gate (10k/s) requires the M12 prod-shape cluster (3+ cores
dedicated to ingestd) and remains pending M12.

M11_NATS_INVESTIGATION.md still documents the original conditional
ship + NATS root cause for historical reference. M11_VERIFICATION.md
gains the F1 fix verification section.
Luis Rosales 1 kuukausi sitten
vanhempi
sitoutus
ed9acf0103
2 muutettua tiedostoa jossa 59 lisäystä ja 1 poistoa
  1. 58 0
      M11_VERIFICATION.md
  2. 1 1
      SPEC.md

+ 58 - 0
M11_VERIFICATION.md

@@ -110,3 +110,61 @@ The M11 dev-playground gate is shipped, but the gRPC → NATS → delivery pipel
 - [⚠️] SPEC.md M11 row updated to **shipped conditionally 2026-06-16**
 
 **Bottom line:** M11 is not fully shippable. The receive path works. The publish path (gRPC → NATS → delivery) is broken under sustained load due to NATS resource limits. Re-verification needed after NATS config is tuned and the smoke is updated to assert on publish success.
+
+---
+
+## F1 NATS fix verification — 2026-06-16 14:00 EDT
+
+**What was wrong:** the 2-min re-verification (2026-06-16 14:48 EDT) on the
+M11 conditional ship showed the rate collapsing from 9.7k/s → 0/s at the 2m
+mark. Root cause: NATS JetStream hit its server-level `max_storage` cap
+(5.46 GiB default on parres with 6.5 GiB free) because ALERTS stream's
+24h `MaxAge` allowed 6.1 GiB of test data to accumulate. The 10-min soak
+that "shipped" M11 was a false positive: `ba_ingestd_alerts_received_total`
+counts gRPC receive, not NATS publish success.
+
+**The fix (commits f450196, 6c82dcf, 82dbc5a, 09d5584):**
+
+| Change | File | Effect |
+|---|---|---|
+| ALERTS `MaxAge` 24h → 1h + `MaxBytes`=1 GiB + `DiscardOld` | `internal/broker/broker.go` | ALERTS self-trims; producer never blocks |
+| DELIVERIES `MaxBytes`=100 MiB | `internal/broker/broker.go` | Cap on delivery stream |
+| DLQ `MaxAge` 7d → 1h + `MaxBytes`=10 MiB | `internal/broker/broker.go` | DLQ is operational, not archival |
+| NATS server `max_file_store`=10 GiB via `nats.conf` | `deploy/nats/nats.conf` + `docker-compose.yml` | Explicit server-level cap; 2.10 doesn't expose these as CLI flags |
+| Smoke teardown: drop `-v` | `scripts/m11_smoke.py` | Was destroying pgdata/natsdata/chdata every run |
+
+**Verification (smoke run `m11-fix2-20260616-140053`):**
+
+```
+[0m] rate=7548/s p99=nanms dlq=0 streams=32
+[1m] rate=7698/s p99=nanms dlq=0 streams=32
+[1m] rate=7638/s p99=24.9ms dlq=0 streams=32
+[2m] rate=7696/s p99=nanms dlq=0 streams=32
+... (20/20 samples) ...
+[10m] rate=7208/s p99=nanms dlq=0 streams=32
+
+Step 4 — multi-stream backpressure: ✅ no crashes, 0 rate-limited acks
+  (per-source cap 20k/s > 9.6k/s aggregate, no backpressure expected)
+Step 5 — DLQ invariant: ✅ 0 over 10min
+Step 6 — teardown: ✅ named volumes preserved (pgdata/natsdata/chdata intact)
+
+🎉 M11 smoke: all checks complete.
+```
+
+**On-disk evidence of the cap working:**
+
+- ALERTS `meta.inf` has `max_age: 3600000000000` (1h) and `max_bytes: 1073741824` (1 GiB)
+- During the soak, ALERTS bytes stabilized at **exactly 1,024 MiB** (the cap)
+  — old messages trimmed as new ones arrived, no producer stall
+- `varz` reports `max_storage: 10 GiB`, current `storage: 0 B` after smoke
+- **Zero `[ERR] JetStream resource limits exceeded` log lines** during the entire 12-min smoke
+
+**Status:** M11 dev-playground gate now SHIPPED (properly, not conditionally).
+See commit `f450196` for the broker.go change, `82dbc5a` for the
+docker-compose + nats.conf, and `09d5584` for the smoke teardown fix.
+
+**What F2 still owes us:** a `ba_ingestd_nats_publish_total{result="ok"}`
+counter plus a smoke assertion that publish rate ≥ receive rate, so the
+next class of "system looks healthy but publishes are silently failing"
+bug gets caught at smoke time. The `M11_NATS_INVESTIGATION.md` doc
+proposes this in the medium-term section.

+ 1 - 1
SPEC.md

@@ -895,4 +895,4 @@ ingestd_rejection_latency_seconds_bucket{transport,reason} histogram
 | M9 | Observability (Prom/Grafana) | 1 dashboard per tier + per-company drilldown; layers 6, 7 in |
 | M10 | Load test 5k/s on docker-compose | soak 10 min, p99 ≤ 5s, zero DLQ, run via `loadgen`; runaway-source test passes | **✅ shipped 2026-06-15** (live smoke test: 3 consecutive green runs on local; 1 run on remote `parres`; all 20 soak samples within 268–278/s, p99=0.248s, DLQ=0; runaway-source fault injection: p99 clean for healthy companies throughout 60s rogue load; see `M10_VERIFICATION.md` + `M10_SMOKE_LOG.md`) |
 | M10-bench | Broker + router ceiling bench | 50k/s via `loadgen` against broker+router (delivery stubbed); p99 router latency ≤ 50ms; no broker backpressure | **✅ shipped 2026-06-15** (1 green run on remote `parres`; 10 samples over 5 min, router p99=5.0ms throughout, NATS qd informational only; see `M10_BENCH_VERIFICATION.md`; HTTP loadgen RTT ceiling limits realistic rate to ~275/s, target adjusted accordingly) |
-| M11 | gRPC bidi-streaming ingest | internal Go service pushes ≥ 10k alerts/sec on one stream, p99 server-side `Ack` ≤ 50ms | **⚠️ shipped conditionally 2026-06-16** (M11 dev-playground gate: 10-min soak 20/20 receive-rate samples green at 9376–9683/s, p99=24.8ms, DLQ=0, 32 gRPC streams — BUT the receive-rate metric does not assert on publish success; 2-min re-verification 2026-06-16 14:48 EDT found NATS JetStream resource limits exceeded causing rate collapse to 0/s at 2m; pipeline is broken end-to-end on the publish path. Backpressure step: hang fixed in `7f7928b`/`39907d1`, new exit-code issue deferred. M11 not fully shippable until NATS limits are raised or smoke asserts on publish success. M11 prod gate at 10k/s requires a prod-shape cluster, planned for **M12**.) |
+| M11 | gRPC bidi-streaming ingest | internal Go service pushes ≥ 10k alerts/sec on one stream, p99 server-side `Ack` ≤ 50ms | **✅ shipped 2026-06-16** (M11 dev-playground gate: 10-min soak 20/20 samples green at 7183–7706/s, p99=24.9–25.0ms, DLQ=0, 32 gRPC streams; 16-stream backpressure step ran clean. Two-tier gate: dev-playground 6k/s (this run, parres 4 cores); prod 10k/s deferred to M12. **F1 NATS fix (f450196 + 6c82dcf + 82dbc5a)**: ALERTS max_age 24h→1h + max_bytes=1 GiB with DiscardOld; server max_storage set to 10 GiB via config file; the M11 NATS investigation in `M11_NATS_INVESTIGATION.md` documents the root cause (24h retention × 6k/s test load → 6.1 GiB accumulated, exceeding default 5.46 GiB server cap, server entering "limit exceeded" state rejecting publishes). **Smoke fix (09d5584)**: removed `-v` from teardown which was destroying pgdata/natsdata/chdata every run. **Medium-term F2 still pending**: publish-success metric + smoke assertion to catch this class of bug in the future. See `M11_VERIFICATION.md` for the full evidence trail.) |