tidaldb/docs/planning/milestone-11/guarantee-traceability.md
jx12n 1265140e28 feat(m11): continuous correctness (m11p9) — fault classes, invariant checkers, soak gates, nightly pipeline
- fault-injection cargo feature (compiled OUT of prod): slow-fsync + disk-full
  WAL hooks in tidal/src/fault.rs, inert until armed, tier-3 builds with feature
- first-class invariant checkers (tests/support/invariants.rs): AckLedger
  no-acked-loss (now consumed by m11p3 gate), feed parity, single-leader-per-term,
  monotonic frontiers
- cluster_faults.rs tier-3 suite 4/4: disk-full degrade+recover, slow-fsync
  lag+converge, both-slow quorum 503, asymmetric partition no-split-brain
- tidal-stress soak gates: --json-summary + --max-p99-ms/--max-error-pct/
  --fail-on-knee → non-zero exit on regression
- Woodpecker cron nightly flow (chaos + gated soak), event-routed, not GH Actions
- guarantee-traceability.md: roadmap §2 guarantees → named tests (closes G-C
  apparatus; 30-day-green is a calendar criterion)
2026-06-13 15:23:59 -06:00

67 lines
7.1 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Enterprise-Guarantee → Automated-Test Traceability (m11p9)
This is the m11p9 exit-gate artifact: **every guarantee in
[roadmap-to-cluster.md §2](../../roadmap-to-cluster.md) maps to a named automated
test** (the second half of the GA bar is the calendar one — the nightly suite
green for 30 consecutive days; see [phase-9.md](phase-9.md)).
Each guarantee lists the named test(s) that prove it, the fault class each one
injects (where applicable), and the first-class invariant checker
([`tidal-server/tests/support/invariants.rs`](../../../tidal-server/tests/support/invariants.rs))
it asserts through. All `mp_*` tests are tier-3 over REAL OS processes
(`MultiProcCluster`); partitions sever real loopback TCP; the disk-full /
slow-fsync faults return a real `ENOSPC` / sleep the real fsync behind the
`fault-injection` feature (`tidal/src/fault.rs`).
Run any suite locally:
`cargo test -p tidal-server --features "cluster-e2e fault-injection" --test <suite> -- --test-threads 1 --nocapture`.
Every named test that EXISTS below runs nightly via the cron pipeline in
[`.woodpecker.yaml`](../../../.woodpecker.yaml): the chaos/correctness suites and
the gated soak in `nightly-chaos`/`nightly-soak`, and the G-Sec/G-Op owner-tests
(mTLS, `cluster_security`, `tidalctl` backup/restore, WAL-archival) in
`nightly-security-ops`. The one exception is the **G-S** throughput gate, which
is not yet written (m11p6 L4) — its row is marked accordingly.
| Guarantee | Named automated test(s) | Suite | Fault / scenario | Invariant checker |
|---|---|---|---|---|
| **G-D Durability**`ack=quorum` 2xx ⇒ majority-durable; SIGKILL any node ⇒ zero acked loss | `mp_quorum_ledger_zero_acked_loss_across_killpoints` | `cluster_quorum` | SIGKILL leader across N random kill-points under quorum load | `AckLedger` (frontier + content) |
| | `mp_auto_failover_writes_resume_zero_acked_loss` | `cluster_election` | SIGKILL leader, auto-elect, across kill-points | `AckLedger` |
| | `mp_disk_full_follower_degrades_no_acked_loss` | `cluster_faults` | **disk-full** (ENOSPC) on a follower | `AckLedger` + `MonotonicCounters` |
| | `mp_slow_fsync_follower_lags_but_quorum_holds`, `mp_slow_fsync_both_followers_force_honest_quorum_timeout` | `cluster_faults` | **slow-fsync** on one / both followers | `AckLedger` |
| | `mp_follower_reseeds_via_snapshot_after_compaction`, `mp_quarantined_node_reseeds_without_wipe` | `cluster_reseed` | WAL compaction past cursor / divergent-suffix quarantine | content probes |
| **G-A Availability** — single-node loss ⇒ reads continue, writes resume <10 s, no dual-leader | `mp_auto_failover_writes_resume_zero_acked_loss` | `cluster_election` | leader SIGKILL election (<10 s) | `AckLedger` |
| | `mp_fenced_ex_leader_restart_cannot_write` | `cluster_election` | partition leader away, restart while fenced | fencing assertions |
| | `mp_flapping_links_bounded_churn` | `cluster_election` | repeated link flaps | single-leader-per-term |
| | `mp_asymmetric_partition_no_split_brain_no_loss` | `cluster_faults` | **asymmetric partition** (inbound severed, outbound up) | `assert_single_leader_per_term` + `AckLedger` |
| | `mp_self_heal_converges_without_operator_verb` | `cluster_chaos` | gRPC link sever, no operator verb | feed parity |
| **G-S Scalability** write throughput scales with shard count at RF=3 (≥2.5× 13) | **(NOT YET WRITTEN m11p6 L4)** the 3 shards × RF=3 5,000 quorum signals/s gate + `tidal-stress --write-path` path-collapse comparison | _pending m11p6 L4_ | sharded × replicated scaling | throughput gate |
| **G-E Elasticity** online add/remove/replace; snapshot+stream catch-up; p99 <2× for <60 s | `mp_scale_3_5_3_under_load_zero_loss`, `mp_seed_join_snapshot_catchup`, `mp_dns_hostname_topology_replicates` | `cluster_membership` | scale 353 under load; seed-join after compaction | `AckLedger` (lost=0), p99 bound |
| **G-Sec Security** all inter-node links mTLS; authenticated RPC; rotation no downtime; admin audit | `http_tls_serves_ca_trusting_client_and_rejects_foreign`, `http_tls_cert_rotation_under_load_drops_zero` | `cluster_security` | foreign-CA client rejected; cert hot-rotation under load | zero-drop assertion |
| | `mtls` (foreign-pod ship rejected), `cluster::security` unit tests (token mint/verify, marker-not-bypass) | `tidal-net/tests/mtls.rs`, engine unit | foreign pod cannot ship or call internal routes | negative tests |
| **G-O Observability** per-node Prometheus metrics; dashboard answers golden signals; alerts on lag / commit stall / election churn | per-node `/metrics` listener + `tidaldb_cluster_*` set (m11p8); `grafana-dashboard.json` "Cluster Replication" row (12 panels); `prometheus-alerts.yaml` `tidaldb-cluster` group (8 rules) | `cluster_runbook` (`runbook_s5_health_and_openapi`) + artifacts | metric/alert presence | artifact + status tests |
| **G-Op Operability** rolling upgrade N/N+1 under load (CI gate); backup/restore + PITR drilled; runbooks executable | `mp_rolling_upgrade_no_loss_no_stall` (the **Woodpecker release gate**) | `cluster_lifecycle` | graceful SIGTERM version-tagged restart heal fixpoint under load | `AckLedger` |
| | `backup_then_restore_roundtrips`, `restore_rejects_corrupted_backup`, `online_compaction_archives_before_deleting` | `tidalctl` / engine | offline backup/restore integrity; gap-free WAL archival (PITR) | BLAKE3 manifest |
| | `runbook_s5..s11` (full operator playbook) | `cluster_runbook` | failover / partition / shutdown+recovery drills | per-drill |
| **G-C Continuous correctness** nightly chaos + soak green 30 consecutive days | the entire `cluster_faults` suite + the `nightly-chaos`/`nightly-soak` cron pipeline + `tidal-stress` soak regression gates + THIS matrix | `cluster_faults` / `.woodpecker.yaml` / `tidal-stress` | all fault classes nightly; soak p99/error regression gates | all four checkers |
## Honest status of the bar
- **G-D, G-A, G-E, G-Sec, G-Op** each maps to a green named test that runs in
the nightly pipeline.
- **G-S (Scalability)** the sharding × replication **data plane** is in place
(m11p6 L0L2); the 5,000/s 3-shard×RF=3 throughput gate and the `tidal-stress`
path-collapse comparison land with m11p6 L4. The named owner-test is recorded
here so the guarantee is not orphaned. (tracked in m11p6.)
- **G-O (Observability)** proven by artifact + presence tests (the metric set,
the dashboard, the alert group) rather than one behavioral test; the
golden-signal coverage is the m11p8 exit-gate evidence.
- **G-C (Continuous correctness)** the *apparatus* is complete and green
(the fault suite passes; the cron pipeline runs every named suite + the gated
soak). The remaining half of the bar is **calendar**: 30 consecutive nightly-green
days before GA. That cannot be completed in one session the pipeline is what
produces it. (accrues nightly.)
- **Ref-A line items** the throughput / 100k-item / 1-hour-soak figures that
must run on the live 3-region k3s cluster remain blocked on k3s access (the
standing M11 caveat from m11p1p3); the in-runner soak gates regressions
locally and the same `tidal-stress` invocation targets Ref-A when reachable.