-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Feat: leader election
- Loading branch information
Showing
23 changed files
with
998 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
.idea | ||
tmp |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
source 'https://rubygems.org' | ||
|
||
gemspec | ||
|
||
group :development, :test do | ||
gem 'pry' | ||
end | ||
|
||
group :test do | ||
gem 'rspec' | ||
gem 'timecop' | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
PATH | ||
remote: . | ||
specs: | ||
byraft (0.1.0) | ||
grpc (~> 1) | ||
|
||
GEM | ||
remote: https://rubygems.org/ | ||
specs: | ||
coderay (1.1.3) | ||
diff-lcs (1.5.0) | ||
google-protobuf (3.21.2) | ||
googleapis-common-protos-types (1.3.2) | ||
google-protobuf (~> 3.14) | ||
grpc (1.47.0) | ||
google-protobuf (~> 3.19) | ||
googleapis-common-protos-types (~> 1.0) | ||
method_source (1.0.0) | ||
pry (0.14.1) | ||
coderay (~> 1.1) | ||
method_source (~> 1.0) | ||
rspec (3.11.0) | ||
rspec-core (~> 3.11.0) | ||
rspec-expectations (~> 3.11.0) | ||
rspec-mocks (~> 3.11.0) | ||
rspec-core (3.11.0) | ||
rspec-support (~> 3.11.0) | ||
rspec-expectations (3.11.0) | ||
diff-lcs (>= 1.2.0, < 2.0) | ||
rspec-support (~> 3.11.0) | ||
rspec-mocks (3.11.1) | ||
diff-lcs (>= 1.2.0, < 2.0) | ||
rspec-support (~> 3.11.0) | ||
rspec-support (3.11.0) | ||
timecop (0.9.5) | ||
|
||
PLATFORMS | ||
arm64-darwin-21 | ||
ruby | ||
|
||
DEPENDENCIES | ||
byraft! | ||
pry | ||
rspec | ||
timecop | ||
|
||
BUNDLED WITH | ||
2.3.7 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,33 @@ | ||
# byraft | ||
# Byraft | ||
|
||
Byraft is an implementation of Raft consensus algorithm in Ruby using gRPC. | ||
|
||
## Example | ||
|
||
Three nodes communicates each other with the following configuration. | ||
- `election timeout` between 1 and 2 sec | ||
- `update period` as 0.1 sec | ||
- `verbose` | ||
- Nodes | ||
- #1 on localhost:50051 | ||
- #2 on localhost:50052 | ||
- #3 on localhost:50053 | ||
|
||
Run examples in different terminal tabs. | ||
|
||
```shell | ||
bin/example 1 # terminal 1 | ||
bin/example 2 # terminal 2 | ||
bin/example 3 # terminal 3 | ||
``` | ||
|
||
## Test | ||
|
||
```shell | ||
# rspec | ||
bin/test | ||
``` | ||
|
||
## Reference | ||
|
||
[paper](https://raft.github.io/raft.pdf) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
#!/usr/bin/env ruby | ||
|
||
require 'optparse' | ||
require 'byraft' | ||
|
||
params = {} | ||
OptionParser.new do |parser| | ||
parser.banner = <<-BANNER | ||
Run byraft application | ||
Usage: byraft [options] | ||
-h --help Print help | ||
-i <id> Configure id for current node | ||
-p <port> Configure port for current node | ||
-n <id>=<address> Configure node id and address | ||
-e <range> Configure election timeout in sec (ex) 0.1, 0.1:0.3 | ||
-u Configure update period in sec | ||
-v Configure verbose option | ||
Option: | ||
BANNER | ||
parser.on("-h", "--help") do | ||
puts parser | ||
exit | ||
end | ||
parser.on("-i", "--id <id>", String) do |id| | ||
params[:id] = id | ||
end | ||
parser.on("-p", "--port <port>", Integer) do |port| | ||
params[:port] = port | ||
end | ||
parser.on("-n", "--node <id>=<address>", String) do |str| | ||
id, address = str.split('=') | ||
params[:node] ||= {} | ||
params[:node][id] = address | ||
end | ||
parser.on("-e", "--e <range>", String) do |str| | ||
s, e = str.split(':') | ||
e = s unless e | ||
s, e = s.to_f, e.to_f | ||
params[:election_timeout] = s..e | ||
end | ||
parser.on("-u", "--update <period>", Float) do |p| | ||
params[:update_period] = p | ||
end | ||
parser.on("-v", "--verbose", TrueClass) do |v| | ||
params[:verbose] = v | ||
end | ||
end.parse! | ||
|
||
unless params[:id] | ||
puts "Please configure id (ex) -i 1" | ||
exit | ||
end | ||
|
||
unless params[:node] | ||
puts "Please configure nodes (ex) -n 1=localhost:50051" | ||
exit | ||
end | ||
|
||
id, port, nodes, opts = params[:id], params[:port], params[:node], params.except(:id, :port, :node) | ||
Byraft.start(id, port, nodes, **opts) | ||
Byraft.join |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
#!/bin/bash | ||
|
||
port1=50051 | ||
port2=50052 | ||
port3=50053 | ||
if [ -z ${1+x} ]; then echo "Please set id from 1 to 3"; fi | ||
script_dir=$(dirname $0) | ||
id=$1 | ||
case ${id} in | ||
1) | ||
port=${port1} | ||
;; | ||
2) | ||
port=${port2} | ||
;; | ||
3) | ||
port=${port3} | ||
esac | ||
bundle exec ${script_dir}/byraft -i ${id} -p ${port} -n 1=localhost:${port1} -n 2=localhost:${port2} -n 3=localhost:${port3} -v |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
#!/bin/bash | ||
|
||
bundle exec rspec |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
Gem::Specification.new do |s| | ||
s.name = 'byraft' | ||
s.version = '0.1.0' | ||
s.platform = Gem::Platform::RUBY | ||
s.authors = ["taekop"] | ||
s.email = ["taekop@naver.com"] | ||
s.homepage = 'http://github.com/taekop/byraft' | ||
s.summary = "Raft Implemention in Ruby" | ||
s.description = s.summary | ||
s.license = 'MIT' | ||
|
||
s.add_dependency 'grpc', '~> 1' | ||
|
||
s.files = ["lib/byraft.rb"] | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
require 'byraft/node' | ||
|
||
module Byraft | ||
# @param id [String] | ||
# @param nodes [Hash]: id as key, and address as value | ||
# | ||
# @example | ||
# | ||
# Byraft.start('1', 50051, { 1 => '0.0.0.0:50051', 2 => '0.0.0.0:50052', 3 => '0.0.0.0:50053' }) | ||
def self.start(id, port, nodes, **opts) | ||
node = Node.new(id, nodes, **opts) | ||
address = "localhost:#{port}" | ||
@server_thread = Thread.new do | ||
puts "Node##{id} running..." if opts[:verbose] | ||
s = ::GRPC::RpcServer.new | ||
s.add_http2_port(address, :this_port_is_insecure) | ||
s.handle(node) | ||
s.run_till_terminated_or_interrupted(['INT', 'TERM']) | ||
end | ||
@ping_thread = Thread.new do | ||
loop do | ||
sleep(node.update_period) | ||
node.update | ||
end | ||
end | ||
end | ||
|
||
def self.join | ||
@server_thread.join | ||
@ping_thread.kill | ||
end | ||
|
||
def self.stop | ||
@server_thread.kill | ||
@ping_thread.kill | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
require 'google/protobuf' | ||
|
||
Google::Protobuf::DescriptorPool.generated_pool.build do | ||
add_message "byraft.Entry" do | ||
optional :index, :int32, 1 | ||
optional :term, :int32, 2 | ||
optional :command, :string, 3 | ||
end | ||
|
||
add_message "byraft.AppendEntriesRequest" do | ||
optional :term, :int32, 1 | ||
optional :leader_id, :string, 2 | ||
optional :prev_log_index, :int32, 3 | ||
optional :prev_log_term, :int32, 4 | ||
repeated :entries, :message, 5, "byraft.Entry" | ||
optional :leader_commit, :int32, 6 | ||
end | ||
|
||
add_message "byraft.AppendEntriesResponse" do | ||
optional :term, :int32, 1 | ||
optional :success, :bool, 2 | ||
end | ||
|
||
add_message "byraft.RequestVoteRequest" do | ||
optional :term, :int32, 1 | ||
optional :candidate_id, :string, 2 | ||
optional :last_log_index, :int32, 3 | ||
optional :last_log_term, :int32, 4 | ||
end | ||
|
||
add_message "byraft.RequestVoteResponse" do | ||
optional :term, :int32, 1 | ||
optional :vote_granted, :bool, 2 | ||
end | ||
end | ||
|
||
module Byraft | ||
module GRPC | ||
Entry = Google::Protobuf::DescriptorPool.generated_pool.lookup("byraft.Entry").msgclass | ||
AppendEntriesRequest = Google::Protobuf::DescriptorPool.generated_pool.lookup("byraft.AppendEntriesRequest").msgclass | ||
AppendEntriesResponse = Google::Protobuf::DescriptorPool.generated_pool.lookup("byraft.AppendEntriesResponse").msgclass | ||
RequestVoteRequest = Google::Protobuf::DescriptorPool.generated_pool.lookup("byraft.RequestVoteRequest").msgclass | ||
RequestVoteResponse = Google::Protobuf::DescriptorPool.generated_pool.lookup("byraft.RequestVoteResponse").msgclass | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
require 'grpc' | ||
|
||
require 'byraft/grpc/byraft_pb' | ||
|
||
module Byraft | ||
module GRPC | ||
# Interface exported by the server. | ||
module Byraft | ||
def self.included klass | ||
klass.class_eval do | ||
include ::GRPC::GenericService | ||
|
||
klass.marshal_class_method = :encode | ||
klass.unmarshal_class_method = :decode | ||
klass.service_name = 'byraft.RaftNode' | ||
|
||
klass.rpc :AppendEntries, AppendEntriesRequest, AppendEntriesResponse | ||
klass.rpc :RequestVote, RequestVoteRequest, RequestVoteResponse | ||
end | ||
end | ||
end | ||
|
||
Stub = Class.new.include(Byraft).rpc_stub_class | ||
end | ||
end |
Oops, something went wrong.