[Tarantool-patches] [PATCH 3/8] raft: introduce raft_ev
Vladislav Shpilevoy
v.shpilevoy at tarantool.org
Sun Dec 13 20:15:25 MSK 2020
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)
More information about the Tarantool-patches
mailing list