rswim 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
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: []