Splits monolithic cluster.rs into tidal-server/src/cluster/ modules. Adds redeliver-missed relay, bounded HLC drift, lag tracking, and reconcile idempotence. Five new tier-3 test suites (chaos, lifecycle, multiproc, region, routes, runbook) all green. Docs, CHANGELOG, and ROADMAP updated with G4/G5/G6 known gaps.
4.9 KiB
Task 05: TCP-proxy partition injection + UAT steps 3/4 + reconcile timing
Delivers
Real network-partition injection for the multi-process cluster via an in-harness TCP
relay proxy (the ROADMAP-sanctioned "toxiproxy or similar" alternative — root-free,
macOS/Linux portable), and the partition-centric tier-3 tests: degraded global query
(UAT step 3), heal + deterministic CRDT reconciliation with no loss/duplication
(UAT step 4), and the reconciliation < 100ms performance assertion. Lives in
tidal-server/tests/cluster_chaos.rs (cluster-e2e feature), reusing the task-04
harness support module.
Complexity: L
Dependencies
Task 04 (harness + support module).
Technical Design
PartitionProxy (in tests/support/)
Plain std::net::TcpListener + per-connection pump threads:
struct PartitionProxy {
listen: SocketAddr, // free port the PEERS dial
target: SocketAddr, // the region's real grpc/http listener
severed: Arc<AtomicBool>,
conns: Arc<Mutex<Vec<TcpStream>>>, // live streams, shutdown() on sever
}
impl PartitionProxy {
fn start(target: SocketAddr) -> Self;
fn sever(&self); // refuse new conns (accept→drop) AND shutdown existing streams
fn heal_link(&self);
}
Severing kills established TCP streams (both directions) and refuses new connects —
the leader's in-flight ShipSegment fails for real, the circuit breaker opens, HTTP
fan-outs time out. This is a genuine network-layer partition between OS processes.
Topology wiring
Harness option with_proxies(region): the isolated region's grpc_addr and http_addr
as PUBLISHED IN THE TOPOLOGY are proxy listen ports; the process itself binds the real
targets. The test client talks to the region's REAL http addr directly (operator's
console survives the partition; only peer traffic is severed) — matching the runbook
partition drill's "read the stale follower" step.
Tests
mp_uat_step3_degraded_query_during_partition: 3 nodes, ap-south proxied. Seed + converge.sever(ap-south). Writes to leader keep returning 204 (best-effort ship — the write-durability contract); ap-south's localappliedstalls while leaderlast_seqgrows (lag climbs, observed via direct addrs); aggregated/cluster/statuson the leader shows ap-southreachable: false/worst-case lag.GET /sharded/feed(deadline 1000ms) on the leader → 200,degraded: true,unavailable_shards == ["ap-south"], results still contain live-shard items. Direct read of ap-south's own/feedserves the PRE-partition data (eventual consistency demonstrated).mp_uat_step4_heal_reconcile_no_loss_no_dup: continue from a partition with N writes during the window plus aPOST /hardnegshide recorded on the leader for a seeded user/item.heal_link()thenPOST /cluster/heal {ap-south}on the leader →wait_converged_all; assert ap-south applied == leader last_seq (no loss) and decay parity 1e-6 (no duplication); call/cluster/healAGAIN → scores unchanged (idempotent redelivery). ThenPOST /cluster/reconcile {region: ap-south}on the leader → hide present on ap-south (hard negative converged, "hides remain hidden": the hidden item no longer appears in ap-south's/feed?user_id=…for that user); assertlocal_elapsed_ms < 100 && remote_elapsed_ms < 100; repeat reconcile → no-op, state identical (determinism/idempotence).mp_partition_between_followers: ROADMAP criterion verbatim — proxy BOTH followers, sever the eu-west↔ap-south direction only (each follower's proxy severed for nothing but… follower↔follower links carry no replication traffic in the leader-ships topology, so the meaningful assertion is): writes continue on the leader and BOTH followers converge (their leader links are intact) — i.e. a follower↔follower partition is invisible to correctness. Then additionally sever leader→ap-south to show the contrast (lag climbs only then). This makes the "partition between two followers: writes continue on leader; heal restores convergence with no data loss" criterion an explicit, honest test.
Tier-3 timing: every wait bounded with diagnosable timeout messages; suite total budget documented in the file header.
Acceptance Criteria
- Partition is a real TCP-level severance between OS processes (no engine flags involved in the chaos path)
- UAT step 3 passes: degraded scatter-gather (
degraded: true, named shard), leader writes continue, stale follower serves - UAT step 4 passes: heal → zero loss, zero duplication (parity 1e-6, idempotent re-heal), reconcile converges hard negatives ("hides remain hidden" via /feed), reconcile merge+apply < 100ms both sides, idempotent on repeat
- Follower↔follower partition criterion covered explicitly
- Suite green via
cargo test -p tidal-server --features cluster-e2e --test cluster_chaos