rswim 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +17 -0
- data/.rspec +3 -0
- data/.ruby-version +1 -0
- data/.travis.yml +6 -0
- data/CHANGELOG.md +1 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +79 -0
- data/Guardfile +42 -0
- data/LICENSE.txt +21 -0
- data/README.md +72 -0
- data/Rakefile +6 -0
- data/bin/console +14 -0
- data/bin/run_node +30 -0
- data/bin/setup +8 -0
- data/lib/rswim.rb +33 -0
- data/lib/rswim/agent.rb +69 -0
- data/lib/rswim/directory.rb +27 -0
- data/lib/rswim/integration/deserializer.rb +51 -0
- data/lib/rswim/integration/serializer.rb +29 -0
- data/lib/rswim/integration/udp/node.rb +61 -0
- data/lib/rswim/logger.rb +31 -0
- data/lib/rswim/member/ack_responder.rb +22 -0
- data/lib/rswim/member/base.rb +11 -0
- data/lib/rswim/member/forwarding_state/base.rb +33 -0
- data/lib/rswim/member/forwarding_state/forwarding_ack.rb +26 -0
- data/lib/rswim/member/forwarding_state/ready.rb +24 -0
- data/lib/rswim/member/health_state/alive.rb +31 -0
- data/lib/rswim/member/health_state/base.rb +71 -0
- data/lib/rswim/member/health_state/confirmed.rb +31 -0
- data/lib/rswim/member/health_state/suspected.rb +32 -0
- data/lib/rswim/member/me.rb +45 -0
- data/lib/rswim/member/peer.rb +85 -0
- data/lib/rswim/member/transmission_state/awaiting_ack.rb +38 -0
- data/lib/rswim/member/transmission_state/base.rb +48 -0
- data/lib/rswim/member/transmission_state/off.rb +21 -0
- data/lib/rswim/member/transmission_state/ready.rb +27 -0
- data/lib/rswim/member/transmission_state/sending_ping.rb +30 -0
- data/lib/rswim/member/transmission_state/sending_ping_request.rb +31 -0
- data/lib/rswim/member_pool.rb +114 -0
- data/lib/rswim/message.rb +18 -0
- data/lib/rswim/node.rb +50 -0
- data/lib/rswim/pipe.rb +32 -0
- data/lib/rswim/protocol_state.rb +65 -0
- data/lib/rswim/status_report.rb +29 -0
- data/lib/rswim/update_entry.rb +17 -0
- data/lib/rswim/version.rb +5 -0
- data/log/.keep +0 -0
- data/rswim.gemspec +37 -0
- data/tmp/.keep +0 -0
- metadata +195 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 672b410f63b01447de5ba794f65d20f4da0fc0966381c39d3fa333cd21482717
|
4
|
+
data.tar.gz: 369e88eeaf54294cda7b98ad65c9ee6f8c79c00b8ba362e9e3d844fb6f450ada
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 7236a1d7c5159b4113672f3eca7c931b9978ab54a353ea44198000e0894c4ef9266b9fcb2dcf6045476402057748682388200322a2b2eba3f9754c10c5091402
|
7
|
+
data.tar.gz: 48f657cd9ff5d89cb93d9c684d546e876dba5b02a16183b50c0794b37e779f989bf9869e39de5aa2c91539ed39fa2380af091c2cf754ac50bdd37628d1477e06
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.ruby-version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
2.7.1
|
data/.travis.yml
ADDED
data/CHANGELOG.md
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
# 1.0.0 Complete implementation for UDP plus simple, human readable serialisation of messages
|
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,79 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
rswim (1.0.0)
|
5
|
+
zeitwerk (~> 2.2)
|
6
|
+
|
7
|
+
GEM
|
8
|
+
remote: https://rubygems.org/
|
9
|
+
specs:
|
10
|
+
byebug (11.1.3)
|
11
|
+
coderay (1.1.3)
|
12
|
+
diff-lcs (1.4.4)
|
13
|
+
ffi (1.13.1)
|
14
|
+
formatador (0.2.5)
|
15
|
+
fuubar (2.5.0)
|
16
|
+
rspec-core (~> 3.0)
|
17
|
+
ruby-progressbar (~> 1.4)
|
18
|
+
guard (2.16.2)
|
19
|
+
formatador (>= 0.2.4)
|
20
|
+
listen (>= 2.7, < 4.0)
|
21
|
+
lumberjack (>= 1.0.12, < 2.0)
|
22
|
+
nenv (~> 0.1)
|
23
|
+
notiffany (~> 0.0)
|
24
|
+
pry (>= 0.9.12)
|
25
|
+
shellany (~> 0.0)
|
26
|
+
thor (>= 0.18.1)
|
27
|
+
guard-compat (1.2.1)
|
28
|
+
guard-rspec (4.7.3)
|
29
|
+
guard (~> 2.1)
|
30
|
+
guard-compat (~> 1.1)
|
31
|
+
rspec (>= 2.99.0, < 4.0)
|
32
|
+
listen (3.2.1)
|
33
|
+
rb-fsevent (~> 0.10, >= 0.10.3)
|
34
|
+
rb-inotify (~> 0.9, >= 0.9.10)
|
35
|
+
lumberjack (1.2.8)
|
36
|
+
method_source (1.0.0)
|
37
|
+
nenv (0.3.0)
|
38
|
+
notiffany (0.1.3)
|
39
|
+
nenv (~> 0.1)
|
40
|
+
shellany (~> 0.0)
|
41
|
+
pry (0.13.1)
|
42
|
+
coderay (~> 1.1)
|
43
|
+
method_source (~> 1.0)
|
44
|
+
rake (12.3.3)
|
45
|
+
rb-fsevent (0.10.4)
|
46
|
+
rb-inotify (0.10.1)
|
47
|
+
ffi (~> 1.0)
|
48
|
+
rspec (3.9.0)
|
49
|
+
rspec-core (~> 3.9.0)
|
50
|
+
rspec-expectations (~> 3.9.0)
|
51
|
+
rspec-mocks (~> 3.9.0)
|
52
|
+
rspec-core (3.9.3)
|
53
|
+
rspec-support (~> 3.9.3)
|
54
|
+
rspec-expectations (3.9.2)
|
55
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
56
|
+
rspec-support (~> 3.9.0)
|
57
|
+
rspec-mocks (3.9.1)
|
58
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
59
|
+
rspec-support (~> 3.9.0)
|
60
|
+
rspec-support (3.9.3)
|
61
|
+
ruby-progressbar (1.10.1)
|
62
|
+
shellany (0.0.1)
|
63
|
+
thor (1.0.1)
|
64
|
+
zeitwerk (2.4.0)
|
65
|
+
|
66
|
+
PLATFORMS
|
67
|
+
ruby
|
68
|
+
|
69
|
+
DEPENDENCIES
|
70
|
+
bundler (~> 2.1.4)
|
71
|
+
byebug
|
72
|
+
fuubar (~> 2.5)
|
73
|
+
guard-rspec (~> 4.7)
|
74
|
+
rake (~> 12.0)
|
75
|
+
rspec (~> 3.0)
|
76
|
+
rswim!
|
77
|
+
|
78
|
+
BUNDLED WITH
|
79
|
+
2.1.4
|
data/Guardfile
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
# A sample Guardfile
|
2
|
+
# More info at https://github.com/guard/guard#readme
|
3
|
+
|
4
|
+
## Uncomment and set this to only include directories you want to watch
|
5
|
+
# directories %w(app lib config test spec features) \
|
6
|
+
# .select{|d| Dir.exist?(d) ? d : UI.warning("Directory #{d} does not exist")}
|
7
|
+
|
8
|
+
## Note: if you are using the `directories` clause above and you are not
|
9
|
+
## watching the project directory ('.'), then you will want to move
|
10
|
+
## the Guardfile to a watched dir and symlink it back, e.g.
|
11
|
+
#
|
12
|
+
# $ mkdir config
|
13
|
+
# $ mv Guardfile config/
|
14
|
+
# $ ln -s config/Guardfile .
|
15
|
+
#
|
16
|
+
# and, you'll have to watch "config/Guardfile" instead of "Guardfile"
|
17
|
+
|
18
|
+
# Note: The cmd option is now required due to the increasing number of ways
|
19
|
+
# rspec may be run, below are examples of the most common uses.
|
20
|
+
# * bundler: 'bundle exec rspec'
|
21
|
+
# * bundler binstubs: 'bin/rspec'
|
22
|
+
# * spring: 'bin/rspec' (This will use spring if running and you have
|
23
|
+
# installed the spring binstubs per the docs)
|
24
|
+
# * zeus: 'zeus rspec' (requires the server to be started separately)
|
25
|
+
# * 'just' rspec: 'rspec'
|
26
|
+
|
27
|
+
guard :rspec, cmd: "bundle exec rspec" do
|
28
|
+
require "guard/rspec/dsl"
|
29
|
+
dsl = Guard::RSpec::Dsl.new(self)
|
30
|
+
|
31
|
+
# Feel free to open issues for suggestions and improvements
|
32
|
+
|
33
|
+
# RSpec files
|
34
|
+
rspec = dsl.rspec
|
35
|
+
watch(rspec.spec_helper) { rspec.spec_dir }
|
36
|
+
watch(rspec.spec_support) { rspec.spec_dir }
|
37
|
+
watch(rspec.spec_files)
|
38
|
+
|
39
|
+
# Ruby files
|
40
|
+
ruby = dsl.ruby
|
41
|
+
dsl.watch_spec_files_for(ruby.lib_files)
|
42
|
+
end
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2019 Erik Madsen
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,72 @@
|
|
1
|
+
# RSwim
|
2
|
+
|
3
|
+
RSwim is a Ruby implementation of the SWIM gossip protocol, a mechanism for discovering new peers and getting updates about liveness of existing peers in a network.
|
4
|
+
|
5
|
+
It is an implementation inspired by the original [SWIM: Scalable Weakly-consistent Infection-style Process Group Membership Protocol](https://www.cs.cornell.edu/projects/Quicksilver/public_pdfs/SWIM.pdf) paper by Abhinandan Das, Indranil Gupta, Ashish Motivala.
|
6
|
+
|
7
|
+
The implementation is kept intentionally simple and limited to the features described in the paper.
|
8
|
+
No attempts have been made to address known security issues such as Byzantine attacks.
|
9
|
+
|
10
|
+
Currently RSwim runs on UDP with a custom, human readable serialization format.
|
11
|
+
|
12
|
+
|
13
|
+
## Installation
|
14
|
+
|
15
|
+
Add this line to your application's Gemfile:
|
16
|
+
|
17
|
+
```ruby
|
18
|
+
gem 'rswim'
|
19
|
+
```
|
20
|
+
|
21
|
+
And then execute:
|
22
|
+
|
23
|
+
$ bundle install
|
24
|
+
|
25
|
+
Or install it yourself as:
|
26
|
+
|
27
|
+
$ gem install rswim
|
28
|
+
|
29
|
+
## Usage
|
30
|
+
|
31
|
+
Example:
|
32
|
+
```ruby
|
33
|
+
require 'rswim'
|
34
|
+
|
35
|
+
port = 4545
|
36
|
+
|
37
|
+
# known, running nodes to connect with initially.
|
38
|
+
seed_hosts = ['192.168.1.42', '192.168.1.43']
|
39
|
+
|
40
|
+
puts "Starting node"
|
41
|
+
|
42
|
+
# Instantiate node, setting my_host to nil to auto detect host IP.
|
43
|
+
node = RSwim::Node.udp(nil, seed_hosts, port)
|
44
|
+
|
45
|
+
# Subscribe to updates
|
46
|
+
node.subscribe do |host, status|
|
47
|
+
puts "Update: #{host} entered state #{status}"
|
48
|
+
end
|
49
|
+
|
50
|
+
puts "Ready\n"
|
51
|
+
begin
|
52
|
+
# Run node (blocking)
|
53
|
+
node.start
|
54
|
+
rescue Interrupt
|
55
|
+
end
|
56
|
+
puts "\nDone"
|
57
|
+
|
58
|
+
```
|
59
|
+
|
60
|
+
## Development
|
61
|
+
|
62
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
63
|
+
|
64
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
65
|
+
|
66
|
+
## Contributing
|
67
|
+
|
68
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/beatmadsen/rswim.
|
69
|
+
|
70
|
+
## License
|
71
|
+
|
72
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
data/Rakefile
ADDED
data/bin/console
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "rswim"
|
5
|
+
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
8
|
+
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
+
# require "pry"
|
11
|
+
# Pry.start
|
12
|
+
|
13
|
+
require "irb"
|
14
|
+
IRB.start(__FILE__)
|
data/bin/run_node
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
#!/usr/bin/env ruby --jit
|
2
|
+
require "bundler/setup"
|
3
|
+
require 'rswim'
|
4
|
+
puts "Ruby version: #{RUBY_VERSION}"
|
5
|
+
|
6
|
+
PORT = 4545
|
7
|
+
|
8
|
+
puts "Enter seed nodes"
|
9
|
+
input = gets
|
10
|
+
seed_hosts = input.split(" ")
|
11
|
+
abort 'EOF' if seed_hosts.nil?
|
12
|
+
puts "Operating with no seed nodes" if seed_hosts.empty?
|
13
|
+
|
14
|
+
puts "Starting node"
|
15
|
+
|
16
|
+
# Instantiate node, setting my_host to nil to auto detect host IP.
|
17
|
+
node = RSwim::Node.udp(nil, seed_hosts, PORT)
|
18
|
+
|
19
|
+
# Subscribe to updates
|
20
|
+
node.subscribe do |host, status|
|
21
|
+
puts "Update: #{host} entered state #{status}"
|
22
|
+
end
|
23
|
+
|
24
|
+
puts "Ready\n"
|
25
|
+
begin
|
26
|
+
# Run node (blocking)
|
27
|
+
node.start
|
28
|
+
rescue Interrupt
|
29
|
+
end
|
30
|
+
puts "\nDone"
|
data/bin/setup
ADDED
data/lib/rswim.rb
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'logger'
|
4
|
+
require 'zeitwerk'
|
5
|
+
require 'byebug'
|
6
|
+
|
7
|
+
# frozen_string_literal: true
|
8
|
+
|
9
|
+
class MyInflector < Zeitwerk::Inflector
|
10
|
+
def camelize(basename, _abspath)
|
11
|
+
case basename
|
12
|
+
when 'rswim' then 'RSwim'
|
13
|
+
else super
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
loader = Zeitwerk::Loader.for_gem
|
19
|
+
loader.inflector = MyInflector.new
|
20
|
+
loader.setup
|
21
|
+
|
22
|
+
module RSwim
|
23
|
+
K = 3
|
24
|
+
|
25
|
+
# Protocol time, millis
|
26
|
+
T_MS = 30_000
|
27
|
+
|
28
|
+
# Roundtrip time, millis
|
29
|
+
R_MS = 10_000
|
30
|
+
|
31
|
+
class Error < StandardError; end
|
32
|
+
# Your code goes here...
|
33
|
+
end
|
data/lib/rswim/agent.rb
ADDED
@@ -0,0 +1,69 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RSwim
|
4
|
+
module Agent
|
5
|
+
class Base
|
6
|
+
def initialize(pipe, node_member_id, seed_member_ids, t_ms, r_ms)
|
7
|
+
@pipe = pipe
|
8
|
+
@state = ProtocolState.new(node_member_id, seed_member_ids, t_ms, r_ms)
|
9
|
+
end
|
10
|
+
|
11
|
+
def subscribe(&block)
|
12
|
+
@state.subscribe(&block)
|
13
|
+
end
|
14
|
+
|
15
|
+
def run
|
16
|
+
loop do
|
17
|
+
elapsed_seconds = pause
|
18
|
+
output_messages = @state.advance(@pipe.inbound, elapsed_seconds)
|
19
|
+
output_messages.each { |message| @pipe.send(message) }
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
protected
|
24
|
+
|
25
|
+
def pause
|
26
|
+
raise 'implement this in a subclass'
|
27
|
+
end
|
28
|
+
|
29
|
+
def monotonic_seconds
|
30
|
+
Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
class SleepBased < Base
|
35
|
+
def initialize(pipe, node_member_id, seed_member_ids, sleep_time_seconds = 0.1, t_ms = T_MS, r_ms = R_MS)
|
36
|
+
super(pipe, node_member_id, seed_member_ids, t_ms, r_ms)
|
37
|
+
@sleep_time_seconds = sleep_time_seconds
|
38
|
+
end
|
39
|
+
|
40
|
+
protected
|
41
|
+
|
42
|
+
def pause
|
43
|
+
t = monotonic_seconds
|
44
|
+
sleep @sleep_time_seconds
|
45
|
+
t′ = monotonic_seconds
|
46
|
+
t′ - t
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
class FiberBased < Base
|
51
|
+
def run
|
52
|
+
@f = Fiber.new { super }
|
53
|
+
end
|
54
|
+
|
55
|
+
def resume
|
56
|
+
@f.resume
|
57
|
+
end
|
58
|
+
|
59
|
+
protected
|
60
|
+
|
61
|
+
def pause
|
62
|
+
t = monotonic_seconds
|
63
|
+
Fiber.yield
|
64
|
+
t′ = monotonic_seconds
|
65
|
+
t′ - t
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RSwim
|
4
|
+
class Directory
|
5
|
+
def initialize
|
6
|
+
@i = 0
|
7
|
+
@ids = {}
|
8
|
+
@hosts = {}
|
9
|
+
end
|
10
|
+
|
11
|
+
def id(host)
|
12
|
+
result = @ids[host]
|
13
|
+
if result.nil?
|
14
|
+
@i += 1
|
15
|
+
@ids[host] = @i
|
16
|
+
@hosts[@i] = host
|
17
|
+
@i
|
18
|
+
else
|
19
|
+
result
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def host(id)
|
24
|
+
@hosts[id]
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|