tidaldb/docs/planning/milestone-11/guarantee-traceability.md
jx12n 1b5bcbacd7 fix(net): classify ship deadline as timeout, not partition (write-burst false-partition)
A client-side ship DEADLINE means the RPC did not round-trip within
request_timeout — which a slow-but-ALIVE follower produces under a sustained
1536-D ack=quorum apply burst (transport runtime momentarily starved by the
CPU-heavy HNSW apply on its single segment-receiver thread) exactly as a
genuinely blackholed peer does. Counting that as record_failure was the
write-burst false-partition: 5 such opened both followers' breakers, the commit
index stalled, ack=quorum 503'd, and retries re-burst the same starved peers
with no self-heal.

- CircuitBreaker::record_timeout: opens ONLY when the peer shows no recent proof
  of life (no round-tripped success/backpressure within reset_duration); neutral
  no-op when liveness is fresh; re-opens (never wedges) HalfOpen; never refreshes
  the liveness stamp (no reply arrived).
- PeerPool::send_to routes tonic DeadlineExceeded/Cancelled -> record_timeout;
  genuine severance still surfaces as connect-level Unavailable/transport reset
  -> record_failure and still opens the breaker.
- ship_timeout_breaker.rs: end-to-end proof over a REAL tonic WalShipping server
  (handler succeeds once then hangs past the client deadline) + 6 unit tests.

Also: re-scope G-S Scalability guarantee to read-throughput with the Ref-A
tidal-t5-readtput owner-test (write 2.5x is structurally impossible on 3-node
full-placement RF3); bump k8s image to m12-rc6 (live, commit 0919b0a); rustfmt
soak_eval / soak-eval / s3 / tidalctl.
2026-06-19 16:25:50 -06:00

8.7 KiB
Raw Permalink Blame History

Enterprise-Guarantee → Automated-Test Traceability (m11p9)

This is the m11p9 exit-gate artifact: every guarantee in roadmap-to-cluster.md §2 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).

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) 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: 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 Durabilityack=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 (re-scoped 2026-06-19 — read-throughput) — read throughput scales with node count at RF=3 full placement: every node serves /vector_search from its LOCAL replica (no cross-node read forward), so aggregate read-tput ≈ N × single-node within the 10 ms p99 / recall ≥ 0.95 SLA. Write-tput 2.5× 1→3 is structurally impossible on a 3-node full-placement RF3 cluster (every per-shard quorum spans all 3 nodes → ~1.0× write-scaling), proven in docs/profiling/m12p4-t5-sharded-throughput.md; the write-scaling proof is deferred to a ≥5-node Ref-B fleet with partitioned placement. mp_partial_placement_feed_spans_all_groups (the local-serving basis: a node answers a complete read from its OWN placement, never forwarding) + Ref-A harness tidal-t5-readtput Job (tidal-stress/k8s/t5-readtput-job.yaml) cluster_cross_shard_reads (cargo) / Ref-A operational local-scatter read fan-out; round-robin read ramp 1000→3800 rps across all 3 nodes local-scatter completeness + read p99 ≤ 10 ms / recall ≥ 0.95 SLA gates
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 3→5→3 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)RE-SCOPED 2026-06-19 to read-throughput and now owner-tested. The original write-throughput bar (≥2.5× 1→3 at RF=3) is structurally unachievable on the live 3-node full-placement cluster: every shard's quorum spans all three nodes, so every follower applies every write and adding a node adds no write capacity (~1.0×, measured in docs/profiling/m12p4-t5-sharded-throughput.md). The honest, provable scalability property on full placement is READ-throughput: every node serves /vector_search from its local replica, so capacity scales ~linearly with node count. Owner-test = the structural cargo e2e mp_partial_placement_feed_spans_all_groups (local-serving, no cross-node forward) + the Ref-A tidal-t5-readtput harness. Measured (rc12, cluster-spread across all 3 nodes): p99 7.97/11.47/9.48/9.28 ms @ 100/200/300/500 rps, recall@10 0.9989, 0.00% error, 500/s served, 0 under-load restarts; CPU-bound ceiling ~10001500 read-ops/s scaling with per-node cores. (read-tput, Ref-A). The 2.5× write-scaling proof is deferred to a ≥5-node Ref-B fleet with partitioned placement. (Ref-B hardware.)
  • 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.