fix(esd): re-join after transparent reconnect -- bug-1978
A controller restart dropped xceesd's resval pushes forever: the fresh endpoint is created by handle_connection() with priv->role = HostRole_client, and only OPCode_join sets HostRole_extres -- but the old esd_on_reconnect cleared only g_needs_full_push, so the daemon re-PUSHED onto an un-joined endpoint and every push landed in do_update_resvals's neither-branch, refused -EPERM forever (live sighting: pushes_ok frozen, rc=-1 every 10s). Mirror the worker's needs_rejoin contract: the reconnect handler now sets BOTH g_needs_rejoin and g_needs_full_push; the main loop consumes the re-join first (xcecli_join(HostRole_extres) on the main thread, outside the failover lock -- the xcecd_on_reconnect no-I/O constraint), retrying next tick on failure, then does the full re-push. Live t1/t2 proof: controller restart -> "esd: re-joined after reconnect (rc=0)" + "full re-push: 1/1 source(s)" + extlic back in `xcecli resources distribution list`. Two new white-box unit tests pin the both-flags contract (13/13 in the esd suite). Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
parent
b38ef6de63
commit
b471ce31ca
5 changed files with 219 additions and 7 deletions
|
|
@ -26,6 +26,13 @@
|
|||
/* White-box: the stale predicate and the status-file writer (Phase 2,
|
||||
* design.md 3.5 "运维面"). */
|
||||
#include "../src/esd_status.c"
|
||||
/* White-box: the reconnect-flag contract (bug-1978). esd.c's main loop
|
||||
* drives the live client library, but the reconnect handler's flag contract
|
||||
* -- set BOTH rejoin and full-push, and the loop consuming rejoin first --
|
||||
* is unit-testable here without a controller: esd_run() itself is not
|
||||
* linked (its join sequence would need a live primary); we drive
|
||||
* esd_on_reconnect and read the loop's guard statics directly. */
|
||||
#include "../src/esd.c"
|
||||
|
||||
static char g_tmpdir[256];
|
||||
|
||||
|
|
@ -373,6 +380,38 @@ static void test_status_write_marks_stale(void)
|
|||
free(cfg.src);
|
||||
}
|
||||
|
||||
/* ── bug-1978: the reconnect-flag contract ───────────────────────────── */
|
||||
|
||||
static void test_reconnect_sets_both_rejoin_and_full_push(void)
|
||||
{
|
||||
g_needs_rejoin = 0;
|
||||
g_needs_full_push = 0;
|
||||
|
||||
TEST_ASSERT_EQUAL_INT(0, esd_on_reconnect((xcecli_ctx_t)0x1, NULL));
|
||||
|
||||
/* BOTH flags: the re-join must happen before any re-push (an un-joined
|
||||
* push is refused -EPERM on the fresh endpoint), and the re-push must
|
||||
* still happen (the fresh controller knows no values). The bug-1978
|
||||
* defect cleared only g_needs_full_push -- a daemon that re-pushed onto
|
||||
* an un-joined HostRole_client endpoint, every push -EPERM forever. */
|
||||
TEST_ASSERT_EQUAL_INT(1, g_needs_rejoin);
|
||||
TEST_ASSERT_EQUAL_INT(1, g_needs_full_push);
|
||||
}
|
||||
|
||||
static void test_reconnect_flags_recover_from_any_previous_state(void)
|
||||
{
|
||||
/* The handler is level-set, not level-toggle: regardless of what a
|
||||
* previous (already consumed) reconnect left behind, a new callback
|
||||
* invocation re-arms both flags. */
|
||||
g_needs_rejoin = 0;
|
||||
g_needs_full_push = 1; /* stale leftover from an unconsumed cycle */
|
||||
|
||||
TEST_ASSERT_EQUAL_INT(0, esd_on_reconnect((xcecli_ctx_t)0x2, NULL));
|
||||
|
||||
TEST_ASSERT_EQUAL_INT(1, g_needs_rejoin);
|
||||
TEST_ASSERT_EQUAL_INT(1, g_needs_full_push);
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
UNITY_BEGIN();
|
||||
|
|
@ -388,6 +427,8 @@ int main(void)
|
|||
RUN_TEST(test_stale_predicate);
|
||||
RUN_TEST(test_status_write_basic);
|
||||
RUN_TEST(test_status_write_marks_stale);
|
||||
RUN_TEST(test_reconnect_sets_both_rejoin_and_full_push);
|
||||
RUN_TEST(test_reconnect_flags_recover_from_any_previous_state);
|
||||
|
||||
return UNITY_END();
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue