feat(ha-repl-hardening): add StateReport to RequestReply capnp schema (task 1.1)

Add StateReport struct {appliedLsn, configTag} as stateReportMsg @15
variant in RequestReply.data union. Standby populates this in heartbeat
replies so the primary can track per-standby convergence.

capnpc-c 1.0.1 has a bug with inline :group inside codecgen unions;
workaround uses named outer struct (wire-equivalent). typemap.h
extended with state_report_t and union member for C codec access.
This commit is contained in:
Rongsong Shen 2026-06-04 13:34:11 +00:00
parent e31673dd88
commit 9a977b3a43
3 changed files with 16 additions and 1 deletions

View file

@ -9,7 +9,7 @@
--- ---
- [ ] 1. Foundation: Schema changes and state tracking fields - [ ] 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 - Add `appliedLsn :UInt64 @15` and `configTag :UInt64 @16` fields inside a `stateReport :group` variant
- Regenerate capnp bindings; verify compilation succeeds - Regenerate capnp bindings; verify compilation succeeds
- Existing decode paths and opcode dispatch unaffected - Existing decode paths and opcode dispatch unaffected

View file

@ -186,6 +186,12 @@ typedef struct {
uint64_t peer_last_log_lsn; uint64_t peer_last_log_lsn;
} request_vote_reply_t; } 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 { typedef struct {
uint64_t seq; uint64_t seq;
uint32_t opcode; uint32_t opcode;
@ -201,6 +207,7 @@ typedef struct {
upgrade_config_reply_t* upgradeconfigreplymsg; upgrade_config_reply_t* upgradeconfigreplymsg;
ctrl_hello_reply_t* ctrlhelloreplymsg; ctrl_hello_reply_t* ctrlhelloreplymsg;
request_vote_reply_t* requestvotereplymsg; request_vote_reply_t* requestvotereplymsg;
state_report_t* state_report;
} data; } data;
/* Req 7: standby redirect fields — populated when status == XCE_ERR_STANDBY */ /* Req 7: standby redirect fields — populated when status == XCE_ERR_STANDBY */
char *redirect_host; char *redirect_host;

View file

@ -559,6 +559,7 @@ struct RequestReply $C.mapname("xce_reply_t"){
upgradeconfigreplymsg @10: UpgradeConfigReply; upgradeconfigreplymsg @10: UpgradeConfigReply;
ctrlhelloreplymsg @13: CtrlHelloReply; ctrlhelloreplymsg @13: CtrlHelloReply;
requestvotereplymsg @14: RequestVoteReply; requestvotereplymsg @14: RequestVoteReply;
stateReportMsg @15: StateReport $C.mapname("state_report"); # heartbeat reply: standby state
} }
redirectHost @11: Text $C.mapname("redirect_host"); 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) ─────────── # ── 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") { struct RequestVoteRequest $C.mapname("request_vote_req_t") {
term @0 :UInt64 $C.mapname("term"); term @0 :UInt64 $C.mapname("term");
candidateId @1 :Text $C.mapname("candidate_id"); candidateId @1 :Text $C.mapname("candidate_id");