rswim 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RSwim
|
4
|
+
class Message
|
5
|
+
attr_reader :to, :from, :type, :payload
|
6
|
+
|
7
|
+
def initialize(to, from, type, payload = {})
|
8
|
+
@to = to
|
9
|
+
@from = from
|
10
|
+
@type = type
|
11
|
+
@payload = payload
|
12
|
+
end
|
13
|
+
|
14
|
+
def to_s
|
15
|
+
"message of type #{type} from #{from} to #{to} with #{payload[:updates].to_a.size} updates"
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
data/lib/rswim/node.rb
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'socket'
|
4
|
+
|
5
|
+
module RSwim
|
6
|
+
class Node
|
7
|
+
def initialize(my_host, seed_hosts)
|
8
|
+
@my_host = my_host
|
9
|
+
@directory = Directory.new
|
10
|
+
@my_id = @directory.id(@my_host)
|
11
|
+
@deserializer = Integration::Deserializer.new(@directory, @my_id)
|
12
|
+
@serializer = Integration::Serializer.new(@directory)
|
13
|
+
@pipe = RSwim::Pipe.simple
|
14
|
+
seed_ids = seed_hosts.map { |host| @directory.id(host) }
|
15
|
+
@agent = RSwim::Agent::SleepBased.new(@pipe, @my_id, seed_ids)
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.udp(my_host, seed_hosts, port)
|
19
|
+
Integration::Udp::Node.new(my_host, seed_hosts, port)
|
20
|
+
end
|
21
|
+
|
22
|
+
def subscribe(&block)
|
23
|
+
@agent.subscribe do |id, status|
|
24
|
+
host = @directory.host(id)
|
25
|
+
block.call(host, status)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
# blocks until interrupted
|
30
|
+
def start
|
31
|
+
logger.info 'starting node'
|
32
|
+
before_start
|
33
|
+
@agent.run
|
34
|
+
rescue StandardError => e
|
35
|
+
logger.debug("Error: #{e}")
|
36
|
+
end
|
37
|
+
|
38
|
+
protected
|
39
|
+
|
40
|
+
def before_start
|
41
|
+
raise 'must be implemented in subclass'
|
42
|
+
end
|
43
|
+
|
44
|
+
def logger
|
45
|
+
@_logger ||= begin
|
46
|
+
RSwim::Logger.new(self.class, STDERR)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
data/lib/rswim/pipe.rb
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RSwim
|
4
|
+
class Pipe
|
5
|
+
def self.simple
|
6
|
+
Simple.new
|
7
|
+
end
|
8
|
+
|
9
|
+
def initialize(q_in, q_out)
|
10
|
+
@q_in = q_in
|
11
|
+
@q_out = q_out
|
12
|
+
end
|
13
|
+
|
14
|
+
def send(message)
|
15
|
+
@q_out << message
|
16
|
+
end
|
17
|
+
|
18
|
+
# returns list of inputs. Empty if none have been received
|
19
|
+
def inbound
|
20
|
+
Array.new(@q_in.size) { @q_in.pop }.tap(&:compact!)
|
21
|
+
end
|
22
|
+
|
23
|
+
class Simple < Pipe
|
24
|
+
attr_reader :q_in, :q_out
|
25
|
+
|
26
|
+
def initialize
|
27
|
+
@q_in, @q_out = 2.times.map { Queue.new }
|
28
|
+
super(@q_in, @q_out)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RSwim
|
4
|
+
class ProtocolState
|
5
|
+
def initialize(node_member_id, seed_member_ids, t_ms, r_ms)
|
6
|
+
@t_ms = t_ms
|
7
|
+
@r_ms = r_ms
|
8
|
+
@member_pool = MemberPool.new(node_member_id, seed_member_ids)
|
9
|
+
@node_member_id = node_member_id
|
10
|
+
@t = @r = 1
|
11
|
+
end
|
12
|
+
|
13
|
+
def subscribe(&block)
|
14
|
+
@member_pool.subscribe(&block)
|
15
|
+
end
|
16
|
+
|
17
|
+
def advance(input_messages, elapsed_seconds)
|
18
|
+
@t += elapsed_seconds * 1000
|
19
|
+
@t = 0 if @t >= @t_ms
|
20
|
+
|
21
|
+
@r += elapsed_seconds * 1000
|
22
|
+
@r = 0 if @r >= @r_ms
|
23
|
+
|
24
|
+
input_messages.each do |message|
|
25
|
+
raise 'message must be of type Message' unless message.is_a? Message
|
26
|
+
|
27
|
+
update_member(message)
|
28
|
+
end
|
29
|
+
|
30
|
+
@member_pool.update_members(elapsed_seconds)
|
31
|
+
output_messages = @member_pool.prepare_output
|
32
|
+
|
33
|
+
# TODO: more deterministic steady state mechanism,
|
34
|
+
# e.g. output_messages = @member_pool.next_steady_state(elapsed_seconds)
|
35
|
+
# using a flag set by member state
|
36
|
+
3.times do
|
37
|
+
@member_pool.update_members(0)
|
38
|
+
output_messages.concat(@member_pool.prepare_output)
|
39
|
+
end
|
40
|
+
|
41
|
+
@member_pool.send_ping_to_random_healthy_member if @t == 0
|
42
|
+
|
43
|
+
3.times do
|
44
|
+
@member_pool.update_members(0)
|
45
|
+
output_messages.concat(@member_pool.prepare_output)
|
46
|
+
end
|
47
|
+
|
48
|
+
@member_pool.status_report if @r == 0
|
49
|
+
|
50
|
+
output_messages
|
51
|
+
end
|
52
|
+
|
53
|
+
private
|
54
|
+
|
55
|
+
def logger
|
56
|
+
@_logger ||= Logger.new(self.class, STDERR)
|
57
|
+
end
|
58
|
+
|
59
|
+
def update_member(message)
|
60
|
+
@member_pool.update_member(message)
|
61
|
+
# rescue StandardError => e
|
62
|
+
# logger.error(e)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RSwim
|
4
|
+
class StatusReport
|
5
|
+
class << self
|
6
|
+
|
7
|
+
def print(node_member_id, members)
|
8
|
+
b = members.map { |k, m| "#{k}: #{m.prepare_update_entry.status}\n" }.join
|
9
|
+
a = <<~REPORT
|
10
|
+
|
11
|
+
==========================================
|
12
|
+
Status report for node #{node_member_id}:
|
13
|
+
==========================================
|
14
|
+
|
15
|
+
#{b}
|
16
|
+
==========================================
|
17
|
+
|
18
|
+
REPORT
|
19
|
+
logger.info(a)
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def logger
|
25
|
+
@_logger ||= Logger.new(self, 'log/status.log', 10, 1024000)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module RSwim
|
3
|
+
class UpdateEntry
|
4
|
+
attr_reader :member_id, :status, :incarnation_number, :propagation_count
|
5
|
+
|
6
|
+
def initialize(member_id, status, incarnation_number, propagation_count = 0)
|
7
|
+
@member_id = member_id
|
8
|
+
@status = status
|
9
|
+
@incarnation_number = incarnation_number
|
10
|
+
@propagation_count = propagation_count
|
11
|
+
end
|
12
|
+
|
13
|
+
def increment_propagation_count
|
14
|
+
@propagation_count += 1
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
data/log/.keep
ADDED
File without changes
|
data/rswim.gemspec
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
lib = File.expand_path("lib", __dir__)
|
2
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
3
|
+
require "rswim/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |spec|
|
6
|
+
spec.name = "rswim"
|
7
|
+
spec.version = RSwim::VERSION
|
8
|
+
spec.authors = ["Erik Madsen"]
|
9
|
+
spec.email = ["beatmadsen@gmail.com"]
|
10
|
+
|
11
|
+
spec.summary = %q{Ruby implementation of the SWIM gossip protocol}
|
12
|
+
spec.description = %q{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.}
|
13
|
+
spec.homepage = "https://github.com/beatmadsen/rswim"
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.metadata["homepage_uri"] = spec.homepage
|
17
|
+
spec.metadata["source_code_uri"] = "https://github.com/beatmadsen/rswim"
|
18
|
+
spec.metadata["changelog_uri"] = "https://github.com/beatmadsen/rswim/blob/master/CHANGELOG.md"
|
19
|
+
|
20
|
+
# Specify which files should be added to the gem when it is released.
|
21
|
+
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
22
|
+
spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
|
23
|
+
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
24
|
+
end
|
25
|
+
spec.bindir = "exe"
|
26
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
27
|
+
spec.require_paths = ["lib"]
|
28
|
+
|
29
|
+
spec.add_dependency 'zeitwerk', '~> 2.2'
|
30
|
+
|
31
|
+
spec.add_development_dependency "bundler", "~> 2.1.4"
|
32
|
+
spec.add_development_dependency "rake", "~> 12.0"
|
33
|
+
spec.add_development_dependency "rspec", "~> 3.0"
|
34
|
+
spec.add_development_dependency 'guard-rspec', '~> 4.7'
|
35
|
+
spec.add_development_dependency 'fuubar', '~> 2.5'
|
36
|
+
spec.add_development_dependency 'byebug'
|
37
|
+
end
|
data/tmp/.keep
ADDED
File without changes
|
metadata
ADDED
@@ -0,0 +1,195 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: rswim
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Erik Madsen
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2020-10-06 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: zeitwerk
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '2.2'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '2.2'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: bundler
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 2.1.4
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: 2.1.4
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rake
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '12.0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '12.0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rspec
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '3.0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '3.0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: guard-rspec
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '4.7'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '4.7'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: fuubar
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - "~>"
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '2.5'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - "~>"
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '2.5'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: byebug
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - ">="
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - ">="
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
111
|
+
description: RSwim is a Ruby implementation of the SWIM gossip protocol, a mechanism
|
112
|
+
for discovering new peers and getting updates about liveness of existing peers in
|
113
|
+
a network.
|
114
|
+
email:
|
115
|
+
- beatmadsen@gmail.com
|
116
|
+
executables: []
|
117
|
+
extensions: []
|
118
|
+
extra_rdoc_files: []
|
119
|
+
files:
|
120
|
+
- ".gitignore"
|
121
|
+
- ".rspec"
|
122
|
+
- ".ruby-version"
|
123
|
+
- ".travis.yml"
|
124
|
+
- CHANGELOG.md
|
125
|
+
- Gemfile
|
126
|
+
- Gemfile.lock
|
127
|
+
- Guardfile
|
128
|
+
- LICENSE.txt
|
129
|
+
- README.md
|
130
|
+
- Rakefile
|
131
|
+
- bin/console
|
132
|
+
- bin/run_node
|
133
|
+
- bin/setup
|
134
|
+
- lib/rswim.rb
|
135
|
+
- lib/rswim/agent.rb
|
136
|
+
- lib/rswim/directory.rb
|
137
|
+
- lib/rswim/integration/deserializer.rb
|
138
|
+
- lib/rswim/integration/serializer.rb
|
139
|
+
- lib/rswim/integration/udp/node.rb
|
140
|
+
- lib/rswim/logger.rb
|
141
|
+
- lib/rswim/member/ack_responder.rb
|
142
|
+
- lib/rswim/member/base.rb
|
143
|
+
- lib/rswim/member/forwarding_state/base.rb
|
144
|
+
- lib/rswim/member/forwarding_state/forwarding_ack.rb
|
145
|
+
- lib/rswim/member/forwarding_state/ready.rb
|
146
|
+
- lib/rswim/member/health_state/alive.rb
|
147
|
+
- lib/rswim/member/health_state/base.rb
|
148
|
+
- lib/rswim/member/health_state/confirmed.rb
|
149
|
+
- lib/rswim/member/health_state/suspected.rb
|
150
|
+
- lib/rswim/member/me.rb
|
151
|
+
- lib/rswim/member/peer.rb
|
152
|
+
- lib/rswim/member/transmission_state/awaiting_ack.rb
|
153
|
+
- lib/rswim/member/transmission_state/base.rb
|
154
|
+
- lib/rswim/member/transmission_state/off.rb
|
155
|
+
- lib/rswim/member/transmission_state/ready.rb
|
156
|
+
- lib/rswim/member/transmission_state/sending_ping.rb
|
157
|
+
- lib/rswim/member/transmission_state/sending_ping_request.rb
|
158
|
+
- lib/rswim/member_pool.rb
|
159
|
+
- lib/rswim/message.rb
|
160
|
+
- lib/rswim/node.rb
|
161
|
+
- lib/rswim/pipe.rb
|
162
|
+
- lib/rswim/protocol_state.rb
|
163
|
+
- lib/rswim/status_report.rb
|
164
|
+
- lib/rswim/update_entry.rb
|
165
|
+
- lib/rswim/version.rb
|
166
|
+
- log/.keep
|
167
|
+
- rswim.gemspec
|
168
|
+
- tmp/.keep
|
169
|
+
homepage: https://github.com/beatmadsen/rswim
|
170
|
+
licenses:
|
171
|
+
- MIT
|
172
|
+
metadata:
|
173
|
+
homepage_uri: https://github.com/beatmadsen/rswim
|
174
|
+
source_code_uri: https://github.com/beatmadsen/rswim
|
175
|
+
changelog_uri: https://github.com/beatmadsen/rswim/blob/master/CHANGELOG.md
|
176
|
+
post_install_message:
|
177
|
+
rdoc_options: []
|
178
|
+
require_paths:
|
179
|
+
- lib
|
180
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
181
|
+
requirements:
|
182
|
+
- - ">="
|
183
|
+
- !ruby/object:Gem::Version
|
184
|
+
version: '0'
|
185
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
186
|
+
requirements:
|
187
|
+
- - ">="
|
188
|
+
- !ruby/object:Gem::Version
|
189
|
+
version: '0'
|
190
|
+
requirements: []
|
191
|
+
rubygems_version: 3.1.2
|
192
|
+
signing_key:
|
193
|
+
specification_version: 4
|
194
|
+
summary: Ruby implementation of the SWIM gossip protocol
|
195
|
+
test_files: []
|