tidaldb/tidal-server/src
jx12n 0919b0a4bf fix(m12): seed-join learner auto-promotes after a snapshot install (report the caught-up frontier on the heartbeat)
Fixes a regression the reseed-loop fix (c8ea05b "Fix 1") introduced: a node
that seed-joins and converges via a SNAPSHOT INSTALL never auto-promotes
Learner -> Voter. It catches up fully (applied == leader frontier, lag 0) but
sits a Learner forever; `mp_seed_join_snapshot_catchup` caught it (base
580142d passes, c8ea05b on fails — bisected).

Root cause: the leader's durable per-peer `learner_mark` (which the
auto-promotion gate reads: `flushed - learner_mark <= learner_promote_lag`)
advances ONLY from a follower frontier-report, which the receiver emits AFTER
applying a streamed event. Before Fix 1 a joiner seeded its frontier from
`last_wal_seq()` (0 on the empty WAL a checkpoint restore leaves), so it
re-pulled the whole log from seqno 1 and THOSE stream applies emitted the
reports that advanced `learner_mark`. Fix 1 seeds the frontier to
`snapshot_seq` to stop the prod reseed loop, so the leader has nothing to
ship, no stream applies, and the joiner never tells the leader it is caught
up.

Fix: in the m12p5 heartbeat idle-readiness drive
(`note_leader_frontier_for_readiness`) report this node's caught-up frontier
back to the leader via the existing `ReportApplied` channel. The heartbeat
flows on an idle cluster and carries the current term, so the report is both
recurring (survives the join/registration race) and term-correct (the
term-checked `update_peer_for_term` fold accepts it — a boot-time report
stamped term 0 does not). SCOPED to a Learner: a Voter's frontier already
reaches the leader via ship-acks and DOES feed `compute_commit`, so folding
one off the heartbeat could perturb the same-term commit gate (Raft fig-8); a
learner mark never feeds `compute_commit`, so this is provably commit-safe.
`notify_applied` dedups, so a steady follower never spams.

Verified: mp_seed_join_snapshot_catchup PASS (joiner promotes, 4-voter
quorum); safety preserved — mp_quarantined (divergent quarantine+reseed, no
wipe), mp_graceful_rolling_restart_under_load_no_reseed (0-reseed), the
failover oracle (no false quarantine), tidal-server lib 154/154, engine
durability 4/4, clippy -D clean. The 5600-item voter-reseed e2e are inert to
this Learner-scoped change (host RAM cannot run them locally; verified live).
2026-06-19 01:46:46 -06:00
..
cluster fix(m12): seed-join learner auto-promotes after a snapshot install (report the caught-up frontier on the heartbeat) 2026-06-19 01:46:46 -06:00
config.rs feat: kubernetes deployment, OpenAPI spec, guides, and docker consolidation 2026-06-09 17:06:34 -06:00
dto.rs feat(m12p4): sharded ingestion — scatter-gather pool + cross-shard unified reads (L4) 2026-06-14 15:17:35 -06:00
error.rs feat(m11): sharding × replication + rebalancing (m11p6 L3-L5) 2026-06-13 18:23:43 -06:00
health.rs fix: M0-M10 third-pass remediation — durability, replication, and CLI hardening 2026-06-08 10:28:34 -06:00
lib.rs feat(m11): membership, snapshot install, and reseed (m11p5) 2026-06-12 19:55:54 -06:00
main.rs fix(m12-rc13): read-SLA collapse + WAL_RETENTION_SEGMENTS 16 + tidalctl S3 DR 2026-06-17 15:47:37 -06:00
offload.rs fix(m12-rc13): read-SLA collapse + WAL_RETENTION_SEGMENTS 16 + tidalctl S3 DR 2026-06-17 15:47:37 -06:00
openapi.rs feat(m12): vector retrieval G1/G2 — recall harness, ANN in RETRIEVE, index tuning 2026-06-14 11:07:09 -06:00
router.rs feat(m12p4): sharded ingestion — scatter-gather pool + cross-shard unified reads (L4) 2026-06-14 15:17:35 -06:00
scatter_gather.rs feat(m12p4): sharded ingestion — scatter-gather pool + cross-shard unified reads (L4) 2026-06-14 15:17:35 -06:00
self_exit.rs feat(m11): membership, snapshot install, and reseed (m11p5) 2026-06-12 19:55:54 -06:00
state.rs feat(m12): vector retrieval G1/G2 — recall harness, ANN in RETRIEVE, index tuning 2026-06-14 11:07:09 -06:00