feat: wire HA candidate-list reconnect into worker+scheduler join paths

Worker (xcecd):
- Add xce_cluster_config_t cluster_cfg field to controller_connector_ctx_t
  (controller_connector.h includes xce/cluster.h)
- Populate cc_ctx->cluster_cfg from parsed cluster_cfg after xcecd_start()
  sets cc_ctx (main.c, removes the P3 placeholder)
- Replace xcecd_join_cluster() single-shot connect with a candidate-iterating
  loop: tries each candidate, calls xcecli_stop() before the next attempt,
  falls back to the original single-host path when candidates_count == 0

Scheduler (xce_scheduler):
- Add xce_cluster_config_t cluster_cfg field to xce_scheduler_t
  (scheduler.h includes xce/cluster.h)
- Add sched_set_cluster_cfg() setter
- Replace the hardcoded xcecli_start/xcecli_join pair in sched_init() with
  the same candidate-iterating pattern; original single-host path preserved
- main.c calls sched_set_cluster_cfg() before sched_init() when --peers given
  (removes the P3 placeholder comment)

checking_connection() already calls xcecd_join_cluster() on retry, giving
the worker free reconnect-after-failover behaviour for no additional code.
This commit is contained in:
Rongsong Shen 2026-05-08 03:05:56 +00:00
parent fd9cfce1c1
commit b75703ba6b
6 changed files with 131 additions and 20 deletions

View file

@ -3,6 +3,7 @@
#include "client.h"
#include "controller.capnp.h"
#include "thread_pool.h"
#include "xce/cluster.h"
#include "xcecd_utils.h"
#include "xcectx.h"
@ -13,14 +14,15 @@ typedef struct {
} api_ctx_t;
typedef struct controller_connector_ctx_s {
void* thread_pool;
void* process_manager;
char* remote_host;
char* virtual_name;
void** worker_list;
int remote_port;
int join_interval;
bool running;
void* thread_pool;
void* process_manager;
char* remote_host;
char* virtual_name;
void** worker_list;
int remote_port;
int join_interval;
bool running;
xce_cluster_config_t cluster_cfg; /* HA candidate list — empty means single-host mode */
} controller_connector_ctx_t;
extern controller_connector_ctx_t* cc_ctx;

View file

@ -227,6 +227,10 @@ int checking_connection()
/**
* @brief Joins the cluster using the provided host attributes.
*
* In HA mode (cc_ctx->cluster_cfg.candidates_count > 0), iterates through
* all candidates until one accepts the join as primary. In single-host mode
* the original path is taken unchanged.
*
* @param attrs Pointer to the host attributes structure.
* @return int 0 on success, -1 on failure.
*/
@ -236,15 +240,63 @@ int xcecd_join_cluster(hostattrs_t* attrs)
return -1;
}
int rc;
int role = HostRole_worker;
/* HA mode: try each candidate until we find the primary */
if (cc_ctx != NULL && cc_ctx->cluster_cfg.candidates_count > 0) {
for (int i = 0; i < cc_ctx->cluster_cfg.candidates_count; i++) {
const char* host = cc_ctx->cluster_cfg.candidates[i].hostname;
int port = cc_ctx->cluster_cfg.candidates[i].port;
/* Point cc_ctx at this candidate so init_client_config() picks it up */
cc_ctx->remote_host = (char*)host;
cc_ctx->remote_port = port;
XCECD_LOG(XCE_LOG_INFO,
"xcecd_join_cluster: trying candidate %d: %s:%d",
i, host, port);
init_client_config(&xce_client_cfg);
xcecli_ctx_init(&xcecli_ctx, &xce_client_cfg);
xcecli_set_handler(xcecli_ctx, &myreq_handler, NULL);
int rc = xcecli_start(xcecli_ctx);
if (rc < 0) {
XCECD_LOG(XCE_LOG_WARN,
"xcecd_join_cluster: connect to %s:%d failed (rc=%d)",
host, port, rc);
continue;
}
rc = xcecli_join(xcecli_ctx, role, attrs);
if (rc == 0) {
XCECD_LOG(XCE_LOG_INFO,
"xcecd_join_cluster: joined primary at %s:%d",
host, port);
return 0;
}
/* Standby replied — try next candidate */
XCECD_LOG(XCE_LOG_INFO,
"xcecd_join_cluster: candidate %s:%d returned %d "
"(likely standby redirect), trying next",
host, port, rc);
xcecli_stop(xcecli_ctx);
}
XCECD_LOG(XCE_LOG_ERROR,
"xcecd_join_cluster: all %d candidates failed",
cc_ctx->cluster_cfg.candidates_count);
return -1;
}
/* Single-host fallback (original path) */
init_client_config(&xce_client_cfg);
xcecli_ctx_init(&xcecli_ctx, &xce_client_cfg);
xcecli_set_handler(xcecli_ctx, &myreq_handler, NULL);
xcecli_start(xcecli_ctx);
rc = xcecli_join(xcecli_ctx, role, attrs);
int rc = xcecli_join(xcecli_ctx, role, attrs);
if (rc < 0) {
errno = -rc;
XCECD_LOG(XCE_LOG_ERROR, "Call xcecli_join() failure: %s",

View file

@ -534,7 +534,7 @@ int main(int argc, char* argv[])
"(host=%s port=%d)",
config.remote_host, config.remote_port);
}
(void)use_ha; /* P3 will consume this to call xce_connect_cluster() */
/* use_ha and cluster_cfg are consumed after xcecd_start() sets cc_ctx */
struct stat st = { 0 };
@ -641,6 +641,13 @@ int main(int argc, char* argv[])
return 1;
}
if (use_ha && cc_ctx != NULL) {
cc_ctx->cluster_cfg = cluster_cfg;
XCECD_LOG(XCE_LOG_INFO,
"HA mode active: %d candidates, primary will be auto-discovered",
cluster_cfg.candidates_count);
}
#ifndef VIRTUAL_CLUSTER
if (!ipc_master_init(config.domain_sock_file)) {
XCECD_LOG(XCE_LOG_ERROR, "Failed to initialize XCECD IPC.");