From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from [87.239.111.99] (localhost [127.0.0.1]) by dev.tarantool.org (Postfix) with ESMTP id 71DEA6F87A; Fri, 16 Apr 2021 19:30:21 +0300 (MSK) DKIM-Filter: OpenDKIM Filter v2.11.0 dev.tarantool.org 71DEA6F87A DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=tarantool.org; s=dev; t=1618590621; bh=X3b9VrNHrrbEnYFM2QWHamsbphG5VOiIy1F07kSoBZ8=; h=To:Date:In-Reply-To:References:Subject:List-Id:List-Unsubscribe: List-Archive:List-Post:List-Help:List-Subscribe:From:Reply-To:Cc: From; b=Aopmh4IZmWNM1GFFW+aqVI0KUus0RLDF2exl38pQvHQHKcd9DsVzAXHEDbK3nBNwv 4etIrZAN8YfBYraPtMs42ZlGA/JNK6oXI9mtfjnlZds9apacmhcDAMY2SqxcRq4D/3 ydvp544dqSNCziLg8I+QfW18kOsF5ysXLC7NwnQ4= Received: from smtp17.mail.ru (smtp17.mail.ru [94.100.176.154]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by dev.tarantool.org (Postfix) with ESMTPS id 95EFD74B73 for ; Fri, 16 Apr 2021 19:26:00 +0300 (MSK) DKIM-Filter: OpenDKIM Filter v2.11.0 dev.tarantool.org 95EFD74B73 Received: by smtp17.mail.ru with esmtpa (envelope-from ) id 1lXRI7-00008e-Qr; Fri, 16 Apr 2021 19:26:00 +0300 To: v.shpilevoy@tarantool.org, gorcunov@gmail.com Date: Fri, 16 Apr 2021 19:25:40 +0300 Message-Id: X-Mailer: git-send-email 2.24.3 (Apple Git-128) In-Reply-To: References: MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-7564579A: 646B95376F6C166E X-77F55803: 4F1203BC0FB41BD92FFCB8E6708E7480D608FE24BC85426BB1B55F651FED8C70182A05F53808504063331EED27E1812E52FB1E3A39CDB21BEE36AB5062AAE2AE86924DB1AF41E193 X-7FA49CB5: FF5795518A3D127A4AD6D5ED66289B5278DA827A17800CE7D228792DE5216D03EA1F7E6F0F101C67BD4B6F7A4D31EC0BCC500DACC3FED6E28638F802B75D45FF8AA50765F7900637D08E1E5B2BD3D3B78638F802B75D45FF914D58D5BE9E6BC1A93B80C6DEB9DEE97C6FB206A91F05B2BAF6F3F6C834E40530BC30EFF8EB5F528E320D419CB56618D2E47CDBA5A96583C09775C1D3CA48CFA12191B5F2BB8629117882F4460429724CE54428C33FAD30A8DF7F3B2552694AC26CFBAC0749D213D2E47CDBA5A9658378DA827A17800CE7ECE82AE7387CF2AD9FA2833FD35BB23DF004C90652538430302FCEF25BFAB3454AD6D5ED66289B5278DA827A17800CE78760090E968D047FD32BA5DBAC0009BE395957E7521B51C20BC6067A898B09E4090A508E0FED6299176DF2183F8FC7C0D62E3522C432C21CCD04E86FAF290E2D7E9C4E3C761E06A71DD303D21008E298D5E8D9A59859A8B6B372FE9A2E580EFC725E5C173C3A84C3CE9959E2676FD87735872C767BF85DA2F004C90652538430E4A6367B16DE6309 X-C1DE0DAB: C20DE7B7AB408E4181F030C43753B8186998911F362727C4C7A0BC55FA0FE5FCB710B7F030ED88FCC2C0802006200A1438D144602D339481B1881A6453793CE9C32612AADDFBE061C61BE10805914D3804EBA3D8E7E5B87ABF8C51168CD8EBDB63AF70AF8205D7DCDC48ACC2A39D04F89CDFB48F4795C241BDAD6C7F3747799A X-C8649E89: 4E36BF7865823D7055A7F0CF078B5EC49A30900B95165D34A503FBFE8BE8FC49959B58DE5562239B82EBD5B8B0131BE1E8DF038E3645412399B51CD84EA53A061D7E09C32AA3244CFC422CB03CA3F3940E4DC13C212BB9533A76366E8A9DE7CA927AC6DF5659F194 X-D57D3AED: 3ZO7eAau8CL7WIMRKs4sN3D3tLDjz0dLbV79QFUyzQ2Ujvy7cMT6pYYqY16iZVKkSc3dCLJ7zSJH7+u4VD18S7Vl4ZUrpaVfd2+vE6kuoey4m4VkSEu530nj6fImhcD4MUrOEAnl0W826KZ9Q+tr5ycPtXkTV4k65bRjmOUUP8cvGozZ33TWg5HZplvhhXbhDGzqmQDTd6OAevLeAnq3Ra9uf7zvY2zzsIhlcp/Y7m53TZgf2aB4JOg4gkr2bioj3S6P1v0GIqSoYddc/M5QwQ== X-Mailru-Sender: 583F1D7ACE8F49BDD2846D59FC20E9F8DF49C5E8585DB5375602B66AFB12E6C133E57416D9AF418F424AE0EB1F3D1D21E2978F233C3FAE6EE63DB1732555E4A8EE80603BA4A5B0BC112434F685709FCF0DA7A0AF5A3A8387 X-Mras: Ok Subject: [Tarantool-patches] [PATCH v4 09/12] raft: introduce raft_start/stop_candidate X-BeenThere: tarantool-patches@dev.tarantool.org X-Mailman-Version: 2.1.34 Precedence: list List-Id: Tarantool development patches List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , From: Serge Petrenko via Tarantool-patches Reply-To: Serge Petrenko Cc: tarantool-patches@dev.tarantool.org Errors-To: tarantool-patches-bounces@dev.tarantool.org Sender: "Tarantool-patches" Extract raft_start_candidate and raft_stop_candidate functions from raft_cfg_is_candidate. These functions will be used in manual elections. Prerequisite #3055 --- src/lib/raft/raft.c | 83 ++++++++++++++++++++++++++++--------------- src/lib/raft/raft.h | 13 +++++++ test/unit/raft.c | 33 +++++++++++++++-- test/unit/raft.result | 10 +++++- 4 files changed, 108 insertions(+), 31 deletions(-) diff --git a/src/lib/raft/raft.c b/src/lib/raft/raft.c index e9ce8cade..b21693642 100644 --- a/src/lib/raft/raft.c +++ b/src/lib/raft/raft.c @@ -848,38 +848,65 @@ raft_cfg_is_enabled(struct raft *raft, bool is_enabled) void raft_cfg_is_candidate(struct raft *raft, bool is_candidate) { - bool old_is_candidate = raft->is_candidate; raft->is_cfg_candidate = is_candidate; - raft->is_candidate = is_candidate && raft->is_enabled; - if (raft->is_candidate == old_is_candidate) - return; + is_candidate = is_candidate && raft->is_enabled; + if (is_candidate) + raft_start_candidate(raft); + else + raft_stop_candidate(raft, true); +} - if (raft->is_candidate) { - assert(raft->state == RAFT_STATE_FOLLOWER); - if (raft->is_write_in_progress) { - /* - * If there is an on-going WAL write, it means there was - * some node who sent newer data to this node. So it is - * probably a better candidate. Anyway can't do anything - * until the new state is fully persisted. - */ - } else if (raft->leader != 0) { - raft_sm_wait_leader_dead(raft); - } else { - raft_sm_wait_leader_found(raft); - } +void +raft_start_candidate(struct raft *raft) +{ + if (raft->is_candidate) + return; + raft->is_candidate = true; + assert(raft->state != RAFT_STATE_CANDIDATE); + /* + * May still be the leader after raft_stop_candidate + * with demote = false. + */ + if (raft->state == RAFT_STATE_LEADER) + return; + if (raft->is_write_in_progress) { + /* + * If there is an on-going WAL write, it means there was + * some node who sent newer data to this node. So it is + * probably a better candidate. Anyway can't do anything + * until the new state is fully persisted. + */ + } else if (raft->leader != 0) { + raft_sm_wait_leader_dead(raft); } else { - if (raft->state != RAFT_STATE_LEADER) { - /* Do not wait for anything while being a voter. */ - raft_ev_timer_stop(raft_loop(), &raft->timer); - } - if (raft->state != RAFT_STATE_FOLLOWER) { - if (raft->state == RAFT_STATE_LEADER) - raft->leader = 0; - raft->state = RAFT_STATE_FOLLOWER; - /* State is visible and changed - broadcast. */ - raft_schedule_broadcast(raft); + raft_sm_wait_leader_found(raft); + } +} + +void +raft_stop_candidate(struct raft *raft, bool demote) +{ + if (!raft->is_candidate) + return; + raft->is_candidate = false; + if (raft->state != RAFT_STATE_LEADER) { + /* Do not wait for anything while being a voter. */ + raft_ev_timer_stop(raft_loop(), &raft->timer); + } + if (raft->state != RAFT_STATE_FOLLOWER) { + if (raft->state == RAFT_STATE_LEADER) { + if (!demote) { + /* + * Remain leader until someone + * triggers new elections. + */ + return; + } + raft->leader = 0; } + raft->state = RAFT_STATE_FOLLOWER; + /* State is visible and changed - broadcast. */ + raft_schedule_broadcast(raft); } } diff --git a/src/lib/raft/raft.h b/src/lib/raft/raft.h index a5f7e08d9..69dec63c6 100644 --- a/src/lib/raft/raft.h +++ b/src/lib/raft/raft.h @@ -327,6 +327,19 @@ raft_cfg_is_enabled(struct raft *raft, bool is_enabled); void raft_cfg_is_candidate(struct raft *raft, bool is_candidate); +/** + * Make the instance a candidate. + */ +void +raft_start_candidate(struct raft *raft); + +/** + * Make the instance stop taking part in new elections. + * @param demote whether to stop being a leader immediately or not. + */ +void +raft_stop_candidate(struct raft *raft, bool demote); + /** Configure Raft leader election timeout. */ void raft_cfg_election_timeout(struct raft *raft, double timeout); diff --git a/test/unit/raft.c b/test/unit/raft.c index 0306cefcd..575886932 100644 --- a/test/unit/raft.c +++ b/test/unit/raft.c @@ -1296,15 +1296,43 @@ raft_test_term_filter(void) ok(!raft_is_node_outdated(&node.raft, 3), "node doesn't become " "outdated"); - raft_node_destroy(&node); raft_finish_test(); } +static void +raft_test_start_stop_candidate(void) +{ + raft_start_test(4); + struct raft_node node; + raft_node_create(&node); + + raft_node_cfg_is_candidate(&node, false); + raft_node_cfg_election_quorum(&node, 1); + + raft_start_candidate(&node.raft); + raft_run_next_event(); + is(node.raft.state, RAFT_STATE_LEADER, "became leader after " + "start_candidate"); + raft_stop_candidate(&node.raft, false); + raft_run_for(node.cfg_death_timeout); + is(node.raft.state, RAFT_STATE_LEADER, "remain leader after " + "stop_candidate"); + + is(raft_node_send_vote_request(&node, + 3 /* Term. */, + "{}" /* Vclock. */, + 2 /* Source. */ + ), 0, "vote request from 2"); + is(node.raft.state, RAFT_STATE_FOLLOWER, "demote once new election " + "starts"); + raft_finish_test(); +} + static int main_f(va_list ap) { - raft_start_test(14); + raft_start_test(15); (void) ap; fakeev_init(); @@ -1323,6 +1351,7 @@ main_f(va_list ap) raft_test_enable_disable(); raft_test_too_long_wal_write(); raft_test_term_filter(); + raft_test_start_stop_candidate(); fakeev_free(); diff --git a/test/unit/raft.result b/test/unit/raft.result index ecb962e42..bb799936b 100644 --- a/test/unit/raft.result +++ b/test/unit/raft.result @@ -1,5 +1,5 @@ *** main_f *** -1..14 +1..15 *** raft_test_leader_election *** 1..24 ok 1 - 1 pending message at start @@ -233,4 +233,12 @@ ok 13 - subtests ok 9 - node doesn't become outdated ok 14 - subtests *** raft_test_term_filter: done *** + *** raft_test_start_stop_candidate *** + 1..4 + ok 1 - became leader after start_candidate + ok 2 - remain leader after stop_candidate + ok 3 - vote request from 2 + ok 4 - demote once new election starts +ok 15 - subtests + *** raft_test_start_stop_candidate: done *** *** main_f: done *** -- 2.24.3 (Apple Git-128)