feat(job-array-single-wal-record): build the compact API-server push
Adds apiserver_push_jobarray() (apiserver.c), mirroring task 5.1's xcesched_push_jobarray() exactly: same (base jobid, array_size, one spec) Data.jobArray derivation, same direct xce_request()/OPCode_push transport. apiserver_job_new_fun() branches on l->count > 1; the existing single-job path (apiserver_push_joblist()) is byte-for-byte unchanged. Additionally, and unlike the scheduler (single target), replicates apiserver_push_joblist()'s existing per-connected-server fan-out loop (N_APISERVER_MAX iteration, same lock discipline) -- verified against 2 simulated connected servers, each receiving exactly one push. New standalone apiserver_push_unit_test binary (same link-collision rationale as task 5.1's sched_push_unit_test), correctly omitting egress.c since apiserver_push_joblist() has no WAL-commit filter. Task 5.2 of job-array-single-wal-record (Requirements 6.2, 6.4).
This commit is contained in:
parent
5e7a4aa392
commit
e0cec75284
4 changed files with 449 additions and 2 deletions
|
|
@ -116,7 +116,7 @@
|
|||
- _Requirements: 6.1, 6.3, 6.4_
|
||||
- _Boundary: Compact Scheduler/API-Server Push (controller-side scheduler push)_
|
||||
|
||||
- [ ] 5.2 (P) Build the compact API-server push
|
||||
- [x] 5.2 (P) Build the compact API-server push
|
||||
- Same construction as 5.1, for the API-server push handler and its own existing per-job push
|
||||
- Observable: a synthetic multi-element notification produces exactly one compact push per connected API server, not N individually-encoded job structs; the single-element branch is unaffected
|
||||
- _Requirements: 6.2, 6.4_
|
||||
|
|
@ -179,3 +179,4 @@
|
|||
- **Task 4.1**: `persist_apply_pump_run()` now halts the walk (no `last_applied_lsn` advance) on ANY negative return from `apply_committed_entry()`, not just the new array type — this is a general fix. Investigated whether this newly exposes a hazard for the pre-existing job-state fact entries (`WAL_JOB_START/FINISH/FAIL/UPDATE`), which have their own real `-ENOENT` branch when the target jobid isn't in `c->jobmgr`: confirmed NOT a hazard, because `c->jobmgr` (`xkvmgr_type`) has no `key_del` (jobs are never removed for the process lifetime, corroborated by `job.c:428,455,645`), and this task's own strict sequential-no-skip apply means a fact's LSN is only reached after its causally-prior submit already applied. So a live `-ENOENT` on a fact can now only mean genuine corruption/divergence — halting is correct, not a new footgun. Independent reviewer confirmed this via `test_term_safety_adapter.c:2039-2043`'s synthetic (non-production-representative) trigger. design.md's "Existing Architecture Analysis" table row overclaims ("every existing WAL type... returns 0 or positive in practice") — technically inaccurate re: the fact-ENOENT branch — left as-is since the persist.c code comment at the site already documents the exception correctly; low-severity, non-blocking.
|
||||
- **Task 4.2**: New `_Atomic uint64_t apply_resource_fenced_lsn` field on `persist_t` (0 = clear, nonzero = the stuck LSN) plus `persist_apply_resource_fenced()`/`persist_mark_apply_resource_fenced()`/`persist_clear_apply_resource_fence()`, wired into `persist_apply_pump_run()`'s existing halt branch (engage) and unconditionally after every successful advance (clear). The unconditional clear is provably equivalent to "clear only on the fenced LSN's own successful retry" because `last_applied_lsn` has exactly 3 writers codebase-wide, the other two (`replay_wal`, `persist_recover`) are boot-only and temporally disjoint from the live pump, and a failed walk never advances past the stuck LSN — so the next success by construction IS that LSN's retry. **Known residual gap (out of this task's boundary, flagged by review, NOT yet fixed)**: `replay_wal()` (boot/restart replay, persist.c:3875-3888) still discards `apply_committed_entry()`'s failure return the same way the live pump did before task 4.1, and `persist_init()` unconditionally zeroes the fence field on every restart. A node that crashes while fenced and re-hits the identical resource-exhaustion condition during boot replay will silently pass it and look fully promotable while actually divergent — task 4.1/4.2's protection does not yet extend to the boot-replay path. This is a pre-existing gap (not introduced by this feature) but is now inconsistent with the live path's new guarantee; worth a dedicated follow-up task before anything downstream places full trust in this fence across a restart.
|
||||
- **Task 5.1**: New `xcesched_push_jobarray()` (`sched.c`) sends via direct `xce_request()`/`OPCode_push`/`Request_body_pushmsg` rather than the existing `xce_push_data_req()` helper — `xce_push_data_req()`'s dispatch switch hard-rejects (`-EINVAL`) anything outside jobs/workers/resources/resvals, so it cannot carry a single non-list `Data.jobArray` descriptor. design.md corrected to match (Dependencies + API Contract for the Compact Scheduler/API-Server Push component). Task 5.2's API-server push will hit the identical fork and should follow the same direct-`xce_request()` pattern rather than re-deriving it. One accepted, low-severity trade-off: the direct path does not go through `xce_push_data_req()`'s `checkf`-gated `check_controller`/`c->running` liveness check that every other push gets — a narrow shutdown-race behavioral difference from sibling pushes, not a new safety hazard; not fixed here since threading a liveness check into a single new call site duplicates rather than reuses the shared gate. `sched.c`/`dataex.c`/`egress.c` were never linked into `controller_unit_test` (two existing test files there ship their own fake `on_scheduler_join()`/`notify()`/`collect_confvars()` precisely because of this, and linking the real ones would collide) — task 5.1's tests live in a new standalone `sched_push_unit_test` binary instead; task 5.2 will likely face the same fork for `apiserver.c` and should check for an analogous existing-fakes collision before choosing a test-binary strategy.
|
||||
- **Task 5.2**: `apiserver_push_jobarray()` (`apiserver.c`) mirrors task 5.1's `xcesched_push_jobarray()` exactly — same field derivation, same direct-`xce_request()` transport, same `l->count > 1` discriminator — and additionally, correctly replicates `apiserver_push_joblist()`'s existing per-connected-server fan-out loop (`N_APISERVER_MAX` iteration, same lock discipline), independently verified against 2 simulated connected servers each receiving exactly one push. Confirmed the same `apiserver.c`/`dataex.c` link-collision task 5.1 hit for `sched.c` (a fake `notify()` already exists in `test_commit_barrier_reply.c`) — new standalone `apiserver_push_unit_test` binary added, correctly omitting `egress.c` since `apiserver_push_joblist()`, unlike `sched.c`'s push path, never references `job_submit_committed()`/`collect_jobs_committed()`. One low-severity, non-blocking nit (not fixed): the new test file's comment justifying its `apiserver_init()` idempotency guard is factually inaccurate — it claims a second `apiserver_init()` call would double-register hooks, but `xce_add_hook()` has deduped by `(fn,data)` since bug-845, so that specific claim is wrong; the guard itself is still correct/necessary for a different, real reason (avoids re-`mylock_init()`-ing an already-initialized mutex, UB).
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue