1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219
|
#include "../lib/cluster.h"
#include "../lib/runner.h"
struct fixture
{
FIXTURE_CLUSTER;
};
static void *setUp(const MunitParameter params[], MUNIT_UNUSED void *user_data)
{
struct fixture *f = munit_malloc(sizeof *f);
SETUP_CLUSTER();
return f;
}
static void tearDown(void *data)
{
struct fixture *f = data;
TEAR_DOWN_CLUSTER();
free(f);
}
SUITE(start)
/* Start a server that has no persisted state whatsoever. */
TEST(start, NoState, setUp, tearDown, 0, NULL)
{
struct fixture *f = data;
CLUSTER_START(1);
CLUSTER_TRACE("[ 0] 1 > no state\n");
munit_assert_ullong(raft_timeout(CLUSTER_RAFT(1)), ==, 100);
return MUNIT_OK;
}
/* Start a server that has a persisted its term. */
TEST(start, PersistedTerm, setUp, tearDown, 0, NULL)
{
struct fixture *f = data;
CLUSTER_SET_TERM(1 /* ID */, 1 /* term */);
CLUSTER_START(1 /* ID */);
CLUSTER_TRACE("[ 0] 1 > term 1\n");
return MUNIT_OK;
}
/* Start a server that has a persisted its term and has a snapshot. */
TEST(start, PersistedTermAndSnapshot, setUp, tearDown, 0, NULL)
{
struct fixture *f = data;
CLUSTER_SET_TERM(1 /* ID */, 2 /* term */);
CLUSTER_SET_SNAPSHOT(1, /* ID */
6, /* last index */
2, /* last term */
2, /* N servers */
2, /* N voting */
1 /* conf index */);
CLUSTER_START(1 /* ID */);
CLUSTER_TRACE("[ 0] 1 > term 2, 1 snapshot (6^2)\n");
return MUNIT_OK;
}
/* Start a server that has a persisted its term and has the initial bootstrap
* log entry. */
TEST(start, PersistedTermAndEntries, setUp, tearDown, 0, NULL)
{
struct fixture *f = data;
CLUSTER_SET_TERM(1 /* ID */, 1 /* term */);
CLUSTER_ADD_ENTRY(1 /* ID */, RAFT_CHANGE, 2 /* servers */, 2 /* voters */);
CLUSTER_START(1 /* ID */);
CLUSTER_TRACE("[ 0] 1 > term 1, 1 entry (1^1)\n");
return MUNIT_OK;
}
/* There are two servers. The first has a snapshot present and no other
* entries. */
TEST(start, OneSnapshotAndNoEntries, setUp, tearDown, 0, NULL)
{
struct fixture *f = data;
CLUSTER_SET_TERM(1 /* ID */, 2 /* term */);
CLUSTER_SET_SNAPSHOT(1, /* ID */
6, /* last index */
2, /* last term */
2, /* N servers */
2, /* N voting */
1 /* conf index */);
CLUSTER_SET_TERM(2 /* ID */, 1 /* term */);
CLUSTER_ADD_ENTRY(2 /* ID */, RAFT_CHANGE, 2 /* servers */, 2 /* voters */);
/* Server 1 becomes leader. */
CLUSTER_START(1 /* ID */);
CLUSTER_START(2 /* ID */);
CLUSTER_TRACE(
"[ 0] 1 > term 2, 1 snapshot (6^2)\n"
"[ 0] 2 > term 1, 1 entry (1^1)\n"
"[ 100] 1 > timeout as follower\n"
" convert to candidate, start election for term 3\n"
"[ 110] 2 > recv request vote from server 1\n"
" remote term is higher (3 vs 1) -> bump term\n"
" remote log is more recent (6^2 vs 1^1) -> grant vote\n"
"[ 120] 1 > recv request vote result from server 2\n"
" quorum reached with 2 votes out of 2 -> convert to leader\n"
" probe server 2 sending a heartbeat (no entries)\n");
/* It eventually replicates the snapshot. */
CLUSTER_TRACE(
"[ 130] 2 > recv append entries from server 1\n"
" missing previous entry (6^2) -> reject\n"
"[ 140] 1 > recv append entries result from server 2\n"
" log mismatch -> send old entries\n"
" missing previous entry at index 1 -> needs snapshot\n"
" sending snapshot (6^2) to server 2\n"
"[ 150] 2 > recv install snapshot from server 1\n"
" start persisting snapshot (6^2)\n"
"[ 160] 2 > persisted snapshot (6^2)\n"
" send success result to 1\n");
/* When the server 1 receives the result it immediately transition server 2
* to pipeline mode. */
CLUSTER_TRACE(
"[ 170] 1 > recv append entries result from server 2\n"
" pipeline server 2 sending a heartbeat (no entries)\n");
return MUNIT_OK;
}
/* There are two servers. The first has a snapshot along with some follow-up
* entries. */
TEST(start, OneSnapshotAndSomeFollowUpEntries, setUp, tearDown, 0, NULL)
{
struct fixture *f = data;
CLUSTER_SET_TERM(1 /* ID */, 2 /* term */);
CLUSTER_SET_SNAPSHOT(1, /* ID */
6, /* last index */
2, /* last term */
2, /* N servers */
2, /* N voting */
1 /* conf index */);
CLUSTER_ADD_ENTRY(1 /* ID */, RAFT_COMMAND, 1 /* term */, 0 /* payload */);
CLUSTER_ADD_ENTRY(1 /* ID */, RAFT_COMMAND, 1 /* term */, 0 /* payload */);
CLUSTER_SET_TERM(2 /* ID */, 1 /* term */);
CLUSTER_ADD_ENTRY(2 /* ID */, RAFT_CHANGE, 2 /* servers */, 2 /* voters */);
/* Server 1 becomes leader. */
CLUSTER_START(1 /* ID */);
CLUSTER_START(2 /* ID */);
CLUSTER_TRACE(
"[ 0] 1 > term 2, 1 snapshot (6^2), 2 entries (7^1..8^1)\n"
"[ 0] 2 > term 1, 1 entry (1^1)\n"
"[ 100] 1 > timeout as follower\n"
" convert to candidate, start election for term 3\n"
"[ 110] 2 > recv request vote from server 1\n"
" remote term is higher (3 vs 1) -> bump term\n"
" remote log is longer (8^1 vs 1^1) -> grant vote\n"
"[ 120] 1 > recv request vote result from server 2\n"
" quorum reached with 2 votes out of 2 -> convert to leader\n"
" replicate 1 new barrier entry (9^3)\n"
" probe server 2 sending 1 entry (9^3)\n"
"[ 130] 1 > persisted 1 entry (9^3)\n"
" next uncommitted entry (8^1) has 1 vote out of 2\n"
"[ 130] 2 > recv append entries from server 1\n"
" missing previous entry (8^1) -> reject\n"
"[ 140] 1 > recv append entries result from server 2\n"
" log mismatch -> send old entries\n"
" missing previous entry at index 1 -> needs snapshot\n"
" sending snapshot (6^2) to server 2\n");
test_cluster_step(&f->cluster_);
test_cluster_step(&f->cluster_);
test_cluster_step(&f->cluster_);
test_cluster_step(&f->cluster_);
return MUNIT_OK;
}
/* There is a single voting server in the cluster, which immediately elects
* itself when starting. */
TEST(start, SingleVotingSelfElect, setUp, tearDown, 0, NULL)
{
struct fixture *f = data;
CLUSTER_SET_TERM(1 /* ID */, 1 /* term */);
CLUSTER_ADD_ENTRY(1 /* ID */, RAFT_CHANGE, 1 /* servers */, 1 /* voters */);
CLUSTER_START(1 /* ID */);
CLUSTER_TRACE(
"[ 0] 1 > term 1, 1 entry (1^1)\n"
" self elect and convert to leader\n");
munit_assert_int(raft_state(CLUSTER_RAFT(1)), ==, RAFT_LEADER);
/* The server can make progress alone. */
CLUSTER_SUBMIT(1 /* ID */, COMMAND, 8 /* size */);
CLUSTER_TRACE(
"[ 0] 1 > submit 1 new client entry\n"
" replicate 1 new command entry (2^1)\n"
"[ 10] 1 > persisted 1 entry (2^1)\n"
" commit 1 new entry (2^1)\n");
return MUNIT_OK;
}
/* There are two servers in the cluster, one is voting and the other is
* not. When started, the non-voting server does not elects itself. */
TEST(start, SingleVotingNotUs, setUp, tearDown, 0, NULL)
{
struct fixture *f = data;
CLUSTER_SET_TERM(2 /* ID */, 1 /* term */);
CLUSTER_ADD_ENTRY(2 /* ID */, RAFT_CHANGE, 2 /* servers */, 1 /* voters */);
CLUSTER_START(2 /* ID */);
CLUSTER_TRACE("[ 0] 2 > term 1, 1 entry (1^1)\n");
munit_assert_int(raft_state(CLUSTER_RAFT(2)), ==, RAFT_FOLLOWER);
return MUNIT_OK;
}
|