From 97a501e96e8ce500e37bc0fdfd4d1f3b2a30a6a2 Mon Sep 17 00:00:00 2001 From: Rongsong Shen Date: Sat, 20 Jun 2026 04:32:42 +0000 Subject: [PATCH] feat(controller-status): add ctrlstatus opcode + CtrlStatus/CtrlPeer schema + typemap typedefs (task 1.1) --- .kiro/specs/controller-status/spec.json | 6 ++--- .kiro/specs/controller-status/tasks.md | 3 ++- src/include/typemap.h | 30 +++++++++++++++++++++++++ src/schema/controller.capnp | 24 ++++++++++++++++++++ 4 files changed, 59 insertions(+), 4 deletions(-) diff --git a/.kiro/specs/controller-status/spec.json b/.kiro/specs/controller-status/spec.json index 50e3589a..d5fcaa47 100644 --- a/.kiro/specs/controller-status/spec.json +++ b/.kiro/specs/controller-status/spec.json @@ -15,8 +15,8 @@ }, "tasks": { "generated": true, - "approved": false + "approved": true } }, - "ready_for_implementation": false -} + "ready_for_implementation": true +} \ No newline at end of file diff --git a/.kiro/specs/controller-status/tasks.md b/.kiro/specs/controller-status/tasks.md index 8d23dae4..1e5be1c7 100644 --- a/.kiro/specs/controller-status/tasks.md +++ b/.kiro/specs/controller-status/tasks.md @@ -4,7 +4,7 @@ > The work runs along a single request path (schema → controller handler → dispatch gate → libxce client → test command → wait-primary), so tasks are sequential by design; no `(P)` markers. The one HA-critical change is the dispatch-gate allow-list (task 2.2). - [ ] 1. Foundation: status reply schema + codegen -- [ ] 1.1 Add the `ctrlstatus` opcode and the status reply payload to the schema +- [x] 1.1 Add the `ctrlstatus` opcode and the status reply payload to the schema - Add a new request opcode `ctrlstatus` (next free ordinal) and a new reply-union member (next free ordinal), plus a lean status payload struct carrying: cluster name, primary name, responder name and role, term, loaded config version and content checksum, committed config version, commit and apply positions, fenced flag, and a bounded list of configured controllers (name, host, port) with a count. - Keep the payload focused on identity/health only — it must NOT carry the resource/queue/worker inventory. - Observable: the build regenerates the C types for the new payload and opcode (the generated status type, its reply-union tag, and a free helper exist) and the schema compiles clean. @@ -67,3 +67,4 @@ - All new C types are generated from the schema (task 1.1) — no hand-written struct headers. Build regenerates `controller.capnp.h/.c`. - Task 2.2 is the one HA-critical change (request-handling path): keep it scoped to the single read-only opcode and prove no other opcode's gating widened. - The authoritative redirect (3.2) is what makes wait-primary trustworthy (no stale-standby false success) — it is the resolution of the design-review decision; do not collapse it back into a plain any-node read for wait-primary. +- 1.1 LEARNING (affects later tasks): the `$C.mapname(...)` capnp types are NOT auto-generated in this schema — the generated `controller.capnp.c` references HAND-AUTHORED C typedefs in `src/include/typemap.h` (same convention as `xce_clusterconf_t`). So `xce_ctrlstatus_t`/`xce_ctrlpeer_t` (and the `xce_reply_t.data.ctrlstatus` union member) live in `typemap.h`, verified field-for-field against the regenerated codec. `OPCode_ctrlstatus = 40`, `RequestReply_data_ctrlstatus = 12`. `configChecksum` is `xce_data_t*`. The handler/libxce tasks build/encode against these typemap.h types. diff --git a/src/include/typemap.h b/src/include/typemap.h index 5e340b02..ae49edd2 100644 --- a/src/include/typemap.h +++ b/src/include/typemap.h @@ -203,6 +203,35 @@ typedef struct { int success; } wal_ack_reply_t; +/* ctrlstatus reply payload (controller-status spec): one configured + * controller (CtrlPeer) and the lean identity/health snapshot (CtrlStatus). + * The peers list maps via $C.mapname("peers.list")/$C.maplistcount("peers.count") + * to the nested { count, list } member below. */ +typedef struct { + char* name; + char* host; + uint32_t port; +} xce_ctrlpeer_t; + +typedef struct { + char* clusterName; + char* primaryName; + char* responderName; + uint8_t responderRole; + uint64_t term; + uint64_t configVersion; + xce_data_t* configChecksum; + uint64_t committedConfigVersion; + uint64_t committedLsn; + uint64_t appliedLsn; + unsigned fenced; + struct { + int count; + xce_ctrlpeer_t** list; + } peers; + uint32_t peerCount; +} xce_ctrlstatus_t; + typedef struct { uint64_t seq; uint32_t opcode; @@ -220,6 +249,7 @@ typedef struct { request_vote_reply_t* requestvotereplymsg; state_report_t* state_report; wal_ack_reply_t* walackreplymsg; + xce_ctrlstatus_t* ctrlstatus; } 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 288bd460..7bb3f56b 100644 --- a/src/schema/controller.capnp +++ b/src/schema/controller.capnp @@ -48,6 +48,7 @@ enum OPCode { walCatchupEntry @37; preVoteRequest @38; preVoteReply @39; + ctrlstatus @40; } struct PeerInfo $C.mapname("xce_peerinfo_t") { @@ -486,6 +487,28 @@ struct NotifyData $C.mapname("xce_notify_data_t"){ } } +struct CtrlPeer $C.mapname("xce_ctrlpeer_t") { + name @0: Text; + host @1: Text; + port @2: UInt32; +} + +struct CtrlStatus $C.mapname("xce_ctrlstatus_t") { + clusterName @0: Text; + primaryName @1: Text; + responderName @2: Text; + responderRole @3: UInt8; + term @4: UInt64; + configVersion @5: UInt64; + configChecksum @6: Data; + committedConfigVersion @7: UInt64; + committedLsn @8: UInt64; + appliedLsn @9: UInt64; + fenced @10: Bool; + peers @11: List(CtrlPeer) $C.mapname("peers.list") $C.maplistcount("peers.count"); + peerCount @12: UInt32; +} + struct ClusterConf $C.mapname("xce_clusterconf_t") { name @0: Text; resources @1: List(Resource) $C.mapname("reslist.res") $C.maplistcount("reslist.count"); @@ -563,6 +586,7 @@ struct RequestReply $C.mapname("xce_reply_t"){ requestvotereplymsg @14: RequestVoteReply; stateReportMsg @15: StateReport $C.mapname("state_report"); # heartbeat reply: standby state walackreplymsg @16: WalAckReply $C.mapname("walackreplymsg"); # WAL apply ACK from standby + ctrlstatus @17: CtrlStatus; # ctrlstatus reply: identity/health snapshot } redirectHost @11: Text $C.mapname("redirect_host");