fix(controller): size the config-push barrier for loaded hosts and admit its wait (bug-1914)

The configAck byte-delivery barrier waited 500 ms, below scheduling
delay on an oversubscribed host, so a push to a healthy majority was
rejected with -110 while its already-appended marker still committed.
CONF_PUSH_TIMEOUT_MS is now 2000 ms, the wait holds a commit-wait
admission slot (new commit_blocking_admit/release; a full budget fails
fast with not-ready), and the timeout reply no longer claims the change
was not committed.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Rongsong Shen 2026-10-04 23:59:29 +00:00
parent 431a6d709e
commit 06e99b9081
Signed by untrusted user: rshen
GPG key ID: 80533536B0EB651C
6 changed files with 101 additions and 4 deletions

View file

@ -1185,6 +1185,50 @@ static void test_configack_self_plus_one_covering_is_majority_of_three(void)
printf("test_configack_self_plus_one_covering_is_majority_of_three: PASS\n");
}
/* bug-1914: under heavy CPU load (40 busy loops on 32 cores) a standby's
* config ACK arrives a few hundred ms late; the 500 ms barrier rejected a push
* whose marker then committed anyway (client told -110, cluster activated
* it). An ACK landing 700 ms in must still meet the production barrier
* bound, and while it waits the barrier holds a commit-wait admission slot
* (bounded blocking on a vtask is allowed only under that reservation). */
static atomic_int g_1914_inflight_seen;
static void* b1914_late_config_ack(void* arg)
{
(void)arg;
usleep(100 * 1000);
pthread_mutex_lock(&g_bce.lock);
atomic_store(&g_1914_inflight_seen, g_bce.inflight_waits);
pthread_mutex_unlock(&g_bce.lock);
usleep(600 * 1000);
repl_on_config_ack(&g_bp.repl, &g_bep_a, 0x100);
return NULL;
}
static void test_configack_late_ack_under_load_meets_barrier(void)
{
pthread_t th;
int rc;
barrier_setup(3, 2, 0);
atomic_store(&g_1914_inflight_seen, -1);
TEST_ASSERT_EQUAL_INT(0, pthread_create(&th, NULL, b1914_late_config_ack,
NULL));
rc = repl_wait_majority_ack(&g_bp.repl, 0x100, CONF_PUSH_TIMEOUT_MS,
(uint8_t)OPCode_upgradeConfigAck);
pthread_join(th, NULL);
TEST_ASSERT_EQUAL_INT_MESSAGE(0, rc,
"bug-1914: a config ACK 700 ms late (CPU-oversubscribed standby) must "
"still meet the config-push barrier");
TEST_ASSERT_EQUAL_INT_MESSAGE(1, atomic_load(&g_1914_inflight_seen),
"bug-1914: the configAck wait must hold a commit-wait admission slot");
TEST_ASSERT_EQUAL_INT(0, g_bce.inflight_waits);
barrier_teardown();
printf("test_configack_late_ack_under_load_meets_barrier: PASS\n");
}
/* n_peers=1: single-member cluster — immediate success without consuming
* the timeout. PIN: green today (via the n_active == 0 short-circuit) and
* green after task 5.2 (explicit n_peers <= 1 rule). */
@ -2530,6 +2574,7 @@ void test_commit_barrier_rollout(void)
RUN_TEST(test_configack_previous_tag_ack_does_not_count);
RUN_TEST(test_configack_no_active_standbys_times_out);
RUN_TEST(test_configack_self_plus_one_covering_is_majority_of_three);
RUN_TEST(test_configack_late_ack_under_load_meets_barrier);
RUN_TEST(test_configack_single_member_immediate_success);
RUN_TEST(test_push_config_withheld_acks_returns_etimedout);
RUN_TEST(test_push_config_majority_acks_returns_zero_and_commits);