CtrlHelloRequest.key was declared `Data` in controller.capnp. This project's capnp-c backend silently no-ops encoding of Data-typed fields without zero-initializing the caller's local struct, leaving an uninitialized capn_ptr that write_CtrlHelloRequest unconditionally wrote to the wire -- SIGSEGV in capn_setp/copy_ptr. Hit live: ctrl-1 won its Raft election and crashed calling ha_send_ctrlhello_to_all_standbys(). Same bug class as bug-1504/bug-1505, both already fixed by switching the field from Data to List(UInt8); bug-1505's own writeup had flagged CtrlHelloRequest.key as a known-but-unfixed instance. Applied the identical fix here: Data -> List(UInt8) + keyLen companion in the schema, uint8_t*/key_len in typemap.h. The field is unused in production (always NULL), so no consumer code changed. Added a round-trip regression test covering both the NULL-key production shape and real key content. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
822 lines
32 KiB
C
822 lines
32 KiB
C
#if !defined(_TYPEMAP_H_)
|
|
|
|
#define _TYPEMAP_H_ 1
|
|
#include <stdint.h>
|
|
|
|
#include "clusterconfig.h"
|
|
#include "xce/types.h"
|
|
|
|
/* Forward declarations for config-replication types used in
|
|
* xce_reply_t and xce_request_t unions below. */
|
|
typedef struct repl_config_reply_s repl_config_reply_t;
|
|
typedef struct repl_config_push_reply_s repl_config_push_reply_t;
|
|
typedef struct upgrade_config_reply_s upgrade_config_reply_t;
|
|
typedef struct repl_config_req_s repl_config_req_t;
|
|
typedef struct repl_config_push_req_s repl_config_push_req_t;
|
|
typedef struct upgrade_config_req_s upgrade_config_req_t;
|
|
typedef struct heartbeat_msg_s heartbeat_msg_t;
|
|
typedef struct ctrl_hello_req_s ctrl_hello_req_t;
|
|
typedef struct ctrl_hello_reply_s ctrl_hello_reply_t;
|
|
typedef struct wal_catchup_entry wal_catchup_entry_t;
|
|
typedef struct snapshot_transfer_s snapshot_transfer_t;
|
|
|
|
typedef struct {
|
|
char* resname;
|
|
char* hostname;
|
|
} xce_reskey_t;
|
|
|
|
typedef struct {
|
|
int type;
|
|
union {
|
|
uint64_t jobid;
|
|
char* wname;
|
|
xce_reskey_t* reskey;
|
|
} condition;
|
|
} xce_match_t;
|
|
|
|
typedef struct {
|
|
int table;
|
|
int nrows;
|
|
void* rows;
|
|
} dataset_t;
|
|
|
|
typedef struct {
|
|
int role;
|
|
int data_which;
|
|
union {
|
|
hostattrs_t* attrs;
|
|
} data;
|
|
} xce_joindata_t;
|
|
|
|
typedef struct {
|
|
char* clustername;
|
|
int n_confvars;
|
|
xce_config_t** confvars;
|
|
int n_resources;
|
|
xce_resource_t** resources;
|
|
int n_queues;
|
|
xce_queue_t** queues;
|
|
int n_ugroups;
|
|
xce_group_config_t** ugroups;
|
|
int n_hgroups;
|
|
xce_group_config_t** hgroups;
|
|
int nresvals;
|
|
xce_name_val_t** resvals;
|
|
int nlimits;
|
|
xce_limit_config_t** limits;
|
|
int nhproviders;
|
|
scaler_config_t** hproviders;
|
|
} xce_conf4sched_t;
|
|
|
|
typedef struct {
|
|
char* clustername;
|
|
int n_confvars;
|
|
xce_config_t** confvars;
|
|
/* worker-rejoin-resync task 6.1 (Req 3.7): mirrors clustername above --
|
|
* pulled directly from c->myconf->cluster_config->worker_resync_window_ms
|
|
* (worker.c's worker_push_config()), not part of the generic confvars
|
|
* key/value list. */
|
|
int worker_resync_window_ms;
|
|
} xce_conf4worker_t;
|
|
|
|
typedef struct {
|
|
int data_which;
|
|
union {
|
|
xce_conf4sched_t* conf4sched;
|
|
xce_conf4worker_t* conf4worker;
|
|
cluster_config_t* clusterconfig;
|
|
} data;
|
|
} xce_configdata_t;
|
|
|
|
typedef struct {
|
|
int n_total;
|
|
int pos;
|
|
int count;
|
|
xce_job_t** jobs;
|
|
} xce_jobs_data_t;
|
|
|
|
typedef struct {
|
|
int n_total;
|
|
int pos;
|
|
int count;
|
|
xce_worker_t** workers;
|
|
} xce_workers_data_t;
|
|
|
|
typedef struct {
|
|
int n_total;
|
|
int pos;
|
|
int count;
|
|
xce_resource_t** resources;
|
|
} xce_resource_data_t;
|
|
|
|
typedef struct {
|
|
int n_total;
|
|
int pos;
|
|
int count;
|
|
xce_resval_t** vals;
|
|
} xce_resval_data_t;
|
|
|
|
typedef struct {
|
|
int count;
|
|
xce_resval_t** vals;
|
|
} xce_resval_list_t;
|
|
|
|
typedef struct {
|
|
uint64_t jobid;
|
|
int npids;
|
|
int* pids;
|
|
int npgids;
|
|
int* pgids;
|
|
int count;
|
|
xce_rusage_t** usages;
|
|
} xce_job_rusages_t;
|
|
|
|
typedef struct {
|
|
int table;
|
|
union {
|
|
xce_jobs_data_t* jobs;
|
|
xce_workers_data_t* workers;
|
|
xce_resource_data_t* resources;
|
|
xce_resval_data_t* resvals;
|
|
xce_job_submit_t* job_array; /* job-array-single-wal-record 1.2: compact
|
|
* (base jobid, array size, one spec) push
|
|
* descriptor -- same JobSubmit shape as the
|
|
* client submit request and WAL payload. */
|
|
} data;
|
|
} xce_data_t;
|
|
|
|
typedef struct {
|
|
int table;
|
|
xce_match_t* condition;
|
|
} xce_get_t;
|
|
|
|
typedef struct {
|
|
int event;
|
|
union {
|
|
job_pidinfo_t* pidinfo;
|
|
job_failinfo_t* failinfo;
|
|
job_failinfo_t* dispatchfail;
|
|
xce_jobfinish_t* finishinfo;
|
|
uint64_t sched_start_job;
|
|
/* bug-1737: carries exit_code alongside jobid now -- see
|
|
* controller.capnp's schedExitJob field comment. */
|
|
xce_jobfinish_t* sched_exit_job;
|
|
uint64_t sched_fail_job;
|
|
xce_job_t* sched_update_job;
|
|
xce_resval_list_t* sched_update_resvals;
|
|
uint64_t stopped_job;
|
|
uint64_t resumed_job;
|
|
xce_ckptinfo_t* ckptinfo;
|
|
xce_queue_t* sched_update_queue;
|
|
xce_task_pidinfo_t* taskinfo;
|
|
xce_task_failinfo_t* taskfailinfo;
|
|
xce_task_finish_t* taskfinish;
|
|
uint64_t pdone_job;
|
|
xce_job_rusages_t* sched_job_rusages;
|
|
xce_job_t* update_job;
|
|
} data;
|
|
} xce_notify_data_t;
|
|
|
|
typedef struct {
|
|
int table;
|
|
union {
|
|
xce_resval_list_t* resvals;
|
|
xce_job_rusages_t* jobrusages;
|
|
} data;
|
|
} xce_update_data_t;
|
|
|
|
typedef struct {
|
|
uint64_t term;
|
|
char *candidate_id;
|
|
uint64_t config_tag;
|
|
uint64_t last_log_lsn;
|
|
/* Raft (lastLogTerm, lastLogIndex) freshness tuple (ha-term-safety task 1.2):
|
|
* feeds the library logOk gate, distinct from config_tag/last_log_lsn which
|
|
* drive the application freshness gate. last_log_index == candidate log_len. */
|
|
uint64_t last_log_term;
|
|
uint64_t last_log_index;
|
|
} request_vote_req_t;
|
|
|
|
typedef struct {
|
|
uint64_t term;
|
|
int vote_granted;
|
|
uint64_t peer_config_tag;
|
|
uint64_t peer_last_log_lsn;
|
|
} request_vote_reply_t;
|
|
|
|
typedef struct {
|
|
char *name;
|
|
} probe_config_tag_req_t;
|
|
|
|
typedef struct {
|
|
uint64_t config_tag;
|
|
} probe_config_tag_reply_t;
|
|
|
|
/* Heartbeat reply state report — carries standby's applied_lsn and config_tag,
|
|
* plus its raft term so the primary can discover a higher term it lost track
|
|
* of (bug-1421 term-discovery: an AE reject response never reaches the raft
|
|
* library, so a heartbeat-reply term stamp is the fallback path). */
|
|
typedef struct {
|
|
uint64_t applied_lsn;
|
|
uint64_t config_tag;
|
|
uint64_t term;
|
|
} state_report_t;
|
|
|
|
/* WAL apply ACK — sent by standby to primary after applying a WAL catch-up
|
|
* entry (Task 3.1, ha-wal-replication). Must be declared before xce_reply_t
|
|
* so the walackreplymsg union arm compiles correctly. */
|
|
typedef struct {
|
|
uint64_t term;
|
|
uint64_t lsn;
|
|
int success;
|
|
} wal_ack_reply_t;
|
|
|
|
/* Scheduler admin opcode request/reply (scheduler-core task 7.4, Req 5 AC 10).
|
|
* OPCode_schedAdmin: start/stop/query a scheduler-side `.replay` recording
|
|
* session. No field-level $C.mapname on either capnp struct (per the exact
|
|
* verbatim schema spec), so -- same convention as xce_ctrlstatus_t below --
|
|
* the generated accessors and this mapped struct's field names match the
|
|
* capnp schema's camelCase names exactly (no snake_case conversion). action
|
|
* mirrors the existing HostRole/JoinData.role convention: an enum-typed
|
|
* capnp field maps to a plain `int` here, not a separate named C enum type
|
|
* (the generated `enum SchedAdminAction` in controller.capnp.h already
|
|
* supplies the SchedAdminAction_recordStart/recordStop/recordStatus
|
|
* constants for callers that want them). */
|
|
typedef struct {
|
|
int action;
|
|
char *recordFile;
|
|
} sched_admin_req_t;
|
|
|
|
typedef struct {
|
|
int ok;
|
|
char *error;
|
|
int recording;
|
|
char *file;
|
|
uint64_t eventCount;
|
|
uint64_t startedAt;
|
|
} sched_admin_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;
|
|
uint8_t status; /* @3: 0=unknown 1=ok 2=catching-up 3=dead (Req 13.1) */
|
|
} xce_ctrlpeer_t;
|
|
|
|
typedef struct {
|
|
char* clusterName;
|
|
char* primaryName;
|
|
char* responderName;
|
|
uint8_t responderRole;
|
|
uint64_t term;
|
|
uint64_t configVersion;
|
|
char* configChecksum;
|
|
uint64_t committedConfigVersion;
|
|
uint64_t committedLsn;
|
|
uint64_t appliedLsn;
|
|
unsigned fenced;
|
|
struct {
|
|
int count;
|
|
xce_ctrlpeer_t** list;
|
|
} peers;
|
|
uint32_t peerCount;
|
|
uint64_t responder_time_ms; /* @13: responder wall-clock at sample time (epoch ms) */
|
|
uint64_t elected_at_ms; /* @14: current primary's election time (epoch ms),
|
|
* learned off-primary via heartbeat; 0 = unknown */
|
|
} xce_ctrlstatus_t;
|
|
|
|
/* ── Worker rejoin/resync job-state reconciliation (worker-rejoin-resync
|
|
* task 1.1, Req 2.1/2.2/3.5/3.7/5.3/8.1-8.3) ────────────────────────────
|
|
* Frame-only wire types for the controller<->worker job-state
|
|
* reconciliation exchange (OPCode_jobStateQuery/OPCode_jobStateReport) --
|
|
* same "no live C struct" convention as capnp_host_admin_state_t/
|
|
* replay_cluster_t above (see controller.capnp's comment at `struct
|
|
* JobStateEntry`). An enum-typed capnp field (status) maps to a plain
|
|
* `int` here, not a separate named C enum type -- mirrors the existing
|
|
* HostRole/JoinData.role and SchedAdminAction/sched_admin_req_t.action
|
|
* convention (the generated `enum JobStateStatus` in controller.capnp.h
|
|
* already supplies the JobStateStatus_running/finishedOk/finishedFail/
|
|
* unrecognized constants for callers that want them). This is schema-only:
|
|
* the C-side send/receive/encode/decode logic is later tasks (1.4 wire
|
|
* codec helpers; 6.1/6.2 controller wiring; 4.x worker wiring). */
|
|
typedef struct {
|
|
uint64_t jobid;
|
|
int status; /* JobStateStatus_running/finishedOk/finishedFail/unrecognized */
|
|
int32_t exit_code;
|
|
uint64_t finish_time_ms;
|
|
int queried;
|
|
} xce_job_state_entry_t;
|
|
|
|
typedef struct {
|
|
int n_total;
|
|
int pos;
|
|
int count;
|
|
uint64_t* jobids;
|
|
} xce_job_state_query_t;
|
|
|
|
typedef struct {
|
|
int n_total;
|
|
int pos;
|
|
int count;
|
|
xce_job_state_entry_t** entries;
|
|
} xce_job_state_report_t;
|
|
|
|
typedef struct {
|
|
uint64_t seq;
|
|
uint32_t opcode;
|
|
int32_t status;
|
|
int data_which;
|
|
union {
|
|
char* debugmsg;
|
|
uint64_t jobid;
|
|
xce_data_t* getdata;
|
|
xce_clusterconf_t* clusterconf;
|
|
repl_config_reply_t* replconfigreplymsg;
|
|
repl_config_push_reply_t* replconfigpushreplymsg;
|
|
upgrade_config_reply_t* upgradeconfigreplymsg;
|
|
ctrl_hello_reply_t* ctrlhelloreplymsg;
|
|
request_vote_reply_t* requestvotereplymsg;
|
|
state_report_t* state_report;
|
|
wal_ack_reply_t* walackreplymsg;
|
|
xce_ctrlstatus_t* ctrlstatus;
|
|
xce_fetchfile_reply_t* fetchfilereply;
|
|
sched_admin_reply_t* schedadminreplymsg;
|
|
probe_config_tag_reply_t* probeconfigtagreplymsg;
|
|
} data;
|
|
/* Req 7: standby redirect fields — populated when status == XCE_ERR_STANDBY */
|
|
char *redirect_host;
|
|
uint16_t redirect_port;
|
|
} xce_reply_t;
|
|
|
|
typedef struct {
|
|
uint32_t opcode;
|
|
uint64_t seq;
|
|
xce_peerinfo_t* from;
|
|
int body_which;
|
|
union {
|
|
xce_reply_t* replymsg;
|
|
char* hellomsg;
|
|
xce_joindata_t* joinmsg;
|
|
xce_configdata_t* configpushmsg;
|
|
xce_data_t* pushmsg;
|
|
xce_get_t* getmsg;
|
|
dataset_t* datamsg;
|
|
xce_job_dispatch_t* dispatchmsg;
|
|
xce_job_exec_t* jobstartmsg;
|
|
xce_notify_data_t* notifymsg;
|
|
xce_job_submit_t* jobsubmsg;
|
|
xce_update_data_t* updatemsg;
|
|
xce_jobctrl_t* jobctrlmsg;
|
|
xce_queuectrl_t* queuectrlmsg;
|
|
xce_taskrun_t* runtaskmsg;
|
|
xce_preason_set_t* preasonmsg;
|
|
xce_clear_reason_t* clrpreasonmsg;
|
|
xce_hostctrl_t* hostctrlmsg;
|
|
repl_config_req_t* replconfigrequestmsg;
|
|
repl_config_push_req_t* replconfigpushrequestmsg;
|
|
upgrade_config_req_t* upgradeconfigrequestmsg;
|
|
heartbeat_msg_t* heartbeatmsg;
|
|
ctrl_hello_req_t* ctrlhellomsg;
|
|
request_vote_req_t* requestvotemsg;
|
|
wal_catchup_entry_t* walcatchupentrymsg;
|
|
snapshot_transfer_t* snapshottransfermsg;
|
|
xce_fetchfile_t* fetchfilemsg;
|
|
sched_admin_req_t* schedadminmsg;
|
|
probe_config_tag_req_t* probeconfigtagrequestmsg;
|
|
xce_job_state_query_t* jobstatequerymsg;
|
|
xce_job_state_report_t* jobstatereportmsg;
|
|
} body;
|
|
} xce_request_t;
|
|
|
|
/* ── HA election and replication Cap'n Proto mapped types ────────────────
|
|
* C-side counterparts for the HA protocol capnp structs added in the
|
|
* ha-config-bootstrap spec (controller.capnp structs with $C.mapname).
|
|
* Must be declared here so controller.capnp.h can reference them.
|
|
* request_vote_req_t and request_vote_reply_t are defined above
|
|
* xce_reply_t so they can be used in the xce_request_t body union. */
|
|
|
|
typedef struct {
|
|
uint64_t term;
|
|
uint64_t lsn;
|
|
} wal_ack_req_t;
|
|
|
|
typedef struct {
|
|
uint64_t term;
|
|
uint64_t acked_tag;
|
|
int success;
|
|
} upgrade_config_ack_t;
|
|
|
|
struct snapshot_transfer_s {
|
|
uint64_t snapshot_lsn; /* checkpoint LSN this snapshot corresponds to */
|
|
xce_data_t *payload; /* legacy decoded field — broken for raw bytes
|
|
* (decode_Data_ptr misinterprets List(UInt8));
|
|
* kept NULL on send, do not use */
|
|
uint32_t chunk_index; /* 0-based chunk number */
|
|
uint64_t chunk_offset; /* byte offset in the full snapshot */
|
|
uint64_t total_size; /* total snapshot size in bytes */
|
|
int is_last; /* true (non-zero) for the final chunk */
|
|
/* Raw chunk bytes — same pattern as repl_config_push_req_t.
|
|
* Send side: filled by the sender, encoded by xce_snapshot_chunk_req_raw.
|
|
* Receive side: extracted by xce_decode() before capn_free(); malloc'd,
|
|
* owned by the struct, freed by xce_free_request(). */
|
|
uint8_t *raw_payload;
|
|
size_t raw_payload_len;
|
|
/* raft-ha-core-safety task 3.2 (Req 3.1/5.1): the leader's term for the
|
|
* checkpoint LSN above. 0 = unset (legacy sender) -> receiver treats as
|
|
* WAL_LEGACY_TERM, same convention as persist's existing legacy-0 term
|
|
* mapping (see entry_term_locked in persist.c). */
|
|
uint64_t checkpoint_term;
|
|
};
|
|
|
|
struct wal_catchup_entry {
|
|
uint64_t lsn;
|
|
uint8_t entry_type;
|
|
/* bug-1505 fix: List(UInt8) now, not Data (see controller.capnp's
|
|
* WalCatchupEntry.payload comment for the full root cause) — a real
|
|
* uint8_t* + wire_payload_len pair via the generic codec, same shape
|
|
* bytes_read/content already establish elsewhere. Still legacy/unused
|
|
* by production (wal_entry_to_wire() in request.c deliberately never
|
|
* sets it — see that comment); this genuinely round-trips now instead
|
|
* of being a documented-broken placeholder, but nothing reads it. */
|
|
uint8_t *payload;
|
|
int n_entries; /* batch: number of sub-entries */
|
|
struct wal_catchup_entry **entries; /* batch: array of sub-entry pointers */
|
|
/* Raw WAL entry payload bytes — same pattern as repl_config_push_req_t.
|
|
* Send side: filled from the WAL segment file, encoded per sub-entry by
|
|
* xce_wal_batch_req_raw. Receive side: extracted by xce_decode() before
|
|
* capn_free(); malloc'd, owned, freed by xce_free_request(). */
|
|
uint8_t *raw_payload;
|
|
size_t raw_payload_len;
|
|
/* Raft AppendEntries term fields (ha-term-safety task 1.1, Req 1.3/1.4).
|
|
* term == this entry's term stamp; prev_log_index == lsn-1; prev_log_term ==
|
|
* term of the entry at prev_log_index (0 if absent). Consumed by the log
|
|
* callbacks (tasks 2.x) for the AppendEntries logOk / truncation decision. */
|
|
uint64_t term;
|
|
uint64_t prev_log_index;
|
|
uint64_t prev_log_term;
|
|
/* ha-term-safety task 3.2 (Req 2.5/4.1): the leader's commit index carried on
|
|
* the AppendEntries so the standby intake passes it into
|
|
* raft_on_append_entries_request. Wire field leaderCommit @7. */
|
|
uint64_t leader_commit;
|
|
/* bug-1067: the streaming leader's CURRENT term — the Raft AppendEntries
|
|
* HEADER term, DISTINCT from `term` above (the entry's STORED term). Equal for
|
|
* a live append; for a HISTORICAL catch-up entry the stored term is lower than
|
|
* the leader's current term. The standby intake uses this as the AE header
|
|
* term (UpdateTerm/stale-check) so cross-term catch-up is not stale-rejected;
|
|
* 0 = unset (legacy sender) → fall back to the stored term. Wire field
|
|
* leaderCurrentTerm @8. */
|
|
uint64_t leader_current_term;
|
|
/* bug-847: this sub-entry's payload length, carried as a plain scalar (wire
|
|
* field wirePayloadLen @9) so it decodes via the generic generated decode
|
|
* exactly like lsn/entry_type/term — never subject to the per-element Data
|
|
* cross-segment loss. Set by the encoder for every xce_wal_batch_encode_raw
|
|
* send; used on decode to slice this entry's bytes out of the TOP-LEVEL
|
|
* container's batch_payloads blob (below). */
|
|
uint32_t wire_payload_len;
|
|
/* bug-1505 fix: List(UInt8) now, not Data (controller.capnp's
|
|
* WalCatchupEntry.batchPayloads comment has the full root cause —
|
|
* same class as `payload` above and SnapshotData.internalState,
|
|
* bug-1504). Generic decoded field for wire field batchPayloads @10;
|
|
* genuinely round-trips now via the generic codec, but still unused by
|
|
* production — the real bytes are hand-extracted by xce_decode() into
|
|
* raw_batch_payloads/raw_batch_payloads_len below (a separate,
|
|
* already-correct code path this fix does not touch), then sliced per
|
|
* sub-entry using each entry's wire_payload_len into that entry's OWN
|
|
* raw_payload/raw_payload_len (so every consumer keeps reading
|
|
* raw_payload exactly as before). Only meaningful on the TOP-LEVEL
|
|
* container (never on a nested sub-entry). batch_payloads_wire_len is
|
|
* its own maplistcount companion (wire field batchPayloadsLen @11) —
|
|
* distinct from raw_batch_payloads_len below, which belongs to the
|
|
* separate hand-rolled extraction path. */
|
|
uint8_t *batch_payloads;
|
|
uint32_t batch_payloads_wire_len;
|
|
uint8_t *raw_batch_payloads;
|
|
size_t raw_batch_payloads_len;
|
|
};
|
|
|
|
/* ── Persistence Cap'n Proto mapped types (P0-9) ─────────────────────
|
|
* These are the C-side counterparts for the capnp encode/decode shims.
|
|
* Named with capnp_ prefix to avoid collision with the plain-C structs
|
|
* in persist.h (persist_config_t) and the on-disk binary formats. */
|
|
typedef struct {
|
|
uint32_t walSyncMs;
|
|
uint32_t snapIntervalS;
|
|
uint32_t snapWalSizeMb;
|
|
uint32_t finishedRetainS;
|
|
uint32_t workerRetainS;
|
|
uint32_t syncTimeoutMs;
|
|
uint32_t snapKeepN;
|
|
uint32_t transientJobTimeoutS;
|
|
} capnp_persist_cfg_t;
|
|
|
|
typedef struct {
|
|
uint64_t lsn;
|
|
uint64_t term;
|
|
uint64_t timestamp;
|
|
uint32_t version;
|
|
uint32_t nJobs;
|
|
uint32_t nQueues;
|
|
uint32_t nWorkers;
|
|
uint32_t checksum;
|
|
uint32_t headerChecksum;
|
|
} capnp_snap_hdr_t;
|
|
|
|
typedef struct {
|
|
uint64_t term;
|
|
uint64_t lsn;
|
|
uint32_t entryType;
|
|
xce_data_t* payload; /* owned; decoded via decode_Data_ptr */
|
|
uint32_t crc;
|
|
uint32_t prevCrc;
|
|
} capnp_repl_envelope_t;
|
|
|
|
/* ── Upgrade snapshot admin-state frames (config-upgrade-snapshot task 1,
|
|
* Req 1.9/5.7) ───────────────────────────────────────────────────────────
|
|
* Frame-only wire types for the checkpoint's host/queue admin-state
|
|
* sections -- carry only the administratively-set (name, status) pair,
|
|
* never the full xce_worker_t/xce_queue_t (design.md Design Invariant 5: a
|
|
* snapshot frame must never inherit a live struct's dynamic fields just
|
|
* because reusing it is convenient). Neither has a corresponding "live" C
|
|
* struct, so each gets its own frame-only counterpart here, same as
|
|
* capnp_snap_hdr_t/capnp_persist_cfg_t/capnp_repl_envelope_t above. */
|
|
typedef struct {
|
|
char* name;
|
|
uint32_t status;
|
|
} capnp_host_admin_state_t;
|
|
|
|
typedef struct {
|
|
char* name;
|
|
uint32_t status;
|
|
} capnp_queue_admin_state_t;
|
|
|
|
/* ── Config replication Cap'n Proto mapped types (Phase 1) ──────────────
|
|
* C-side counterparts for the capnp encode/decode shims for config sync.
|
|
* Struct tags match the forward declarations near the top of this file. */
|
|
|
|
struct repl_config_req_s {
|
|
uint64_t version_tag; /* local config version tag */
|
|
};
|
|
|
|
struct repl_config_reply_s {
|
|
int up_to_date; /* 1 if standby is current */
|
|
uint64_t version_tag; /* primary's current version tag */
|
|
xce_data_t *sha256; /* 32-byte SHA-256 of config blob (may be NULL) */
|
|
xce_data_t *bytes; /* serialised ClusterConfig blob (may be NULL) */
|
|
};
|
|
|
|
struct repl_config_push_req_s {
|
|
uint64_t version_tag; /* version tag of the pushed config */
|
|
xce_data_t *sha256; /* 32-byte SHA-256 (decoded, not usable as raw bytes) */
|
|
xce_data_t *bytes; /* serialised ClusterConfig blob (decoded, not usable) */
|
|
/* Pre-extracted raw fields populated by xce_decode() before capn_free(),
|
|
* mirroring the same pattern as upgrade_config_req_t. Owned by the struct. */
|
|
uint8_t *raw_sha256; /* 32-byte SHA-256, malloc'd, or NULL */
|
|
uint8_t *raw_bytes; /* serialised ClusterConfig blob, malloc'd, or NULL */
|
|
size_t raw_bytes_len; /* length of raw_bytes */
|
|
};
|
|
|
|
struct repl_config_push_reply_s {
|
|
int accepted; /* 1 if standby accepted the push */
|
|
uint64_t version_tag; /* standby's current version tag after decision */
|
|
uint64_t term; /* replying member's current term (raft-adapter-
|
|
* conformance Req 7.4 term-discovery channel) */
|
|
};
|
|
|
|
struct upgrade_config_req_s {
|
|
/* Legacy decoded fields — populated by generated decode_UpgradeConfigRequest()
|
|
* via decode_Data_ptr. These are structurally broken for List(UInt8) wire
|
|
* fields (decode_Data_ptr reads a discriminant and misinterprets the bytes),
|
|
* but must remain for the generated code to compile. Do NOT use these for
|
|
* actual byte extraction. */
|
|
xce_data_t *sha256; /* broken: always a bogus xce_jobs_data_t */
|
|
xce_data_t *bytes; /* broken: always a bogus xce_jobs_data_t */
|
|
|
|
/* Correct raw bytes extracted by xce_decode() before capn_free() runs,
|
|
* using UpgradeConfigRequest_get_sha256/bytes + capn_getv8.
|
|
* Both are malloc'd and owned here; freed by free_upgrade_config_req_raw()
|
|
* called from free_Request() after free_UpgradeConfigRequest().
|
|
* raw_sha256 is exactly 32 bytes when non-NULL. */
|
|
uint8_t *raw_sha256; /* 32-byte SHA-256, malloc'd, or NULL */
|
|
uint8_t *raw_bytes; /* serialised ClusterConfig blob, malloc'd, or NULL */
|
|
size_t raw_bytes_len; /* length of raw_bytes */
|
|
};
|
|
|
|
struct upgrade_config_reply_s {
|
|
uint64_t version_tag; /* new version tag after upgrade */
|
|
char *error; /* NULL on success; error message on failure */
|
|
uint32_t acked_count; /* number of standbys that acknowledged */
|
|
};
|
|
|
|
struct heartbeat_msg_s {
|
|
uint32_t sender_priority; /* sender's HA priority */
|
|
uint64_t term; /* sender's current term */
|
|
uint64_t committed_lsn; /* Req 5: primary's committed WAL LSN */
|
|
uint64_t committed_config_tag; /* Req 8: primary's committed config tag */
|
|
uint64_t elected_at_ms; /* Req 12.2: primary's own last_known_elected_at_ms,
|
|
* echoed on every heartbeat */
|
|
};
|
|
|
|
/* CtrlHello state sync (Req 6.5, 6.6, 6.7) */
|
|
struct ctrl_hello_req_s {
|
|
char *name; /* standby's logical name */
|
|
uint8_t *key; /* SHA-256(cluster_name + shared_secret) auth key,
|
|
* malloc'd, or NULL; wire type is List(UInt8),
|
|
* not Data (see controller.capnp's own comment) */
|
|
uint64_t applied_lsn; /* standby's last applied WAL LSN */
|
|
uint64_t config_tag; /* standby's current config tag */
|
|
uint32_t key_len; /* length of key */
|
|
};
|
|
|
|
struct ctrl_hello_reply_s {
|
|
uint64_t committed_lsn; /* primary's committed WAL LSN */
|
|
int needs_snapshot; /* 1 if standby LSN < oldest WAL segment */
|
|
uint64_t config_tag; /* primary's current config tag */
|
|
};
|
|
|
|
/* ── Replay stream envelope frames (scheduler-core Group 7 task 7.1,
|
|
* Req 5 AC 20) ──────────────────────────────────────────────────────────
|
|
* Frame-only wire types for the `.replay` stream envelope -- same "no live
|
|
* C struct" convention as capnp_snap_hdr_t/capnp_persist_cfg_t/
|
|
* capnp_host_admin_state_t above (see controller.capnp's comment at
|
|
* `struct ReplayCluster` for the full Group 7 task split). The C-side
|
|
* reader/writer that actually populates/consumes these (recording,
|
|
* `.replay` file I/O) is task 9+, out of this schema-only task's scope. */
|
|
typedef struct {
|
|
char *name;
|
|
char **controllers;
|
|
int n_controllers;
|
|
char *recording_host;
|
|
} replay_cluster_t;
|
|
|
|
typedef struct {
|
|
char *recorder_version;
|
|
/* configHash is a Data field -- the generated encode_/decode_ codec is
|
|
* a documented no-op for Data fields in this codebase's capnpc-c setup
|
|
* (same limitation as e.g. upgrade_config_req_s.sha256/bytes above);
|
|
* real byte extraction is task 9+ hand-written code, same raw_sha256-
|
|
* style pattern used throughout this file. This pointer/len pair exists
|
|
* only so the mapname target type is well-formed; nothing here reads it
|
|
* yet. */
|
|
uint8_t *config_hash;
|
|
size_t config_hash_len;
|
|
replay_cluster_t *cluster;
|
|
uint64_t recording_start_ts;
|
|
char *recording_start_wall;
|
|
char *sched_policy;
|
|
} replay_header_t;
|
|
|
|
/* task 7.3 (Req 5 AC 21): SchedUser/SchedProject mirror controller.capnp's
|
|
* SchedUser/SchedProject structs. Field lists match xce_sched_user_t/
|
|
* xce_sched_project_t (src/scheduler/include/sched/types.h) exactly -- name
|
|
* only, name+priority respectively -- but these are FRESH, wire-only C
|
|
* types under different names, NOT aliases of the scheduler-internal ones.
|
|
* See controller.capnp's comment at `struct SchedUser` for the full
|
|
* rationale: sched/types.h is scheduler-private and already `#include`s
|
|
* this file, so a duplicate `xce_sched_user_t`/`xce_sched_project_t`
|
|
* typedef here would break every scheduler .c file that includes both
|
|
* headers (confirmed: core_events.c, schedobj.c already do). */
|
|
typedef struct {
|
|
char *name;
|
|
} sched_snapshot_user_t;
|
|
|
|
typedef struct {
|
|
char *name;
|
|
int32_t priority;
|
|
} sched_snapshot_project_t;
|
|
|
|
/* task 7.3 (Req 5 AC 21): the full snapshot struct, replacing the task 7.1
|
|
* placeholder wholesale. Every domain-object pointer reuses a pre-existing
|
|
* native type (already used natively throughout the scheduler/controller)
|
|
* except users/projects (sched_snapshot_user_t/sched_snapshot_project_t
|
|
* above) and the two scheduler-internal-only opaque fields. internal_version
|
|
* is a format tag for internal_state so an incompatible future internal
|
|
* layout is rejected rather than misinterpreted (this task's Observable).
|
|
*
|
|
* internal_state/internal_state_len map controller.capnp's internalState,
|
|
* a List(UInt8) (task 12.1 review finding, fixed -- was Data, whose
|
|
* generated codec is a silent no-op in this codebase's capnpc-c setup and
|
|
* left a genuinely uninitialized capn_ptr on the wire, a real reproducible
|
|
* crash; see controller.capnp's SnapshotData comment for the full story).
|
|
* List(UInt8)'s generated encode_/decode_ actually runs (proven precedent:
|
|
* xce_fetchfile_reply_t.content/bytes_read below), so internal_state_len
|
|
* must be uint32_t to match internalStateLen's UInt32 wire type -- same
|
|
* maplistcount convention as content/bytes_read. */
|
|
typedef struct {
|
|
xce_conf4sched_t *config;
|
|
xce_jobs_data_t *jobs;
|
|
xce_queue_t **queues;
|
|
int n_queues;
|
|
xce_workers_data_t *workers;
|
|
xce_resource_data_t *resources;
|
|
xce_resval_list_t *resvals;
|
|
sched_snapshot_user_t **users;
|
|
int n_users;
|
|
sched_snapshot_project_t **projects;
|
|
int n_projects;
|
|
uint64_t prng_seed;
|
|
uint8_t *internal_state;
|
|
uint32_t internal_state_len;
|
|
uint32_t internal_version;
|
|
} sched_snapshot_data_t;
|
|
|
|
typedef struct {
|
|
uint64_t ts;
|
|
sched_snapshot_data_t *data;
|
|
} replay_snapshot_entry_t;
|
|
|
|
/* task 7.2 (Req 5 AC 20): the two genuinely new event payload types -- every
|
|
* other replay_event_t.body variant above reuses a pre-existing wire type
|
|
* (see controller.capnp's comment at `struct JobStateChangeEvent`). */
|
|
typedef struct {
|
|
uint64_t jobid;
|
|
uint32_t old_state;
|
|
uint32_t new_state;
|
|
int32_t exit_code;
|
|
} job_state_change_event_t;
|
|
|
|
typedef struct {
|
|
uint64_t jobid;
|
|
/* changedFields is a UInt32 bitmask (JOB_F_PRIORITY, JOB_F_QUEUE,
|
|
* JOB_F_RESREQ per design.md) -- no JOB_F_* constants exist anywhere in
|
|
* the codebase yet (task 5.1's grep); concrete bit assignments are
|
|
* task 9+ event-translation/recording code's job, not this schema-only
|
|
* task's. */
|
|
uint32_t changed_fields;
|
|
xce_job_t *job; /* full updated record -- reuses xce_job_t (Job in
|
|
* controller.capnp) */
|
|
} job_update_event_t;
|
|
|
|
typedef struct {
|
|
uint64_t ts;
|
|
int body_which;
|
|
union {
|
|
xce_conf4sched_t *config_push;
|
|
xce_jobs_data_t *jobs_push;
|
|
xce_workers_data_t *workers_push;
|
|
xce_resource_data_t *resources_push;
|
|
xce_resval_list_t *resvals_full;
|
|
xce_resval_list_t *resvals_delta;
|
|
xce_queue_t *queue_update;
|
|
xce_job_rusages_t *job_rusage;
|
|
/* endSession: Void, no storage */
|
|
job_state_change_event_t *job_state_change;
|
|
job_update_event_t *job_update;
|
|
} body;
|
|
} replay_event_t;
|
|
|
|
typedef struct {
|
|
uint64_t duration_ns;
|
|
uint32_t n_decisions;
|
|
uint32_t n_dispatched;
|
|
uint32_t n_pended;
|
|
} session_summary_t;
|
|
|
|
/* task 7.3 (Req 5 AC 5, AC 20): the three decision payload types --
|
|
* dispatch/pend reuse existing native types (xce_job_alloc_t,
|
|
* xce_job_preason_t); preempt_victim_t/sched_preempt_decision_t are fresh,
|
|
* wire-only types, deliberately distinct from decision.h's core-internal
|
|
* xce_preempt_victim_t/xce_preempt_t (task 4.4's in-process live-decision
|
|
* representation) -- see controller.capnp's matching comment. */
|
|
typedef struct {
|
|
xce_job_alloc_t *alloc;
|
|
} sched_dispatch_decision_t;
|
|
|
|
typedef struct {
|
|
uint64_t jobid;
|
|
xce_job_preason_t *reason;
|
|
} sched_pend_decision_t;
|
|
|
|
typedef struct {
|
|
uint64_t jobid;
|
|
char *workerid;
|
|
} preempt_victim_t;
|
|
|
|
typedef struct {
|
|
uint64_t jobid;
|
|
preempt_victim_t **victims;
|
|
int n_victims;
|
|
xce_job_preason_t *reason;
|
|
} sched_preempt_decision_t;
|
|
|
|
typedef struct {
|
|
uint64_t ts;
|
|
int body_which;
|
|
union {
|
|
sched_dispatch_decision_t *dispatch;
|
|
sched_pend_decision_t *pend;
|
|
sched_preempt_decision_t *preempt;
|
|
} body;
|
|
} replay_decision_t;
|
|
|
|
typedef struct {
|
|
uint64_t begin_round;
|
|
replay_decision_t **decisions;
|
|
int n_decisions;
|
|
session_summary_t *summary;
|
|
} replay_session_t;
|
|
|
|
#endif
|