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.
Files changed (51) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +17 -0
  3. data/.rspec +3 -0
  4. data/.ruby-version +1 -0
  5. data/.travis.yml +6 -0
  6. data/CHANGELOG.md +1 -0
  7. data/Gemfile +4 -0
  8. data/Gemfile.lock +79 -0
  9. data/Guardfile +42 -0
  10. data/LICENSE.txt +21 -0
  11. data/README.md +72 -0
  12. data/Rakefile +6 -0
  13. data/bin/console +14 -0
  14. data/bin/run_node +30 -0
  15. data/bin/setup +8 -0
  16. data/lib/rswim.rb +33 -0
  17. data/lib/rswim/agent.rb +69 -0
  18. data/lib/rswim/directory.rb +27 -0
  19. data/lib/rswim/integration/deserializer.rb +51 -0
  20. data/lib/rswim/integration/serializer.rb +29 -0
  21. data/lib/rswim/integration/udp/node.rb +61 -0
  22. data/lib/rswim/logger.rb +31 -0
  23. data/lib/rswim/member/ack_responder.rb +22 -0
  24. data/lib/rswim/member/base.rb +11 -0
  25. data/lib/rswim/member/forwarding_state/base.rb +33 -0
  26. data/lib/rswim/member/forwarding_state/forwarding_ack.rb +26 -0
  27. data/lib/rswim/member/forwarding_state/ready.rb +24 -0
  28. data/lib/rswim/member/health_state/alive.rb +31 -0
  29. data/lib/rswim/member/health_state/base.rb +71 -0
  30. data/lib/rswim/member/health_state/confirmed.rb +31 -0
  31. data/lib/rswim/member/health_state/suspected.rb +32 -0
  32. data/lib/rswim/member/me.rb +45 -0
  33. data/lib/rswim/member/peer.rb +85 -0
  34. data/lib/rswim/member/transmission_state/awaiting_ack.rb +38 -0
  35. data/lib/rswim/member/transmission_state/base.rb +48 -0
  36. data/lib/rswim/member/transmission_state/off.rb +21 -0
  37. data/lib/rswim/member/transmission_state/ready.rb +27 -0
  38. data/lib/rswim/member/transmission_state/sending_ping.rb +30 -0
  39. data/lib/rswim/member/transmission_state/sending_ping_request.rb +31 -0
  40. data/lib/rswim/member_pool.rb +114 -0
  41. data/lib/rswim/message.rb +18 -0
  42. data/lib/rswim/node.rb +50 -0
  43. data/lib/rswim/pipe.rb +32 -0
  44. data/lib/rswim/protocol_state.rb +65 -0
  45. data/lib/rswim/status_report.rb +29 -0
  46. data/lib/rswim/update_entry.rb +17 -0
  47. data/lib/rswim/version.rb +5 -0
  48. data/log/.keep +0 -0
  49. data/rswim.gemspec +37 -0
  50. data/tmp/.keep +0 -0
  51. 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
@@ -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
@@ -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
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RSwim
4
+ VERSION = '1.0.0'
5
+ end
File without changes
@@ -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
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: []