Commit graph

396 commits

Author SHA1 Message Date
5ef14a2717 feat: implement handle_upgrade_config() (gated), push_config_to_standbys(), create_lost_and_found() stub
- Add do_upgrade_config_with_bytes() helper with full validate/decode/
  re-encode/save/push/restart logic gated by CONF_ENABLE_UPGRADE_CONFIG
- Add push_config_to_standbys() stub: logs connected standbys and returns
  0; full parallel fan-out deferred to Task 11 (typemap byte shims needed)
- Add send_upgrade_error() private helper for consistent error replies
- Expand create_lost_and_found() stub with WARN log explaining Phase 2
  deferral; core config-replication feature works without it
- Declare push_config_to_standbys() in config_repl.h
2026-05-13 18:12:32 +00:00
a95476be4e feat: add schedule_checkpoint() with WAL record-count trigger and idempotency CAS guard 2026-05-13 17:56:46 +00:00
8f4db308da feat: implement handle_config_request(), fetch_config_from_primary(), handle_controller_join()
- handle_config_request(): primary-side handler sends up-to-date flag or
  version_tag; SHA-256 integrity check guards against in-place file changes;
  sha256/bytes fields left NULL pending Task 11 typemap shims
- fetch_config_from_primary(): standby fetch with exponential backoff retry
  (20 attempts, 500ms..5s); falls back to saved config via load_primary_config();
  do_one_config_fetch() stubbed (-ENOSYS) until Task 11 wires capnp over SSL
- handle_controller_join(): registers standby endpoint in ha.standby_endpoints
  via zarray_store() for OP_upgradeConfig fan-out; sends success reply
- Moved load_primary_config() from myconf.c to config_repl.c so the unit test
  binary (which links config_repl.c but not myconf.c) can resolve it
2026-05-13 17:46:56 +00:00
93b8616f11 fix: capture am_i_primary() result once; set ha->role before phase2; cluster_init wrapper cleanup on failure
- myapp.c: capture am_i_primary() into is_primary (avoids redundant
  second probe call); after load_primary_config() succeeds, record
  CTRL_PRIMARY role via atomic_store_explicit so cluster_init_phase2()
  sees the correct role (ha_init() defaults to CTRL_STANDBY)
- myapp.c: add #include "ha.h" for CTRL_PRIMARY, atomic_store_explicit,
  and memory_order_release
- cluster.c: cluster_init() wrapper now calls cluster_fini(c) before
  returning on create_ssl_context failure, hook failure, and
  cluster_init_phase2 failure — prevents SSL ctx and phase1 resource leak
2026-05-13 17:32:32 +00:00
6b06aa02d9 feat: split cluster_init() into phase1/phase2; update myapp_run() for two-phase startup with am_i_primary gating
- cluster_init_phase1(): hooks, HA init (priority from --peers), standby_endpoints zarray, cbuf
- cluster_init_phase2(): SSL ctx from ha.ssl_ctx, cluster_config guard, kvmgr sizing, iomgr, endpoint, persist, role promotion, create_lost_and_found, persist_recover
- cluster_init(): legacy wrapper calling both phases with inline SSL init for backward compat
- myapp_run(): phase1 → SSL init → am_i_primary → load_primary_config/fetch_config_from_primary → phase2 → start → run
- myreq_handler(): add OPCode_replConfig and OPCode_upgradeConfig dispatch
- handle_endpoint_error(): prune standby_endpoints on HostRole_controller disconnect
- cluster_run(): log restart_requested on exit
- config_repl.c: add create_lost_and_found, handle_config_request, handle_upgrade_config stubs
- config_repl.h: add declarations for new stubs; include netio.h + request.h
- cluster.h: declare cluster_init_phase1 and cluster_init_phase2
- myconf.c: fix FATAL → ERROR (FATAL macro undefined); add fetch_config_from_primary stub
2026-05-13 17:06:45 +00:00
ec3cab40c3 fix: capture SSL_get_error once per retry; lock ha->lock in ha_check_priority_stepdown 2026-05-13 16:44:55 +00:00
fe3cc57f27 feat: implement real am_i_primary() with election probing, ha_is_primary(), ha_check_priority_stepdown()
- Replace stub am_i_primary() with real election probe logic
- probe_thread(): non-blocking TCP connect with optional SSL handshake
- wait_any_probe(): deadline-based spin-poll across all probe threads
- am_i_primary(): priority-0 always primary; higher priorities probe
  lower-priority peers and self-promote if deadline expires
- ha_is_primary(): lock-free role check
- ha_check_priority_stepdown(): simultaneous-boot stepdown at term=0
- start/stop_heartbeat_listener(): stubs for Task 7
- ha_init(): explicit initialization of ssl_ctx/jitter_seed/standby_endpoints
- TDD: add test_am_i_primary suite with 3 tests covering p0/p1 paths
2026-05-13 16:39:59 +00:00
8d86a1bcfe fix: get_default_conf bypass load_cluster_config to avoid dangling hook pointer (UAF) 2026-05-13 16:13:26 +00:00
6141264219 fix: free cluster_config on error paths in load_primary_config; fix get_default_conf wrapper leak 2026-05-13 16:11:58 +00:00
cd02cf40d2 feat: add load_primary_config(), myconf_fini(); remove static cf global from myconf.c
- load_primary_config(): resolves conf/current, reads raw bytes, computes
  SHA-256 tag, decodes Cap'n Proto via load_cluster_config_from_file(),
  checks reserved names, sets config_generation, checks staleness
- myconf_fini(): frees conffile and cluster_config (calls free_ClusterConfig)
- Remove static cf global from myconf.c; get_default_conf() now uses a
  local struct cluster_configuration* via load_cluster_config()
- Add load_primary_config_for_test() under XCE_TESTING in config_repl.c
  to test path resolution without triggering FATAL
- Add xce/cluster_def.h include to config_repl.c and test_config_repl.c
  so cluster_t is complete when XCE_TESTING is active
- TDD: test_load_primary_config_missing_returns_error passes (18/18)
2026-05-13 16:06:18 +00:00
ce62406f2c fix: validate --conf-keep-n >= 1 and --conf-stale-timeout >= 0 in argument parsing 2026-05-13 16:00:09 +00:00
e74fd2aac3 feat: main.c — null conffile (now heap-allocated), add --conf-keep-n/--conf-stale-timeout flags, peers validation 2026-05-13 15:57:02 +00:00
2f2bfa9a3c fix: conf_trim early-return on missing current pointer; check fsync/fseek return values 2026-05-13 15:53:40 +00:00
491c2550c2 feat: add config_repl Phase 1 — HEAD-file storage, SHA-256, trim, stale check, sanitise_cn
- config_repl.h/.c: atomic versioned file save, pointer file, trim, stale check
- test_config_repl.c: 17 Unity tests covering all Phase 1 storage functions
- typemap.h: add repl/heartbeat types + union members for xce_request_t/xce_reply_t
- clusterconfig.h: add config_generation field to cluster_config_t (capnp schema ref)
- meson.build: add config_repl.c and test_config_repl.c to build targets
- controller_test.c: register test_config_repl dispatch function
2026-05-13 15:45:28 +00:00
1c4a103a66 fix: resolve probe_ctx_t type tag, add DEFAULT_MAX_WAL_RECORDS macro, fix atomic type consistency, move ha_seed_jitter to ha.c, add API doc comments 2026-05-13 14:27:07 +00:00
8a9fce1be4 fix: ha_seed_jitter loop-until-full partial read; byteorder.h musl portability 2026-05-13 14:18:43 +00:00
2c375cf476 feat: add config replication fields to myconf_t, cluster_t, ha_state_t, persist_t; create byteorder.h 2026-05-13 14:15:30 +00:00
78adf3f843 fix: normalize capnp union arm naming to lowercase; fix UpgradeConfigRequest field order
- Rename replConfigRequestMsg/replConfigPushRequestMsg/upgradeConfigRequestMsg
  to replconfigrequestmsg/replconfigpushrequestmsg/upgradeconfigrequestmsg in
  Request.body union to match existing lowercase convention
- Rename replConfigReplyMsg/replConfigPushReplyMsg/upgradeConfigReplyMsg
  to replconfigreplymsg/replconfigpushreplymsg/upgradeconfigreplymsg in
  RequestReply.data union
- Swap UpgradeConfigRequest fields so sha256@0/bytes@1 matches sibling
  ReplConfigPushRequest pattern
- Move HeartbeatMsg out of the config replication section into its own
  '# ── HA heartbeat ──' section above the replication structs
2026-05-13 14:10:33 +00:00
683c092b77 feat: add replConfig/replConfigPush/upgradeConfig opcodes and message structs to capnp schema 2026-05-13 14:06:51 +00:00
6a47ef5c39 save document 2026-05-13 11:05:04 +00:00
e51687a354 Merge branch 'scheduler' into working 2026-05-13 11:02:43 +00:00
48f3a2d99e update .gitignore 2026-05-13 11:02:34 +00:00
5dbe404015 save kiro settings 2026-05-13 11:00:32 +00:00
033f34c07f add kiro-sdd documents 2026-05-13 10:58:51 +00:00
e847a32546 add user rshen 2026-05-13 10:51:32 +00:00
aae4c672b5 make unsatisfy_() just print unmeet resources 2026-05-13 10:48:38 +00:00
febb4a6c4d fix: preserve admin-set host state across worker re-registration in makeWorker_
When a worker re-joins and already exists in workermgr, makeWorker_()
now overrides the existing slot instead of returning EEXIST. Admin-set
flags (LOCKEDU, CLOSED, HIDDEN, CLOSED_BY_ADMIN) survive the override.

Add XCE_WORKER_CLOSED_BY_ADMIN flag to distinguish admin-closed hosts
from workload-closed ones. worker_ctrl sets/clears it alongside CLOSED.

Refactor worker_init/worker_destroy into copy_worker/kill_worker helpers
shared by both fresh creation and override paths.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13 10:38:39 +00:00
d47b3375e7 controller will pass resource <name, value> pair than <idx, value> pair 2026-05-13 07:18:03 +00:00
9f6741c2c4 fix scheduler notification data pointer ownership and better trace log for unsatisfy_() 2026-05-12 13:45:58 +00:00
a07a74a943 Merge branch 'persistent_ha' into scheduler 2026-05-12 10:52:46 +00:00
475ed20bba use fmt_jobid() when job submission 2026-05-12 10:52:28 +00:00
450a4d467c fix: sel_key_cmp_fn padding-byte mismatch in kvtab lookups
sel_key_cmp_fn compared keys with memcmp on the whole sel_node_key_t
struct, including the 4-byte padding between type and op1.  The stack-
allocated search key in gc_sweep_select_nodes can have different padding
bytes than the heap-allocated copy stored by key_add, causing key_del
to miss the entry and leave a dangling pointer in the CSE intern table.

Replace memcmp with field-by-field comparison on type, op1, op2.

Also fix GC sweep tests that asserted pointer inequality after re-intern:
calloc may lawfully reuse the freed address, so pointer inequality does
not reliably indicate a successful sweep.  Track the swept node's
node_id before freeing and assert the re-interned node gets a fresh id.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-12 09:53:59 +00:00
3e652d2d7e feat: fill scheduler controller message gaps
Wire up 6 previously-stubbed message handlers in the scheduler:
resource dataset, resval dataset, job update notify, queue update
notify, resval update notify, and job rusage notify. Remove the
unreachable pushmsg branch (vtask_entry rewrites all pushmsg to
datamsg before the handler fires).

Changes by component:
- helper.h/helper.c: add xce_notify_event_name() for symbolic log names
- scheduler.h: add 5 new xce_sched_op_t values and matching union fields
- plugin.h/plugin.c: add plugin_job_rusage_hook_t and plugin_job_rusage_update()
- schedobj.c: add sched_update_existing_queue() with NULL/-ENOENT guards
- scheduler.c: wire 5 new dequeue cases; add sched_update_existing_job()
  and static sched_op_name(); emit WARN log on handler failure
- handler.c: implement sched_handle_resources/resvals (dataset) and
  sched_handle_sched_update_{job,queue,resvals}/job_rusage (notify);
  remove dead sched_handle_jobpush/workerpush and pushmsg branch;
  update TRACE logs to use symbolic name helpers
- test.c: 5 new unit tests covering notify event names, enum values,
  and queue update NULL guard (15/15 pass)
2026-05-12 07:01:52 +00:00
f80839cd99 add cc-std 2026-05-11 08:19:42 +00:00
ce2edf0dac refactor: represent time fields as int64 Unix seconds throughout the stack
Collapses a four-step conversion chain (time_t → int64 → time.Time →
RFC3339 string → string → time.Time) to a direct int64 pass-through:

  C time_t → rawJobData int64 → JobInfo int64 → JSON integer → QueryJob int64 → time.Unix() for display

Changes:
- apiserver/models/jobs.go: SubmitTime, QueueTime, StartTime, EndTime,
  ReserveTime in JobInfo and JobAdditionalFields: time.Time → int64
- apiserver/models/common.go: ActionControl.SubmitTime: time.Time → int64
- backend/convert_pure.go: remove time.Unix() calls; assign int64 directly
- backend/convert_test.go: update time assertions to compare int64 values
- xcecli/models/jobs.go: SubmitTime, StartTime, EndTime, ReserveTime in
  QueryJob: string/time.Time → int64; remove time import
- xcecli/common/output/time.go: replace parseJobTime(string) with
  unixToTime(int64) and fmtUnixTime(int64) helpers
- xcecli/common/output/page_print_jobs.go: replace parseJobTime() calls
  with unixToTime()/fmtUnixTime(); remove !startTime.IsZero() guard
  (unixToTime returns false for ts<=0, making it redundant)
- xcecli/common/output/page_print_array_elements_jobs.go: replace
  string-split+time.Parse submit-time block with fmtUnixTime(); remove
  unused time import

Side effect: eliminates the "0001-01-01T00:00:00Z" zero-time
serialization issue at the source — zero timestamps are now 0 (integer)
and unixToTime/fmtUnixTime return false/"-" for ts<=0 without special-
casing needed in callers.
2026-05-09 07:00:49 +00:00
14de6c4fa6 fix incorrect handle of -n option 2026-05-09 06:58:57 +00:00
a2476d0176 fix: xcecli jobs list -l — hide spurious start line and show pending reason
Three fixes:

1. ExecHosts.UnmarshalJSON: JSON null (sent when no allocation exists)
   was treated as a non-"-" string because json.Unmarshal(null, &str)
   returns nil with str="". This created a phantom HostSlot{NumSlots:1,
   HostName:""} producing "<1*>" for every PEND job. Now null and ""
   are both treated as empty hosts.

2. page_print_jobs.go: added !startTime.IsZero() guard so a zero
   timestamp ("0001-01-01T00:00:00Z") never produces a start line
   even if the status check is bypassed.

3. Added PENDING_REASON field to JobFieldsMap so it is requested from
   the API when -l is passed, and rendered in printJobsVerboseOutput
   for PEND/PSUSP jobs before the start-time section.
2026-05-09 05:15:31 +00:00
af578c29ca fix show exec host for pend jobs 2026-05-09 04:09:16 +00:00
064717959d use fmt_jobid() in scheduler 2026-05-09 03:08:56 +00:00
6429ef0bff fix: correct controller restart recovery — jobs, workers, and resource ordering
Four bugs fixed:

1. persist.c: array job WAL loop aborted after index [0] when replication
   returned -ENODEV (no standbys). Loop guard now uses the return value of
   persist_wal_sync_append() which is 0 on local-disk success regardless of
   replication status.

2. join.c: WAL-recovered worker slot (status=UNAVAIL) caused add_worker() to
   return -EEXIST on reconnect. New EEXIST path in handle_worker_join() looks
   up the existing slot by name, updates its endpoint, and clears UNAVAIL so
   the normal hook/reply/on_worker_join flow proceeds.

3. xcectx.c: cbuf_get() in xce_hello() blocked indefinitely when the TCP
   connection dropped because no error handler was registered. Added
   handle_disconnect() which sets ctx->state=XCE_CTX_ERR and calls
   cbuf_tryput(-ECONNRESET) to unblock the waiter, allowing
   checking_connection() to detect the failure and rejoin.

4. cluster.c: persist_recover() was called before cluster_build_resources()
   populated resmgr, causing restore_worker() → add_hostattrs_resvals() to
   fail with EEXIST noise. Moved persist_recover() to after HOOK_AFTER_EVENT
   so resmgr is fully populated before WAL replay.
2026-05-09 03:02:39 +00:00
b830cecf97 fix: skip BEFORE_EVENT in persist hook functions to prevent crash
All three persist hook functions (job/worker/queue) were ignoring the
situation argument, so they fired on both BEFORE and AFTER events.
BEFORE_EVENT hooks pass different va_list layouts than AFTER_EVENT:
- WORKER_JOIN BEFORE: (cluster_t*, char* name, int 0) vs AFTER: (cluster_t*, worker_t*)
- JOB_NEW BEFORE: (cluster_t*, xce_job_submit_t*) vs AFTER: (cluster_t*, xlist_t*)

Reading the wrong type from va_list caused a dereference crash.
Fix: return 0 immediately on HOOK_BEFORE_EVENT in all three hooks.
2026-05-08 08:46:27 +00:00
6df3bd5906 feat: implement job state, queue state, and worker recovery in WAL replay
WAL payloads:
- Job state transitions (START/FINISH/FAIL/STOP/RESUME/UPDATE etc):
  wal_jobstate_t {uint64_t jobid, uint32_t state} — restores exact state flags
- Queue state (QUEUE_CTRL): wal_queuehdr_t {uint32_t status} + name string —
  re-applies admin open/close/active/inactive after config queues are built
- Worker CTRL: existing [uint32_t detail][capnp xce_worker_t] unchanged

New recovery functions:
- restore_job_state(c, jobid, state): looks up job, overwrites state flags
- restore_queue_state(c, name, status): looks up queue, overwrites status

apply_wal_entry: dispatches all WAL types including job state and queue
test_repl_apply.c: weak stubs for restore_job_state/restore_queue_state
  so unit test binary links without job.c/queue.c
2026-05-08 08:19:20 +00:00
efafcdc6e9 fix leak of lock 2026-05-08 07:57:26 +00:00
9b6a91a89e feat: implement restore_job/worker_from_capnp for WAL recovery
WAL hooks now write real Cap'n Proto payloads instead of stub uint32_t:
- WAL_JOB_SUBMIT: full xce_job_t serialized via encode_Job/write_Job
- WAL_WORKER_CTRL: [uint32_t detail][xce_worker_t capnp] via encode_Worker
- Other job events (START/FINISH/FAIL/STOP etc): uint64_t jobid only

restore_job(): deserializes Cap'n Proto xce_job_t, guards against duplicate
replay with key_find check, then calls create_job()

worker_from_capnp(): skips 4-byte detail prefix, deserializes xce_worker_t,
guards against duplicate replay, then calls restore_worker()

join.c: pass worker_t* (looked up by idx via vget) to WORKER_JOIN hook
so persist hook can serialize worker data without touching workermgr RTTI
2026-05-08 07:49:27 +00:00
b80ca3deaa fix: use tlog macros consistently in persist.c
Replace all fprintf(stderr,...) calls with INFO/WARN/ERROR/TRACE so
persist.c output appears in the controller's structured log rather than
mixing plain text on stderr with tlog-formatted output.
2026-05-08 07:17:35 +00:00
7e4528a0b9 refactor: consolidate parse_peers into xce_parse_peers in libxce
Three identical static copies (controller, worker, scheduler) replaced
with a single xce_parse_peers() in src/lib/cluster.c.

The libxce version is the canonical implementation: stores the logical
name from 'name=host:port' syntax so daemons on the same dev host can
identify themselves by name rather than IP address.
2026-05-08 07:06:57 +00:00
a05c9bd344 feat: support name=host:port format in controller --peers
- xce_candidate_t gains name[] field for logical peer identity
- parse_peers stores name prefix before '=' (empty when omitted)
- cluster_init matches own identity by name first, hostname as fallback
  enabling multiple controllers on the same dev host via
  --peers ctrl-1=10.0.1.1:7000,ctrl-2=10.0.1.2:7001
2026-05-08 06:51:54 +00:00
59597e105e feat: add --peers to controller for HA peer list and priority election
- myconf_t gains peers_cfg (xce_cluster_config_t) populated from --peers
- controller/main.c: parse --peers host:port,... via argp; ca_cert_path
  auto-derived from workdir/certs/xce.crt (no --ca-cert flag needed)
- cluster_init: derive my_priority from position in peers list (index 0 =
  primary); fall back to 0 when --peers not given (single-node mode)
2026-05-08 04:40:27 +00:00
d61f91b99c refactor: simplify ha.c comments and extract HA config constants
- Add HA_DEFAULT_* constants to ha.h; use them in cluster_init()
- Strip stale task-label comments (P1-3a, P1-5, P1-6, P3-6 refs)
- Remove self-documenting 'Step N:' annotations from ha_do_promote/demote
- Rewrite stale forward-reference notes as current WHY comments
- Remove resolved TODO P0-8b from cluster.c
2026-05-08 03:56:38 +00:00
da6d967a33 refactor: simplify HA backend peer handling
- parsePeer: use net.SplitHostPort (handles IPv6, stdlib convention)
- tryConnect: take Go strings, own CGO allocation internally
- RegisterToBackend: remove unreachable 'no HA candidates' error path
- Sync Peers field comment between utils and backend config structs
2026-05-08 03:41:11 +00:00