-
Notifications
You must be signed in to change notification settings - Fork 225
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
initial snapshot state machine implementation #659
initial snapshot state machine implementation #659
Conversation
Makefile.am
Outdated
src/raft/flags.c \ | ||
src/raft/heap.c \ | ||
src/raft/log.c \ | ||
$(basic_dqlite_sources) \ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm a bit concerned about making the raft test artifacts link in arbitrary dqlite object code or even libdqlite itself---feels like we should maintain a separation here even if it requires compiling a subset of source files (like tracing.c) more than once.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The problem is that we are going to be using code from dqlite in Raft soon. We need to read pages, check the wal, etc. Right now I needed for the state machines but in the future I thought that we were going to combine both Raft and dqlite.
Makefile.am
Outdated
@@ -339,6 +337,9 @@ libsqlite3_la_SOURCES = sqlite3.c | |||
libsqlite3_la_CFLAGS = -g3 | |||
|
|||
unit_test_LDADD += libsqlite3.la | |||
if BUILD_RAFT_ENABLED | |||
raft_core_unit_test_LDADD += libsqlite3.la |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think tests that use libsqlite3 should be in the dqlite part of the tree.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We are using sqlite to create the hash-table, it does not have anything to do with dqlite per se. The raft code itself (code for creating the ht) has a dependency on SQLite, not only the tests. We could abstract it away to maintain Raft more agnostic but I am not sure there is a point any more in the extra complexity.
src/raft.h
Outdated
typedef unsigned int checksum_t; | ||
typedef unsigned long int pageno_t; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would prefer fixed-size integers here, also pageno_t
should be only 32 bits (that this is enough is fixed by the SQLite file formats).
src/raft.h
Outdated
|
||
const char *db; | ||
struct page_from_to page_from_to; | ||
unsigned int cs_page_no; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
pageno_t?
src/raft/recv_install_snapshot.c
Outdated
#include "src/raft.h" | ||
#include "src/raft/recv_install_snapshot.h" | ||
#include "src/raft/uv_os.h" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the relative paths here are wrong, they should be ../raft.h
etc. (Arguably we should switch to just passing -Isrc
into the build so we can dispense with all the ..
stuff but for now that's where we're at.)
src/raft/recv_install_snapshot.c
Outdated
|
||
struct insert_checksum_data *data = (struct insert_checksum_data *)req->data; | ||
sqlite3_stmt *stmt = data->state->ht_stmt; | ||
for (unsigned int i = 0; i < data->cs_nr; i++) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
unsigned
not unsigned int
src/raft/recv_install_snapshot.c
Outdated
//} | ||
int rv; | ||
|
||
struct insert_checksum_data *data = (struct insert_checksum_data *)req->data; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You should be able to drop the explicit cast here.
src/raft/recv_install_snapshot.c
Outdated
|
||
struct create_ht_data *data = (struct create_ht_data *)req->data; | ||
struct snapshot_leader_state *state = data->state; | ||
sprintf(db_filename, "ht-%lld", state->follower_id); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
snprintf
src/raft.h
Outdated
OK = 0, | ||
UNEXPECTED = 1, | ||
DONE = 2, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think these names should be prefixed (RAFT_RESULT_OK
etc.)
src/raft/recv_install_snapshot.c
Outdated
assert(leader != NULL); | ||
assert(msg != NULL); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
PRE
?
src/raft/recv_install_snapshot.c
Outdated
__attribute__((unused)) static bool leader_invariant(const struct sm *sm, | ||
int prev_state) | ||
{ | ||
bool rv; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would prefer that rv
always be an int
(that is, to use a different name here).
src/raft/recv_install_snapshot.h
Outdated
unsigned long last_page; | ||
unsigned long last_page_acked; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
pageno_t?
src/raft/recv_install_snapshot.h
Outdated
sqlite3 *ht; | ||
sqlite3_stmt *ht_stmt; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Generally I think we want to have all mentions of sqlite3 types and functions on the dqlite side of the boundary.
src/raft/recv_install_snapshot.c
Outdated
}, | ||
[LS_SIGNATURES_CALC_STARTED] = { | ||
.flags = 0, | ||
.name = "signatures_calc_started", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note (action is not needed): .name
is a label which will be plotted on observability diagrams as a state name.
src/raft.h
Outdated
typedef unsigned int checksum_t; | ||
typedef unsigned long int pageno_t; | ||
|
||
struct page_checksum_t { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We don't have this as a well-defined style (I believe it is a general practice), still, typically _t
postfixes are being used for types defined with typedefs.
src/raft/recv_install_snapshot.c
Outdated
void send_snapshot_cb(struct raft_io_send *req, int status) { | ||
(void)req; | ||
(void)status; | ||
struct snapshot_leader_state *state = (struct snapshot_leader_state *) req->data; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
not sure if it is a good moment for this (but it's always not late to step on the path of samurai).
It looks more maintainable and better from perspective of code analysis (there's no guarantee that req->data lives the same live as the structure containing it) just to embed struct raft_io_send
into struct snapshot_leader_state
and use container_of() to obtain struct snapshot_leader_state
back in the function.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wouldn't that be a problem in the context of scheduling async callbacks? We could have several raft_io_send
when processing several messages. Or should we control that and not process more than 1 message?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So far we have one, still no one prevents us from having a flexible array of raft_io_send
structures if needed.
struct x
{
unsigned sends_nr;
struct raft_io_send sends[];
}
src/raft/recv_install_snapshot.c
Outdated
PRE(msg->server_id == state->follower_id); | ||
// TODO: timeouts. | ||
int leader_state = sm_state(leader); | ||
if (leader_state == LS_FOLLOWER_ONLINE) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Suggestion: Case construction looks more idiomatic and natural here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was using a switch statement and had some problems when omitting one break;
and also when creating variables as case
does not create a scope.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not pushing for switch/case; JFYI: the following syntax is available:
switch (x) {
case Y: {
}
}
src/raft/recv_install_snapshot.c
Outdated
struct snapshot_leader_state *state = | ||
CONTAINER_OF(sm, struct snapshot_leader_state, sm); | ||
|
||
if (sm_state(sm) == LS_SIGNATURES_CALC_STARTED || |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if it's more useful, you can use >=
for states here.
src/raft/recv_install_snapshot.c
Outdated
void send_snapshot_cb(struct raft_io_send *req, int status) { | ||
(void)req; | ||
(void)status; | ||
struct snapshot_leader_state *state = (struct snapshot_leader_state *) req->data; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So far we have one, still no one prevents us from having a flexible array of raft_io_send
structures if needed.
struct x
{
unsigned sends_nr;
struct raft_io_send sends[];
}
src/raft/recv_install_snapshot.c
Outdated
PRE(msg->server_id == state->follower_id); | ||
// TODO: timeouts. | ||
int leader_state = sm_state(leader); | ||
if (leader_state == LS_FOLLOWER_ONLINE) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not pushing for switch/case; JFYI: the following syntax is available:
switch (x) {
case Y: {
}
}
04087ac
to
1619f9d
Compare
Codecov ReportAttention: Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## master #659 +/- ##
==========================================
- Coverage 77.41% 70.91% -6.50%
==========================================
Files 196 196
Lines 27269 26664 -605
Branches 5455 2833 -2622
==========================================
- Hits 21111 18910 -2201
- Misses 4297 5419 +1122
- Partials 1861 2335 +474 ☔ View full report in Codecov by Sentry. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good work!
sender_cb_op cb; | ||
}; | ||
|
||
struct timeout { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We had a talk about it, still I'll leave this comment here.
It could be beneficial to embed real implementation of timeout
into timeout
structure itself. Implementation would be ignored by the test code and will be used by the real implementation or in integration tests.
Same for work
- and sender
- structs
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agree, I was planning to introduce that gradually in the next patches.
enum leader_states { | ||
LS_F_ONLINE, | ||
LS_HT_WAIT, | ||
LS_F_NEEDS_SNAP, | ||
LS_CHECK_F_HAS_SIGS, | ||
LS_WAIT_SIGS, | ||
|
||
LS_REQ_SIG_LOOP, | ||
LS_RECV_SIG_PART, | ||
LS_PERSISTED_SIG_PART, | ||
|
||
LS_READ_PAGES_LOOP, | ||
LS_PAGE_READ, | ||
LS_PAGE_SENT, | ||
|
||
LS_SNAP_DONE, | ||
LS_FINAL, | ||
|
||
LS_NR, | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These names were possessed from other patches and can be updated according to your naming style (for instance, follower state names names are good), still please don't make them too long.
FS_SIGS_CALC_MSG_RECEIVED, | ||
FS_SIGS_CALC_DONE, | ||
|
||
FS_SIG_RECEIVING, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: The leader's SMs denote such states as XYZ_LOOP
. I'm fine with XYZ_*ING
either, however, please generalize this semantics in the next patches (don't do this right away, still have it in mind).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In the beginning the names were uniform but I wanted to convey somehow that the leader has a loop that involves sending messages and waiting for replies or timeouts, but the follower waits indefinitely for messages, the loop so to speak has different semantics.
I agree that the names should be more uniform in next patches.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks, this is great!
First version of state machines for leader and follower. Included very rudimentary unit tests which show progress as an event log and which can be easily converted to a graph.
abe3384
to
ee07596
Compare
No description provided.