Unverified Commit b1a110d3 authored by Ozan Tezcan's avatar Ozan Tezcan Committed by GitHub
Browse files

Add automatic leader transfer target selection (#95)

parent 8ee3f264
......@@ -1517,10 +1517,14 @@ int raft_queue_read_request(raft_server_t* me, raft_read_request_callback_f cb,
*/
void raft_process_read_queue(raft_server_t* me);
/*
* invoke a leadership transfer to targeted node
* node_id = targeted node
* timeout = timeout in ms before this transfer is aborted. if 0, use default election timeout
/** Invoke a leadership transfer to targeted node
*
* @param[in] node_id targeted node, RAFT_NODE_ID_NONE for automatic target
* selection. Node with the most entries will be selected.
* @param[in] timeout timeout in ms before this transfer is aborted.
* if 0, use default election timeout
* @return an error if leadership transfer is already in progress or
* the targeted node_id is unknown
*/
int raft_transfer_leader(raft_server_t* me, raft_node_id_t node_id, long timeout);
......
......@@ -1951,11 +1951,6 @@ void raft_process_read_queue(raft_server_t* me)
}
}
/* invoke a leadership transfer
* node_id = targeted node we are transferring to
* timeout = how long this should be allowed to take in milliseconds, as calculated by calls to raft_periodic
* return an error if leadership transfer is already in progress of the targeted node_id is unknown
*/
int raft_transfer_leader(raft_server_t* me, raft_node_id_t node_id, long timeout)
{
if (me->state != RAFT_STATE_LEADER) {
......@@ -1966,7 +1961,26 @@ int raft_transfer_leader(raft_server_t* me, raft_node_id_t node_id, long timeout
return RAFT_ERR_LEADER_TRANSFER_IN_PROGRESS;
}
raft_node_t *target = raft_get_node(me, node_id);
raft_node_t *target = NULL;
if (node_id != RAFT_NODE_ID_NONE) {
target = raft_get_node(me, node_id);
} else {
/* Find the most up-to-date node. As we need to replicate less entries
* to this node, it is the best candidate for leader transfer. */
raft_index_t max = 0;
for (int i = 0; i < raft_get_num_nodes(me); i++) {
raft_node_t *node = raft_get_node_from_idx(me, i);
raft_index_t match = raft_node_get_match_idx(node);
if (node != me->node && match > max) {
target = node;
max = match;
}
}
}
if (target == NULL || target == me->node) {
return RAFT_ERR_INVALID_NODEID;
}
......@@ -1977,7 +1991,7 @@ int raft_transfer_leader(raft_server_t* me, raft_node_id_t node_id, long timeout
me->sent_timeout_now = 1;
}
me->node_transferring_leader_to = node_id;
me->node_transferring_leader_to = raft_node_get_id(target);
me->transfer_leader_time = (timeout != 0) ? timeout : me->election_timeout;
return 0;
......
......@@ -4541,6 +4541,33 @@ void Test_transfer_leader_not_leader(CuTest *tc)
CuAssertIntEquals(tc, RAFT_ERR_NOT_LEADER, ret);
}
void Test_transfer_automatic(CuTest *tc)
{
raft_leader_transfer_e state = RAFT_LEADER_TRANSFER_TIMEOUT;
raft_cbs_t funcs = {
.notify_transfer_event = cb_notify_transfer_event,
};
raft_server_t *r = raft_new();
raft_set_callbacks(r, &funcs, &state);
raft_node_t *n1 = raft_add_node(r, NULL, 100, 1);
raft_set_state(r, RAFT_STATE_LEADER);
raft_node_t *n2 = raft_add_node(r, NULL, 2, 0);
raft_node_set_match_idx(n2, 5);
raft_node_t *n3 = raft_add_node(r, NULL, 3, 0);
raft_node_set_match_idx(n3, 8);
raft_node_t *n4 = raft_add_node(r, NULL, 3, 0);
raft_node_set_match_idx(n3, 6);
int ret = raft_transfer_leader(r, RAFT_NODE_ID_NONE, 0);
CuAssertIntEquals(tc, 0, ret);
CuAssertIntEquals(tc, 3, raft_get_transfer_leader(r));
}
int main(void)
{
CuString *output = CuStringNew();
......@@ -4684,6 +4711,7 @@ int main(void)
SUITE_ADD_TEST(suite, Test_transfer_leader_success);
SUITE_ADD_TEST(suite, Test_transfer_leader_unexpected);
SUITE_ADD_TEST(suite, Test_transfer_leader_not_leader);
SUITE_ADD_TEST(suite, Test_transfer_automatic);
CuSuiteRun(suite);
CuSuiteDetails(suite, output);
printf("%s\n", output->buffer);
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment