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