feat(scheduler-dispatch-commit): add dispatch-fail retry counter to job_t (task 1.3)
Add uint32_t dispatch_fail_count to the job_t wrapper struct (xce/types.h, alongside submit_lsn, same in-memory-only/reset-on-failover precedent), per Requirement 4.3. Data model only -- xce_job_t (wire type) untouched, job.c untouched; increment/reset wiring and the bounded-retry threshold check are tasks 3.1/3.3. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
parent
8eea74909c
commit
19bbac6ae0
5 changed files with 111 additions and 1 deletions
|
|
@ -15,7 +15,7 @@
|
||||||
- _Requirements: 2.4_
|
- _Requirements: 2.4_
|
||||||
- _Boundary: WAL Dispatch Fact_
|
- _Boundary: WAL Dispatch Fact_
|
||||||
|
|
||||||
- [ ] 1.3 (P) Track how many consecutive times a job's dispatch has failed for a retryable reason
|
- [x] 1.3 (P) Track how many consecutive times a job's dispatch has failed for a retryable reason
|
||||||
- Add an in-memory counter on the controller's job record that increments on each retryable dispatch failure and resets to zero on a successful dispatch
|
- Add an in-memory counter on the controller's job record that increments on each retryable dispatch failure and resets to zero on a successful dispatch
|
||||||
- This counter does not need to survive a controller failover — resetting to zero after a failover is an acceptable, safe degradation
|
- This counter does not need to survive a controller failover — resetting to zero after a failover is an acceptable, safe degradation
|
||||||
- Observable completion: the counter can be incremented, read, and reset independently of any actual dispatch attempt
|
- Observable completion: the counter can be incremented, read, and reset independently of any actual dispatch attempt
|
||||||
|
|
|
||||||
|
|
@ -170,6 +170,7 @@ controller_unit_test_src = [
|
||||||
'testsuite/test_apply_committed_entry.c',
|
'testsuite/test_apply_committed_entry.c',
|
||||||
'testsuite/test_replay_apply_seam.c',
|
'testsuite/test_replay_apply_seam.c',
|
||||||
'testsuite/test_wal_job_dispatch_fact.c',
|
'testsuite/test_wal_job_dispatch_fact.c',
|
||||||
|
'testsuite/test_job_dispatch_fail_count.c',
|
||||||
'testsuite/test_apply_reapply_idempotency.c',
|
'testsuite/test_apply_reapply_idempotency.c',
|
||||||
'testsuite/test_apply_correctness_verification.c',
|
'testsuite/test_apply_correctness_verification.c',
|
||||||
'testsuite/test_apply_pump.c',
|
'testsuite/test_apply_pump.c',
|
||||||
|
|
|
||||||
|
|
@ -84,6 +84,7 @@ extern void test_apply_seam(void);
|
||||||
extern void test_apply_committed_entry(void);
|
extern void test_apply_committed_entry(void);
|
||||||
extern void test_replay_apply_seam(void);
|
extern void test_replay_apply_seam(void);
|
||||||
extern void test_wal_job_dispatch_fact(void);
|
extern void test_wal_job_dispatch_fact(void);
|
||||||
|
extern void test_job_dispatch_fail_count(void);
|
||||||
extern void test_apply_reapply_idempotency(void);
|
extern void test_apply_reapply_idempotency(void);
|
||||||
extern void test_apply_correctness_verification(void);
|
extern void test_apply_correctness_verification(void);
|
||||||
extern void test_apply_pump(void);
|
extern void test_apply_pump(void);
|
||||||
|
|
@ -249,6 +250,7 @@ int main(int argc, char* argv[])
|
||||||
TEST(test_apply_committed_entry);
|
TEST(test_apply_committed_entry);
|
||||||
TEST(test_replay_apply_seam);
|
TEST(test_replay_apply_seam);
|
||||||
TEST(test_wal_job_dispatch_fact);
|
TEST(test_wal_job_dispatch_fact);
|
||||||
|
TEST(test_job_dispatch_fail_count);
|
||||||
TEST(test_apply_reapply_idempotency);
|
TEST(test_apply_reapply_idempotency);
|
||||||
TEST(test_apply_correctness_verification);
|
TEST(test_apply_correctness_verification);
|
||||||
TEST(test_apply_pump);
|
TEST(test_apply_pump);
|
||||||
|
|
|
||||||
98
src/controller/testsuite/test_job_dispatch_fail_count.c
Normal file
98
src/controller/testsuite/test_job_dispatch_fail_count.c
Normal file
|
|
@ -0,0 +1,98 @@
|
||||||
|
/* test_job_dispatch_fail_count.c — scheduler-dispatch-commit task 1.3
|
||||||
|
*
|
||||||
|
* Proves job_t.dispatch_fail_count (xce/types.h) -- a new in-memory-only
|
||||||
|
* uint32_t counter on the job_t wrapper ({xce_job_t job; uint64_t
|
||||||
|
* submit_lsn;}), NOT on xce_job_t itself -- exists and behaves as a plain,
|
||||||
|
* directly settable/readable/resettable scalar (Requirement 4.3, design.md
|
||||||
|
* "Logical Data Model": "in-memory only... following submit_lsn's exact
|
||||||
|
* precedent... recomputed... never serialized").
|
||||||
|
*
|
||||||
|
* This task is the data-model addition ONLY. It does NOT wire any
|
||||||
|
* increment/reset call site into dispatch_a_job_() (task 3.1/3.3's job) and
|
||||||
|
* does NOT implement the bounded-retry threshold check (also task 3.3).
|
||||||
|
* Every test here manipulates the field directly on a bare stack-local
|
||||||
|
* job_t -- no jobmgr/RTTI/makeJob machinery -- mirroring
|
||||||
|
* test_persist_types.c's test_slot_key_t_fields precedent for a plain
|
||||||
|
* struct-field addition, since this is not a behavioral/dispatch-path test.
|
||||||
|
*/
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include <unity/unity.h>
|
||||||
|
|
||||||
|
#include "xce/types.h"
|
||||||
|
|
||||||
|
static void test_dispatch_fail_count_default_zero(void)
|
||||||
|
{
|
||||||
|
job_t j;
|
||||||
|
memset(&j, 0, sizeof(j));
|
||||||
|
|
||||||
|
TEST_ASSERT_EQUAL_UINT32(0, j.dispatch_fail_count);
|
||||||
|
printf("test_dispatch_fail_count_default_zero: PASS\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_dispatch_fail_count_increments_independently(void)
|
||||||
|
{
|
||||||
|
job_t j;
|
||||||
|
memset(&j, 0, sizeof(j));
|
||||||
|
|
||||||
|
/* Simulates 3 consecutive retryable dispatch failures with no
|
||||||
|
* intervening successful dispatch (Requirement 4.3) -- incremented
|
||||||
|
* directly, with no actual dispatch attempt involved. */
|
||||||
|
j.dispatch_fail_count++;
|
||||||
|
TEST_ASSERT_EQUAL_UINT32(1, j.dispatch_fail_count);
|
||||||
|
|
||||||
|
j.dispatch_fail_count++;
|
||||||
|
j.dispatch_fail_count++;
|
||||||
|
TEST_ASSERT_EQUAL_UINT32_MESSAGE(3, j.dispatch_fail_count,
|
||||||
|
"counter must be incrementable directly, independent of any actual "
|
||||||
|
"dispatch attempt (Observable completion)");
|
||||||
|
printf("test_dispatch_fail_count_increments_independently: PASS\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_dispatch_fail_count_resets_to_zero(void)
|
||||||
|
{
|
||||||
|
job_t j;
|
||||||
|
memset(&j, 0, sizeof(j));
|
||||||
|
|
||||||
|
j.dispatch_fail_count = 7;
|
||||||
|
TEST_ASSERT_EQUAL_UINT32(7, j.dispatch_fail_count);
|
||||||
|
|
||||||
|
/* A successful dispatch resets the counter to zero -- task 3.1/3.3 wire
|
||||||
|
* the real call site; this proves the field itself supports the
|
||||||
|
* reset-to-zero operation the design requires, independent of any
|
||||||
|
* actual dispatch attempt. */
|
||||||
|
j.dispatch_fail_count = 0;
|
||||||
|
TEST_ASSERT_EQUAL_UINT32_MESSAGE(0, j.dispatch_fail_count,
|
||||||
|
"counter must be independently resettable to zero, matching "
|
||||||
|
"submit_lsn's in-memory-only precedent (no durability obligation, "
|
||||||
|
"safe to lose across a controller failover)");
|
||||||
|
printf("test_dispatch_fail_count_resets_to_zero: PASS\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_dispatch_fail_count_does_not_alias_submit_lsn(void)
|
||||||
|
{
|
||||||
|
job_t j;
|
||||||
|
memset(&j, 0, sizeof(j));
|
||||||
|
|
||||||
|
j.submit_lsn = 12345;
|
||||||
|
j.dispatch_fail_count = 6;
|
||||||
|
|
||||||
|
TEST_ASSERT_EQUAL_UINT64(12345, j.submit_lsn);
|
||||||
|
TEST_ASSERT_EQUAL_UINT32_MESSAGE(6, j.dispatch_fail_count,
|
||||||
|
"dispatch_fail_count must be its own field on job_t, distinct from "
|
||||||
|
"submit_lsn (both in-memory-only siblings on the wrapper struct)");
|
||||||
|
printf("test_dispatch_fail_count_does_not_alias_submit_lsn: PASS\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_job_dispatch_fail_count(void)
|
||||||
|
{
|
||||||
|
UnitySetTestFile(__FILE__);
|
||||||
|
printf("\n>> job_t.dispatch_fail_count test\n\n");
|
||||||
|
|
||||||
|
RUN_TEST(test_dispatch_fail_count_default_zero);
|
||||||
|
RUN_TEST(test_dispatch_fail_count_increments_independently);
|
||||||
|
RUN_TEST(test_dispatch_fail_count_resets_to_zero);
|
||||||
|
RUN_TEST(test_dispatch_fail_count_does_not_alias_submit_lsn);
|
||||||
|
}
|
||||||
|
|
@ -487,6 +487,15 @@ typedef struct {
|
||||||
* serialized (wire type xce_job_t above is untouched). 0 = unknown /
|
* serialized (wire type xce_job_t above is untouched). 0 = unknown /
|
||||||
* pre-feature = always exportable (see job_submit_committed, cluster.h). */
|
* pre-feature = always exportable (see job_submit_committed, cluster.h). */
|
||||||
uint64_t submit_lsn;
|
uint64_t submit_lsn;
|
||||||
|
/* scheduler-dispatch-commit (1.3): consecutive retryable dispatch-failure
|
||||||
|
* count for this job, with no intervening successful dispatch. In-memory
|
||||||
|
* only, following submit_lsn's exact precedent above — never serialized
|
||||||
|
* (wire type xce_job_t above is untouched), reset to 0 by a successful
|
||||||
|
* dispatch. Resetting to 0 after a controller failover is an acceptable,
|
||||||
|
* safe degradation (more retries allowed, never fewer, never a
|
||||||
|
* misrouted terminal/retry decision) — see Controller Dispatch-Failure
|
||||||
|
* Classification, design.md. */
|
||||||
|
uint32_t dispatch_fail_count;
|
||||||
} job_t;
|
} job_t;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue