forked from lei.chen/myxce
Merge branch 'persistent_ha' into scheduler
This commit is contained in:
commit
a07a74a943
106 changed files with 18528 additions and 637 deletions
|
|
@ -71,4 +71,26 @@ typedef uint64_t jobid_t;
|
|||
#define REGEX_JOBARRAY_ID \
|
||||
"[ \t]*([0-9]+)-([0-9]+)-([0-9]+)-([0-9]+)\\[([0-9]+)\\][ \t]*"
|
||||
|
||||
/* ======================================================
|
||||
* Persistence & HA system constants (Part 2.6, Appendix A.10)
|
||||
* ====================================================== */
|
||||
#define MAX_STANDBYS 3
|
||||
#define MAX_HA_CANDIDATES 4
|
||||
#define WAL_FORMAT_VERSION 1
|
||||
#define WAL_ENTRY_TYPE_MAX 100 /* WAL_CHECKPOINT = 100, highest known type */
|
||||
#define MAX_WAL_PAYLOAD_BYTES (1 << 20) /* 1 MB per replication entry */
|
||||
#define MAX_SNAPSHOT_BYTES (256 * 1024 * 1024) /* 256 MB max snapshot size */
|
||||
#define WAL_HDR_CRC_SIZE 32 /* sizeof(wal_entry_hdr_t) - 2*sizeof(uint32_t) */
|
||||
#define WAL_CRC_NONE 0xFFFFFFFFu /* sentinel: no predecessor in chain */
|
||||
|
||||
#define DEFAULT_WAL_SYNC_MS 10
|
||||
#define DEFAULT_SNAP_INTERVAL_S 60
|
||||
#define DEFAULT_SNAP_WAL_SIZE_MB 64
|
||||
#define DEFAULT_SNAP_KEEP_N 2
|
||||
#define DEFAULT_TRANSIENT_JOB_TIMEOUT_S 300
|
||||
#define DEFAULT_FINISHED_RETAIN_S 3600
|
||||
#define DEFAULT_WORKER_RETAIN_S 86400
|
||||
#define DEFAULT_SYNC_TIMEOUT_MS 50
|
||||
#define DEFAULT_HB_INTERVAL_MS 3000
|
||||
|
||||
#endif
|
||||
|
|
|
|||
47
src/include/ctrl_metrics.h
Normal file
47
src/include/ctrl_metrics.h
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
#pragma once
|
||||
/*
|
||||
* ctrl_metrics.h — P3-7: Operational metrics for the XCE controller.
|
||||
*
|
||||
* All metrics use C11 _Atomic uint64_t for lock-free, thread-safe access.
|
||||
* Metric names use the ctrl_ prefix.
|
||||
*
|
||||
* Design doc: Part P3-7, review A8-007.
|
||||
*/
|
||||
#include <stdatomic.h>
|
||||
#include <stdint.h>
|
||||
|
||||
/* ── Counter/gauge globals ──────────────────────────────────────────── */
|
||||
extern _Atomic uint64_t ctrl_repl_degraded_count; /* counter */
|
||||
extern _Atomic uint64_t ctrl_split_brain_detected; /* counter */
|
||||
extern _Atomic uint64_t ctrl_async_events_lost; /* counter */
|
||||
extern _Atomic uint64_t ctrl_transient_jobs_stuck; /* counter */
|
||||
extern _Atomic uint64_t ctrl_wal_bytes; /* gauge */
|
||||
extern _Atomic uint64_t ctrl_checkpoint_duration_ms; /* gauge */
|
||||
extern _Atomic uint64_t ctrl_repl_lag_lsn; /* gauge */
|
||||
|
||||
/* ── Macros ─────────────────────────────────────────────────────────── */
|
||||
|
||||
/* Increment a monotonic counter by 1 (relaxed ordering — counters are
|
||||
* sampled, not used for synchronization). */
|
||||
#define CTRL_METRIC_INC(name) \
|
||||
atomic_fetch_add_explicit(&(name), 1, memory_order_relaxed)
|
||||
|
||||
/* Overwrite a gauge with a new absolute value. */
|
||||
#define CTRL_METRIC_SET(name, val) \
|
||||
atomic_store_explicit(&(name), (uint64_t)(val), memory_order_relaxed)
|
||||
|
||||
/* ── Descriptor table for metric discovery ──────────────────────────── */
|
||||
typedef struct {
|
||||
const char* name;
|
||||
_Atomic uint64_t* value;
|
||||
const char* description;
|
||||
int is_counter; /* 1 = counter (monotonic), 0 = gauge */
|
||||
} ctrl_metric_desc_t;
|
||||
|
||||
/* Terminated by a { NULL, NULL, NULL, 0 } sentinel entry. */
|
||||
extern const ctrl_metric_desc_t ctrl_metric_descriptors[];
|
||||
|
||||
/* ── Lifecycle ──────────────────────────────────────────────────────── */
|
||||
|
||||
/* Reset all metrics to zero. Call from persist_start() and ha_init(). */
|
||||
void ctrl_metrics_init(void);
|
||||
|
|
@ -23,7 +23,7 @@ typedef struct {
|
|||
typedef struct {
|
||||
pthread_t worker;
|
||||
int running;
|
||||
int precision;
|
||||
int precision_ms;
|
||||
task_run_fn_t runner_fn;
|
||||
timer_t timerid;
|
||||
ordq_t tasks;
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
#define __HOOK_H__ 1
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <stdatomic.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#include "xlist.h"
|
||||
|
|
@ -12,8 +13,8 @@
|
|||
|
||||
typedef int (*hook_fn_t)(int, int, int, void*, va_list);
|
||||
typedef struct {
|
||||
hook_fn_t fn;
|
||||
void* data;
|
||||
_Atomic hook_fn_t fn;
|
||||
void* data;
|
||||
} hook_item_t;
|
||||
|
||||
typedef struct {
|
||||
|
|
@ -23,6 +24,7 @@ typedef struct {
|
|||
|
||||
extern int xce_hook_init(hooksys_t*, int);
|
||||
extern int xce_add_hook(hooksys_t*, int, hook_fn_t, void*);
|
||||
extern int xce_disable_hook(hooksys_t*, int, hook_fn_t);
|
||||
extern int xce_run_hooks(hooksys_t*, int, int, int, ...);
|
||||
extern void xce_hook_fini(hooksys_t*);
|
||||
|
||||
|
|
|
|||
|
|
@ -30,4 +30,22 @@ extern kv_key_ops_t int_key_ops;
|
|||
extern kv_key_ops_t string_key_ops;
|
||||
|
||||
extern int key_cmp_default(void*, void*, size_t);
|
||||
|
||||
/*
|
||||
* kvtab_foreach — iterate every live entry in the kvtab (P0-14).
|
||||
*
|
||||
* For each entry, cb(key, index, arg) is called.
|
||||
* - key : the raw key pointer stored in the entry (char* for
|
||||
* string-keyed tables, (void*)(long)intval for int-keyed).
|
||||
* - index : the slot index stored as the entry value.
|
||||
* - arg : caller-supplied context.
|
||||
*
|
||||
* Iteration order is unspecified.
|
||||
* Returns the number of entries visited, or -EINVAL if kvtab is NULL.
|
||||
*/
|
||||
typedef void (*kvtab_foreach_fn_t)(void* key, int index, void* arg);
|
||||
extern int kvtab_foreach(var kvtab,
|
||||
kvtab_foreach_fn_t cb,
|
||||
void* arg);
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@
|
|||
#include "xce/types.h"
|
||||
#include "xlist.h"
|
||||
#include "zarray.h"
|
||||
#include "persist.h"
|
||||
|
||||
typedef enum {
|
||||
KVMGR_KEY_INT = 0,
|
||||
|
|
@ -22,4 +23,22 @@ typedef struct {
|
|||
|
||||
extern typeinfo_t xkvmgr_type;
|
||||
|
||||
/*
|
||||
* kvmgr_capture_keys — capture key→index mappings for checkpoint
|
||||
* serialization (Part 3.8).
|
||||
*
|
||||
* Caller MUST hold the kvmgr write lock for the full duration of
|
||||
* this call. The function allocates *keys_out and populates it with
|
||||
* *n_keys_out entries. Each entry's key[] field is an owned deep
|
||||
* copy that remains valid after the write lock is released.
|
||||
*
|
||||
* Caller must free(*keys_out) when done.
|
||||
*
|
||||
* Returns 0 on success.
|
||||
* Returns -ENOMEM if allocation fails.
|
||||
*/
|
||||
int kvmgr_capture_keys(var kvmgr,
|
||||
slot_key_t** keys_out,
|
||||
size_t* n_keys_out);
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@
|
|||
#include <tlog.h>
|
||||
|
||||
#include "clusterconfig.h"
|
||||
#include "xce/cluster.h"
|
||||
|
||||
struct logging {
|
||||
char* logdir;
|
||||
|
|
@ -14,10 +15,13 @@ struct logging {
|
|||
};
|
||||
|
||||
typedef struct {
|
||||
cluster_config_t* cluster_config;
|
||||
char* conffile;
|
||||
char* certfile;
|
||||
char* keyfile;
|
||||
cluster_config_t* cluster_config;
|
||||
char* conffile;
|
||||
char* certfile;
|
||||
char* keyfile;
|
||||
char* workdir; /* working directory for WAL/snapshot storage */
|
||||
char* name; /* controller name (hostname by default) */
|
||||
xce_cluster_config_t peers_cfg; /* HA peer list parsed from --peers; count==0 = single-node */
|
||||
} myconf_t;
|
||||
|
||||
extern cluster_config_t* get_default_conf(void*);
|
||||
|
|
|
|||
152
src/include/persist.h
Normal file
152
src/include/persist.h
Normal file
|
|
@ -0,0 +1,152 @@
|
|||
#pragma once
|
||||
#include <pthread.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdatomic.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "config.h"
|
||||
#include "wal_types.h" /* wal_entry_hdr_t, wal_entry_type — P2-4: shared with repl.h */
|
||||
#include "repl.h" /* replicator_t — P2-4: embedded in persist_t */
|
||||
|
||||
/* Forward declaration */
|
||||
typedef struct cluster_s cluster_t;
|
||||
|
||||
/* ============================================================
|
||||
* Persistence configuration (Part 2.6)
|
||||
* ============================================================ */
|
||||
typedef struct {
|
||||
uint32_t wal_sync_ms; /* fsync interval in ms (default 10) */
|
||||
uint32_t snap_interval_s; /* checkpoint interval in seconds (default 60) */
|
||||
uint32_t snap_wal_size_mb; /* checkpoint when WAL exceeds this MB (default 64) */
|
||||
uint32_t snap_keep_n; /* snapshots to retain (default 2) */
|
||||
uint32_t transient_job_timeout_s; /* seconds before TRANSIENT job auto-terminates (default 300) */
|
||||
uint32_t finished_retain_s; /* retain finished jobs N seconds (default 3600) */
|
||||
uint32_t worker_retain_s; /* retain unavailable workers N seconds (default 86400) */
|
||||
uint32_t sync_timeout_ms; /* sync replication ACK timeout ms (default 50) */
|
||||
} persist_config_t;
|
||||
|
||||
static inline persist_config_t persist_config_defaults(void)
|
||||
{
|
||||
persist_config_t c;
|
||||
c.wal_sync_ms = DEFAULT_WAL_SYNC_MS;
|
||||
c.snap_interval_s = DEFAULT_SNAP_INTERVAL_S;
|
||||
c.snap_wal_size_mb = DEFAULT_SNAP_WAL_SIZE_MB;
|
||||
c.snap_keep_n = DEFAULT_SNAP_KEEP_N;
|
||||
c.transient_job_timeout_s = DEFAULT_TRANSIENT_JOB_TIMEOUT_S;
|
||||
c.finished_retain_s = DEFAULT_FINISHED_RETAIN_S;
|
||||
c.worker_retain_s = DEFAULT_WORKER_RETAIN_S;
|
||||
c.sync_timeout_ms = DEFAULT_SYNC_TIMEOUT_MS;
|
||||
return c;
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
* Key→index mapping for checkpoint serialization (Part 3.8)
|
||||
* ============================================================ */
|
||||
typedef struct {
|
||||
int index; /* shadow slot index in the frozen zarray */
|
||||
char key[XCE_MAXNAME_LEN]; /* owned deep copy — not a borrowed kvtab pointer */
|
||||
size_t key_len; /* length including null terminator */
|
||||
} slot_key_t;
|
||||
|
||||
/* ============================================================
|
||||
* persist_t — concrete definition (exposed for stack/calloc allocation)
|
||||
* The opaque pattern can be restored in a later refactor once the
|
||||
* full API surface is stable.
|
||||
* ============================================================ */
|
||||
struct persist_s {
|
||||
/* Back-pointer to owning cluster (set by persist_init, used in persist_start) */
|
||||
cluster_t* cluster;
|
||||
|
||||
/* WAL state */
|
||||
int wal_fd;
|
||||
uint64_t current_lsn;
|
||||
uint32_t last_crc;
|
||||
uint32_t last_replicated_crc;
|
||||
uint64_t term;
|
||||
uint32_t seg_offset;
|
||||
uint64_t checkpoint_lsn;
|
||||
uint64_t total_wal_bytes;
|
||||
char seg_path[4096];
|
||||
|
||||
/* Paths (derived from workdir + name via persist_set_workdir) */
|
||||
char wal_path[4096];
|
||||
char snap_path[4096];
|
||||
|
||||
/* Config */
|
||||
persist_config_t config;
|
||||
|
||||
/* Concurrency */
|
||||
pthread_mutex_t lock;
|
||||
atomic_int fsync_stop;
|
||||
atomic_int fsync_thread_running;
|
||||
pthread_t fsync_thread;
|
||||
|
||||
/* Checkpoint (stubs for now — wired in Task 10) */
|
||||
atomic_int checkpoint_in_progress;
|
||||
atomic_int checkpoint_requested;
|
||||
pthread_cond_t checkpoint_done;
|
||||
pthread_mutex_t checkpoint_mutex;
|
||||
pthread_t snap_thread;
|
||||
|
||||
/* Fan-out replicator — P2-4 */
|
||||
replicator_t repl;
|
||||
};
|
||||
typedef struct persist_s persist_t;
|
||||
|
||||
/* Set workdir and controller name before persist_init().
|
||||
* Derives wal_path and snap_path as <workdir>/<name>/data/{wal,snap}. */
|
||||
void persist_set_workdir(persist_t* p, const char* workdir, const char* name);
|
||||
|
||||
/* Lifecycle */
|
||||
int persist_init(persist_t* p, persist_config_t* config, cluster_t* c);
|
||||
int persist_start(persist_t* p);
|
||||
void persist_stop(persist_t* p);
|
||||
void persist_fini(persist_t* p);
|
||||
int persist_recover(persist_t* p);
|
||||
|
||||
/* WAL write paths */
|
||||
int persist_wal_async(persist_t* p, uint32_t type,
|
||||
const void* payload, uint32_t len);
|
||||
int persist_wal_sync_append(persist_t* p, uint32_t type,
|
||||
const void* payload, uint32_t len,
|
||||
int* sync_rc_out);
|
||||
|
||||
/* Force WAL fdatasync (for tests and clean shutdown) */
|
||||
int persist_flush(persist_t* p);
|
||||
|
||||
/* Checkpoint */
|
||||
int persist_checkpoint(persist_t* p);
|
||||
|
||||
/* Logging helper — never logs payload content (security invariant) */
|
||||
void persist_log_entry_metadata(const wal_entry_hdr_t* hdr);
|
||||
|
||||
/* ============================================================
|
||||
* AFTER_EVENT hook functions — registered by persist_start()
|
||||
*
|
||||
* Signature matches hook_fn_t: int fn(int type, int situation,
|
||||
* int detail, void* data, va_list ap)
|
||||
* cls/data points to the persist_t instance.
|
||||
*
|
||||
* P0: writes a minimal 4-byte payload (detail value) to the WAL.
|
||||
* Full Cap'n Proto serialization is wired in Task 15.
|
||||
* ============================================================ */
|
||||
int persist_job_hook_fun(int type, int situation, int detail,
|
||||
void* cls, va_list ap);
|
||||
int persist_queue_hook_fun(int type, int situation, int detail,
|
||||
void* cls, va_list ap);
|
||||
int persist_worker_hook_fun(int type, int situation, int detail,
|
||||
void* cls, va_list ap);
|
||||
|
||||
/* Apply a single WAL entry to in-memory state (standby replay path).
|
||||
* No hooks fire — hook system is empty on standbys until promotion.
|
||||
* Returns 0 on success (including skip for checkpoint/unknown types),
|
||||
* negative errno on error (oversized payload, NULL arguments). */
|
||||
int apply_wal_entry(persist_t* p, const wal_entry_hdr_t* hdr, const void* payload);
|
||||
|
||||
/* GC retention predicates (P0-5a / P0-8 / P0-13) */
|
||||
#include <stdbool.h>
|
||||
#include "xce/types.h"
|
||||
bool job_is_terminal(const job_t* j);
|
||||
bool job_should_retain(const job_t* j, const persist_config_t* cfg);
|
||||
bool worker_should_retain(const xce_worker_t* w, const persist_config_t* cfg);
|
||||
72
src/include/repl.h
Normal file
72
src/include/repl.h
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
#pragma once
|
||||
/*
|
||||
* repl.h — P2-1: Replicator structures and public API.
|
||||
* Fan-out TCP replication via existing iomgr/SSL endpoint pipeline.
|
||||
* ACKs processed in io_worker read callback (NOT vtaskmgr) to prevent deadlock.
|
||||
*/
|
||||
#include <pthread.h>
|
||||
#include <stdatomic.h>
|
||||
#include <stdint.h>
|
||||
#include <time.h>
|
||||
#include "config.h"
|
||||
#include "mylock.h"
|
||||
#include "wal_types.h" /* wal_entry_hdr_t — P2-4: use wal_types.h to break circular include */
|
||||
|
||||
/* Endpoint is an opaque pointer — full type in netio.h (avoid heavy include) */
|
||||
typedef void* repl_ep_t;
|
||||
|
||||
/* Per-standby connection slot */
|
||||
typedef struct {
|
||||
repl_ep_t ep; /* NULL = slot empty */
|
||||
_Atomic uint64_t acked_lsn; /* Last LSN ACKed by this standby */
|
||||
_Atomic int in_catch_up; /* Non-zero while catching up */
|
||||
uint64_t join_applied_lsn; /* LSN standby reported on reconnect */
|
||||
char host[XCE_MAXNAME_LEN];
|
||||
uint32_t port;
|
||||
uint64_t last_disconnected_lsn;
|
||||
time_t last_disconnect_time;
|
||||
} standby_conn_t;
|
||||
|
||||
/* Replicator — embedded in persist_t or cluster_t (P2-4).
|
||||
* Named tag replicator_s matches the forward declaration in persist.h. */
|
||||
typedef struct replicator_s {
|
||||
standby_conn_t standbys[MAX_STANDBYS];
|
||||
int nstandbys; /* Active standby count */
|
||||
mylock_t lock; /* Protects standbys[], nstandbys */
|
||||
pthread_cond_t ack_cond; /* Signaled when any ACK arrives */
|
||||
pthread_mutex_t ack_lock; /* Guards ack_cond */
|
||||
} replicator_t;
|
||||
|
||||
/* Lifecycle */
|
||||
int repl_init(replicator_t *r);
|
||||
void repl_fini(replicator_t *r);
|
||||
|
||||
/* Standby management */
|
||||
int repl_add_standby(replicator_t *r, repl_ep_t ep, const char *host, uint32_t port);
|
||||
void repl_remove_standby(replicator_t *r, repl_ep_t ep);
|
||||
void repl_expire_disconnect_slots(replicator_t *r, time_t now_s, uint32_t snap_interval_s);
|
||||
|
||||
/* Replication send (stub in P2-1; wired in P2-4) */
|
||||
int repl_broadcast(replicator_t *r, const wal_entry_hdr_t *hdr,
|
||||
const void *payload, uint32_t payload_len);
|
||||
|
||||
/* ACK handling — call from io_worker read callback ONLY */
|
||||
void repl_on_ack(replicator_t *r, repl_ep_t ep, uint64_t acked_lsn);
|
||||
|
||||
/* Sync replication (stub in P2-1; wired in P2-3) */
|
||||
int repl_wait_ack(replicator_t *r, uint64_t target_lsn, uint32_t timeout_ms);
|
||||
|
||||
/* WAL trim guard */
|
||||
uint64_t repl_trim_lsn(replicator_t *r, uint64_t checkpoint_lsn, uint32_t snap_interval_s);
|
||||
|
||||
/* Catch-up state management */
|
||||
|
||||
/* Mark standby as entering catch-up mode; stores join_applied_lsn */
|
||||
void repl_start_catchup(replicator_t *r, repl_ep_t ep, uint64_t join_applied_lsn);
|
||||
|
||||
/* Mark standby catch-up complete (called on OP_replCatchUpDone receipt) */
|
||||
void repl_complete_catchup(replicator_t *r, repl_ep_t ep);
|
||||
|
||||
/* Returns 1 if standby needs a full snapshot transfer (its LSN predates oldest WAL).
|
||||
* oldest_wal_lsn == 0 means no WAL exists — no snapshot needed for LSN 0 standbys. */
|
||||
int repl_needs_snapshot(replicator_t *r, uint64_t standby_lsn, uint64_t oldest_wal_lsn);
|
||||
|
|
@ -200,4 +200,40 @@ typedef struct {
|
|||
} body;
|
||||
} xce_request_t;
|
||||
|
||||
/* ── 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;
|
||||
|
||||
#endif
|
||||
|
|
|
|||
58
src/include/wal_types.h
Normal file
58
src/include/wal_types.h
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
#pragma once
|
||||
/*
|
||||
* wal_types.h — WAL on-disk structures shared between persist.h and repl.h.
|
||||
* Extracted in P2-4 to break the persist.h ↔ repl.h circular include.
|
||||
*/
|
||||
#include <stdint.h>
|
||||
#include "config.h"
|
||||
|
||||
/* ── WAL payload helper structs ────────────────────────────────────────────
|
||||
* These are the binary formats embedded after wal_entry_hdr_t for each type.
|
||||
* All fields are native-endian (single-node WAL; replication uses capnp). */
|
||||
|
||||
/* WAL_JOB_START / FINISH / FAIL / STOP / RESUME / UPDATE / CKPT / PREASON / RUSAGE */
|
||||
typedef struct {
|
||||
uint64_t jobid;
|
||||
uint32_t state; /* xce_job state flags after this transition */
|
||||
uint32_t _pad;
|
||||
} wal_jobstate_t;
|
||||
|
||||
/* WAL_QUEUE_CTRL — queue name is a NUL-terminated string immediately after
|
||||
* this header. Total payload = sizeof(wal_queuehdr_t) + strlen(name)+1. */
|
||||
typedef struct {
|
||||
uint32_t status; /* xce_queue status flags after this transition */
|
||||
} wal_queuehdr_t;
|
||||
|
||||
/* ============================================================
|
||||
* WAL entry header — 40 bytes, 8-byte aligned (Part 2.2)
|
||||
* ============================================================ */
|
||||
typedef struct {
|
||||
uint64_t term; /* Election term (incremented on failover) */
|
||||
uint64_t lsn; /* Log sequence number within this term */
|
||||
uint64_t timestamp; /* Epoch microseconds */
|
||||
uint32_t type; /* wal_entry_type enum value */
|
||||
uint32_t payload_len; /* Bytes of payload following this header */
|
||||
uint32_t crc; /* CRC32(header[0..WAL_HDR_CRC_SIZE-1] + payload) */
|
||||
uint32_t prev_crc; /* CRC of previous record; WAL_CRC_NONE for first in segment */
|
||||
} wal_entry_hdr_t;
|
||||
_Static_assert(sizeof(wal_entry_hdr_t) == 40,
|
||||
"wal_entry_hdr_t must be 40 bytes for on-disk format stability");
|
||||
|
||||
/* ============================================================
|
||||
* WAL entry types (Part 2.3)
|
||||
* ============================================================ */
|
||||
typedef enum {
|
||||
WAL_JOB_SUBMIT = 1, /* SYNC replication */
|
||||
WAL_JOB_START = 2,
|
||||
WAL_JOB_FINISH = 3,
|
||||
WAL_JOB_FAIL = 4,
|
||||
WAL_JOB_STOP = 5,
|
||||
WAL_JOB_RESUME = 6,
|
||||
WAL_JOB_UPDATE = 7,
|
||||
WAL_JOB_RUSAGE = 8,
|
||||
WAL_JOB_CKPT = 9,
|
||||
WAL_JOB_PREASON = 10,
|
||||
WAL_QUEUE_CTRL = 20,
|
||||
WAL_WORKER_CTRL = 30,
|
||||
WAL_CHECKPOINT = 100, /* Local WAL marker only — never replicated */
|
||||
} wal_entry_type;
|
||||
128
src/include/xce/cluster.h
Normal file
128
src/include/xce/cluster.h
Normal file
|
|
@ -0,0 +1,128 @@
|
|||
/* src/include/xce/cluster.h — cluster discovery and reconnect API (P2.5-1)
|
||||
*
|
||||
* xce_connect_cluster() returns endpoint_t* (cast to void*).
|
||||
* The SSL* is accessible as ((endpoint_t*)result)->ssl.
|
||||
*/
|
||||
#ifndef XCE_CLUSTER_H
|
||||
#define XCE_CLUSTER_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include "config.h" /* MAX_HA_CANDIDATES, XCE_MAXHOSTNAME_LEN */
|
||||
|
||||
/**
|
||||
* A single controller candidate.
|
||||
* name: logical name from "name=host:port" syntax (empty if omitted).
|
||||
* Matching against myconf->name allows multiple controllers on the same
|
||||
* dev host to distinguish themselves by name rather than IP address.
|
||||
*/
|
||||
typedef struct {
|
||||
char name[XCE_MAXNAME_LEN];
|
||||
char hostname[XCE_MAXHOSTNAME_LEN];
|
||||
int port;
|
||||
} xce_candidate_t;
|
||||
|
||||
/**
|
||||
* Cluster connection configuration.
|
||||
* Populated by each caller (worker/scheduler/apiserver) from --peers and
|
||||
* --ca-cert CLI flags. libxce never reads config files.
|
||||
*
|
||||
* candidates_count must be in [1, MAX_HA_CANDIDATES].
|
||||
* A WARN is logged at startup if candidates_count > 3.
|
||||
*/
|
||||
typedef struct {
|
||||
xce_candidate_t candidates[MAX_HA_CANDIDATES];
|
||||
int candidates_count;
|
||||
char ca_cert_path[4096]; /* path to CA cert for TLS verify */
|
||||
char identity[256]; /* "from" name sent to controller */
|
||||
char key[256]; /* authentication key */
|
||||
} xce_cluster_config_t;
|
||||
|
||||
/**
|
||||
* Reconnect state machine states.
|
||||
*/
|
||||
typedef enum {
|
||||
XCE_RECONNECT_IDLE = 0,
|
||||
XCE_RECONNECT_CONNECTING,
|
||||
XCE_RECONNECT_BACKOFF,
|
||||
XCE_RECONNECT_CONNECTED,
|
||||
XCE_RECONNECT_FAILED
|
||||
} xce_reconnect_state_t;
|
||||
|
||||
/**
|
||||
* Reconnect context (use xce_reconnect_* functions to operate).
|
||||
*/
|
||||
typedef struct {
|
||||
xce_cluster_config_t* config;
|
||||
xce_reconnect_state_t state;
|
||||
int attempt;
|
||||
int backoff_ms; /* current backoff delay in ms */
|
||||
void* endpoint; /* endpoint_t* when CONNECTED */
|
||||
} xce_reconnect_ctx_t;
|
||||
|
||||
/* Redirect follow limit */
|
||||
#define XCE_MAX_REDIRECTS 4
|
||||
|
||||
/* Backoff parameters — initial kept short so tests stay fast */
|
||||
#define XCE_RECONNECT_INITIAL_BACKOFF_MS 100
|
||||
#define XCE_RECONNECT_MAX_BACKOFF_MS 30000
|
||||
#define XCE_RECONNECT_BACKOFF_MULTIPLIER 2
|
||||
|
||||
/* --- Public API --- */
|
||||
|
||||
/**
|
||||
* xce_cluster_config_validate() — validate a populated xce_cluster_config_t.
|
||||
*
|
||||
* Returns 0 on success, -EINVAL if:
|
||||
* - candidates_count < 1 or > MAX_HA_CANDIDATES
|
||||
* - any candidate has empty hostname or port <= 0
|
||||
* - ca_cert_path is empty
|
||||
* - identity is empty
|
||||
*/
|
||||
int xce_cluster_config_validate(const xce_cluster_config_t* cfg);
|
||||
|
||||
/**
|
||||
* xce_connect_cluster() — connect to the cluster via candidate list.
|
||||
*
|
||||
* Iterates candidates in order, attempting a TLS connection to each.
|
||||
* Returns endpoint_t* (cast to void*) on success, NULL if all fail.
|
||||
*/
|
||||
void* xce_connect_cluster(xce_cluster_config_t* cfg);
|
||||
|
||||
/**
|
||||
* xce_reconnect_init() — initialise a reconnect context.
|
||||
* Returns 0 on success, -EINVAL on bad arguments.
|
||||
*/
|
||||
int xce_reconnect_init(xce_reconnect_ctx_t* rctx, xce_cluster_config_t* cfg);
|
||||
|
||||
/**
|
||||
* xce_reconnect_step() — drive one step of the reconnect state machine.
|
||||
*
|
||||
* Call in a loop until state == XCE_RECONNECT_CONNECTED or FAILED.
|
||||
* Sleeps backoff_ms in BACKOFF state (skipped when mock connect is set).
|
||||
*
|
||||
* Returns 0 to continue, -1 when in terminal state.
|
||||
*/
|
||||
int xce_reconnect_step(xce_reconnect_ctx_t* rctx);
|
||||
|
||||
/**
|
||||
* xce_reconnect_fini() — release resources held by a reconnect context.
|
||||
*/
|
||||
void xce_reconnect_fini(xce_reconnect_ctx_t* rctx);
|
||||
|
||||
/**
|
||||
* xce_parse_peers() — parse "--peers name=host:port,..." into cfg->candidates[].
|
||||
*
|
||||
* Each token may be "name=host:port" or bare "host:port".
|
||||
* The name field is stored when present; it allows multiple daemons on the
|
||||
* same host to identify themselves by logical name rather than IP address.
|
||||
*
|
||||
* Returns 0 on success, -1 on empty/NULL input or any parse error.
|
||||
*/
|
||||
int xce_parse_peers(const char* peers_str, xce_cluster_config_t* cfg);
|
||||
|
||||
/* Test seam: set to non-NULL to override the real connect (unit tests only) */
|
||||
typedef void* (*xce_connect_fn_t)(const char* host, int port,
|
||||
const char* ca_cert);
|
||||
void xce_cluster_set_connect_fn(xce_connect_fn_t fn);
|
||||
|
||||
#endif /* XCE_CLUSTER_H */
|
||||
|
|
@ -14,7 +14,11 @@
|
|||
#include "myconf.h"
|
||||
#include "mylock.h"
|
||||
#include "netio.h"
|
||||
#include "persist.h"
|
||||
#include "vtaskmgr.h"
|
||||
|
||||
/* ha.h forward-declares cluster_t; including it here is safe (no circular dep) */
|
||||
#include "ha.h"
|
||||
#include "xce/types.h"
|
||||
#include "xlist.h"
|
||||
#include "zarray.h"
|
||||
|
|
@ -55,7 +59,9 @@ enum {
|
|||
HOOK_DETAIL_QUEUE_UPDATE,
|
||||
HOOK_DETAIL_USERGROUP_UPDATE,
|
||||
HOOK_DETAIL_HOSTGROUP_UPDATE,
|
||||
HOOK_DETAIL_JOB_RUSAGE_UPDATE
|
||||
HOOK_DETAIL_JOB_RUSAGE_UPDATE,
|
||||
HOOK_DETAIL_JOB_STOP,
|
||||
HOOK_DETAIL_JOB_RESUME
|
||||
};
|
||||
|
||||
#define XCE_NS_CONFIG (0)
|
||||
|
|
@ -88,7 +94,7 @@ typedef struct {
|
|||
endpoint_t* ep;
|
||||
} cluster_apiserver_t;
|
||||
|
||||
typedef struct {
|
||||
typedef struct cluster_s {
|
||||
myconf_t* myconf;
|
||||
xce_peerinfo_t* myhost;
|
||||
hooksys_t hooksys;
|
||||
|
|
@ -112,6 +118,8 @@ typedef struct {
|
|||
vtaskmgr_t vtaskmgr;
|
||||
cbuf_t wbuf;
|
||||
int running;
|
||||
persist_t persist; /* Persistence module — P0-F */
|
||||
ha_state_t ha; /* HA role management — P1-5 */
|
||||
} cluster_t;
|
||||
|
||||
#endif /* XCE_CLUSTER_DEF_H */
|
||||
|
|
|
|||
|
|
@ -19,6 +19,8 @@ enum {
|
|||
E_XCE_BAD_QUEUE,
|
||||
E_XCE_BAD_GROUP,
|
||||
E_XCE_INTERNAL,
|
||||
E_XCE_NOMASTER, /* Standby has no known primary — client should retry */
|
||||
E_XCE_REDIRECT, /* Standby is redirecting client to primary */
|
||||
E_XCE_MAX
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -172,6 +172,7 @@ typedef struct {
|
|||
#define XCE_WORKER_LOCKEDW (0x00040000)
|
||||
#define XCE_WORKER_BUSY (0x00080000)
|
||||
#define XCE_WORKER_CLOSED (0x00100000)
|
||||
#define XCE_WORKER_HIDDEN (0x00200000) /* Admin explicitly removed — invisible but not yet cleaned up */
|
||||
|
||||
#define IS_WORKER_UNAVAIL(status) (((status) & XCE_WORKER_UNAVAIL) != 0)
|
||||
#define IS_WORKER_LOCKEDU(status) (((status) & XCE_WORKER_LOCKEDU) != 0)
|
||||
|
|
@ -353,7 +354,8 @@ typedef struct {
|
|||
|
||||
#define JOB_STAT_PDONE (0x80) /**< Post job process done successfully */
|
||||
#define JOB_STAT_PERR (0x100) /**< Post job process has error */
|
||||
#define JOB_STAT_WAIT (0x200) /**< Chunk job waiting its turn to exec */
|
||||
#define JOB_STAT_WAIT (0x200) /**< Chunk job waiting its turn to exec */
|
||||
#define JOB_STAT_TRANSIENT (0x400) /**< Job state uncertain after failover — awaiting worker re-join */
|
||||
#define JOB_STAT_RUNKWN \
|
||||
0x8000 /* Flag : Job status is UNKWN caused by losting contact with remote \
|
||||
cluster */
|
||||
|
|
@ -381,6 +383,7 @@ typedef struct {
|
|||
#define QUEUE_STAT_DISC 0x10 /**< Remote queue status is disconnected. */
|
||||
#define QUEUE_STAT_FAIRSHARE_DEFAULT \
|
||||
0x20 /**< Fairshare queue with null user share defined*/
|
||||
#define QUEUE_STAT_HIDDEN (0x0100) /**< Admin deleted — invisible but not yet cleaned up */
|
||||
|
||||
/* FIXME: improve following later */
|
||||
#define JOB_IS_PEND(state) (((state) & JOB_STAT_PEND) != 0)
|
||||
|
|
@ -472,6 +475,7 @@ typedef struct {
|
|||
char* ckptinfo;
|
||||
int64_t pending_ctrl;
|
||||
xce_job_preason_t* pend_reasons;
|
||||
uint64_t transient_since; /* epoch seconds; set when job enters JOB_STAT_TRANSIENT */
|
||||
} xce_job_t;
|
||||
|
||||
typedef struct {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue