Skip to content

Commit 239ce24

Browse files
author
Justin Fudally
committed
Move patched raft package to internal
1 parent 3c2e0b4 commit 239ce24

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

46 files changed

+10156
-4
lines changed

go.mod

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,10 @@ module github.com/github/freno
33
go 1.13
44

55
require (
6+
github.com/armon/go-metrics v0.0.0-20190430140413-ec5e00d3c878
67
github.com/bradfitz/gomemcache v0.0.0-20190913173617-a41fca850d0b
78
github.com/go-sql-driver/mysql v1.5.0 // indirect
9+
github.com/hashicorp/go-msgpack v0.5.5
810
github.com/hashicorp/raft v1.1.2
911
github.com/hashicorp/raft-boltdb v0.0.0-20191021154308-4207f1bf0617
1012
github.com/julienschmidt/httprouter v1.3.0

internal/raft/.gitignore

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# Compiled Object files, Static and Dynamic libs (Shared Objects)
2+
*.o
3+
*.a
4+
*.so
5+
6+
# Folders
7+
_obj
8+
_test
9+
10+
# Architecture specific extensions/prefixes
11+
*.[568vq]
12+
[568vq].out
13+
14+
*.cgo1.go
15+
*.cgo2.c
16+
_cgo_defun.c
17+
_cgo_gotypes.go
18+
_cgo_export.*
19+
20+
_testmain.go
21+
22+
*.exe
23+
*.test

internal/raft/.travis.yml

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
language: go
2+
3+
go:
4+
- 1.4
5+
- 1.5
6+
- 1.6
7+
- tip
8+
9+
install: make deps
10+
script:
11+
- make integ
12+
13+
notifications:
14+
flowdock:
15+
secure: fZrcf9rlh2IrQrlch1sHkn3YI7SKvjGnAl/zyV5D6NROe1Bbr6d3QRMuCXWWdhJHzjKmXk5rIzbqJhUc0PNF7YjxGNKSzqWMQ56KcvN1k8DzlqxpqkcA3Jbs6fXCWo2fssRtZ7hj/wOP1f5n6cc7kzHDt9dgaYJ6nO2fqNPJiTc=
16+

internal/raft/LICENSE

Lines changed: 354 additions & 0 deletions
Large diffs are not rendered by default.

internal/raft/Makefile

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
DEPS = $(go list -f '{{range .TestImports}}{{.}} {{end}}' ./...)
2+
3+
test:
4+
go test -timeout=30s ./...
5+
6+
integ: test
7+
INTEG_TESTS=yes go test -timeout=3s -run=Integ ./...
8+
9+
deps:
10+
go get -d -v ./...
11+
echo $(DEPS) | xargs -n1 go get -d
12+
13+
cov:
14+
INTEG_TESTS=yes gocov test github.com/hashicorp/raft | gocov-html > /tmp/coverage.html
15+
open /tmp/coverage.html
16+
17+
.PHONY: test cov integ deps

internal/raft/README.md

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
raft [![Build Status](https://travis-ci.org/hashicorp/raft.png)](https://travis-ci.org/hashicorp/raft)
2+
====
3+
4+
raft is a [Go](http://www.golang.org) library that manages a replicated
5+
log and can be used with an FSM to manage replicated state machines. It
6+
is a library for providing [consensus](http://en.wikipedia.org/wiki/Consensus_(computer_science)).
7+
8+
The use cases for such a library are far-reaching as replicated state
9+
machines are a key component of many distributed systems. They enable
10+
building Consistent, Partition Tolerant (CP) systems, with limited
11+
fault tolerance as well.
12+
13+
## Building
14+
15+
If you wish to build raft you'll need Go version 1.2+ installed.
16+
17+
Please check your installation with:
18+
19+
```
20+
go version
21+
```
22+
23+
## Documentation
24+
25+
For complete documentation, see the associated [Godoc](http://godoc.org/github.com/hashicorp/raft).
26+
27+
To prevent complications with cgo, the primary backend `MDBStore` is in a separate repository,
28+
called [raft-mdb](http://github.com/hashicorp/raft-mdb). That is the recommended implementation
29+
for the `LogStore` and `StableStore`.
30+
31+
A pure Go backend using [BoltDB](https://github.com/boltdb/bolt) is also available called
32+
[raft-boltdb](https://github.com/hashicorp/raft-boltdb). It can also be used as a `LogStore`
33+
and `StableStore`.
34+
35+
## Protocol
36+
37+
raft is based on ["Raft: In Search of an Understandable Consensus Algorithm"](https://ramcloud.stanford.edu/wiki/download/attachments/11370504/raft.pdf)
38+
39+
A high level overview of the Raft protocol is described below, but for details please read the full
40+
[Raft paper](https://ramcloud.stanford.edu/wiki/download/attachments/11370504/raft.pdf)
41+
followed by the raft source. Any questions about the raft protocol should be sent to the
42+
[raft-dev mailing list](https://groups.google.com/forum/#!forum/raft-dev).
43+
44+
### Protocol Description
45+
46+
Raft nodes are always in one of three states: follower, candidate or leader. All
47+
nodes initially start out as a follower. In this state, nodes can accept log entries
48+
from a leader and cast votes. If no entries are received for some time, nodes
49+
self-promote to the candidate state. In the candidate state nodes request votes from
50+
their peers. If a candidate receives a quorum of votes, then it is promoted to a leader.
51+
The leader must accept new log entries and replicate to all the other followers.
52+
In addition, if stale reads are not acceptable, all queries must also be performed on
53+
the leader.
54+
55+
Once a cluster has a leader, it is able to accept new log entries. A client can
56+
request that a leader append a new log entry, which is an opaque binary blob to
57+
Raft. The leader then writes the entry to durable storage and attempts to replicate
58+
to a quorum of followers. Once the log entry is considered *committed*, it can be
59+
*applied* to a finite state machine. The finite state machine is application specific,
60+
and is implemented using an interface.
61+
62+
An obvious question relates to the unbounded nature of a replicated log. Raft provides
63+
a mechanism by which the current state is snapshotted, and the log is compacted. Because
64+
of the FSM abstraction, restoring the state of the FSM must result in the same state
65+
as a replay of old logs. This allows Raft to capture the FSM state at a point in time,
66+
and then remove all the logs that were used to reach that state. This is performed automatically
67+
without user intervention, and prevents unbounded disk usage as well as minimizing
68+
time spent replaying logs.
69+
70+
Lastly, there is the issue of updating the peer set when new servers are joining
71+
or existing servers are leaving. As long as a quorum of nodes is available, this
72+
is not an issue as Raft provides mechanisms to dynamically update the peer set.
73+
If a quorum of nodes is unavailable, then this becomes a very challenging issue.
74+
For example, suppose there are only 2 peers, A and B. The quorum size is also
75+
2, meaning both nodes must agree to commit a log entry. If either A or B fails,
76+
it is now impossible to reach quorum. This means the cluster is unable to add,
77+
or remove a node, or commit any additional log entries. This results in *unavailability*.
78+
At this point, manual intervention would be required to remove either A or B,
79+
and to restart the remaining node in bootstrap mode.
80+
81+
A Raft cluster of 3 nodes can tolerate a single node failure, while a cluster
82+
of 5 can tolerate 2 node failures. The recommended configuration is to either
83+
run 3 or 5 raft servers. This maximizes availability without
84+
greatly sacrificing performance.
85+
86+
In terms of performance, Raft is comparable to Paxos. Assuming stable leadership,
87+
committing a log entry requires a single round trip to half of the cluster.
88+
Thus performance is bound by disk I/O and network latency.
89+

internal/raft/bench/bench.go

Lines changed: 171 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,171 @@
1+
package raftbench
2+
3+
// raftbench provides common benchmarking functions which can be used by
4+
// anything which implements the raft.LogStore and raft.StableStore interfaces.
5+
// All functions accept these interfaces and perform benchmarking. This
6+
// makes comparing backend performance easier by sharing the tests.
7+
8+
import (
9+
"github.com/hashicorp/raft"
10+
"testing"
11+
)
12+
13+
func FirstIndex(b *testing.B, store raft.LogStore) {
14+
// Create some fake data
15+
var logs []*raft.Log
16+
for i := 1; i < 10; i++ {
17+
logs = append(logs, &raft.Log{Index: uint64(i), Data: []byte("data")})
18+
}
19+
if err := store.StoreLogs(logs); err != nil {
20+
b.Fatalf("err: %s", err)
21+
}
22+
b.ResetTimer()
23+
24+
// Run FirstIndex a number of times
25+
for n := 0; n < b.N; n++ {
26+
store.FirstIndex()
27+
}
28+
}
29+
30+
func LastIndex(b *testing.B, store raft.LogStore) {
31+
// Create some fake data
32+
var logs []*raft.Log
33+
for i := 1; i < 10; i++ {
34+
logs = append(logs, &raft.Log{Index: uint64(i), Data: []byte("data")})
35+
}
36+
if err := store.StoreLogs(logs); err != nil {
37+
b.Fatalf("err: %s", err)
38+
}
39+
b.ResetTimer()
40+
41+
// Run LastIndex a number of times
42+
for n := 0; n < b.N; n++ {
43+
store.LastIndex()
44+
}
45+
}
46+
47+
func GetLog(b *testing.B, store raft.LogStore) {
48+
// Create some fake data
49+
var logs []*raft.Log
50+
for i := 1; i < 10; i++ {
51+
logs = append(logs, &raft.Log{Index: uint64(i), Data: []byte("data")})
52+
}
53+
if err := store.StoreLogs(logs); err != nil {
54+
b.Fatalf("err: %s", err)
55+
}
56+
b.ResetTimer()
57+
58+
// Run GetLog a number of times
59+
for n := 0; n < b.N; n++ {
60+
if err := store.GetLog(5, new(raft.Log)); err != nil {
61+
b.Fatalf("err: %s", err)
62+
}
63+
}
64+
}
65+
66+
func StoreLog(b *testing.B, store raft.LogStore) {
67+
// Run StoreLog a number of times
68+
for n := 0; n < b.N; n++ {
69+
log := &raft.Log{Index: uint64(n), Data: []byte("data")}
70+
if err := store.StoreLog(log); err != nil {
71+
b.Fatalf("err: %s", err)
72+
}
73+
}
74+
}
75+
76+
func StoreLogs(b *testing.B, store raft.LogStore) {
77+
// Run StoreLogs a number of times. We want to set multiple logs each
78+
// run, so we create 3 logs with incrementing indexes for each iteration.
79+
for n := 0; n < b.N; n++ {
80+
b.StopTimer()
81+
offset := 3 * (n + 1)
82+
logs := []*raft.Log{
83+
&raft.Log{Index: uint64(offset - 2), Data: []byte("data")},
84+
&raft.Log{Index: uint64(offset - 1), Data: []byte("data")},
85+
&raft.Log{Index: uint64(offset), Data: []byte("data")},
86+
}
87+
b.StartTimer()
88+
89+
if err := store.StoreLogs(logs); err != nil {
90+
b.Fatalf("err: %s", err)
91+
}
92+
}
93+
}
94+
95+
func DeleteRange(b *testing.B, store raft.LogStore) {
96+
// Create some fake data. In this case, we create 3 new log entries for each
97+
// test case, and separate them by index in multiples of 10. This allows
98+
// some room so that we can test deleting ranges with "extra" logs to
99+
// to ensure we stop going to the database once our max index is hit.
100+
var logs []*raft.Log
101+
for n := 0; n < b.N; n++ {
102+
offset := 10 * n
103+
for i := offset; i < offset+3; i++ {
104+
logs = append(logs, &raft.Log{Index: uint64(i), Data: []byte("data")})
105+
}
106+
}
107+
if err := store.StoreLogs(logs); err != nil {
108+
b.Fatalf("err: %s", err)
109+
}
110+
b.ResetTimer()
111+
112+
// Delete a range of the data
113+
for n := 0; n < b.N; n++ {
114+
offset := 10 * n
115+
if err := store.DeleteRange(uint64(offset), uint64(offset+9)); err != nil {
116+
b.Fatalf("err: %s", err)
117+
}
118+
}
119+
}
120+
121+
func Set(b *testing.B, store raft.StableStore) {
122+
// Run Set a number of times
123+
for n := 0; n < b.N; n++ {
124+
if err := store.Set([]byte{byte(n)}, []byte("val")); err != nil {
125+
b.Fatalf("err: %s", err)
126+
}
127+
}
128+
}
129+
130+
func Get(b *testing.B, store raft.StableStore) {
131+
// Create some fake data
132+
for i := 1; i < 10; i++ {
133+
if err := store.Set([]byte{byte(i)}, []byte("val")); err != nil {
134+
b.Fatalf("err: %s", err)
135+
}
136+
}
137+
b.ResetTimer()
138+
139+
// Run Get a number of times
140+
for n := 0; n < b.N; n++ {
141+
if _, err := store.Get([]byte{0x05}); err != nil {
142+
b.Fatalf("err: %s", err)
143+
}
144+
}
145+
}
146+
147+
func SetUint64(b *testing.B, store raft.StableStore) {
148+
// Run SetUint64 a number of times
149+
for n := 0; n < b.N; n++ {
150+
if err := store.SetUint64([]byte{byte(n)}, uint64(n)); err != nil {
151+
b.Fatalf("err: %s", err)
152+
}
153+
}
154+
}
155+
156+
func GetUint64(b *testing.B, store raft.StableStore) {
157+
// Create some fake data
158+
for i := 0; i < 10; i++ {
159+
if err := store.SetUint64([]byte{byte(i)}, uint64(i)); err != nil {
160+
b.Fatalf("err: %s", err)
161+
}
162+
}
163+
b.ResetTimer()
164+
165+
// Run GetUint64 a number of times
166+
for n := 0; n < b.N; n++ {
167+
if _, err := store.Get([]byte{0x05}); err != nil {
168+
b.Fatalf("err: %s", err)
169+
}
170+
}
171+
}

0 commit comments

Comments
 (0)