feat(libxce-failover): controller populates primary host/port on E_XCE_REDIRECT replies (task 5.3)
This commit is contained in:
parent
fdb8938059
commit
5d1d1e3f12
5 changed files with 124 additions and 1 deletions
|
|
@ -16,6 +16,21 @@ static int validate_host(cluster_t* c, const char* host, const char* key)
|
|||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* fill_redirect_reply: populate an E_XCE_REDIRECT reply with the primary's
|
||||
* host and port from ha state so the libxce client can use it as a one-hop
|
||||
* hint (libxce-failover Req 8.1). The redirect_host pointer aliases the
|
||||
* ha_state_t.primary_host buffer (capnp serialises it when non-NULL — see
|
||||
* request.c); the caller must keep *ha live until xce_reply() runs, which it
|
||||
* does (ha is embedded in cluster_t). Pure aside from the field writes — no
|
||||
* locking, no allocation.
|
||||
*/
|
||||
void fill_redirect_reply(xce_reply_t* reply, const ha_state_t* ha)
|
||||
{
|
||||
reply->redirect_host = (char*)ha->primary_host;
|
||||
reply->redirect_port = (uint16_t)ha->primary_port;
|
||||
}
|
||||
|
||||
static int handle_client_join(cluster_t* c, endpoint_t* ep,
|
||||
xce_request_t* request)
|
||||
{
|
||||
|
|
@ -264,6 +279,7 @@ static int handle_join_(cluster_t* c, endpoint_t* ep, xce_request_t* request)
|
|||
rc = -E_XCE_REDIRECT;
|
||||
reply.status = rc;
|
||||
reply.data_which = RequestReply_data_nodata;
|
||||
fill_redirect_reply(&reply, &c->ha);
|
||||
xce_reply(ep, c->myhost, &reply);
|
||||
return rc;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -94,6 +94,7 @@ controller_unit_test_src = [
|
|||
'testsuite/test_ha_role_lifecycle.c',
|
||||
'testsuite/test_ha_cluster.c',
|
||||
'testsuite/test_ha_redirect.c',
|
||||
'testsuite/test_join_redirect.c',
|
||||
'testsuite/test_p1_integration.c',
|
||||
'testsuite/test_repl_unit.c',
|
||||
'testsuite/test_repl_apply.c',
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ extern void test_ha_role(void);
|
|||
extern void test_ha_role_lifecycle(void);
|
||||
extern void test_ha_cluster(void);
|
||||
extern void test_ha_redirect(void);
|
||||
extern void test_join_redirect(void);
|
||||
extern void test_p1_integration(void);
|
||||
extern void test_repl_unit(void);
|
||||
extern void test_repl_apply(void);
|
||||
|
|
@ -150,6 +151,7 @@ int main(int argc, char* argv[])
|
|||
TEST(test_ha_role_lifecycle);
|
||||
TEST(test_ha_cluster);
|
||||
TEST(test_ha_redirect);
|
||||
TEST(test_join_redirect);
|
||||
TEST(test_p1_integration);
|
||||
TEST(test_repl_unit);
|
||||
TEST(test_repl_apply);
|
||||
|
|
|
|||
103
src/controller/testsuite/test_join_redirect.c
Normal file
103
src/controller/testsuite/test_join_redirect.c
Normal file
|
|
@ -0,0 +1,103 @@
|
|||
/*
|
||||
* test_join_redirect.c — Task 5.3 (libxce-failover)
|
||||
*
|
||||
* ControllerRedirectAddress: the controller's E_XCE_REDIRECT reply must carry
|
||||
* the primary's host and port so the libxce client can use it as a one-hop hint
|
||||
* (Req 8.1). When no primary is known, the path must yield E_XCE_NOMASTER
|
||||
* rather than an addressless redirect (Req 8.2).
|
||||
*
|
||||
* handle_join() is not directly unit-testable (needs a live endpoint + capnp
|
||||
* transport), so join.c factors fill_redirect_reply(reply, ha) — the exact
|
||||
* helper the production E_XCE_REDIRECT branch uses to populate the reply from
|
||||
* ha state. These tests drive that helper.
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <unity/unity.h>
|
||||
|
||||
#include "ha.h" /* ha_state_t, ha_init, ha_should_redirect, xce_reply_t (via typemap.h) */
|
||||
#include "xce/errnox.h" /* E_XCE_REDIRECT, E_XCE_NOMASTER */
|
||||
|
||||
/*
|
||||
* Production helper defined in join.c and exercised on the E_XCE_REDIRECT path.
|
||||
* Declared here (rather than in a controller header) because cluster.h pulls in
|
||||
* netio.h/OpenSSL, which ha.h deliberately avoids; join.c is linked into this
|
||||
* test binary, so an extern prototype is sufficient.
|
||||
*/
|
||||
extern void fill_redirect_reply(xce_reply_t *reply, const ha_state_t *ha);
|
||||
|
||||
/* ── helpers ────────────────────────────────────────────────── */
|
||||
|
||||
static ha_config_t valid_cfg(void)
|
||||
{
|
||||
ha_config_t cfg = {
|
||||
.heartbeat_interval_ms = 3000,
|
||||
.heartbeat_timeout_ms = 10000,
|
||||
.election_timeout_ms = 2000,
|
||||
.probe_connect_timeout_ms = 500,
|
||||
.max_promotion_retries = 5,
|
||||
};
|
||||
return cfg;
|
||||
}
|
||||
|
||||
/* ── tests ──────────────────────────────────────────────────── */
|
||||
|
||||
/*
|
||||
* Req 8.1: when a primary is known, the redirect reply carries the primary's
|
||||
* host and port verbatim from ha state.
|
||||
*/
|
||||
static void test_redirect_reply_carries_primary_address(void)
|
||||
{
|
||||
ha_state_t ha;
|
||||
ha_config_t cfg = valid_cfg();
|
||||
xce_reply_t reply;
|
||||
|
||||
TEST_ASSERT_EQUAL_INT(0, ha_init(&ha, &cfg, 1));
|
||||
/* Default role after init is STANDBY; set a known primary. */
|
||||
strncpy(ha.primary_host, "10.0.1.7", sizeof(ha.primary_host) - 1);
|
||||
ha.primary_port = 7001;
|
||||
|
||||
/* Sanity: this is genuinely a redirect situation. */
|
||||
TEST_ASSERT_EQUAL_INT(1, ha_should_redirect(&ha));
|
||||
|
||||
memset(&reply, 0, sizeof(reply));
|
||||
fill_redirect_reply(&reply, &ha);
|
||||
|
||||
TEST_ASSERT_NOT_NULL(reply.redirect_host);
|
||||
TEST_ASSERT_EQUAL_STRING("10.0.1.7", reply.redirect_host);
|
||||
TEST_ASSERT_EQUAL_UINT16(7001, reply.redirect_port);
|
||||
|
||||
ha_fini(&ha);
|
||||
printf("test_redirect_reply_carries_primary_address: PASS\n");
|
||||
}
|
||||
|
||||
/*
|
||||
* Req 8.2: when no primary is known, this is NOT a redirect situation
|
||||
* (ha_should_redirect == 0, ha_no_primary_known == 1), so the join path
|
||||
* returns E_XCE_NOMASTER rather than an addressless redirect.
|
||||
*/
|
||||
static void test_no_primary_known_yields_nomaster_not_redirect(void)
|
||||
{
|
||||
ha_state_t ha;
|
||||
ha_config_t cfg = valid_cfg();
|
||||
|
||||
TEST_ASSERT_EQUAL_INT(0, ha_init(&ha, &cfg, 1));
|
||||
/* primary_host empty after init — no known primary. */
|
||||
|
||||
TEST_ASSERT_EQUAL_INT(0, ha_should_redirect(&ha));
|
||||
TEST_ASSERT_EQUAL_INT(1, ha_no_primary_known(&ha));
|
||||
|
||||
ha_fini(&ha);
|
||||
printf("test_no_primary_known_yields_nomaster_not_redirect: PASS\n");
|
||||
}
|
||||
|
||||
/* ── suite entry point ─────────────────────────────────────── */
|
||||
|
||||
void test_join_redirect(void)
|
||||
{
|
||||
RUN_TEST(test_redirect_reply_carries_primary_address);
|
||||
RUN_TEST(test_no_primary_known_yields_nomaster_not_redirect);
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue