From fe37f4d692ae99e446ae506393b5b35705e7c2bb Mon Sep 17 00:00:00 2001 From: Rongsong Shen Date: Sat, 30 May 2026 00:10:23 +0000 Subject: [PATCH] fix(ha-bootstrap): register listener with iomgr epoll before election MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit vote_request_thread connects to peers via SSL. iomgr was started before election but c->ep was only registered with epoll inside cluster_start(), which runs after election. The TCP connect succeeded (kernel accepts it) but SSL_accept was never called server-side, so SSL_connect failed immediately — both vote threads went to deny in <1ms and ctrl-a could never win a quorum. Fix: add cluster_register_listener() that wires c->ep into iomgr's epoll watch. Call it from myapp_run() right after iomgr_start/vtaskmgr_start, before election. cluster_start() still calls iomgr_monitor again (idempotent via EPOLL_CTL_MOD). --- src/controller/cluster.c | 16 ++++++++++++++++ src/controller/include/cluster.h | 1 + src/controller/myapp.c | 13 +++++++++++++ 3 files changed, 30 insertions(+) diff --git a/src/controller/cluster.c b/src/controller/cluster.c index 3a1b70f..9d7cf75 100644 --- a/src/controller/cluster.c +++ b/src/controller/cluster.c @@ -1095,6 +1095,22 @@ static int check_workers_timeout(void* args) return rc; } +/* + * cluster_register_listener: wire c->ep into iomgr's epoll watch. + * + * Called before election from myapp_run() so that incoming OP_requestVote + * SSL connections are accepted and dispatched during the election phase. + * cluster_start() is idempotent — it calls iomgr_monitor again but + * iomgr_monitor is safe to call twice (epoll EPOLL_CTL_MOD handles it). + */ +int cluster_register_listener(cluster_t *c) +{ + endpoint_set_owner(&(c->ep), c); + endpoint_set_conn_handler(&(c->ep), (conn_handler_t)handle_connection); + c->ep.iomgr = &(c->iomgr); + return iomgr_monitor(&(c->ep)); +} + int cluster_start(cluster_t* c) { int rc; diff --git a/src/controller/include/cluster.h b/src/controller/include/cluster.h index c42cbb7..5340039 100644 --- a/src/controller/include/cluster.h +++ b/src/controller/include/cluster.h @@ -24,6 +24,7 @@ typedef int (*collect_check_fn_t)(void*); extern int check_controller(void*); extern int cluster_init_phase1(cluster_t*); extern int cluster_open_listener(cluster_t*); +extern int cluster_register_listener(cluster_t*); extern int cluster_init_phase2(cluster_t*); extern int cluster_init(cluster_t*); extern int cluster_start(cluster_t*); diff --git a/src/controller/myapp.c b/src/controller/myapp.c index b58e3f1..309d8d4 100644 --- a/src/controller/myapp.c +++ b/src/controller/myapp.c @@ -75,6 +75,19 @@ int myapp_run(myconf_t* myconf) iomgr_start(&(c.iomgr)); vtaskmgr_start(&(c.vtaskmgr)); + /* Register the listener socket with epoll NOW so that iomgr can accept + * and dispatch incoming OP_requestVote connections during the election. + * Without this, iomgr runs but has no fd to watch — SSL_connect from + * peers succeeds at TCP level but hangs waiting for SSL_accept. */ + { + int monitor_rc = cluster_register_listener(&c); + if (monitor_rc < 0) { + ERROR("cluster_register_listener: %s", strerror(-monitor_rc)); + cluster_fini(&c); + return monitor_rc; + } + } + /* Pre-load config from disk if a local config file exists, so that this * node's config_tag is known before election. This allows the freshness gate * in am_i_primary_quorum() (Req 2.4) to work correctly: a node with a valid