[Tarantool-patches] [PATCH v2 2/5] raft: fix ev_timer.at incorrect usage
Vladislav Shpilevoy
v.shpilevoy at tarantool.org
Thu Jan 20 03:43:44 MSK 2022
ev_timer.at was used as timeout. But after ev_timer_start() it
turns into the deadline - totally different value.
The patch makes sure ev_timer.at is not used in raft at all.
To test that the fakeev subsystem is patched to start its time not
from 0. Otherwise ev_timer.at often really matched the timeout
even for an active timer.
---
src/lib/fakesys/fakeev.c | 10 +++++--
src/lib/raft/raft.c | 59 ++++++++++++++++++++++------------------
src/lib/raft/raft.h | 2 +-
3 files changed, 41 insertions(+), 30 deletions(-)
diff --git a/src/lib/fakesys/fakeev.c b/src/lib/fakesys/fakeev.c
index 5c3469486..333c1646f 100644
--- a/src/lib/fakesys/fakeev.c
+++ b/src/lib/fakesys/fakeev.c
@@ -37,8 +37,11 @@
#include "say.h"
#include <stdbool.h>
+/** Start not from 0 to catch bugs like incorrect usage of ev_timer members. */
+#define FAKEEV_START_TIME 10000
+
/** Global watch, propagated by new events. */
-static double watch = 0;
+static double watch = FAKEEV_START_TIME;
/**
* Increasing event identifiers are used to preserve order of
@@ -278,6 +281,7 @@ fakeev_timer_start(struct ev_loop *loop, struct ev_timer *base)
return;
/* Create the periodic watcher and one event. */
fakeev_timer_event_new((struct ev_watcher *)base, base->at);
+ base->at += watch;
}
double
@@ -287,6 +291,7 @@ fakeev_timer_remaining(struct ev_loop *loop, struct ev_timer *base)
struct fakeev_event *e = fakeev_event_by_ev((struct ev_watcher *)base);
if (e == NULL)
return base->at;
+ assert(e->deadline == base->at);
return e->deadline - fakeev_time();
}
@@ -298,6 +303,7 @@ fakeev_timer_again(struct ev_loop *loop, struct ev_timer *base)
return;
/* Create the periodic watcher and one event. */
fakeev_timer_event_new((struct ev_watcher *)base, base->repeat);
+ base->at = watch + base->repeat;
}
/** Time stop cancels the event if the timer is active. */
@@ -339,7 +345,7 @@ fakeev_reset(void)
fakeev_event_delete(e);
assert(mh_size(events_hash) == 0);
event_id = 0;
- watch = 0;
+ watch = FAKEEV_START_TIME;
}
void
diff --git a/src/lib/raft/raft.c b/src/lib/raft/raft.c
index 1ae8fe87f..d6a849503 100644
--- a/src/lib/raft/raft.c
+++ b/src/lib/raft/raft.c
@@ -931,22 +931,23 @@ raft_restore(struct raft *raft)
void
raft_cfg_election_timeout(struct raft *raft, double timeout)
{
- if (timeout == raft->election_timeout)
+ double old_timeout = raft->election_timeout;
+ if (timeout == old_timeout)
return;
raft->election_timeout = timeout;
- if (raft->vote != 0 && raft->leader == 0 && raft->is_candidate &&
- !raft->is_write_in_progress) {
- 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;
- if (timeout < 0)
- timeout = 0;
- raft_ev_timer_stop(loop, &raft->timer);
- raft_ev_timer_set(&raft->timer, timeout, timeout);
- raft_ev_timer_start(loop, &raft->timer);
- }
+ if (raft->vote == 0 || raft->leader != 0 || !raft->is_candidate ||
+ raft->is_write_in_progress)
+ return;
+
+ assert(raft_ev_is_active(&raft->timer));
+ struct ev_loop *loop = raft_loop();
+ timeout += raft_ev_timer_remaining(loop, &raft->timer) - old_timeout;
+ if (timeout < 0)
+ timeout = 0;
+ raft_ev_timer_stop(loop, &raft->timer);
+ raft_ev_timer_set(&raft->timer, timeout, raft->election_timeout);
+ raft_ev_timer_start(loop, &raft->timer);
}
void
@@ -961,21 +962,25 @@ raft_cfg_election_quorum(struct raft *raft, int election_quorum)
}
void
-raft_cfg_death_timeout(struct raft *raft, double death_timeout)
+raft_cfg_death_timeout(struct raft *raft, double timeout)
{
- raft->death_timeout = death_timeout;
- if (raft->state == RAFT_STATE_FOLLOWER && raft->is_candidate &&
- raft->leader != 0) {
- 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;
- if (timeout < 0)
- timeout = 0;
- raft_ev_timer_stop(loop, &raft->timer);
- raft_ev_timer_set(&raft->timer, timeout, timeout);
- raft_ev_timer_start(loop, &raft->timer);
- }
+ double old_timeout = raft->death_timeout;
+ if (timeout == old_timeout)
+ return;
+
+ raft->death_timeout = timeout;
+ if (raft->state != RAFT_STATE_FOLLOWER || !raft->is_candidate ||
+ raft->leader == 0)
+ return;
+
+ assert(raft_ev_is_active(&raft->timer));
+ struct ev_loop *loop = raft_loop();
+ timeout += raft_ev_timer_remaining(loop, &raft->timer) - old_timeout;
+ if (timeout < 0)
+ timeout = 0;
+ raft_ev_timer_stop(loop, &raft->timer);
+ raft_ev_timer_set(&raft->timer, timeout, raft->death_timeout);
+ raft_ev_timer_start(loop, &raft->timer);
}
void
diff --git a/src/lib/raft/raft.h b/src/lib/raft/raft.h
index 53e2c58a8..e844c2d07 100644
--- a/src/lib/raft/raft.h
+++ b/src/lib/raft/raft.h
@@ -307,7 +307,7 @@ raft_cfg_election_quorum(struct raft *raft, int election_quorum);
* heartbeats from the leader to consider it dead.
*/
void
-raft_cfg_death_timeout(struct raft *raft, double death_timeout);
+raft_cfg_death_timeout(struct raft *raft, double timeout);
/**
* Configure ID of the given Raft instance. The ID can't be changed after it is
--
2.24.3 (Apple Git-128)
More information about the Tarantool-patches
mailing list