From: Serge Petrenko via Tarantool-patches <tarantool-patches@dev.tarantool.org> To: v.shpilevoy@tarantool.org, gorcunov@gmail.com Cc: tarantool-patches@dev.tarantool.org Subject: [Tarantool-patches] [PATCH v4 10/12] election: support manual elections in clear_synchro_queue() Date: Fri, 16 Apr 2021 19:25:41 +0300 [thread overview] Message-ID: <f63e6d62acd2f78421c6f22229ea3b2379391d28.1618590211.git.sergepetrenko@tarantool.org> (raw) In-Reply-To: <cover.1618590211.git.sergepetrenko@tarantool.org> This patch adds support for manual elections from `box.ctl.clear_synchro_queue()`. When an instance is in `election_mode='manual'`, calling `clear_synchro_queue()` will make it start a new election round. Follow-up #5445 Part of #3055 @TarantoolBot document Title: describe election_mode='manual' Manual election mode is introduced. It may be used when the user wants to control which instance is the leader explicitly instead of relying on Raft election algorithm. When an instance is configured with `election_mode='manual'`, it behaves as follows: 1) By default, the instance acts like a voter: it is read-only and may vote for other instances that are candidates. 2) Once `box.ctl.clear_synchro_queue()` is called, the instance becomes a candidate and starts a new election round. If the instance wins the elections, it remains leader, but won't participate in any new elections. --- src/box/box.cc | 71 ++++++++++++++++++++++++++++++++++++++++--- src/box/errcode.h | 2 ++ src/box/raft.c | 30 +++++++++++++++--- src/box/raft.h | 3 ++ test/box/error.result | 2 ++ 5 files changed, 99 insertions(+), 9 deletions(-) diff --git a/src/box/box.cc b/src/box/box.cc index d5a55a30a..fcd812c09 100644 --- a/src/box/box.cc +++ b/src/box/box.cc @@ -1521,12 +1521,75 @@ box_clear_synchro_queue(bool try_wait) if (!is_box_configured || raft_node_term(box_raft(), instance_id) == box_raft()->term) return 0; + + bool run_elections = false; + + switch (box_election_mode) { + case ELECTION_MODE_OFF: + break; + case ELECTION_MODE_VOTER: + assert(box_raft()->state == RAFT_STATE_FOLLOWER); + diag_set(ClientError, ER_UNSUPPORTED, "election_mode='voter'", + "manual elections"); + return -1; + case ELECTION_MODE_MANUAL: + assert(box_raft()->state == RAFT_STATE_FOLLOWER); + run_elections = true; + try_wait = false; + break; + case ELECTION_MODE_CANDIDATE: + /* + * Leader elections are enabled, and this instance is allowed to + * promote only if it's already an elected leader. No manual + * elections. + */ + if (box_raft()->state != RAFT_STATE_LEADER) { + diag_set(ClientError, ER_UNSUPPORTED, "election_mode=" + "'candidate'", "manual elections"); + return -1; + } + break; + default: + unreachable(); + } + uint32_t former_leader_id = txn_limbo.owner_id; int64_t wait_lsn = txn_limbo.confirmed_lsn; int rc = 0; int quorum = replication_synchro_quorum; in_clear_synchro_queue = true; + if (run_elections) { + /* + * Make this instance a candidate and run until some leader, not + * necessarily this instance, emerges. + */ + raft_start_candidate(box_raft()); + /* + * Trigger new elections without waiting for an old leader to + * disappear. + */ + raft_new_term(box_raft()); + box_raft_wait_leader_found(); + /* + * Do not reset raft mode if it was changed while running the + * elections. + */ + if (box_election_mode == ELECTION_MODE_MANUAL) + raft_stop_candidate(box_raft(), false); + if (!box_raft()->is_enabled) { + diag_set(ClientError, ER_RAFT_DISABLED); + in_clear_synchro_queue = false; + return -1; + } + if (box_raft()->state != RAFT_STATE_LEADER) { + diag_set(ClientError, ER_INTERFERING_PROMOTE, + box_raft()->leader); + in_clear_synchro_queue = false; + return -1; + } + } + if (txn_limbo_is_empty(&txn_limbo)) goto promote; @@ -1544,12 +1607,10 @@ box_clear_synchro_queue(bool try_wait) * transactions. Exit in case someone did that for us. */ if (former_leader_id != txn_limbo.owner_id) { - /* - * TODO: error once we see someone else has become the - * leader already. - */ + diag_set(ClientError, ER_INTERFERING_PROMOTE, + txn_limbo.owner_id); in_clear_synchro_queue = false; - return 0; + return -1; } } diff --git a/src/box/errcode.h b/src/box/errcode.h index c63191fb6..d93820e96 100644 --- a/src/box/errcode.h +++ b/src/box/errcode.h @@ -275,6 +275,8 @@ struct errcode_record { /*220 */_(ER_TOO_EARLY_SUBSCRIBE, "Can't subscribe non-anonymous replica %s until join is done") \ /*221 */_(ER_SQL_CANT_ADD_AUTOINC, "Can't add AUTOINCREMENT: space %s can't feature more than one AUTOINCREMENT field") \ /*222 */_(ER_QUORUM_WAIT, "Couldn't wait for quorum %d: %s") \ + /*223 */_(ER_INTERFERING_PROMOTE, "Instance with replica id %u was promoted first") \ + /*224 */_(ER_RAFT_DISABLED, "Elections were turned off while running box.ctl.promote()")\ /* * !IMPORTANT! Please follow instructions at start of the file diff --git a/src/box/raft.c b/src/box/raft.c index 285dbe4fd..425353207 100644 --- a/src/box/raft.c +++ b/src/box/raft.c @@ -91,11 +91,11 @@ box_raft_update_synchro_queue(struct raft *raft) * If the node became a leader, it means it will ignore all records from * all the other nodes, and won't get late CONFIRM messages anyway. Can * clear the queue without waiting for confirmations. - * It's alright that the user may have called clear_synchro_queue - * manually. In this case the call below will exit immediately and we'll - * simply log a warning. + * In case these are manual elections, we are already in the middle of a + * `clear_synchro_queue` call. No need to call it once again. */ - if (raft->state == RAFT_STATE_LEADER) { + if (raft->state == RAFT_STATE_LEADER && + box_election_mode != ELECTION_MODE_MANUAL) { int rc = 0; uint32_t errcode = 0; do { @@ -336,6 +336,28 @@ fail: panic("Could not write a raft request to WAL\n"); } +static int +box_raft_wait_leader_found_f(struct trigger *trig, void *event) +{ + struct raft *raft = event; + assert(raft == box_raft()); + struct fiber *waiter = trig->data; + if (raft->leader != REPLICA_ID_NIL || !raft->is_enabled) + fiber_wakeup(waiter); + return 0; +} + +void +box_raft_wait_leader_found(void) +{ + struct trigger trig; + trigger_create(&trig, box_raft_wait_leader_found_f, fiber(), NULL); + raft_on_update(box_raft(), &trig); + fiber_yield(); + assert(box_raft()->leader != REPLICA_ID_NIL || !box_raft()->is_enabled); + trigger_clear(&trig); +} + void box_raft_init(void) { diff --git a/src/box/raft.h b/src/box/raft.h index 15f4e80d9..8fce423e1 100644 --- a/src/box/raft.h +++ b/src/box/raft.h @@ -97,6 +97,9 @@ box_raft_checkpoint_remote(struct raft_request *req); int box_raft_process(struct raft_request *req, uint32_t source); +void +box_raft_wait_leader_found(); + void box_raft_init(void); diff --git a/test/box/error.result b/test/box/error.result index 7761c6949..cc8cbaaa9 100644 --- a/test/box/error.result +++ b/test/box/error.result @@ -441,6 +441,8 @@ t; | 220: box.error.TOO_EARLY_SUBSCRIBE | 221: box.error.SQL_CANT_ADD_AUTOINC | 222: box.error.QUORUM_WAIT + | 223: box.error.INTERFERING_PROMOTE + | 224: box.error.RAFT_DISABLED | ... test_run:cmd("setopt delimiter ''"); -- 2.24.3 (Apple Git-128)
next prev parent reply other threads:[~2021-04-16 16:30 UTC|newest] Thread overview: 57+ messages / expand[flat|nested] mbox.gz Atom feed top 2021-04-16 16:25 [Tarantool-patches] [PATCH v4 00/12] raft: introduce manual elections and fix a bug with re-applying rolled back transactions Serge Petrenko via Tarantool-patches 2021-04-16 16:25 ` [Tarantool-patches] [PATCH v4 01/12] wal: make wal_assign_lsn accept journal entry Serge Petrenko via Tarantool-patches 2021-04-16 16:25 ` [Tarantool-patches] [PATCH v4 02/12] xrow: enrich row's meta information with sync replication flags Serge Petrenko via Tarantool-patches 2021-04-16 16:25 ` [Tarantool-patches] [PATCH v4 03/12] xrow: introduce a PROMOTE entry Serge Petrenko via Tarantool-patches 2021-04-16 16:25 ` [Tarantool-patches] [PATCH v4 04/12] box: actualise iproto_key_type array Serge Petrenko via Tarantool-patches 2021-04-16 16:25 ` [Tarantool-patches] [PATCH v4 05/12] box: make clear_synchro_queue() write a PROMOTE entry instead of CONFIRM + ROLLBACK Serge Petrenko via Tarantool-patches 2021-04-16 22:12 ` Vladislav Shpilevoy via Tarantool-patches 2021-04-18 8:24 ` Serge Petrenko via Tarantool-patches 2021-04-20 22:30 ` Vladislav Shpilevoy via Tarantool-patches 2021-04-21 5:58 ` Serge Petrenko via Tarantool-patches 2021-04-16 16:25 ` [Tarantool-patches] [PATCH v4 06/12] box: write PROMOTE even for empty limbo Serge Petrenko via Tarantool-patches 2021-04-19 13:39 ` Serge Petrenko via Tarantool-patches 2021-04-16 16:25 ` [Tarantool-patches] [PATCH v4 07/12] raft: filter rows based on known peer terms Serge Petrenko via Tarantool-patches 2021-04-16 22:21 ` Vladislav Shpilevoy via Tarantool-patches 2021-04-18 8:49 ` Serge Petrenko via Tarantool-patches 2021-04-18 15:44 ` Vladislav Shpilevoy via Tarantool-patches 2021-04-19 9:31 ` Serge Petrenko via Tarantool-patches 2021-04-18 16:27 ` Vladislav Shpilevoy via Tarantool-patches 2021-04-19 9:30 ` Serge Petrenko via Tarantool-patches 2021-04-20 20:29 ` Serge Petrenko via Tarantool-patches 2021-04-20 20:31 ` Serge Petrenko via Tarantool-patches 2021-04-20 20:55 ` Serge Petrenko via Tarantool-patches 2021-04-20 22:30 ` Vladislav Shpilevoy via Tarantool-patches 2021-04-21 5:58 ` Serge Petrenko via Tarantool-patches 2021-04-16 16:25 ` [Tarantool-patches] [PATCH v4 08/12] election: introduce a new election mode: "manual" Serge Petrenko via Tarantool-patches 2021-04-19 22:34 ` Vladislav Shpilevoy via Tarantool-patches 2021-04-20 9:25 ` Serge Petrenko via Tarantool-patches 2021-04-20 17:37 ` Serge Petrenko via Tarantool-patches 2021-04-16 16:25 ` [Tarantool-patches] [PATCH v4 09/12] raft: introduce raft_start/stop_candidate Serge Petrenko via Tarantool-patches 2021-04-16 22:23 ` Vladislav Shpilevoy via Tarantool-patches 2021-04-18 8:59 ` Serge Petrenko via Tarantool-patches 2021-04-19 22:35 ` Vladislav Shpilevoy via Tarantool-patches 2021-04-20 9:28 ` Serge Petrenko via Tarantool-patches 2021-04-19 12:52 ` Serge Petrenko via Tarantool-patches 2021-04-16 16:25 ` Serge Petrenko via Tarantool-patches [this message] 2021-04-16 22:24 ` [Tarantool-patches] [PATCH v4 10/12] election: support manual elections in clear_synchro_queue() Vladislav Shpilevoy via Tarantool-patches 2021-04-18 9:26 ` Serge Petrenko via Tarantool-patches 2021-04-18 16:07 ` Vladislav Shpilevoy via Tarantool-patches 2021-04-19 9:32 ` Serge Petrenko via Tarantool-patches 2021-04-19 12:47 ` Serge Petrenko via Tarantool-patches 2021-04-16 16:25 ` [Tarantool-patches] [PATCH v4 11/12] box: remove parameter from clear_synchro_queue Serge Petrenko via Tarantool-patches 2021-04-16 16:25 ` [Tarantool-patches] [PATCH v4 12/12] box.ctl: rename clear_synchro_queue to promote Serge Petrenko via Tarantool-patches 2021-04-19 22:35 ` Vladislav Shpilevoy via Tarantool-patches 2021-04-20 10:22 ` Serge Petrenko via Tarantool-patches 2021-04-18 12:00 ` [Tarantool-patches] [PATCH v4 13/12] replication: send accumulated Raft messages after relay start Serge Petrenko via Tarantool-patches 2021-04-18 16:03 ` Vladislav Shpilevoy via Tarantool-patches 2021-04-19 12:11 ` Serge Petrenko via Tarantool-patches 2021-04-19 22:36 ` Vladislav Shpilevoy via Tarantool-patches 2021-04-20 10:38 ` Serge Petrenko via Tarantool-patches 2021-04-20 22:31 ` Vladislav Shpilevoy via Tarantool-patches 2021-04-21 5:59 ` Serge Petrenko via Tarantool-patches 2021-04-19 22:37 ` [Tarantool-patches] [PATCH v4 00/12] raft: introduce manual elections and fix a bug with re-applying rolled back transactions Vladislav Shpilevoy via Tarantool-patches 2021-04-20 17:38 ` [Tarantool-patches] [PATCH v4 14/12] txn: make NOPs fully asynchronous Serge Petrenko via Tarantool-patches 2021-04-20 22:31 ` Vladislav Shpilevoy via Tarantool-patches 2021-04-21 5:59 ` Serge Petrenko via Tarantool-patches 2021-04-20 22:30 ` [Tarantool-patches] [PATCH v4 00/12] raft: introduce manual elections and fix a bug with re-applying rolled back transactions Vladislav Shpilevoy via Tarantool-patches 2021-04-21 6:01 ` Serge Petrenko via Tarantool-patches
Reply instructions: You may reply publicly to this message via plain-text email using any one of the following methods: * Save the following mbox file, import it into your mail client, and reply-to-all from there: mbox Avoid top-posting and favor interleaved quoting: https://en.wikipedia.org/wiki/Posting_style#Interleaved_style * Reply using the --to, --cc, and --in-reply-to switches of git-send-email(1): git send-email \ --in-reply-to=f63e6d62acd2f78421c6f22229ea3b2379391d28.1618590211.git.sergepetrenko@tarantool.org \ --to=tarantool-patches@dev.tarantool.org \ --cc=gorcunov@gmail.com \ --cc=sergepetrenko@tarantool.org \ --cc=v.shpilevoy@tarantool.org \ --subject='Re: [Tarantool-patches] [PATCH v4 10/12] election: support manual elections in clear_synchro_queue()' \ /path/to/YOUR_REPLY https://kernel.org/pub/software/scm/git/docs/git-send-email.html * If your mail client supports setting the In-Reply-To header via mailto: links, try the mailto: link
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox