fix(controller): stop a failed test from hanging the unit suite (bug-1429); mkstemp for upgrade temp file (bug-1861)
A failing TEST_ASSERT longjmps out and skips the fixture teardown, leaving the commit engine's sweep thread and persist's fsync_worker asleep on a static struct. The next test re-inits it underneath them, corrupting the condvar, and a later teardown blocks in pthread_cond_destroy/join until the 300s meson timeout. The failure line itself was lost because stdout was fully buffered. - controller_test: line-buffer stdout; tearDown() reaps what a failed test leaked via new XCE_TESTING registries (persist_test_reap_unstopped, then commit_test_reap_unfinalized_engines -- persist_stop before engine fini). Clean run reaps 0; an injected failure now fails in 2s instead of hanging. - persist: flusher_flush_once reports only its own durable advance, so fsync_worker no longer re-fires the durable callback for an advance a concurrent persist_flush made (flaky "must not re-fire" test). Regression test test_flusher_pass_does_not_claim_concurrent_flush_advance. - tests: jap observer is atomic and waits (bounded) for a continuation that may resolve on the flusher thread; pump catch-up tests wait for trailing replies instead of a single scan. - config_repl: upgrade decode temp file via mkstemp instead of a pthread_self()-derived /tmp name (same value across processes, opened without O_EXCL -- collisions and a symlink target). Verified: controller_unit_test 1222/1222; 24 parallel runs (6x4), no hangs or ASAN errors. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
44c1981cff
commit
b5d59253e5
9 changed files with 316 additions and 23 deletions
|
|
@ -2032,9 +2032,9 @@ static int jap_read_wal_entry_at(persist_t *p, off_t at,
|
|||
|
||||
/* Notify-callback capture (persist_job_commit_notify_fn). */
|
||||
typedef struct {
|
||||
int call_count;
|
||||
uint64_t last_lsn;
|
||||
int last_rc;
|
||||
atomic_int call_count;
|
||||
uint64_t last_lsn;
|
||||
int last_rc;
|
||||
} jap_notify_obs_t;
|
||||
|
||||
static jap_notify_obs_t g_jap_obs;
|
||||
|
|
@ -2044,12 +2044,34 @@ static void jap_notify_reset(void)
|
|||
memset(&g_jap_obs, 0, sizeof(g_jap_obs));
|
||||
}
|
||||
|
||||
/* bug-1429: the continuation may resolve on the fsync_worker thread (its
|
||||
* durable-advance callback re-evaluates commit and can claim the
|
||||
* continuation between our ACK being recorded and our own resolve), so
|
||||
* the count is published with release after last_lsn/last_rc. */
|
||||
static void jap_notify_fn(void *ctx, uint64_t lsn, int rc)
|
||||
{
|
||||
jap_notify_obs_t *obs = (jap_notify_obs_t *)ctx;
|
||||
obs->call_count++;
|
||||
obs->last_lsn = lsn;
|
||||
obs->last_rc = rc;
|
||||
atomic_fetch_add_explicit(&obs->call_count, 1, memory_order_release);
|
||||
}
|
||||
|
||||
/* Wait (bounded) for the continuation count to reach `want`: after the
|
||||
* covering ACK, resolution is guaranteed but may be running on the
|
||||
* flusher thread. Returns the count seen. */
|
||||
static int jap_wait_calls(int want)
|
||||
{
|
||||
int i, n = 0;
|
||||
|
||||
for (i = 0; i < 2000; i++) { /* <= 2s */
|
||||
n = atomic_load_explicit(&g_jap_obs.call_count, memory_order_acquire);
|
||||
if (n >= want)
|
||||
break;
|
||||
usleep(1000);
|
||||
}
|
||||
/* let a (wrongly) duplicate resolution surface before asserting == */
|
||||
usleep(2000);
|
||||
return atomic_load_explicit(&g_jap_obs.call_count, memory_order_acquire);
|
||||
}
|
||||
|
||||
/* ── One WAL append, one commit continuation, decodable back to the same
|
||||
|
|
@ -2138,7 +2160,7 @@ static void test_job_array_submit_appends_exactly_one_wal_entry_and_one_continua
|
|||
* resolves the single registered continuation */
|
||||
TEST_ASSERT_EQUAL_INT(0, persist_flush(&g_jap));
|
||||
repl_on_ack(&g_jap.repl, &g_jaep_a, out_lsn);
|
||||
TEST_ASSERT_EQUAL_INT_MESSAGE(1, g_jap_obs.call_count,
|
||||
TEST_ASSERT_EQUAL_INT_MESSAGE(1, jap_wait_calls(1),
|
||||
"the notify callback must fire EXACTLY ONCE for the whole array, "
|
||||
"not once per element");
|
||||
TEST_ASSERT_EQUAL_UINT64(out_lsn, g_jap_obs.last_lsn);
|
||||
|
|
@ -2288,7 +2310,7 @@ static void test_job_array_submit_one_entry_one_continuation_across_sizes(void)
|
|||
"array_size=%llu: exactly ONE commit continuation must "
|
||||
"resolve for the whole array, not one per element",
|
||||
(unsigned long long)array_size);
|
||||
TEST_ASSERT_EQUAL_INT_MESSAGE(1, g_jap_obs.call_count, msg);
|
||||
TEST_ASSERT_EQUAL_INT_MESSAGE(1, jap_wait_calls(1), msg);
|
||||
TEST_ASSERT_EQUAL_INT_MESSAGE(0, g_jap_obs.last_rc, msg);
|
||||
}
|
||||
|
||||
|
|
@ -2450,7 +2472,7 @@ static void test_job_submit_single_payload_wal_type_matches_pre_feature_baseline
|
|||
"the continuation must not resolve before a covering ACK");
|
||||
TEST_ASSERT_EQUAL_INT(0, persist_flush(&g_jap));
|
||||
repl_on_ack(&g_jap.repl, &g_jaep_a, lsns[0]);
|
||||
TEST_ASSERT_EQUAL_INT_MESSAGE(1, g_jap_obs.call_count,
|
||||
TEST_ASSERT_EQUAL_INT_MESSAGE(1, jap_wait_calls(1),
|
||||
"exactly ONE commit continuation must resolve for the single "
|
||||
"payload, same contract as before this feature");
|
||||
TEST_ASSERT_EQUAL_INT_MESSAGE(0, g_jap_obs.last_rc, 0);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue