diff --git a/src/controller/cluster.c b/src/controller/cluster.c index d24bb28..6799ff1 100644 --- a/src/controller/cluster.c +++ b/src/controller/cluster.c @@ -1081,8 +1081,13 @@ int cluster_start(cluster_t* c) return rc; } - iomgr_start(&(c->iomgr)); - vtaskmgr_start(&(c->vtaskmgr)); + /* Guard: myapp_run() starts iomgr and vtaskmgr before election so that + * incoming OP_requestVote RPCs are accepted during the election phase. + * Only start here if they were not already started (legacy cluster_init path). */ + if (c->iomgr.worker == 0) + iomgr_start(&(c->iomgr)); + if (c->vtaskmgr.dispatcher == 0) + vtaskmgr_start(&(c->vtaskmgr)); delaymgr_start(&(c->delaymgr)); /* Start persistence fsync thread and register WAL hooks */ diff --git a/src/controller/myapp.c b/src/controller/myapp.c index 9fcd0d7..837082b 100644 --- a/src/controller/myapp.c +++ b/src/controller/myapp.c @@ -68,6 +68,13 @@ int myapp_run(myconf_t* myconf) vtaskmgr_init(&(c.vtaskmgr), nvcpus); } + /* Req 1.2: start the I/O worker thread so that incoming OP_requestVote RPCs + * from peers are accepted and dispatched during the election phase. + * Without iomgr_start here, connections queue in the kernel (Recv-Q > 0) + * but no thread reads them, causing peers to time out waiting for vote replies. */ + iomgr_start(&(c.iomgr)); + vtaskmgr_start(&(c.vtaskmgr)); + /* Req 3.1–3.4: probe peers for config tags before election so that a node * joining an existing cluster knows to defer its config fetch to post-election * rather than participating as a first-boot candidate with no config. */