Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Menu
Open sidebar
ruanhaishen
RedisLabs Raft
Commits
3c134468
Commit
3c134468
authored
Jul 11, 2018
by
Yossi Gottlieb
Browse files
Merge remote-tracking branch 'upstream/master'
parents
876f8d7c
9b983183
Changes
14
Expand all
Show whitespace changes
Inline
Side-by-side
Makefile
View file @
3c134468
...
...
@@ -72,9 +72,7 @@ tests_full:
.PHONY
:
test_virtraft
test_virtraft
:
cp
src/
*
.c virtraft/deps/raft/
cp
include/
*
.h virtraft/deps/raft/
cd
virtraft
;
make clean
;
make
;
make tests
python3 tests/virtraft2.py
--servers
7
-i
20000
--compaction_rate
50
--drop_rate
5
-P
10
--seed
1
-m
3
.PHONY
:
amalgamation
amalgamation
:
...
...
README.rst
View file @
3c134468
...
...
@@ -27,12 +27,43 @@ Quality Assurance
We use the following methods to ensure that the library is safe:
* A
`
simulator
<https://github.com/willemt/
virtraft
>`_
is used to test the Raft invariants on unreliable networks
* A simulator
(
virtraft
2)
is used to test the Raft invariants on unreliable networks
* `Fuzzing/property-based-testing <https://github.com/willemt/virtraft/blob/master/tests/test_fuzzer.py>`_ via `Hypothesis <https://github.com/DRMacIver/hypothesis/>`_
* All bugs have regression tests
* Many unit tests
* `Usage <https://github.com/willemt/ticketd>`_
virtraft2
---------
This cluster simulator checks the following:
* Log Matching (servers must have matching logs)
* State Machine Safety (applied entries have the same ID)
* Election Safety (only one valid leader per term)
* Current Index Validity (does the current index have an existing entry?)
* Entry ID Monotonicity (entries aren't appended out of order)
* Committed entry popping (committed entries are not popped from the log)
* Log Accuracy (does the server's log match mirror an independent log?)
* Deadlock detection (does the cluster continuously make progress?)
Chaos generated by virtraft2:
* Random bi-directional partitions between nodes
* Message dropping
* Message duplication
* Membership change injection
* Random compactions
Run the simulator using:
.. code-block:: bash
:class: ignore
make test_virtraft
virtraft2 succeeds `virtraft <https://github.com/willemt/virtraft>`_
Single file amalgamation
========================
...
...
@@ -440,7 +471,7 @@ The process works like this:
2. Save the current membership details to the snapshot.
3. Save the finite state machine to the snapshot.
4. End snapshotting with ``raft_end_snapshot``.
5. When the ``send_snapshot`` callback fires, the user must propogate the snapshot to the
other node
.
5. When the ``send_snapshot`` callback fires, the user must propogate the snapshot to the
peer
.
6. Once the peer has the snapshot, they call ``raft_begin_load_snapshot``.
7. Peer calls ``raft_add_node`` to add nodes as per the snapshot's membership info.
8. Peer calls ``raft_node_set_voting`` to nodes as per the snapshot's membership info.
...
...
include/raft.h
View file @
3c134468
...
...
@@ -12,14 +12,21 @@
#include "raft_types.h"
#define RAFT_ERR_NOT_LEADER -2
#define RAFT_ERR_ONE_VOTING_CHANGE_ONLY -3
#define RAFT_ERR_SHUTDOWN -4
#define RAFT_ERR_NOMEM -5
#define RAFT_ERR_NEEDS_SNAPSHOT -6
#define RAFT_ERR_SNAPSHOT_IN_PROGRESS -7
#define RAFT_ERR_SNAPSHOT_ALREADY_LOADED -8
#define RAFT_ERR_LAST -100
typedef
enum
{
RAFT_ERR_NOT_LEADER
=-
2
,
RAFT_ERR_ONE_VOTING_CHANGE_ONLY
=-
3
,
RAFT_ERR_SHUTDOWN
=-
4
,
RAFT_ERR_NOMEM
=-
5
,
RAFT_ERR_NEEDS_SNAPSHOT
=-
6
,
RAFT_ERR_SNAPSHOT_IN_PROGRESS
=-
7
,
RAFT_ERR_SNAPSHOT_ALREADY_LOADED
=-
8
,
RAFT_ERR_LAST
=-
100
,
}
raft_error_e
;
typedef
enum
{
RAFT_MEMBERSHIP_ADD
,
RAFT_MEMBERSHIP_REMOVE
,
}
raft_membership_e
;
#define RAFT_REQUESTVOTE_ERR_GRANTED 1
#define RAFT_REQUESTVOTE_ERR_NOT_GRANTED 0
...
...
@@ -64,7 +71,6 @@ typedef enum {
* Removing nodes is a 2 step process: first demote, then remove.
*/
RAFT_LOGTYPE_REMOVE_NODE
,
RAFT_LOGTYPE_SNAPSHOT
,
/**
* Users can piggyback the entry mechanism by specifying log types that
* are higher than RAFT_LOGTYPE_NUM.
...
...
@@ -297,7 +303,7 @@ typedef int (
* @param[in] raft The Raft server making this callback
* @param[in] user_data User data that is passed from Raft server
* @param[in] term Current term
* @param[in] vote The node value dicating we haven't voted for anybody
* @param[in] vote The node value dic
t
ating we haven't voted for anybody
* @return 0 on success */
typedef
int
(
*
func_persist_term_f
...
...
@@ -337,6 +343,25 @@ typedef int (
raft_index_t
entry_idx
);
/** Callback for being notified of membership changes.
*
* Implementing this callback is optional.
*
* Remove notification happens before the node is about to be removed.
*
* @param[in] raft The Raft server making this callback
* @param[in] user_data User data that is passed from Raft server
* @param[in] node The node that is the subject of this log. Could be NULL.
* @param[in] type The type of membership change */
typedef
void
(
*
func_membership_event_f
)
(
raft_server_t
*
raft
,
void
*
user_data
,
raft_node_t
*
node
,
raft_membership_e
type
);
typedef
struct
{
/** Callback for sending request vote messages */
...
...
@@ -388,6 +413,8 @@ typedef struct
/** Callback for detecting when a non-voting node has sufficient logs. */
func_node_has_sufficient_logs_f
node_has_sufficient_logs
;
func_membership_event_f
notify_membership_event
;
/** Callback for catching debugging log messages
* This callback is optional */
func_log_f
log
;
...
...
@@ -735,12 +762,12 @@ raft_node_id_t raft_node_get_id(raft_node_t* me_);
* @return get state of type raft_state_e. */
int
raft_get_state
(
raft_server_t
*
me_
);
/**
The
the most recent log's term
/**
Get
the most recent log's term
* @return the last log term */
raft_term_t
raft_get_last_log_term
(
raft_server_t
*
me_
);
/** Turn a node into a voting node.
* Voting nodes can take part in elections and in-regards to commiting entries,
* Voting nodes can take part in elections and in-regards to commit
t
ing entries,
* are counted in majorities. */
void
raft_node_set_voting
(
raft_node_t
*
node
,
int
voting
);
...
...
@@ -829,6 +856,9 @@ raft_index_t raft_get_first_entry_idx(raft_server_t* me_);
* This is usually the result of a snapshot being loaded.
* We need to send an appendentries response.
*
* This will remove all other nodes (not ourself). The user MUST use the
* snapshot to load the new membership information.
*
* @param[in] last_included_term Term of the last log of the snapshot
* @param[in] last_included_index Index of the last log of the snapshot
*
...
...
@@ -889,4 +919,19 @@ void raft_set_heap_functions(void *(*_malloc)(size_t),
void
*
(
*
_realloc
)(
void
*
,
size_t
),
void
(
*
_free
)(
void
*
));
/** Confirm that a node's voting status is final
* @param[in] node The node
* @param[in] voting Whether this node's voting status is committed or not */
void
raft_node_set_voting_committed
(
raft_node_t
*
me_
,
int
voting
);
/** Confirm that a node's voting status is final
* @param[in] node The node
* @param[in] committed Whether this node's membership is committed or not */
void
raft_node_set_addition_committed
(
raft_node_t
*
me_
,
int
committed
);
/** Check if a voting change is in progress
* @param[in] raft The Raft server
* @return 1 if a voting change is in progress */
int
raft_voting_change_is_in_progress
(
raft_server_t
*
me_
);
#endif
/* RAFT_H_ */
include/raft_log.h
View file @
3c134468
...
...
@@ -19,7 +19,7 @@ void log_clear(log_t* me_);
* Add entry to log.
* Don't add entry if we've already added this entry (based off ID)
* Don't add entries with ID=0
* @return 0 if unsucessful; 1 otherwise */
* @return 0 if unsuc
c
essful; 1 otherwise */
int
log_append_entry
(
log_t
*
me_
,
raft_entry_t
*
c
);
/**
...
...
include/raft_private.h
View file @
3c134468
src/raft_log.c
View file @
3c134468
...
...
@@ -80,21 +80,7 @@ int log_load_from_snapshot(log_t *me_, raft_index_t idx, raft_term_t term)
log_private_t
*
me
=
(
log_private_t
*
)
me_
;
log_clear
(
me_
);
raft_entry_t
ety
;
ety
.
data
.
len
=
0
;
ety
.
id
=
1
;
ety
.
term
=
term
;
ety
.
type
=
RAFT_LOGTYPE_SNAPSHOT
;
int
e
=
log_append_entry
(
me_
,
&
ety
);
if
(
e
!=
0
)
{
assert
(
0
);
return
e
;
}
me
->
base
=
idx
-
1
;
me
->
base
=
idx
;
return
0
;
}
...
...
@@ -202,10 +188,7 @@ raft_entry_t* log_get_at_idx(log_t* me_, raft_index_t idx)
if
(
idx
==
0
)
return
NULL
;
if
(
idx
<=
me
->
base
)
return
NULL
;
if
(
me
->
base
+
me
->
count
<
idx
)
if
(
me
->
base
+
me
->
count
<
idx
||
idx
<=
me
->
base
)
return
NULL
;
/* idx starts at 1 */
...
...
src/raft_server.c
View file @
3c134468
...
...
@@ -431,15 +431,25 @@ int raft_recv_appendentries(
{
raft_entry_t
*
ety
=
raft_get_entry_from_idx
(
me_
,
ae
->
prev_log_idx
);
/* Is a snapshot */
if
(
ae
->
prev_log_idx
==
me
->
snapshot_last_idx
)
{
if
(
me
->
snapshot_last_term
!=
ae
->
prev_log_term
)
{
/* Should never happen; something is seriously wrong! */
__log
(
me_
,
node
,
"Snapshot AE prev conflicts with committed entry"
);
e
=
RAFT_ERR_SHUTDOWN
;
goto
out
;
}
}
/* 2. Reply false if log doesn't contain an entry at prevLogIndex
whose term matches prevLogTerm (§5.3) */
if
(
!
ety
)
else
if
(
!
ety
)
{
__log
(
me_
,
node
,
"AE no log at prev_idx %d"
,
ae
->
prev_log_idx
);
goto
out
;
}
if
(
ety
->
term
!=
ae
->
prev_log_term
)
else
if
(
ety
->
term
!=
ae
->
prev_log_term
)
{
__log
(
me_
,
node
,
"AE term doesn't match prev_term (ie. %d vs %d) ci:%d comi:%d lcomi:%d pli:%d"
,
ety
->
term
,
ae
->
prev_log_term
,
raft_get_current_idx
(
me_
),
...
...
@@ -528,7 +538,7 @@ static int __should_grant_vote(raft_server_private_t* me, msg_requestvote_t* vr)
if
(
vr
->
term
<
raft_get_current_term
((
void
*
)
me
))
return
0
;
/* TODO: if voted for is candiate return 1 (if below checks pass) */
/* TODO: if voted for is candi
d
ate return 1 (if below checks pass) */
if
(
raft_already_voted
((
void
*
)
me
))
return
0
;
...
...
@@ -541,10 +551,20 @@ static int __should_grant_vote(raft_server_private_t* me, msg_requestvote_t* vr)
return
1
;
raft_entry_t
*
ety
=
raft_get_entry_from_idx
((
void
*
)
me
,
current_idx
);
if
(
ety
->
term
<
vr
->
last_log_term
)
int
ety_term
;
// TODO: add test
if
(
ety
)
ety_term
=
ety
->
term
;
else
if
(
!
ety
&&
me
->
snapshot_last_idx
==
current_idx
)
ety_term
=
me
->
snapshot_last_term
;
else
return
0
;
if
(
ety_term
<
vr
->
last_log_term
)
return
1
;
if
(
vr
->
last_log_term
==
ety
->
term
&&
current_idx
<=
vr
->
last_log_idx
)
if
(
vr
->
last_log_term
==
ety
_
term
&&
current_idx
<=
vr
->
last_log_idx
)
return
1
;
return
0
;
...
...
@@ -591,7 +611,7 @@ int raft_recv_requestvote(raft_server_t* me_,
else
r
->
vote_granted
=
0
;
/*
there
must be in an election. */
/* must be in an election. */
me
->
current_leader
=
NULL
;
me
->
timeout_elapsed
=
0
;
...
...
@@ -722,6 +742,7 @@ int raft_recv_entry(raft_server_t* me_,
int
e
=
raft_append_entry
(
me_
,
ety
);
if
(
0
!=
e
)
return
e
;
for
(
i
=
0
;
i
<
me
->
num_nodes
;
i
++
)
{
raft_node_t
*
node
=
me
->
nodes
[
i
];
...
...
@@ -748,6 +769,7 @@ int raft_recv_entry(raft_server_t* me_,
r
->
idx
=
raft_get_current_idx
(
me_
);
r
->
term
=
me
->
current_term
;
/* FIXME: is this required if raft_append_entry does this too? */
if
(
raft_entry_is_voting_cfg_change
(
ety
))
me
->
voting_cfg_change_log_idx
=
raft_get_current_idx
(
me_
);
...
...
@@ -892,11 +914,16 @@ int raft_send_appendentries(raft_server_t* me_, raft_node_t* node)
if
(
1
<
next_idx
)
{
raft_entry_t
*
prev_ety
=
raft_get_entry_from_idx
(
me_
,
next_idx
-
1
);
if
(
!
prev_ety
)
{
ae
.
prev_log_idx
=
me
->
snapshot_last_idx
;
ae
.
prev_log_term
=
me
->
snapshot_last_term
;
}
else
{
ae
.
prev_log_idx
=
next_idx
-
1
;
if
(
prev_ety
)
ae
.
prev_log_term
=
prev_ety
->
term
;
else
ae
.
prev_log_term
=
me
->
snapshot_last_term
;
}
}
__log
(
me_
,
node
,
"sending appendentries node: ci:%d comi:%d t:%d lc:%d pli:%d plt:%d"
,
...
...
@@ -961,7 +988,12 @@ raft_node_t* raft_add_node(raft_server_t* me_, void* udata, raft_node_id_t id, i
if
(
is_self
)
me
->
node
=
me
->
nodes
[
me
->
num_nodes
-
1
];
return
me
->
nodes
[
me
->
num_nodes
-
1
];
node
=
me
->
nodes
[
me
->
num_nodes
-
1
];
if
(
me
->
cb
.
notify_membership_event
)
me
->
cb
.
notify_membership_event
(
me_
,
raft_get_udata
(
me_
),
node
,
RAFT_MEMBERSHIP_ADD
);
return
node
;
}
raft_node_t
*
raft_add_non_voting_node
(
raft_server_t
*
me_
,
void
*
udata
,
raft_node_id_t
id
,
int
is_self
)
...
...
@@ -981,6 +1013,9 @@ void raft_remove_node(raft_server_t* me_, raft_node_t* node)
{
raft_server_private_t
*
me
=
(
raft_server_private_t
*
)
me_
;
if
(
me
->
cb
.
notify_membership_event
)
me
->
cb
.
notify_membership_event
(
me_
,
raft_get_udata
(
me_
),
node
,
RAFT_MEMBERSHIP_REMOVE
);
assert
(
node
);
int
i
,
found
=
0
;
...
...
@@ -1104,16 +1139,14 @@ void raft_offer_log(raft_server_t* me_, raft_entry_t* ety, const raft_index_t id
}
else
if
(
!
node
)
{
raft_node_t
*
node
=
raft_add_non_voting_node
(
me_
,
NULL
,
node_id
,
is_self
);
node
=
raft_add_non_voting_node
(
me_
,
NULL
,
node_id
,
is_self
);
assert
(
node
);
}
}
break
;
case
RAFT_LOGTYPE_ADD_NODE
:
if
(
!
node
)
{
node
=
raft_add_node
(
me_
,
NULL
,
node_id
,
is_self
);
}
assert
(
node
);
assert
(
raft_node_is_voting
(
node
));
break
;
...
...
@@ -1261,6 +1294,9 @@ int raft_end_snapshot(raft_server_t *me_)
if
(
!
me
->
snapshot_in_progress
||
me
->
snapshot_last_idx
==
0
)
return
-
1
;
assert
(
raft_get_num_snapshottable_logs
(
me_
)
!=
0
);
assert
(
me
->
snapshot_last_idx
==
raft_get_commit_idx
(
me_
));
/* If needed, remove compacted logs */
raft_index_t
i
=
log_get_base
(
me
->
log
)
+
1
,
end
=
raft_get_commit_idx
(
me_
);
for
(;
i
<=
end
;
i
++
)
...
...
@@ -1299,6 +1335,8 @@ int raft_end_snapshot(raft_server_t *me_)
}
}
assert
(
raft_get_log_count
(
me_
)
==
1
);
return
0
;
}
...
...
tests/CuTest.c
View file @
3c134468
tests/CuTest.h
View file @
3c134468
tests/raft_cffi.py
0 → 100644
View file @
3c134468
import
cffi
import
subprocess
ffi
=
cffi
.
FFI
()
ffi
.
set_source
(
"tests"
,
"""
"""
,
sources
=
"""
src/raft_log.c
src/raft_server.c
src/raft_server_properties.c
src/raft_node.c
"""
.
split
(),
include_dirs
=
[
"include"
],
)
library
=
ffi
.
compile
()
ffi
=
cffi
.
FFI
()
lib
=
ffi
.
dlopen
(
library
)
def
load
(
fname
):
return
'
\n
'
.
join
(
[
line
for
line
in
subprocess
.
check_output
(
[
"gcc"
,
"-E"
,
fname
]).
decode
(
'utf-8'
).
split
(
'
\n
'
)])
ffi
.
cdef
(
'void *malloc(size_t __size);'
)
ffi
.
cdef
(
load
(
'include/raft.h'
))
ffi
.
cdef
(
load
(
'include/raft_log.h'
))
tests/test_log.c
View file @
3c134468
...
...
@@ -399,10 +399,7 @@ void TestLog_load_from_snapshot(CuTest * tc)
CuAssertIntEquals
(
tc
,
0
,
log_get_current_idx
(
l
));
CuAssertIntEquals
(
tc
,
0
,
log_load_from_snapshot
(
l
,
10
,
5
));
CuAssertIntEquals
(
tc
,
10
,
log_get_current_idx
(
l
));
/* this is just a marker
* it should never be sent to any nodes because it is part of a snapshot */
CuAssertIntEquals
(
tc
,
1
,
log_count
(
l
));
CuAssertIntEquals
(
tc
,
0
,
log_count
(
l
));
}
void
TestLog_load_from_snapshot_clears_log
(
CuTest
*
tc
)
...
...
@@ -422,7 +419,7 @@ void TestLog_load_from_snapshot_clears_log(CuTest * tc)
CuAssertIntEquals
(
tc
,
2
,
log_get_current_idx
(
l
));
CuAssertIntEquals
(
tc
,
0
,
log_load_from_snapshot
(
l
,
10
,
5
));
CuAssertIntEquals
(
tc
,
1
,
log_count
(
l
));
CuAssertIntEquals
(
tc
,
0
,
log_count
(
l
));
CuAssertIntEquals
(
tc
,
10
,
log_get_current_idx
(
l
));
}
...
...
tests/test_server.c
View file @
3c134468
...
...
@@ -82,14 +82,6 @@ static int __raft_log_offer(raft_server_t* raft,
raft_entry_t
*
entry
,
raft_index_t
entry_idx
)
{
switch
(
entry
->
type
)
{
case
RAFT_LOGTYPE_ADD_NONVOTING_NODE
:
raft_add_non_voting_node
(
raft
,
NULL
,
atoi
(
entry
->
data
.
buf
),
0
);
break
;
case
RAFT_LOGTYPE_ADD_NODE
:
raft_add_node
(
raft
,
NULL
,
atoi
(
entry
->
data
.
buf
),
0
);
break
;
}
return
0
;
}
...
...
@@ -3963,10 +3955,10 @@ void TestRaft_leader_recv_appendentries_response_set_has_sufficient_logs_after_v
.
type
=
RAFT_LOGTYPE_ADD_NONVOTING_NODE
};
msg_entry_response_t
etyr
;
raft_recv_entry
(
r
,
&
ety
,
&
etyr
);
CuAssertIntEquals
(
tc
,
0
,
raft_recv_entry
(
r
,
&
ety
,
&
etyr
)
)
;
ety
.
id
++
;
ety
.
data
.
buf
=
"3"
;
raft_recv_entry
(
r
,
&
ety
,
&
etyr
);
CuAssertIntEquals
(
tc
,
0
,
raft_recv_entry
(
r
,
&
ety
,
&
etyr
)
)
;
msg_appendentries_response_t
aer
=
{
.
term
=
1
,
.
success
=
1
,
.
current_idx
=
2
,
.
first_idx
=
0
...
...
@@ -3999,4 +3991,3 @@ void TestRaft_leader_recv_appendentries_response_set_has_sufficient_logs_after_v
raft_recv_appendentries_response
(
r
,
raft_get_node
(
r
,
2
),
&
aer
);
CuAssertIntEquals
(
tc
,
2
,
has_sufficient_logs_flag
);
}
tests/test_snapshotting.c
View file @
3c134468
...
...
@@ -352,7 +352,7 @@ void TestRaft_follower_load_from_snapshot(CuTest * tc)
CuAssertIntEquals
(
tc
,
0
,
raft_get_log_count
(
r
));
CuAssertIntEquals
(
tc
,
0
,
raft_begin_load_snapshot
(
r
,
5
,
5
));
CuAssertIntEquals
(
tc
,
0
,
raft_end_load_snapshot
(
r
));
CuAssertIntEquals
(
tc
,
1
,
raft_get_log_count
(
r
));
CuAssertIntEquals
(
tc
,
0
,
raft_get_log_count
(
r
));
CuAssertIntEquals
(
tc
,
0
,
raft_get_num_snapshottable_logs
(
r
));
CuAssertIntEquals
(
tc
,
5
,
raft_get_commit_idx
(
r
));
CuAssertIntEquals
(
tc
,
5
,
raft_get_last_applied_idx
(
r
));
...
...
@@ -408,7 +408,7 @@ void TestRaft_follower_load_from_snapshot_fails_if_already_loaded(CuTest * tc)
CuAssertIntEquals
(
tc
,
0
,
raft_get_log_count
(
r
));
CuAssertIntEquals
(
tc
,
0
,
raft_begin_load_snapshot
(
r
,
5
,
5
));
CuAssertIntEquals
(
tc
,
0
,
raft_end_load_snapshot
(
r
));
CuAssertIntEquals
(
tc
,
1
,
raft_get_log_count
(
r
));
CuAssertIntEquals
(
tc
,
0
,
raft_get_log_count
(
r
));
CuAssertIntEquals
(
tc
,
0
,
raft_get_num_snapshottable_logs
(
r
));
CuAssertIntEquals
(
tc
,
5
,
raft_get_commit_idx
(
r
));
CuAssertIntEquals
(
tc
,
5
,
raft_get_last_applied_idx
(
r
));
...
...
@@ -527,7 +527,7 @@ void TestRaft_leader_sends_appendentries_when_node_next_index_was_compacted(CuTe
raft_set_current_term
(
r
,
2
);
CuAssertIntEquals
(
tc
,
0
,
raft_send_appendentries
(
r
,
node
));
CuAssertIntEquals
(
tc
,
2
,
ae
.
term
);
CuAssertIntEquals
(
tc
,
2
,
ae
.
prev_log_idx
);
CuAssertIntEquals
(
tc
,
3
,
ae
.
prev_log_idx
);
CuAssertIntEquals
(
tc
,
2
,
ae
.
prev_log_term
);
}
...
...
@@ -568,3 +568,69 @@ void TestRaft_recv_entry_fails_if_snapshot_in_progress(CuTest* tc)
ety
.
type
=
RAFT_LOGTYPE_ADD_NODE
;
CuAssertIntEquals
(
tc
,
RAFT_ERR_SNAPSHOT_IN_PROGRESS
,
raft_recv_entry
(
r
,
&
ety
,
&
cr
));
}
void
TestRaft_follower_recv_appendentries_is_successful_when_previous_log_idx_equals_snapshot_last_idx
(
CuTest
*
tc
)
{
raft_cbs_t
funcs
=
{
.
persist_term
=
__raft_persist_term
,
};
void
*
r
=
raft_new
();
raft_set_callbacks
(
r
,
&
funcs
,
NULL
);
raft_add_node
(
r
,
NULL
,
1
,
1
);
raft_add_node
(
r
,
NULL
,
2
,
0
);
CuAssertIntEquals
(
tc
,
0
,
raft_begin_load_snapshot
(
r
,
2
,
2
));
CuAssertIntEquals
(
tc
,
0
,
raft_end_load_snapshot
(
r
));
msg_appendentries_t
ae
;
msg_appendentries_response_t
aer
;
memset
(
&
ae
,
0
,
sizeof
(
msg_appendentries_t
));
ae
.
term
=
3
;
ae
.
prev_log_idx
=
2
;
ae
.
prev_log_term
=
2
;
/* include entries */
msg_entry_t
e
[
5
];
memset
(
&
e
,
0
,
sizeof
(
msg_entry_t
)
*
4
);
e
[
0
].
term
=
3
;
e
[
0
].
id
=
3
;
ae
.
entries
=
e
;
ae
.
n_entries
=
1
;
CuAssertIntEquals
(
tc
,
0
,
raft_recv_appendentries
(
r
,
raft_get_node
(
r
,
2
),
&
ae
,
&
aer
));
CuAssertIntEquals
(
tc
,
1
,
aer
.
success
);
}
void
TestRaft_leader_sends_appendentries_with_correct_prev_log_idx_when_snapshotted
(
CuTest
*
tc
)
{
raft_cbs_t
funcs
=
{
.
send_appendentries
=
sender_appendentries
,
.
log
=
NULL
};
void
*
sender
=
sender_new
(
NULL
);
void
*
r
=
raft_new
();
raft_set_callbacks
(
r
,
&
funcs
,
sender
);
CuAssertTrue
(
tc
,
NULL
!=
raft_add_node
(
r
,
NULL
,
1
,
1
));
CuAssertTrue
(
tc
,
NULL
!=
raft_add_node
(
r
,
NULL
,
2
,
0
));
CuAssertIntEquals
(
tc
,
0
,
raft_begin_load_snapshot
(
r
,
2
,
4
));
CuAssertIntEquals
(
tc
,
0
,
raft_end_load_snapshot
(
r
));
/* i'm leader */
raft_set_state
(
r
,
RAFT_STATE_LEADER
);
raft_node_t
*
p
=
raft_get_node_from_idx
(
r
,
1
);
CuAssertTrue
(
tc
,
NULL
!=
p
);
raft_node_set_next_idx
(
p
,
4
);
/* receive appendentries messages */
raft_send_appendentries
(
r
,
p
);
msg_appendentries_t
*
ae
=
sender_poll_msg_data
(
sender
);
CuAssertTrue
(
tc
,
NULL
!=
ae
);
CuAssertIntEquals
(
tc
,
2
,
ae
->
prev_log_term
);
CuAssertIntEquals
(
tc
,
4
,
ae
->
prev_log_idx
);
}
tests/virtraft2.py
0 → 100644
View file @
3c134468
This diff is collapsed.
Click to expand it.
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment