Commit graph

130 commits

Author SHA1 Message Date
f5b0ac0af3 feat: ha_should_redirect + join.c HA redirect/nomaster guard (P1-6)
Add redirect helpers to ha.c/ha.h so a standby controller never accepts
join requests as if it were the primary:

- ha_should_redirect(): returns 1 when role is STANDBY and primary_host/port
  are known — caller should send E_XCE_REDIRECT.
- ha_no_primary_known(): returns 1 when role is STANDBY but primary address
  is not yet known — caller should send E_XCE_NOMASTER (retry later).
- Add E_XCE_NOMASTER and E_XCE_REDIRECT to errnox.h.
- Wire both guards into handle_join_() in join.c (before the role switch)
  so all join types (worker, scheduler, apiserver, client) are protected.
- Full redirect response sends status=-E_XCE_REDIRECT with nodata body;
  the primary host:port are logged so operators can trace misrouted clients.
  Full Cap'n Proto RedirectInfo response body is deferred to a follow-up
  (schema union would require regenerating capnp bindings).

5 new TDD tests — 110 total, 0 failures.
2026-05-07 15:11:53 +00:00
c627f69d50 feat: wire ha_state_t into cluster_t + HA startup/shutdown lifecycle (P1-5) 2026-05-07 14:55:48 +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
b52fbef36d feat: wire persist_init/recover/start/stop into cluster lifecycle (P0-F)
- Add `persist_t persist` field to cluster_t (cluster_def.h includes persist.h)
- Add `workdir` and `name` fields to myconf_t; main.c passes them through
- cluster_init: call persist_set_workdir, persist_init, persist_recover after apiserver_init
- cluster_start: call persist_start (launches fsync thread + registers WAL hooks)
- cluster_fini: call persist_stop + persist_fini before delaymgr/vtaskmgr teardown
- persist_init: store cluster back-pointer (replaces (void)c stub)
- persist_start: register persist_job/queue/worker_hook_fun via xce_add_hook when cluster != NULL
- Add test_cluster_persist.c: compile-time struct check + config defaults + workdir path derivation
- Register test in controller_test.c and meson.build

All controller tests pass (1/1 OK, 50s).
2026-05-07 13:07:47 +00:00
3030012f51 feat: JOB_STAT_TRANSIENT, job_should_retain, worker_should_retain, dead_since (P0-5a, P0-8, P0-13)
Add terminal-state constants and GC retention predicates for the
checkpoint xzarray_thaw() GC pass:

- types.h: add JOB_STAT_TRANSIENT (0x400), XCE_WORKER_HIDDEN (0x00200000),
  QUEUE_STAT_HIDDEN (0x0100)
- persist.c: implement job_is_terminal(), job_should_retain(),
  worker_should_retain() — placed here so the unit test binary
  (which links only persist.c) can access them without RTTI deps
- persist.h: declare the three GC predicates; include xce/types.h
- worker.c: set dead_since = time(NULL) in mark_worker_unavail()
  when dead_since is not already set
- cluster.c: add TODO P0-8b marker on zarray_del endpoint cleanup
- meson.build: register test_gc_retention.c; raise test timeout 60→120s
- testsuite/test_gc_retention.c: 19 tests covering job_is_terminal,
  job_should_retain (TRANSIENT TTL, terminal TTL, non-terminal always
  retained), and worker_should_retain (HIDDEN/UNAVAIL/healthy/zero
  dead_since); all pass (63 total, 0 failures)
2026-05-07 12:52:07 +00:00
6f3b98c6b5 feat: persistence hook functions wired to WAL write on AFTER_EVENT (P0-6)
Implements persist_job_hook_fun, persist_queue_hook_fun, and
persist_worker_hook_fun — AFTER_EVENT hooks that write WAL entries
whenever state mutations occur in the controller.

- detail_to_wal_type() maps all 15 HOOK_DETAIL_* values to WAL types;
  unknown details return 0 and are silently skipped
- HOOK_DETAIL_JOB_NEW uses the sync replication path (WAL_JOB_SUBMIT);
  all others use persist_wal_async
- P0 payload: 4-byte detail value; full Cap'n Proto serialization in Task 15
- Hook signature matches hook_fn_t exactly:
  int fn(int type, int situation, int detail, void* cls, va_list ap)
- Added <stdarg.h> to persist.h for va_list; added hook.h and
  xce/cluster_def.h includes to persist.c
- 18 new unit tests in test_persist_hooks.c covering all 15 detail→WAL
  mappings, skip-on-unknown, and sequential LSN advancement
2026-05-07 12:28:20 +00:00
045d9e3287 feat: add HOOK_DETAIL_JOB_STOP/RESUME; fix xce_reply ordering after AFTER_EVENT hooks (P0-4a)
- cluster_def.h: add struct tag `cluster_s` to cluster_t to resolve
  conflicting typedef with persist.h forward declaration
- cluster_def.h: add HOOK_DETAIL_JOB_STOP and HOOK_DETAIL_JOB_RESUME enum
  values after HOOK_DETAIL_JOB_RUSAGE_UPDATE
- job.c on_job_stop(): replace mark_job_update() call with direct
  xce_run_hooks(BEFORE/AFTER, HOOK_DETAIL_JOB_STOP) so WAL writer
  records stop events with the correct detail value
- job.c on_job_resume(): same pattern with HOOK_DETAIL_JOB_RESUME
- job.c do_clr_preason(): fix typo — second hook call was HOOK_BEFORE_EVENT
  instead of HOOK_AFTER_EVENT (double-before, zero-after bug)
- join.c handle_worker_join(): move xce_reply() to after HOOK_AFTER_EVENT
  so client ACK is not sent before WAL write completes (durability fix)
- testsuite/test_hooks.c: new TDD tests for JOB_STOP/RESUME enum values
- testsuite/controller_test.c + meson.build: register test_hooks suite

Reply ordering audit results:
  job.c handle_jobsubmit: reply before AFTER_EVENT — intentional (jobid
    returned to client before scheduler notification; WAL hook belongs in
    BEFORE_EVENT for this path, handled in Task 13)
  job.c do_set_preason: reply before set_preason() loop — intentional
    fire-and-forget (no AFTER_EVENT hook at top level)
  job.c do_clr_preason: reply before loop — same fire-and-forget pattern
  job.c do_jobcontrol_op: no AFTER_EVENT hook, reply only — OK
  join.c handle_worker_join: FIXED (reply moved after AFTER_EVENT)
  worker.c on_worker_update/dead: reply after all hooks — OK
  cluster.c: AFTER_EVENT hooks are internal (no reply) — OK
2026-05-07 12:08:52 +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
4903c83f28 feat: add PersistConfig, SnapshotHeader, ReplEnvelope Cap'n Proto schemas (P0-9)
Extends src/schema/controller.capnp with persistence structures needed
for WAL replication and snapshots:

- PersistConfig (8 fields, @0-@7): capnp-serialisable copy of the
  persistence configuration; mapped to capnp_persist_cfg_t to avoid
  collision with the plain-C persist_config_t in persist.h
- SnapshotHeader (9 fields, @0-@8): snapshot manifest with LSN, term,
  timestamp, record counts and checksums; mapped to capnp_snap_hdr_t
- ReplEnvelope (6 fields, @0-@5): replication wire envelope (NOT the
  on-disk format); named to avoid collision with wal_entry_hdr_t and
  the WALEntry_ internal symbol c-capnproto generates; mapped to
  capnp_repl_envelope_t

Also extends existing structs:
- Job.transientSince @11 (UInt64=0, mapname transient_since): set when
  job enters JOB_STAT_TRANSIENT; also adds uint64_t transient_since to
  xce_job_t in types.h
- Worker.deadSince @6 (UInt64=0, mapname dead_since): mirrors existing
  time_t dead_since field in xce_worker_t

OPCode enum extended with 7 replication opcodes @22-@28:
  replEntry, replAck, redirect, heartbeat, steppingDown,
  replSnapshot, replCatchUpDone

New C typedefs (capnp_persist_cfg_t, capnp_snap_hdr_t,
capnp_repl_envelope_t) added to typemap.h for the encode/decode shims.

TDD: test_persist_schema.c added (6 tests, all GREEN). Controller test
suite remains 1/1 PASS.
2026-05-07 10:19:33 +00:00
1fe79ff934 fix: check rotate_segment return value; use uint32_t for CRC fields
- In persist_wal_async(), check rotate_segment() return value and propagate -EIO on failure (MEDIUM)
- Change last_crc and last_replicated_crc fields from uint64_t to uint32_t to match CRC32 width (LOW)

Both fixes address code review issues in commit b0a5fbb.
2026-05-07 10:04:43 +00:00
b0a5fbb4e9 feat: persist.c WAL writer with chained CRC32 and dedicated fsync pthread (P0-1)
- Expose persist_t struct concretely in persist.h (opaque pattern deferred)
- persist_set_workdir() derives wal/snap paths from workdir+name
- persist_init() creates WAL/snap directories, rejects NFS mounts
- persist_wal_async() writes WAL entries with chained CRC32 via zlib
- Dedicated fsync pthread fires every wal_sync_ms milliseconds
- persist_flush() forces fdatasync for tests and clean shutdown
- WAL_CHECKPOINT type rejected with -EINVAL (never written via async path)
- Stubs: persist_recover, persist_wal_sync_append, persist_checkpoint
- Add fsync_thread_running flag to guard persist_stop when thread not started
- Add zlib_dep to controller and unit test meson targets
- 3 new TDD tests: init_creates_directories, wal_async_writes_entry,
  checkpoint_type_rejected — all GREEN
2026-05-07 10:00:10 +00:00
fe69dcbd7a feat: add persist.h — WAL types, persist_config_t, slot_key_t (P0-B)
- Define wal_entry_hdr_t (40 bytes, _Static_assert enforced)
- Define wal_entry_type enum (WAL_JOB_SUBMIT=1 … WAL_CHECKPOINT=100)
- Define persist_config_t with persist_config_defaults() inline helper
- Define slot_key_t (index, key[XCE_MAXNAME_LEN], key_len)
- Forward-declare persist_t opaque struct and full persist_* API surface
- Bootstrap controller Unity test suite (controller_unit_test binary)
- Register test('controller test', …, suite: 'controller') in meson
- 4 new tests all PASS: wal_header_size, wal_entry_types,
  persist_config_defaults, slot_key_t_fields
2026-05-07 09:21:36 +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
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
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
4e177047c9 fix: correct SEL_NUM bit-cast in gc test; assert n1 swept; add recompact to protocol comment 2026-04-30 03:55:15 +00:00
9a19a8f932 feat: export gc_clear_marks + gc_mark_select_tree; remove clear from gc_sweep (phase 6)
- Add gc_clear_marks(): iterates intern table and zeroes all gc_mark bits
- Rename static gc_mark_tree → public gc_mark_select_tree (no attribute((unused)))
- Remove Phase 6 guard (live_jobs != NULL early return) from gc_sweep_select_nodes
- Remove the mark-clear loop from gc_sweep_select_nodes; add (void) casts for now-unused params
- Declare gc_clear_marks and gc_mark_select_tree in resreq.h with protocol comment
- Add TDD test test_gc_clear_marks_exported verifying the exported primitives
- Prefix all existing gc_sweep_select_nodes(NULL, 0) call sites in test_select.c with gc_clear_marks()
2026-04-30 03:40:24 +00:00
b38babb7d6 feat: implement Phase 4d const-fold transform for select AST
Adds sel_const_fold_pass() — a single-pass bottom-up const-fold that
runs after the Phase 4c flatten loop in parse_select():

  4d-1  EQ/NE with either operand SEL_ANY → SEL_TRUE / SEL_FALSE
        (checks both child slots — commutativity sort makes SEL_ANY
        position non-deterministic after makeSelectNode interning)
  4d-2  Identity laws: TRUE&&X→X, X&&TRUE→X, FALSE||X→X, X||FALSE→X
  4d-3  Annihilation laws: FALSE&&X→FALSE, TRUE||X→TRUE (symmetric)
  4d-4  Complement laws: !TRUE→FALSE, !FALSE→TRUE

Memory semantics:
  level==0 (API server): frees orphaned nodes via free_select_node /
    sel_free_tree; OOM from makeSelectNode returns original node safely
  level>0  (CSE/scheduler): never manually frees; annihilation folds
    return the already-interned left/right singleton directly (no
    redundant makeSelectNode call); OOM guard unconditional so CSE
    mode also degrades gracefully instead of returning NULL

9 new TDD tests added to test_select.c (RED→GREEN verified).
All 166 tests pass (157 prior + 9 Phase 4d).
2026-04-29 08:24:04 +00:00
dcb1b4fe25 feat: P0-2a C-side plumbing for host control
Add the full call chain for host control operations from the Go API
server through the C library to the controller:

- schema/controller.capnp: HostControl struct + hostctrlmsg @21
  in Request.body union
- include/typemap.h: xce_hostctrl_t* hostctrlmsg in body union
  (placed after @20 clrpreasonmsg, matching capnp ordinal)
- include/client.h, xceapi.h, request.h: declarations for
  xcecli_hostctrl, xce_hostctrl, xce_hostctrl_req
- lib/client.c: xcecli_hostctrl (validate + delegate to xce_hostctrl)
- lib/xceapi.c: xce_hostctrl (validate ctx state + send + wait reply)
- lib/request.c: xce_hostctrl_req (validate hostname/empty/nil,
  accept only HOST_OPEN/HOST_CLOSE, reject HOST_TAG/UNTAG)
- lib/helper.c: Request_body_hostctrlmsg cases in
  xce_request_body() and xce_request_msg_name()
- controller/cluster.c: OPCode_hostcontrol dispatch
- controller/include/cluster.h: do_hostcontrol_op declaration
- controller/worker.c: do_hostcontrol_op + fix pre-existing
  goto-clean bug in worker_ctrl that leaked write lock and
  reference on HOST_OPEN/HOST_CLOSE no-op paths

TDD: 8 xce_hostctrl_req validation tests (null ep, null ctrl,
null/empty hostname, HOST_DO_NONE no-op, HOST_TAG/UNTAG reject,
negative action reject, HOST_OPEN/CLOSE accept via xce_request).

116/116 xcelib tests pass. CGO_ENABLED=0 Go backend tests pass.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-17 07:46:41 +00:00
43dc12a36c save works 2026-03-06 18:20:30 +08:00
3f74f14687 save works 2026-03-04 18:18:53 +08:00
acfbec875b save works 2026-03-03 16:49:26 +08:00
f4b1ba4cd7 save works 2026-03-02 12:14:30 +08:00
b748e30d20 save works 2026-02-28 17:55:50 +08:00
598d381379 improve xkvmgr and testsuite for scheduler 2026-02-26 14:39:12 +08:00
2ee87d4ee6 use librtti for worker/queue/resource 2026-02-25 16:56:50 +08:00
532c68e6e1 - add implement job related objs based on librtti
- add lock()/unlock() to job object
2026-02-23 11:18:45 +08:00
f582742e77 add xzarray_t for replacement of zarray_t finally 2026-02-20 11:25:50 +08:00
ce69347581 - implement xstring by librtti 2026-02-14 14:38:52 +08:00
15fac3cda1 introduce kv 2026-02-13 17:05:41 +08:00
48ea4a0205 - introduce bitset
- add hash_block()
2026-02-13 11:01:33 +08:00
12dcc2a60d - save tmp works
- save works
2026-02-06 17:31:02 +08:00
cd4f3953b2 - use librtti
- Fix issue to xce_get() api cause client side crash
- add parse resreq section
- add rusage parser
- parse resreq with basic support alternative & compound, still miss select
2026-02-05 17:21:27 +08:00
e7dc09c604 fix memory leak in controller and use lock protect apiserver data structure 2026-01-26 15:35:45 +08:00
9e07a1b227 improve api server support 2026-01-23 17:33:10 +08:00
3b728e5135 improve support of api server in controller 2026-01-23 16:05:23 +08:00
44fc5b23e9 push more to api server 2026-01-22 17:23:18 +08:00
00d161aa11 add more helper functions more example code 2026-01-21 16:34:04 +08:00
70970434ca initial work of api server 2026-01-20 18:20:24 +08:00
924bc1517b using xvec related api in scheduler 2026-01-16 18:21:37 +08:00
06209930f9 improve 2026-01-15 17:56:53 +08:00
ecf1916d78 support limit 2026-01-12 15:27:54 +08:00
bef32ace38 1st workable scheduler with resource from controller 2025-12-31 15:50:52 +08:00
780e9fb313 scheduling using real slots number 2025-12-30 18:13:15 +08:00
a27c8bfe6e enhance zarray library function 2025-12-26 16:23:04 +08:00
ae658c4a23 working of scheduler to handle configuration 2025-12-26 15:22:21 +08:00
a46ea822c1 working on scheduler and fix issue #17 2025-12-25 16:24:56 +08:00