fix(controller): a worker join that fails its commit barrier leaves nothing bound (bug-1728)

"ERROR incorrect worker idx -1" on every node after a worker-join commit
timeout (during a PreVote/election) was the visible end of a real
dangling pointer. init_worker/rejoin_worker bind the endpoint to the
worker (apply_join_state: w->ep = ep, UNAVAIL cleared) before the commit
barrier; on barrier failure handle_worker_join returned without setting
priv->u.idx, so when that connection closed handle_endpoint_error saw
idx -1, logged the ERROR and never ran handle_worker_leave -- the worker
kept w->ep pointing at the freed endpoint, available for dispatch, until
it rejoined.

- handle_worker_join: on barrier failure, clear w->ep (if still this
  endpoint) and mark the worker unavailable, as if the join never
  happened; the worker retries its join.
- handle_endpoint_error: an idx -1 worker endpoint is now the legitimate
  "closed before its join completed" case -- WARN, nothing to clean up.

test_worker_join_withheld_acks_replies_error also asserts the worker is
unbound and unavailable after the -ETIMEDOUT reply (RED before).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Rongsong Shen 2026-09-27 05:48:49 +00:00
parent 7aac018c00
commit 33c4f28c6a
Signed by untrusted user: rshen
GPG key ID: 80533536B0EB651C
3 changed files with 39 additions and 1 deletions

View file

@ -873,9 +873,27 @@ static void test_hostctrl_unknown_host_error_reply_immediate(void)
static void test_worker_join_withheld_acks_replies_error(void)
{
worker_t *w = NULL;
cbrp_setup(300);
cbrp_arrange_worker_join("workerA");
cbrp_drive_withheld(handle_join, &g_join_req, 300);
/* bug-1728: a join whose commit barrier failed must not leave the
* worker record bound to the requesting endpoint. priv->u.idx stays
* -1 on that connection, so its teardown never runs
* handle_worker_leave -- a still-set w->ep would dangle once the
* endpoint is freed. */
lock(g_hc.workermgr, RTTI_LOCK_READ);
key_find(g_hc.workermgr, (void *)"workerA", (void **)&w);
unlock(g_hc.workermgr);
if (w != NULL) {
TEST_ASSERT_NULL_MESSAGE(w->ep,
"bug-1728: a failed join must not leave w->ep bound to the "
"requesting endpoint");
TEST_ASSERT_TRUE_MESSAGE(w->host.status & XCE_WORKER_UNAVAIL,
"bug-1728: a worker whose join failed must stay unavailable");
}
cbrp_teardown();
printf("test_worker_join_withheld_acks_replies_error: PASS\n");
}