From: Vladislav Shpilevoy <v.shpilevoy@tarantool.org> To: tarantool-patches@dev.tarantool.org, sergepetrenko@tarantool.org Subject: [Tarantool-patches] [PATCH 3/8] raft: introduce raft_ev Date: Sun, 13 Dec 2020 18:15:25 +0100 [thread overview] Message-ID: <4dd1ae98a065ac8b348aa2da3edaf28f66724adf.1607879643.git.v.shpilevoy@tarantool.org> (raw) In-Reply-To: <cover.1607879643.git.v.shpilevoy@tarantool.org> Raft_ev.h/.c encapsulates usage of libev, to a certain extent. All libev functions are wrapped into raft_ev_* wrappers. Objects and types are left intact. This is done in order to be able to replace raft_ev.c in the soon coming unit tests with fakeev functions. That will allow to simulate time and catch all the raft events with 100% reproducibility, and without actual waiting for the events in real time. The similar approach is used for swim unit tests. Original raft core file is built into a new library 'raft_algo'. It is done for the sake of code coverage in unit tests. A test could be built by directly referencing raft.c in unit/CMakeLists.txt, but it can't apply compilation options to it, including gcov options. When raft.c is built into a library right where it is defined, it gets the gcov options, and the code covered by unit tests can be properly shown. Part of #5303 --- src/lib/raft/CMakeLists.txt | 8 +++++ src/lib/raft/raft.c | 65 +++++++++++++++++++------------------ src/lib/raft/raft_ev.c | 57 ++++++++++++++++++++++++++++++++ src/lib/raft/raft_ev.h | 57 ++++++++++++++++++++++++++++++++ 4 files changed, 156 insertions(+), 31 deletions(-) create mode 100644 src/lib/raft/raft_ev.c create mode 100644 src/lib/raft/raft_ev.h diff --git a/src/lib/raft/CMakeLists.txt b/src/lib/raft/CMakeLists.txt index be14817ad..92614b365 100644 --- a/src/lib/raft/CMakeLists.txt +++ b/src/lib/raft/CMakeLists.txt @@ -1,7 +1,15 @@ set(lib_sources raft.c + raft_ev.c ) set_source_files_compile_flags(${lib_sources}) + add_library(raft STATIC ${lib_sources}) target_link_libraries(raft core vclock) + +# Algorithm library is for unit tests, and is not self-sufficient. In order to +# use it some other source file should define test symbols such as raft event +# loop utilities. +add_library(raft_algo STATIC raft.c) +target_link_libraries(raft_algo core vclock) diff --git a/src/lib/raft/raft.c b/src/lib/raft/raft.c index f91e21423..4d07d37e5 100644 --- a/src/lib/raft/raft.c +++ b/src/lib/raft/raft.c @@ -29,7 +29,7 @@ * SUCH DAMAGE. */ #include "raft.h" - +#include "raft_ev.h" #include "exception.h" #include "fiber.h" #include "tt_static.h" @@ -477,8 +477,8 @@ raft_process_heartbeat(struct raft *raft, uint32_t source) * anything was heard from the leader. Then in the timer callback check * the timestamp, and restart the timer, if it is fine. */ - assert(ev_is_active(&raft->timer)); - ev_timer_stop(loop(), &raft->timer); + assert(raft_ev_is_active(&raft->timer)); + raft_ev_timer_stop(raft_loop(), &raft->timer); raft_sm_wait_leader_dead(raft); } @@ -595,7 +595,7 @@ raft_sm_pause_and_dump(struct raft *raft) assert(raft->state == RAFT_STATE_FOLLOWER); if (raft->is_write_in_progress) return; - ev_timer_stop(loop(), &raft->timer); + raft_ev_timer_stop(raft_loop(), &raft->timer); raft_schedule_async(raft); raft->is_write_in_progress = true; } @@ -611,7 +611,7 @@ raft_sm_become_leader(struct raft *raft) assert(!raft->is_write_in_progress); raft->state = RAFT_STATE_LEADER; raft->leader = raft->self; - ev_timer_stop(loop(), &raft->timer); + raft_ev_timer_stop(raft_loop(), &raft->timer); /* State is visible and it is changed - broadcast. */ raft_schedule_broadcast(raft); } @@ -625,7 +625,7 @@ raft_sm_follow_leader(struct raft *raft, uint32_t leader) raft->state = RAFT_STATE_FOLLOWER; raft->leader = leader; if (!raft->is_write_in_progress && raft->is_candidate) { - ev_timer_stop(loop(), &raft->timer); + raft_ev_timer_stop(raft_loop(), &raft->timer); raft_sm_wait_leader_dead(raft); } /* State is visible and it is changed - broadcast. */ @@ -701,38 +701,38 @@ raft_sm_schedule_new_election_cb(struct ev_loop *loop, struct ev_timer *timer, (void)events; struct raft *raft = timer->data; assert(timer == &raft->timer); - ev_timer_stop(loop, timer); + raft_ev_timer_stop(loop, timer); raft_sm_schedule_new_election(raft); } static void raft_sm_wait_leader_dead(struct raft *raft) { - assert(!ev_is_active(&raft->timer)); + assert(!raft_ev_is_active(&raft->timer)); assert(!raft->is_write_in_progress); assert(raft->is_candidate); assert(raft->state == RAFT_STATE_FOLLOWER); assert(raft->leader != 0); - ev_timer_set(&raft->timer, raft->death_timeout, raft->death_timeout); - ev_timer_start(loop(), &raft->timer); + raft_ev_timer_set(&raft->timer, raft->death_timeout, raft->death_timeout); + raft_ev_timer_start(raft_loop(), &raft->timer); } static void raft_sm_wait_leader_found(struct raft *raft) { - assert(!ev_is_active(&raft->timer)); + assert(!raft_ev_is_active(&raft->timer)); assert(!raft->is_write_in_progress); assert(raft->is_candidate); assert(raft->state == RAFT_STATE_FOLLOWER); assert(raft->leader == 0); - ev_timer_set(&raft->timer, raft->death_timeout, raft->death_timeout); - ev_timer_start(loop(), &raft->timer); + raft_ev_timer_set(&raft->timer, raft->death_timeout, raft->death_timeout); + raft_ev_timer_start(raft_loop(), &raft->timer); } static void raft_sm_wait_election_end(struct raft *raft) { - assert(!ev_is_active(&raft->timer)); + assert(!raft_ev_is_active(&raft->timer)); assert(!raft->is_write_in_progress); assert(raft->is_candidate); assert(raft->state == RAFT_STATE_FOLLOWER || @@ -741,15 +741,15 @@ raft_sm_wait_election_end(struct raft *raft) assert(raft->leader == 0); double election_timeout = raft->election_timeout + raft_new_random_election_shift(raft); - ev_timer_set(&raft->timer, election_timeout, election_timeout); - ev_timer_start(loop(), &raft->timer); + raft_ev_timer_set(&raft->timer, election_timeout, election_timeout); + raft_ev_timer_start(raft_loop(), &raft->timer); } static void raft_sm_start(struct raft *raft) { say_info("RAFT: start state machine"); - assert(!ev_is_active(&raft->timer)); + assert(!raft_ev_is_active(&raft->timer)); assert(!raft->is_enabled); assert(raft->state == RAFT_STATE_FOLLOWER); raft->is_enabled = true; @@ -796,7 +796,7 @@ raft_sm_stop(struct raft *raft) if (raft->state == RAFT_STATE_LEADER) raft->leader = 0; raft->state = RAFT_STATE_FOLLOWER; - ev_timer_stop(loop(), &raft->timer); + raft_ev_timer_stop(raft_loop(), &raft->timer); /* State is visible and changed - broadcast. */ raft_schedule_broadcast(raft); } @@ -872,7 +872,7 @@ raft_cfg_is_candidate(struct raft *raft, bool is_candidate) } else { if (raft->state != RAFT_STATE_LEADER) { /* Do not wait for anything while being a voter. */ - ev_timer_stop(loop(), &raft->timer); + raft_ev_timer_stop(raft_loop(), &raft->timer); } if (raft->state != RAFT_STATE_FOLLOWER) { if (raft->state == RAFT_STATE_LEADER) @@ -892,12 +892,13 @@ raft_cfg_election_timeout(struct raft *raft, double timeout) raft->election_timeout = timeout; if (raft->vote != 0 && raft->leader == 0 && raft->is_candidate) { - assert(ev_is_active(&raft->timer)); - double timeout = ev_timer_remaining(loop(), &raft->timer) - + assert(raft_ev_is_active(&raft->timer)); + struct ev_loop *loop = raft_loop(); + double timeout = raft_ev_timer_remaining(loop, &raft->timer) - raft->timer.at + raft->election_timeout; - ev_timer_stop(loop(), &raft->timer); - ev_timer_set(&raft->timer, timeout, timeout); - ev_timer_start(loop(), &raft->timer); + raft_ev_timer_stop(loop, &raft->timer); + raft_ev_timer_set(&raft->timer, timeout, timeout); + raft_ev_timer_start(loop, &raft->timer); } } @@ -918,12 +919,13 @@ raft_cfg_death_timeout(struct raft *raft, double death_timeout) raft->death_timeout = death_timeout; if (raft->state == RAFT_STATE_FOLLOWER && raft->is_candidate && raft->leader != 0) { - assert(ev_is_active(&raft->timer)); - double timeout = ev_timer_remaining(loop(), &raft->timer) - + assert(raft_ev_is_active(&raft->timer)); + struct ev_loop *loop = raft_loop(); + double timeout = raft_ev_timer_remaining(loop, &raft->timer) - raft->timer.at + raft->death_timeout; - ev_timer_stop(loop(), &raft->timer); - ev_timer_set(&raft->timer, timeout, timeout); - ev_timer_start(loop(), &raft->timer); + raft_ev_timer_stop(loop, &raft->timer); + raft_ev_timer_set(&raft->timer, timeout, timeout); + raft_ev_timer_start(loop, &raft->timer); } } @@ -980,7 +982,8 @@ raft_create(struct raft *raft, const struct raft_vtab *vtab) .death_timeout = 5, .vtab = vtab, }; - ev_timer_init(&raft->timer, raft_sm_schedule_new_election_cb, 0, 0); + raft_ev_timer_init(&raft->timer, raft_sm_schedule_new_election_cb, + 0, 0); raft->timer.data = raft; rlist_create(&raft->on_update); } @@ -988,6 +991,6 @@ raft_create(struct raft *raft, const struct raft_vtab *vtab) void raft_destroy(struct raft *raft) { - ev_timer_stop(loop(), &raft->timer); + raft_ev_timer_stop(raft_loop(), &raft->timer); trigger_destroy(&raft->on_update); } diff --git a/src/lib/raft/raft_ev.c b/src/lib/raft/raft_ev.c new file mode 100644 index 000000000..ce99b3dff --- /dev/null +++ b/src/lib/raft/raft_ev.c @@ -0,0 +1,57 @@ +/* + * Copyright 2010-2020, Tarantool AUTHORS, please see AUTHORS file. + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * 1. Redistributions of source code must retain the above + * copyright notice, this list of conditions and the + * following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY <COPYRIGHT HOLDER> ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * <COPYRIGHT HOLDER> OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +#include "raft_ev.h" +#include "tarantool_ev.h" +#include "fiber.h" + +void +raft_ev_timer_start(struct ev_loop *loop, struct ev_timer *watcher) +{ + ev_timer_start(loop, watcher); +} + +double +raft_ev_timer_remaining(struct ev_loop *loop, struct ev_timer *watcher) +{ + return ev_timer_remaining(loop, watcher); +} + +void +raft_ev_timer_stop(struct ev_loop *loop, struct ev_timer *watcher) +{ + ev_timer_stop(loop, watcher); +} + +struct ev_loop * +raft_loop(void) +{ + return loop(); +} diff --git a/src/lib/raft/raft_ev.h b/src/lib/raft/raft_ev.h new file mode 100644 index 000000000..cfa663743 --- /dev/null +++ b/src/lib/raft/raft_ev.h @@ -0,0 +1,57 @@ +#pragma once +/* + * Copyright 2010-2020, Tarantool AUTHORS, please see AUTHORS file. + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * 1. Redistributions of source code must retain the above + * copyright notice, this list of conditions and the + * following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY <COPYRIGHT HOLDER> ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * <COPYRIGHT HOLDER> OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +struct ev_loop; +struct ev_timer; + +/** + * A set of wrappers around libev functions. Libev is not used directly, because + * in unit tests these wrappers are redefined to use fakeev event loop in order + * to speed the tests up, and have more control over the event loop. + */ + +void +raft_ev_timer_start(struct ev_loop *loop, struct ev_timer *watcher); + +double +raft_ev_timer_remaining(struct ev_loop *loop, struct ev_timer *watcher); + +void +raft_ev_timer_stop(struct ev_loop *loop, struct ev_timer *watcher); + +struct ev_loop * +raft_loop(void); + +#define raft_ev_is_active ev_is_active + +#define raft_ev_timer_init ev_timer_init + +#define raft_ev_timer_set ev_timer_set -- 2.24.3 (Apple Git-128)
next prev parent reply other threads:[~2020-12-13 17:15 UTC|newest] Thread overview: 24+ messages / expand[flat|nested] mbox.gz Atom feed top 2020-12-13 17:15 [Tarantool-patches] [PATCH 0/8] Raft module, part 4 - unit tests Vladislav Shpilevoy 2020-12-13 17:15 ` [Tarantool-patches] [PATCH 1/8] fakesys: fix ev_is_active not working on fake timers Vladislav Shpilevoy 2020-12-15 9:42 ` Serge Petrenko 2020-12-13 17:15 ` [Tarantool-patches] [PATCH 2/8] fakesys: introduce fakeev_timer_remaining() Vladislav Shpilevoy 2020-12-15 9:43 ` Serge Petrenko 2020-12-13 17:15 ` Vladislav Shpilevoy [this message] 2020-12-15 10:02 ` [Tarantool-patches] [PATCH 3/8] raft: introduce raft_ev Serge Petrenko 2020-12-13 17:15 ` [Tarantool-patches] [PATCH 4/8] test: introduce raft unit tests Vladislav Shpilevoy 2020-12-13 18:10 ` Vladislav Shpilevoy 2020-12-16 13:03 ` Serge Petrenko 2020-12-17 22:44 ` Vladislav Shpilevoy 2020-12-18 8:17 ` Serge Petrenko 2020-12-20 17:28 ` Vladislav Shpilevoy 2020-12-21 7:36 ` Serge Petrenko 2020-12-13 17:15 ` [Tarantool-patches] [PATCH 5/8] raft: fix crash when received 0 term message Vladislav Shpilevoy 2020-12-16 13:05 ` Serge Petrenko 2020-12-13 17:15 ` [Tarantool-patches] [PATCH 6/8] raft: fix ignorance of bad state receipt Vladislav Shpilevoy 2020-12-16 13:06 ` Serge Petrenko 2020-12-13 17:15 ` [Tarantool-patches] [PATCH 7/8] raft: fix crash on election timeout decrease Vladislav Shpilevoy 2020-12-16 13:08 ` Serge Petrenko 2020-12-13 17:15 ` [Tarantool-patches] [PATCH 8/8] raft: fix crash on death " Vladislav Shpilevoy 2020-12-16 13:10 ` Serge Petrenko 2020-12-21 16:50 ` [Tarantool-patches] [PATCH 0/8] Raft module, part 4 - unit tests Vladislav Shpilevoy 2020-12-21 17:29 ` Vladislav Shpilevoy
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=4dd1ae98a065ac8b348aa2da3edaf28f66724adf.1607879643.git.v.shpilevoy@tarantool.org \ --to=v.shpilevoy@tarantool.org \ --cc=sergepetrenko@tarantool.org \ --cc=tarantool-patches@dev.tarantool.org \ --subject='Re: [Tarantool-patches] [PATCH 3/8] raft: introduce raft_ev' \ /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