feat(ha-convergence-recovery): commit-gated scheduler egress — submit_lsn tagging + watermark gate (task 1.2)
This commit is contained in:
parent
7ff81156c2
commit
b9ddfa60cb
9 changed files with 241 additions and 67 deletions
|
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
* test_egress_filter.c — RED-phase tests for commit-gated scheduler egress
|
||||
* (ha-convergence-recovery task 1.1).
|
||||
* test_egress_filter.c — tests for commit-gated scheduler egress
|
||||
* (ha-convergence-recovery tasks 1.1 RED / 1.2 GREEN).
|
||||
*
|
||||
* Requirements (this spec's IDs):
|
||||
* 3.1 — a job whose submit entry is not yet committed is EXCLUDED from
|
||||
|
|
@ -15,15 +15,14 @@
|
|||
* status/API read path (plain collect_jobs is NOT commit-filtered).
|
||||
* submit_lsn == 0 is always exportable (pre-feature / unknown).
|
||||
*
|
||||
* RED CONTRACT: these tests are written against the task-1.2 GREEN behavior.
|
||||
* They FAIL today because:
|
||||
* - persist_job_hook_fun does not yet set j->submit_lsn (live tagging gap)
|
||||
* - apply_wal_entry passes 0 (not hdr->lsn) to restore_job, and restore_job
|
||||
* does not tag submit_lsn (replay tagging gap)
|
||||
* - job_submit_committed / collect_jobs_committed are not yet implemented in
|
||||
* dataex.c (the RED scaffold below is intentionally unfiltered — task 1.2
|
||||
* DELETES this scaffold and implements the real predicate + wrapper in
|
||||
* dataex.c per design.md "Egress Filter").
|
||||
* GREEN (task 1.2): the production tagging + watermark gate are now wired and
|
||||
* these tests exercise the REAL symbols:
|
||||
* - persist_job_hook_fun sets j->submit_lsn = written_lsn on live submit
|
||||
* - apply_wal_entry threads hdr->lsn to restore_job, which tags submit_lsn
|
||||
* - job_submit_committed / collect_jobs_committed live in egress.c (linked
|
||||
* into this binary) per design.md "Egress Filter" (D3 — factored out of
|
||||
* dataex.c so the unit test can link the real predicate without dataex.c's
|
||||
* notify()/collect_confvars() clashing with existing test stubs).
|
||||
*
|
||||
* Harness: a real persist_t over a private tmpdir embedded in a test
|
||||
* cluster_t with a registered jobmgr and persistence hooks, a wired commit
|
||||
|
|
@ -60,15 +59,19 @@
|
|||
#include "wal_types.h"
|
||||
#include "xlist.h"
|
||||
|
||||
/* ── Local job collector (avoids linking dataex.c into this unit binary) ──
|
||||
/* ── Status/API read-path collector (unfiltered — Req 3.4) ───────────────
|
||||
*
|
||||
* dataex.c is NOT in controller_unit_test_src (its notify()/collect_confvars()
|
||||
* clash with stubs already in test_commit_barrier_reply.c / test_worker_join.c).
|
||||
* collect_jobs / unlock_collect_jobs therefore are not linkable here, so this
|
||||
* file uses ef_collect_jobs — a faithful copy of collect_jobs' jobmgr iteration
|
||||
* (dataex.c:294) — for the unfiltered status/API path AND as the iteration core
|
||||
* of the RED scaffold below. The behavior asserted is identical to collect_jobs;
|
||||
* only the symbol is local. */
|
||||
* The real scheduler-bound predicate + wrapper (job_submit_committed,
|
||||
* collect_jobs_committed) now live in egress.c, which IS linked into this test
|
||||
* binary (controller_unit_test_src) — these tests exercise the REAL production
|
||||
* symbols. dataex.c (which owns plain collect_jobs / unlock_collect_jobs) is
|
||||
* still NOT linked here: its notify()/collect_confvars() definitions clash with
|
||||
* stubs already provided by test_commit_barrier_reply.c / test_worker_join.c.
|
||||
* For the unfiltered status-path assertion (3.4) this file therefore keeps a
|
||||
* faithful local copy of collect_jobs' iteration — ef_collect_jobs — and a
|
||||
* matching ef_unlock_jobs (reference/dereference only, no per-job lock, since
|
||||
* ef_collect_jobs does not lock). The behavior asserted is identical to
|
||||
* collect_jobs; only the symbol is local. */
|
||||
static void ef_collect_jobs(cluster_t *c, xlist_t *l)
|
||||
{
|
||||
iterator_t it;
|
||||
|
|
@ -103,35 +106,22 @@ static void ef_unlock_jobs(xlist_t *l)
|
|||
}
|
||||
}
|
||||
|
||||
/* ── RED scaffold: minimal, intentionally UNFILTERED stand-ins ───────────
|
||||
*
|
||||
* These satisfy the linker (job_submit_committed / collect_jobs_committed are
|
||||
* declared in cluster.h but not yet defined). They DO NOT implement the commit
|
||||
* watermark — job_submit_committed always returns true and collect_jobs_committed
|
||||
* collects every job. That makes the 3.1/3.2 exclusion assertions FAIL (the
|
||||
* uncommitted job leaks through), which is the intended RED state.
|
||||
*
|
||||
* Task 1.2 (GREEN) MUST delete this entire scaffold block and provide the real
|
||||
* definitions in dataex.c per design.md "Egress Filter":
|
||||
* job_submit_committed -> submit_lsn == 0 || submit_lsn <= committed_lsn
|
||||
* collect_jobs_committed -> collect_jobs filtered by that predicate
|
||||
* At that point dataex.c becomes part of the test link (with the notify/
|
||||
* collect_confvars stub clashes reconciled) and this scaffold is removed to
|
||||
* avoid duplicate symbols. */
|
||||
bool job_submit_committed(cluster_t *c, job_t *j)
|
||||
/* Release a list produced by the REAL collect_jobs_committed (egress.c): it
|
||||
* read-locks each collected job (identical contract to collect_jobs), so the
|
||||
* matching release must unlock AND dereference — mirror of the production
|
||||
* unlock_collect_jobs (dataex.c), which is not linkable in this binary. */
|
||||
static void ef_unlock_committed(xlist_t *l)
|
||||
{
|
||||
(void)c;
|
||||
(void)j;
|
||||
return true; /* RED: unfiltered — task 1.2 replaces with the watermark */
|
||||
for (int i = 0; i < l->count; i++) {
|
||||
xce_job_t *xj = (xce_job_t *)xlist_get(l, i);
|
||||
if (xj == NULL)
|
||||
continue;
|
||||
job_t *j = CONTAINER_OF(xj, job_t, job);
|
||||
unlock(j);
|
||||
dereference(j);
|
||||
}
|
||||
}
|
||||
|
||||
void collect_jobs_committed(cluster_t *c, collect_check_fn_t fn, xlist_t *l)
|
||||
{
|
||||
(void)fn; /* scheduler-bound callers pass NULL */
|
||||
ef_collect_jobs(c, l); /* RED: no commit filtering */
|
||||
}
|
||||
/* ── end RED scaffold ──────────────────────────────────────────────────── */
|
||||
|
||||
/* notify() is provided by test_commit_barrier_reply.c (same test binary) — do
|
||||
* not redefine it here (would be a duplicate symbol). */
|
||||
|
||||
|
|
@ -423,7 +413,7 @@ static void test_uncommitted_job_absent_from_scheduler_export(void)
|
|||
"uncommitted job (submit_lsn=9 > committed_lsn=5) leaked into "
|
||||
"scheduler export");
|
||||
|
||||
ef_unlock_jobs(&out);
|
||||
ef_unlock_committed(&out);
|
||||
xlist_fini(&out);
|
||||
ef_teardown();
|
||||
printf("test_uncommitted_job_absent_from_scheduler_export: checked\n");
|
||||
|
|
@ -449,7 +439,7 @@ static void test_job_present_after_commit_advances(void)
|
|||
TEST_ASSERT_EQUAL_INT(0, xlist_init(&before, 16));
|
||||
collect_jobs_committed(&g_c, NULL, &before);
|
||||
TEST_ASSERT_FALSE(ef_list_has_job(&before, 404));
|
||||
ef_unlock_jobs(&before);
|
||||
ef_unlock_committed(&before);
|
||||
xlist_fini(&before);
|
||||
|
||||
/* Advance committed_lsn to cover the submit entry. */
|
||||
|
|
@ -465,7 +455,7 @@ static void test_job_present_after_commit_advances(void)
|
|||
ef_list_has_job(&after, 404),
|
||||
"job absent from scheduler export after committed_lsn advanced to "
|
||||
"cover its submit entry");
|
||||
ef_unlock_jobs(&after);
|
||||
ef_unlock_committed(&after);
|
||||
xlist_fini(&after);
|
||||
|
||||
ef_teardown();
|
||||
|
|
@ -489,7 +479,7 @@ static void test_submit_lsn_zero_always_exportable(void)
|
|||
TEST_ASSERT_EQUAL_INT(0, xlist_init(&out, 16));
|
||||
collect_jobs_committed(&g_c, NULL, &out);
|
||||
TEST_ASSERT_TRUE(ef_list_has_job(&out, 505));
|
||||
ef_unlock_jobs(&out);
|
||||
ef_unlock_committed(&out);
|
||||
xlist_fini(&out);
|
||||
|
||||
ef_teardown();
|
||||
|
|
@ -526,7 +516,7 @@ static void test_uncommitted_job_visible_to_status_path(void)
|
|||
|
||||
void test_egress_filter(void)
|
||||
{
|
||||
printf("\n>> commit-gated scheduler egress tests (RED, task 1.1)\n\n");
|
||||
printf("\n>> commit-gated scheduler egress tests (GREEN, task 1.2)\n\n");
|
||||
|
||||
RUN_TEST(test_live_submit_tags_submit_lsn);
|
||||
RUN_TEST(test_replay_restore_tags_entry_lsn);
|
||||
|
|
|
|||
|
|
@ -28,9 +28,9 @@ typedef struct cluster_s cluster_t;
|
|||
|
||||
__attribute__((weak))
|
||||
int restore_job(cluster_t* c, uint64_t jobid,
|
||||
const void* capnp_payload, size_t len)
|
||||
const void* capnp_payload, size_t len, uint64_t entry_lsn)
|
||||
{
|
||||
(void)c; (void)jobid; (void)capnp_payload; (void)len;
|
||||
(void)c; (void)jobid; (void)capnp_payload; (void)len; (void)entry_lsn;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue