diff --git a/.kiro/specs/ha-repl-hardening/tasks.md b/.kiro/specs/ha-repl-hardening/tasks.md index 3ce58ff..b5b022f 100644 --- a/.kiro/specs/ha-repl-hardening/tasks.md +++ b/.kiro/specs/ha-repl-hardening/tasks.md @@ -9,7 +9,7 @@ --- - [ ] 1. Foundation: Schema changes and state tracking fields -- [ ] 1.1 Add `stateReport` group to `RequestReply.data` union in controller.capnp +- [x] 1.1 Add `stateReport` group to `RequestReply.data` union in controller.capnp - Add `appliedLsn :UInt64 @15` and `configTag :UInt64 @16` fields inside a `stateReport :group` variant - Regenerate capnp bindings; verify compilation succeeds - Existing decode paths and opcode dispatch unaffected diff --git a/src/include/typemap.h b/src/include/typemap.h index 1628cb6..df2aded 100644 --- a/src/include/typemap.h +++ b/src/include/typemap.h @@ -186,6 +186,12 @@ typedef struct { uint64_t peer_last_log_lsn; } request_vote_reply_t; +/* Heartbeat reply state report — carries standby's applied_lsn and config_tag */ +typedef struct { + uint64_t applied_lsn; + uint64_t config_tag; +} state_report_t; + typedef struct { uint64_t seq; uint32_t opcode; @@ -201,6 +207,7 @@ typedef struct { upgrade_config_reply_t* upgradeconfigreplymsg; ctrl_hello_reply_t* ctrlhelloreplymsg; request_vote_reply_t* requestvotereplymsg; + state_report_t* state_report; } data; /* Req 7: standby redirect fields — populated when status == XCE_ERR_STANDBY */ char *redirect_host; diff --git a/src/schema/controller.capnp b/src/schema/controller.capnp index 9376325..7ccf3f3 100644 --- a/src/schema/controller.capnp +++ b/src/schema/controller.capnp @@ -559,6 +559,7 @@ struct RequestReply $C.mapname("xce_reply_t"){ upgradeconfigreplymsg @10: UpgradeConfigReply; ctrlhelloreplymsg @13: CtrlHelloReply; requestvotereplymsg @14: RequestVoteReply; + stateReportMsg @15: StateReport $C.mapname("state_report"); # heartbeat reply: standby state } redirectHost @11: Text $C.mapname("redirect_host"); @@ -782,6 +783,13 @@ struct ReplEnvelope $C.mapname("capnp_repl_envelope_t") { # ── HA election and replication message structs (Task 1.1) ─────────── +# Heartbeat reply state report — standby piggybacks its replication state +# onto every OPCode_nop reply so the primary can drive convergence decisions. +struct StateReport $C.mapname("state_report_t") { + appliedLsn @0 :UInt64 $C.mapname("applied_lsn"); # standby's last applied WAL LSN + configTag @1 :UInt64 $C.mapname("config_tag"); # standby's current config version tag +} + struct RequestVoteRequest $C.mapname("request_vote_req_t") { term @0 :UInt64 $C.mapname("term"); candidateId @1 :Text $C.mapname("candidate_id");