Skip to content

Commit 53e000c

Browse files
committed
2 parents bb988b1 + 5c6c0eb commit 53e000c

File tree

9 files changed

+1676
-1
lines changed

9 files changed

+1676
-1
lines changed

contrib/raftable/Makefile

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,14 @@ OBJS = raftable.o worker.o state.o blockmem.o
33
EXTENSION = raftable
44
DATA = raftable--1.0.sql
55

6+
raftable.so: raft/lib/libraft.a
7+
8+
raft/lib/libraft.a:
9+
make -C raft
10+
611
EXTRA_INSTALL = contrib/raftable
712

8-
RAFT_PREFIX = $(HOME)/raft
13+
RAFT_PREFIX = raft
914
override LDFLAGS += -L$(RAFT_PREFIX)/lib -Wl,-whole-archive -lraft -Wl,-no-whole-archive
1015
override CFLAGS += -Wfatal-errors
1116
override CPPFLAGS += -I$(RAFT_PREFIX)/include

contrib/raftable/raft/LICENSE

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
Copyright (c) 2015-2016, Constantin S. Pan
2+
All rights reserved.
3+
4+
Redistribution and use in source and binary forms, with or without
5+
modification, are permitted provided that the following conditions are met:
6+
7+
1. Redistributions of source code must retain the above copyright notice, this
8+
list of conditions and the following disclaimer.
9+
10+
2. Redistributions in binary form must reproduce the above copyright notice,
11+
this list of conditions and the following disclaimer in the documentation
12+
and/or other materials provided with the distribution.
13+
14+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
15+
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
16+
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
17+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
18+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
20+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
21+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
22+
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

contrib/raftable/raft/Makefile

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
#override CC := clang
2+
override CFLAGS += -fpic -Wall -Wfatal-errors -O0 -g -pedantic -std=c99
3+
override CPPFLAGS += -I. -Iinclude -DDEBUG
4+
override HEART_LDFLAGS += -Llib -lraft -ljansson
5+
6+
AR = ar
7+
ARFLAGS = -cru
8+
9+
.PHONY: all clean bindir objdir libdir
10+
11+
lib/libraft.a: obj/raft.o obj/util.o | libdir objdir
12+
$(AR) $(ARFLAGS) lib/libraft.a obj/raft.o obj/util.o
13+
14+
all: lib/libraft.a bin/heart
15+
@echo Done.
16+
17+
bin/heart: obj/heart.o lib/libraft.a | bindir objdir
18+
$(CC) -o bin/heart $(CFLAGS) $(CPPFLAGS) \
19+
obj/heart.o $(HEART_LDFLAGS)
20+
21+
obj/%.o: src/%.c | objdir
22+
$(CC) $(CFLAGS) $(CPPFLAGS) -c -o $@ $<
23+
24+
obj/%.o: example/%.c | objdir
25+
$(CC) $(CFLAGS) $(CPPFLAGS) -c -o $@ $<
26+
27+
bindir:
28+
mkdir -p bin
29+
30+
objdir:
31+
mkdir -p obj
32+
33+
libdir:
34+
mkdir -p lib
35+
36+
clean:
37+
rm -rfv bin obj lib

contrib/raftable/raft/README

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
libraft
2+
=======
3+
4+
Raft protocol implementation in C.
5+
6+
Features
7+
--------
8+
9+
+ Leader Election
10+
+ Log Replication
11+
+ Log Compaction
12+
13+
TODO
14+
----
15+
16+
+ Membership Changes

contrib/raftable/raft/example/heart.c

Lines changed: 238 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,238 @@
1+
#define _POSIX_C_SOURCE 2
2+
#define _BSD_SOURCE
3+
#include <stdio.h>
4+
#include <stdlib.h>
5+
#include <string.h>
6+
#include <unistd.h>
7+
#include <assert.h>
8+
#include <time.h>
9+
#include <signal.h>
10+
#include <errno.h>
11+
#include <sys/wait.h>
12+
#include <sys/time.h>
13+
#include <stdbool.h>
14+
15+
#include <jansson.h>
16+
17+
#include "raft.h"
18+
#include "util.h"
19+
20+
static void applier(void *state, raft_update_t update, raft_bool_t snapshot) {
21+
json_error_t error;
22+
23+
json_t *patch = json_loadb(update.data, update.len, 0, &error);
24+
if (!patch) {
25+
shout(
26+
"error parsing json at position %d: %s\n",
27+
error.column,
28+
error.text
29+
);
30+
}
31+
32+
if (snapshot) {
33+
json_object_clear(state);
34+
}
35+
36+
if (json_object_update(state, patch)) {
37+
shout("error updating state\n");
38+
}
39+
40+
json_decref(patch);
41+
42+
char *encoded = json_dumps(state, JSON_INDENT(4) | JSON_SORT_KEYS);
43+
if (encoded) {
44+
debug(
45+
"applied %s: new state is %s\n",
46+
snapshot ? "a snapshot" : "an update",
47+
encoded
48+
);
49+
} else {
50+
shout(
51+
"applied %s, but new state could not be encoded\n",
52+
snapshot ? "a snapshot" : "an update"
53+
);
54+
}
55+
free(encoded);
56+
}
57+
58+
static raft_update_t snapshooter(void *state) {
59+
raft_update_t shot;
60+
shot.data = json_dumps(state, JSON_SORT_KEYS);
61+
shot.len = strlen(shot.data);
62+
if (shot.data) {
63+
debug("snapshot taken: %.*s\n", shot.len, shot.data);
64+
} else {
65+
shout("failed to take a snapshot\n");
66+
}
67+
68+
return shot;
69+
}
70+
71+
static void die(int signum) {
72+
debug("dying\n");
73+
exit(signum);
74+
}
75+
76+
static void usage(char *prog) {
77+
printf(
78+
"Usage: %s -i ID -r ID:HOST:PORT [-r ID:HOST:PORT ...] [-l LOGFILE]\n"
79+
" -l : Run as a daemon and write output to LOGFILE.\n",
80+
prog
81+
);
82+
}
83+
84+
raft_t raft;
85+
86+
static void main_loop(char *host, int port) {
87+
mstimer_t t;
88+
mstimer_reset(&t);
89+
90+
// create a UDP socket for raft
91+
int r = raft_create_udp_socket(raft);
92+
if (r == NOBODY) {
93+
die(EXIT_FAILURE);
94+
}
95+
96+
#define EMIT_EVERY_MS 1000
97+
int emit_ms = 0;
98+
while (true) {
99+
int ms;
100+
raft_msg_t m;
101+
102+
ms = mstimer_reset(&t);
103+
104+
raft_tick(raft, ms);
105+
m = raft_recv_message(raft);
106+
if (m) {
107+
raft_handle_message(raft, m);
108+
}
109+
110+
if (raft_is_leader(raft)) {
111+
emit_ms += ms;
112+
while (emit_ms > EMIT_EVERY_MS) {
113+
emit_ms -= EMIT_EVERY_MS;
114+
char buf[1000];
115+
char key = 'a' + rand() % 5;
116+
int value = rand() % 10000;
117+
sprintf(buf, "{\"key-%c\":%d}", key, value);
118+
shout("emit update: = %s\n", buf);
119+
raft_update_t update = {strlen(buf), buf, NULL};
120+
raft_emit(raft, update);
121+
}
122+
}
123+
}
124+
125+
close(r);
126+
}
127+
128+
int main(int argc, char **argv) {
129+
char *logfilename = NULL;
130+
bool daemonize = false;
131+
132+
int myid = NOBODY;
133+
int id;
134+
char *host;
135+
char *str;
136+
int port;
137+
int opt;
138+
139+
char *myhost = NULL;
140+
int myport;
141+
142+
json_t *state = json_object();
143+
144+
raft_config_t rc;
145+
rc.peernum_max = 64;
146+
rc.heartbeat_ms = 20;
147+
rc.election_ms_min = 150;
148+
rc.election_ms_max = 300;
149+
rc.log_len = 10;
150+
rc.chunk_len = 4;
151+
rc.msg_len_max = 500;
152+
rc.userdata = state;
153+
rc.applier = applier;
154+
rc.snapshooter = snapshooter;
155+
raft = raft_init(&rc);
156+
157+
int peernum = 0;
158+
while ((opt = getopt(argc, argv, "hi:r:l:")) != -1) {
159+
switch (opt) {
160+
case 'i':
161+
myid = atoi(optarg);
162+
break;
163+
case 'r':
164+
if (myid == NOBODY) {
165+
usage(argv[0]);
166+
return EXIT_FAILURE;
167+
}
168+
169+
str = strtok(optarg, ":");
170+
if (str) {
171+
id = atoi(str);
172+
} else {
173+
usage(argv[0]);
174+
return EXIT_FAILURE;
175+
}
176+
177+
host = strtok(NULL, ":");
178+
179+
str = strtok(NULL, ":");
180+
if (str) {
181+
port = atoi(str);
182+
} else {
183+
usage(argv[0]);
184+
return EXIT_FAILURE;
185+
}
186+
187+
if (!raft_peer_up(raft, id, host, port, id == myid)) {
188+
usage(argv[0]);
189+
return EXIT_FAILURE;
190+
}
191+
if (id == myid) {
192+
myhost = host;
193+
myport = port;
194+
}
195+
peernum++;
196+
break;
197+
case 'l':
198+
logfilename = optarg;
199+
daemonize = true;
200+
break;
201+
case 'h':
202+
usage(argv[0]);
203+
return EXIT_SUCCESS;
204+
default:
205+
usage(argv[0]);
206+
return EXIT_FAILURE;
207+
}
208+
}
209+
if (!myhost) {
210+
usage(argv[0]);
211+
return EXIT_FAILURE;
212+
}
213+
214+
if (logfilename) {
215+
if (!freopen(logfilename, "a", stdout)) {
216+
// nowhere to report this failure
217+
return EXIT_FAILURE;
218+
}
219+
if (!freopen(logfilename, "a", stderr)) {
220+
// nowhere to report this failure
221+
return EXIT_FAILURE;
222+
}
223+
}
224+
225+
if (daemonize) {
226+
if (daemon(true, true) == -1) {
227+
shout("could not daemonize: %s\n", strerror(errno));
228+
return EXIT_FAILURE;
229+
}
230+
}
231+
232+
signal(SIGTERM, die);
233+
signal(SIGINT, die);
234+
235+
main_loop(myhost, myport);
236+
237+
return EXIT_SUCCESS;
238+
}

0 commit comments

Comments
 (0)