Commit graph

113 commits

Author SHA1 Message Date
6bdf7edacf feat(raft-library): task 5.4 — Groups F+G, 6 test cases (RDHUP failover + split-brain) 2026-05-31 07:05:45 +00:00
5c677bc52d feat(raft-library): task 5.3 — Groups D+E 7 test cases; fix Req 6.3 on_cancel_timeout in decided path 2026-05-31 06:58:02 +00:00
0aa4c84bfe feat(raft-library): task 5.2 — protocol Groups C+H, 11 test cases (eligible policy + persist) 2026-05-31 06:34:33 +00:00
389047194f feat(raft-library): task 5.1 — protocol Groups A+B, 10 test cases (election + voted_for) 2026-05-31 06:26:52 +00:00
c1b07392df feat(raft-library): task 4.2 — sim deliver/partition/timeout/start-all; 4 tests 2026-05-31 06:20:35 +00:00
d2e2c1ef8a feat(raft-library): task 4.1 — sim data structures, raft_sim_init, dump_log; 2 tests 2026-05-31 06:11:34 +00:00
85a7e325a8 feat(raft-library): task 3.5 — election timeout, leader-lost, check-and-start; 6 tests 2026-05-31 06:02:29 +00:00
f81054930f feat(raft-library): task 3.4 — raft_on_vote_reply quorum tracking; 6 tests + review fixes 2026-05-31 05:56:34 +00:00
e0bcb35554 feat(raft-library): task 3.3 — raft_on_vote_request 6-step decision; 10 tests 2026-05-31 05:44:34 +00:00
4955fa3805 feat(raft-library): task 3.2 — raft_start_election with single-node shortcircuit; 4 tests 2026-05-31 05:30:00 +00:00
1aa7db4583 feat(raft-library): task 3.1 — raft_init validation, state init, raft_snapshot; 12 tests 2026-05-31 05:18:42 +00:00
d109155a21 feat(raft-library): task 2.2 — raft_callbacks_t, raft_config_t, public API signatures 2026-05-31 05:03:04 +00:00
66c9810823 feat(raft-library): task 2.1 — raft_role_t, raft_state_t, raft_snapshot_t in raft.h 2026-05-31 04:56:49 +00:00
7f7de013b9 feat(raft-library): task 1.1 — build scaffold (raft/ files + meson wiring) 2026-05-31 04:51:42 +00:00
dbb750ebd3 fix(election-iomgr-refactor): Raft election, config decode, and RDHUP chain
- ha.c: self-vote in ha_start_election_async (Raft invariant)
- ha.c: heartbeat_timeout_ms for hb_timeout_thread (not election_timeout_ms)
- ha.c: clear voted_for before RDHUP jitter (stale vote from dead primary)
- ha.c: same-term voted_for check in RDHUP jitter
- config_repl.c/h: use xce_decode for upgradeConfig (capnp extraction before capn_free)
- cluster.c: fix upgradeConfig handler direction (standbys apply, primaries skip)
- cluster.c: remove raw_pkt_copy hack; use req directly
- join.c: push config to newly-joined standbys
- typemap.h/request.h/request.c: raw_sha256/raw_bytes fields for decoded upgradeConfig
- test_ha_failover.c: update heartbeat timeout test to use g_fast_cfg
2026-05-31 03:54:30 +00:00
81ea5e8eb8 fix(election-iomgr-refactor): integration bug fixes from 6.3 cluster test
- netio.c: skip SSL_new in endpoint_connect if ep->ssl already set (pre-created client ctx)
- ha.c: pre-create client SSL before endpoint_connect to avoid server-ctx handshake failure
- ha.c: fix double-free of vctx in endpoint_connect error path (let endpoint_fini own ep->data)
- ha.c: add endpoint_set_peer() call after endpoint_init for vote endpoints
- ha.c: fix endpoint_init args to AF_INET/SOCK_STREAM for outgoing vote sockets
- ha.c: wrap handle_request_vote voted_for check in ha->lock to prevent split-brain
- ha.c: skip immediate vote-deny for ECONNREFUSED in production; let election timeout handle
- delay.c: fix task_cmp signature to match ordq_cmp_fn_t (3 args: ctx, arg1, arg2)
2026-05-30 14:59:54 +00:00
c3caab49a6 feat(election-iomgr-refactor): add on_connected fire-once callback to endpoint_t
Add void (*on_connected)(endpoint_t *ep) field to endpoint_t with
endpoint_set_on_connected() helper. io_handler fires it exactly once
immediately after ssl_handshake_done=1 then nulls the pointer.

Enables ha_start_election_async to send OP_requestVote as soon as
the TLS handshake with a peer completes — no blocking SSL_connect.

6 new unit tests pass; controller suite 1/1 OK.
2026-05-30 07:34:08 +00:00
4737b0f8a3 feat(election-iomgr-refactor): add delay_task_ms() for ms-granularity one-shot tasks
Add millisecond-granularity delay_task_ms() to delaymgr using
clock_gettime(CLOCK_MONOTONIC) for deadline computation. Extends
delay_task_t with nextrun_ts field; delay_worker distinguishes
ms-tasks from second-tasks via non-zero nextrun_ts. Backward
compatible with existing delay_task callers.

4 delay tests pass (2 new: tolerance and invalid-args).
Provides the timeout primitive for election and committing watchdogs.
2026-05-30 07:15:54 +00:00
fc77630e6c Merge branch 'claude_skill' into persistent_ha 2026-05-30 08:14:49 +08:00
c8e0f65a39 enhance xce-build skill 2026-05-30 08:13:59 +08:00
e4e34d8f60 fix(ha-bootstrap): increase config fetch retries to 60, downgrade no-config log, suppress false SSL perror 2026-05-29 14:56:48 +00:00
0ec0ffa7e3 feat(ha-config-bootstrap): implement xce_retry_config_t and xce_request_with_retry (tasks 10.1, 10.2) 2026-05-29 03:00:34 +00:00
bb8e531a12 test(ha-config-bootstrap): add RED-phase tests for xce_request_with_retry (task 10.1) 2026-05-29 02:45:24 +00:00
26393e919c feat(config-repl): add raw capnp shims for OP_replConfig wire protocol
xce_replconfig_reply_raw() - primary sends reply with real bytes via endpoint
xce_replconfig_req_ssl() - standby sends request over raw SSL socket
xce_replconfig_reply_ssl_decode() - standby decodes reply from raw SSL
xce_upgrade_config_req_extract() - documents architectural limitation; returns -ENOTSUP
xce_upgrade_config_req_extract_raw() - extract bytes from raw packet for OP_upgradeConfig

Same bypass pattern as existing xce_replconfigpush_req_raw(): build the
capnp message directly via capn_new_list8 + capn_setv8, sidestepping the
encode_Data_ptr path that routes raw bytes through xce_data_t (a
discriminated cluster-data union that cannot carry raw byte arrays).

Unblocks handle_config_request() and do_one_config_fetch() wiring.

Task A research finding: xce_upgrade_config_req_extract(xce_request_t*)
is unimplementable because xce_decode() calls capn_free() before any
handler runs, and decode_Data_ptr converts the wire-format List(UInt8)
into a zero-filled xce_data_t. Task D must capture the raw io_packet_t
bytes before xce_decode tears them down, then call the _raw variant.
2026-05-21 13:02:49 +00:00
86fe35e671 add kiro skills 2026-05-21 03:41:57 +00:00
e5fde7b55f fix(persist): zero nresvals_to_sched after free_name_val_list
Crash: encode_NameValue_list called with count=21, s=NULL on worker
rejoin, causing SIGSEGV in persist_worker_hook_fun.

Root cause: free_name_val_list(xce_name_val_t***, int) accepted count by
value. It zeroed *vals (resvals_to_sched = NULL) but could not zero
nresvals_to_sched. Any caller that freed the scheduler resource snapshot
(opget.c workerlist, reply.c clusterconf, dataex.c, sched.c dispatch)
left nresvals_to_sched stale. When the worker later rejoined and the
WAL HOOK fired, the WAL encoder read count=21 with a NULL pointer.

Fix: change signature to free_name_val_list(xce_name_val_t***, int*)
so both fields are zeroed atomically. Update all 7 call sites.
2026-05-20 09:39:07 +00:00
2e2aaea78c feat: implement push_config_to_standbys() — parallel fan-out via xce_replconfigpush_req_raw() using capn_new_list8 for raw bytes 2026-05-14 03:58:02 +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
a07a74a943 Merge branch 'persistent_ha' into scheduler 2026-05-12 10:52:46 +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
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
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
fd9cfce1c1 feat: P2.5-5/6 add --peers/--ca-cert parsing to worker+scheduler with validation
Worker (P2.5-5):
- Added parse_peers() static helper to src/worker/src/main.c
- Switched getopt to getopt_long to support --peers and --ca-cert long options
- When --peers is given: parses into xce_cluster_config_t, calls
  xce_cluster_config_validate(), logs INFO with candidate count/identity/ca_cert
- If validation fails: prints clear error and exits(1)
- WARN logged when candidates_count > 3
- Backward compatible: no --peers falls back to existing single-controller path

Scheduler (P2.5-6):
- Added parse_peers() static helper to src/scheduler/src/main.c
- Extended argp struct with --peers (-P) and --ca-cert (-C) options
- Same validation/logging pattern as worker
- Backward compatible: no --peers falls back to existing single-controller path

Tests (new):
- src/lib/testsuite/test_cluster_parse.c: 8 unit tests for parse_peers logic
  (single entry, multiple entries, no-name format, empty/null/missing-port errors,
  name-stripped correctly, MAX_HA_CANDIDATES cap respected)
- Registered in test.c and meson.build
- All 8 tests GREEN; 3 pre-existing failures in unrelated GC/CSE tests unchanged
2026-05-08 02:32:56 +00:00
0c11989da7 feat: P2.5-1/2/2a/3/8 xce_cluster_config_t, xce_connect_cluster, xce_reconnect_step
Add libxce cluster discovery and auto-reconnect state machine:

- src/include/xce/cluster.h: xce_candidate_t, xce_cluster_config_t,
  xce_reconnect_ctx_t types; full API with test-seam hook
  xce_cluster_set_connect_fn()
- src/lib/cluster.c: xce_cluster_config_validate() (rejects invalid
  candidate counts, empty hostname/port, missing identity/ca_cert),
  xce_connect_cluster() (candidate fallback with single sweep),
  xce_reconnect_step() state machine (IDLE→CONNECTING→CONNECTED,
  with exponential backoff; nanosleep skipped when mock is active)
- src/lib/meson.build: add cluster.c to libxce_src and
  test_cluster.c to libxce_test_src (P2.5-2a)
- src/lib/testsuite/test_cluster.c: 9 TDD tests covering validation,
  first-candidate success, fallback, all-fail NULL return, reconnect
  to CONNECTED, and backoff state transition (P2.5-8)

All 9 cluster tests pass; 3 pre-existing unrelated failures unchanged.
2026-05-08 02:16:08 +00:00
f155b1ef97 feat: xce_disable_hook() + _Atomic hook_fn_t + NULL-slot reuse (P1-2a)
- hook_item_t.fn changed to _Atomic hook_fn_t (C11 stdatomic)
- xce_disable_hook(): atomically stores NULL into the slot fn field;
  returns 0 on success, -ENOENT if fn not found, -EINVAL on bad args
- xce_add_hook(): scans for NULL (disabled) slots before allocating;
  uses atomic_store_explicit(release) when writing fn pointer
- xce_run_hooks(): atomic_load_explicit(acquire) + NULL guard so
  disabled slots are silently skipped without disrupting the loop
- Add src/lib/testsuite/test_hooks.c with 3 TDD tests:
  test_disable_hook_stops_invocation
  test_disable_hook_returns_enoent_if_not_found
  test_add_hook_reuses_null_slot
- Register test_hooks in meson.build and test.c runner

Required by ha_demote_to_standby() to atomically disable persistence
hooks when the controller steps down from primary, preventing unbounded
hook list growth across promote/demote cycles (resolution A6-015,
A7-004, A7-016).
2026-05-07 13:55:42 +00:00
813a5a3359 test: add KVMGR_KEY_INT coverage to kvmgr_capture_keys tests
Adds test_capture_keys_int_kvmgr() to verify integer-keyed kvmgr path.
Tests that kvmgr_capture_keys() correctly formats integer keys as decimal
strings via snprintf (PRId64), and validates key_len == strlen(key) + 1.

All 4 kvmgr_capture tests pass.
2026-05-07 11:12:11 +00:00
eaf1801d6d feat: implement kvmgr_capture_keys for lock-free checkpoint serialization (P0-14a)
Add kvtab_foreach walk primitive (P0-14) to kv.c/kv.h, then implement
kvmgr_capture_keys in kvmgr.c that deep-copies all key→index mappings
into a malloc'd slot_key_t[] array safe to read after the write lock is
released during checkpoint serialization (Part 3.8).

Key design points:
- Both kvtab_foreach and kvmgr_capture_keys take var (RTTI data pointer)
  and use RTTI_DATA_TO_OBJ internally — the RTTI framework's makeInstance
  returns the data region pointer, not the meta_obj_t header pointer
- String keys are copied via strncpy into slot_key_t.key[XCE_MAXNAME_LEN]
- Integer keys are formatted as decimal strings via snprintf
- Two-pass approach: count live entries first, allocate, then fill
- Tests cover string kvmgr (3 entries), empty kvmgr, and deep-copy safety
2026-05-07 11:07:41 +00:00
d66a8a98b6 fix: use acquire/release ordering for inuse atomic ops in xzarray (P0-2)
Changed all memory_order_relaxed accesses on xzarray_item_t.inuse to use:
- memory_order_acquire for loads
- memory_order_release for stores

This ensures proper synchronization semantics as specified in the P0-2 spec,
even in xzarray_thaw() where accesses are under an exclusive write lock.

15 sites changed:
- xzarray_destroy: 1 load (line 424)
- xzarray_ensure_items: 1 store, 1 load (lines 457, 464)
- xzarray_store: 1 load, 1 store (lines 487, 489)
- xzarray_get_vec: 3 loads (lines 525, 529, 543)
- xzarray_put_vec: 1 load, 1 store (lines 591, 600)
- xzarray_clear: 1 load, 1 store (lines 624, 631)
- xzarray_iter_next: 1 load (line 654)
- xzarray_thaw: 2 loads, 1 store (lines 732, 743, 745)

All COW tests pass (test_freeze_thaw_slots_exist through
test_double_freeze_fails). Same 4 pre-existing failures expected.
2026-05-07 09:51:37 +00:00
4c591a11d3 feat: implement xzarray COW freeze/thaw with pre-grown shadow array (P0-2)
- Add in_cow flag + shadow/shadow_caps fields to xzarray_private_t
- Convert xzarray_item_t.inuse from bitfield to _Atomic _Bool; update
  all read/write sites to use atomic_load/store_explicit
- Implement xzarray_freeze(): O(1) ptr swap, pre-grows active array
  1.5x+16 to avoid realloc during COW, sets pos past shadow range
- Implement xzarray_thaw(): merges shadow into active (shadow wins only
  for slots not updated during COW), supports optional retain_fn GC
- xzarray_get_vec(): falls back to shadow during COW so reads remain
  valid between freeze and thaw
- Add COW guard assertion in xzarray_clear()
- Wire vfreeze/vthaw into xzarray_type interface
- Tests: 5 new COW tests all green; 4 pre-existing failures unchanged
2026-05-07 09:46:28 +00:00
0107828a77 fix: handle precision_ms >= 1000 in delay_worker nanosleep (tv_nsec overflow) 2026-05-07 09:12:10 +00:00
6d4d8eb81d feat: rename delaymgr precision→precision_ms, replace sleep() with nanosleep() (P0-2b)
- Rename `precision` field to `precision_ms` in delaymgr_t struct (delay.h)
- Replace `sleep(mgr->precision)` with `nanosleep()` for 10ms granularity (delay.c)
- Add `#include <time.h>` to delay.c (already in delay.h)
- Update delaymgr_init() to store precision_ms parameter
- Update all call sites: cluster.c (2→200ms), controller/testsuite/test.c (2→2000ms),
  lib/testsuite/test_delay.c (1→1000ms)
- Add TDD test test_delaymgr_precision_ms_field verifying field exists and stores correctly
- delay_task() delay parameter remains in seconds; only sleep granularity changes
2026-05-07 09:08:03 +00:00
c78f9cf91c feat: add vfreeze/vthaw slots to intf_varray_t; designated initializers (P0-0b)
rtti header already present at rtti-0.0.2 with vfreeze_fn_t, vthaw_fn_t,
retain_fn_t, and the two new slots in intf_varray_t. No header modification
needed.

Convert intf_varray_t compound-literal initializers in zarray.c and kvmgr.c
to designated style with explicit .vfreeze = NULL and .vthaw = NULL slots.
kv.c has no intf_varray_t initializer.

Add test_zarray_cow.c with test_freeze_thaw_slots_exist which verifies the
struct layout has the slots accessible and that they are NULL until P0-2
wires the real implementations. Wire test into meson.build and test.c.

Full clean rebuild confirmed: 27/28 targets linked, no new test failures.
Only the 4 pre-existing failures remain.
2026-05-07 08:54:18 +00:00
e0e2f56c81 feat: add persistence/HA system constants to config.h with tests (P0-A)
Adds 17 compile-time constants covering WAL format, HA topology,
snapshot sizing, and operational defaults. test_persistence_constants
in test_config.c validates every constant value; all 6 config tests
pass (4 pre-existing unrelated failures unchanged).
2026-05-07 08:41:19 +00:00
52d67726ec fix select eval ctx share 2026-05-07 04:37:18 +00:00
d7afd72e5b fix front end issues 2026-05-01 21:22:45 +08:00
177cf45af5 docs: mark Phase 6 complete in phase6-scheduler-integration-plan 2026-04-30 07:17:32 +00:00
1eb4eb1335 fix: use _Atomic flag for sel_alloc_thread_id registration, move reg before loop, add wrong-thread guard test 2026-04-30 06:15:01 +00:00
b722e5cb3d feat: add sel_alloc_thread_id ownership check in eval_select and gc_sweep (6-10) 2026-04-30 06:04:21 +00:00