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,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RSwim
4
+ module Integration
5
+ class Deserializer
6
+ def initialize(directory, my_id)
7
+ @directory = directory
8
+ @my_id = my_id
9
+ end
10
+
11
+ def deserialize(sender_host, wire_message)
12
+ l, *ls = wire_message.split("\n")
13
+ # First line is
14
+ # type [target_id]
15
+ ary = l.strip.split(' ')
16
+ type = ary[0].gsub(/-/, '_').to_sym
17
+ payload = type == :ping_req ? { target_id: @directory.id(ary[1]) } : {}
18
+ payload[:updates] = parse_updates(ls)
19
+ from = @directory.id(sender_host)
20
+ RSwim::Message.new(@my_id, from, type, payload)
21
+ rescue StandardError => e
22
+ logger.debug("Failed to parse line `#{l}`: #{e}")
23
+ nil
24
+ end
25
+
26
+ protected
27
+
28
+ def logger
29
+ @_logger ||= begin
30
+ RSwim::Logger.new(self.class, STDERR)
31
+ end
32
+ end
33
+
34
+ private
35
+
36
+ def parse_updates(lines)
37
+ lines.map do |l|
38
+ begin
39
+ # host status incarnation_number
40
+ host, status, incarnation_number = l.strip.split(' ')
41
+ id = @directory.id(host)
42
+ UpdateEntry.new(id, status.to_sym, incarnation_number.to_i)
43
+ rescue StandardError => e
44
+ logger.debug("Failed to parse line `#{l}`: #{e}")
45
+ nil
46
+ end
47
+ end.tap(&:compact!)
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RSwim
4
+ module Integration
5
+ class Serializer
6
+ def initialize(directory)
7
+ @directory = directory
8
+ end
9
+
10
+ def serialize(message)
11
+ l1 = message.type.to_s.gsub(/_/, '-')
12
+ l1 << " #{@directory.host(message.payload[:target_id])}" if message.type == :ping_req
13
+ message.payload[:updates].to_a.each do |update|
14
+ # host status incarnation_number
15
+ l1 << "\n#{@directory.host(update.member_id)} #{update.status} #{update.incarnation_number}"
16
+ end
17
+ l1
18
+ end
19
+
20
+ protected
21
+
22
+ def logger
23
+ @_logger ||= begin
24
+ RSwim::Logger.new(self.class, STDERR)
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,61 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'socket'
4
+
5
+ module RSwim
6
+ module Integration
7
+ module Udp
8
+ class Node < RSwim::Node
9
+ def initialize(my_host, seed_hosts, port)
10
+ my_host ||= Socket.ip_address_list.find { |x| x.ipv4_private? }.ip_address
11
+ super(my_host, seed_hosts)
12
+ @port = port
13
+ end
14
+
15
+ protected
16
+
17
+ def before_start
18
+ @in_s = UDPSocket.new
19
+ @out_s = UDPSocket.new
20
+ @in_s.bind(@my_host, @port)
21
+ logger.info "node listening on UDP port #{@port}"
22
+ Thread.new { receive }.tap { |t| t.abort_on_exception = true }
23
+ Thread.new { send }.tap { |t| t.abort_on_exception = true }
24
+ end
25
+
26
+ private
27
+
28
+ def send
29
+ loop do
30
+ begin
31
+ message = @pipe.q_out.pop
32
+ wire_message = @serializer.serialize(message)
33
+ host = @directory.host(message.to)
34
+ logger.debug "about to send message to #{host}"
35
+ @out_s.send(wire_message, 0, host, @port)
36
+ rescue StandardError => e
37
+ logger.debug("Error while sending: #{e}")
38
+ end
39
+ end
40
+ logger.info 'node no longer receiving'
41
+ end
42
+
43
+ def receive
44
+ loop do
45
+ begin
46
+ logger.debug 'about to recieve message'
47
+ text, sender = @in_s.recvfrom(10_000)
48
+
49
+ message = @deserializer.deserialize(sender[3], text)
50
+ logger.debug "received #{message}"
51
+ @pipe.q_in << message
52
+ rescue StandardError => e
53
+ logger.debug("Error while receiving: #{e}")
54
+ end
55
+ end
56
+ logger.info 'node no longer receiving'
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,31 @@
1
+ module RSwim
2
+ class Logger < ::Logger
3
+ def initialize(klass, *args, **options)
4
+ options[:formatter] = LeFormatter.new(klass)
5
+ super(*args, **options)
6
+ self.level=Logger.level
7
+ end
8
+
9
+ class << self
10
+ def level=(severity)
11
+ @level = severity
12
+ end
13
+
14
+ def level
15
+ @level ||= ::Logger::INFO
16
+ end
17
+ end
18
+
19
+ class LeFormatter < ::Logger::Formatter
20
+ def initialize(klass)
21
+ super()
22
+ @klass = klass
23
+ end
24
+
25
+ def call(severity, timestamp, progname, msg)
26
+ m = String === msg ? msg : msg.inspect
27
+ "#{timestamp} | #{severity} | #{@klass} | #{m}\n"
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RSwim
4
+ module Member
5
+ class AckResponder
6
+ def initialize(id)
7
+ @id = id
8
+ @pending = []
9
+ end
10
+
11
+ def schedule_ack(member_id)
12
+ @pending << Message.new(member_id, @id, :ack)
13
+ end
14
+
15
+ def prepare_output
16
+ result = @pending
17
+ @pending = []
18
+ result
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RSwim
4
+ module Member
5
+ class Base
6
+ def initialize(id)
7
+ @id = id
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RSwim
4
+ module Member
5
+ module ForwardingState
6
+ class Base
7
+ def initialize(id, node_member_id)
8
+ @id = id
9
+ @node_member_id = node_member_id
10
+ logger.debug("Member with id #{id} entered new state: #{self.class}")
11
+ end
12
+
13
+ def forward_ack_to_member; end
14
+
15
+ def advance(_elapsed_seconds)
16
+ self
17
+ end
18
+
19
+ def prepare_output
20
+ []
21
+ end
22
+
23
+ protected
24
+
25
+ def logger
26
+ @_logger ||= begin
27
+ RSwim::Logger.new("Node #{@node_member_id}", STDERR)
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RSwim
4
+ module Member
5
+ module ForwardingState
6
+ class ForwardingAck < Base
7
+ def initialize(id, node_member_id)
8
+ super
9
+ @done = false
10
+ end
11
+
12
+ def prepare_output
13
+ @done = true
14
+ message = Message.new(@id, @node_member_id, :ack)
15
+ [message]
16
+ end
17
+
18
+ def advance(elapsed_seconds)
19
+ if @done then Ready.new(@id, @node_member_id)
20
+ else self
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RSwim
4
+ module Member
5
+ module ForwardingState
6
+ class Ready < Base
7
+ def initialize(id, node_member_id)
8
+ super
9
+ @activated = false
10
+ end
11
+
12
+ def forward_ack_to_member
13
+ @activated = true
14
+ end
15
+
16
+ def advance(_elapsed_seconds)
17
+ if @activated then ForwardingAck.new(@id, @node_member_id)
18
+ else self
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RSwim
4
+ module Member
5
+ module HealthState
6
+ class Alive < Base
7
+ def initialize(id, member_pool, update_entry = UpdateEntry.new(id, :alive, 0, 0))
8
+ super
9
+ @failed_to_reply = false
10
+ end
11
+
12
+ def advance(_elapsed_seconds)
13
+ if @failed_to_reply
14
+ ue = UpdateEntry.new(@id, :suspected, @update_entry.incarnation_number, -1)
15
+ Suspected.new(@id, @member_pool, ue, true)
16
+ else
17
+ self
18
+ end
19
+ end
20
+
21
+ def member_failed_to_reply
22
+ @failed_to_reply = true
23
+ end
24
+
25
+ def can_be_pinged?
26
+ true
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,71 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RSwim
4
+ module Member
5
+ module HealthState
6
+ class Base
7
+ attr_reader :update_entry
8
+
9
+ def initialize(id, member_pool, update_entry)
10
+ @member_pool = member_pool
11
+ @id = id
12
+ @update_entry = update_entry
13
+ logger.debug("Member with id #{id} entered new state: #{self.class}")
14
+ end
15
+
16
+ def advance(_elapsed_seconds)
17
+ self
18
+ end
19
+
20
+ def update_suspicion(status, incarnation_number)
21
+ incarnation_number ||= @update_entry.incarnation_number
22
+ s0 = @update_entry.status
23
+ i0 = @update_entry.incarnation_number
24
+ case status
25
+ when :confirmed
26
+ if (s0 == :confirmed)
27
+ self
28
+ else
29
+ ue = UpdateEntry.new(@id, status, incarnation_number, 0)
30
+ Confirmed.new(@id, @member_pool, ue)
31
+ end
32
+ when :suspected
33
+ if (s0 == :suspected && incarnation_number > i0) ||
34
+ (s0 == :alive && incarnation_number >= i0)
35
+ ue = UpdateEntry.new(@id, status, incarnation_number, 0)
36
+ Suspected.new(@id, @member_pool, ue, false)
37
+ else
38
+ self
39
+ end
40
+ when :alive
41
+ if (s0 == :suspected && incarnation_number > i0) ||
42
+ (s0 == :alive && incarnation_number > i0)
43
+ ue = UpdateEntry.new(@id, status, incarnation_number, 0)
44
+ Alive.new(@id, @member_pool, ue)
45
+ else
46
+ self
47
+ end
48
+ end
49
+ end
50
+
51
+ def member_failed_to_reply; end
52
+
53
+ def increment_propagation_count
54
+ @update_entry.increment_propagation_count
55
+ end
56
+
57
+ def can_be_pinged?
58
+ false
59
+ end
60
+
61
+ protected
62
+
63
+ def logger
64
+ @_logger ||= begin
65
+ RSwim::Logger.new("unknown node", STDERR)
66
+ end
67
+ end
68
+ end
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RSwim
4
+ module Member
5
+ module HealthState
6
+ class Confirmed < Base
7
+ def initialize(id, member_pool, update_entry)
8
+ super
9
+ @member_halted = false
10
+ @member_removed = false
11
+ @life_time_seconds = 0
12
+ end
13
+
14
+ def advance(elapsed_seconds)
15
+ @life_time_seconds += elapsed_seconds
16
+ unless @member_halted
17
+ @member_pool.halt_member(@id)
18
+ @member_halted = true
19
+ end
20
+
21
+ if !@member_removed && @life_time_seconds > 10
22
+ @member_pool.remove_member(@id)
23
+ @member_removed = true
24
+ end
25
+
26
+ self
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RSwim
4
+ module Member
5
+ module HealthState
6
+ class Suspected < Base
7
+ def initialize(id, member_pool, update_entry, send_ping_request)
8
+ super(id, member_pool, update_entry)
9
+ @ping_request_sent = !send_ping_request
10
+ @life_time_seconds = 0
11
+ end
12
+
13
+ def advance(elapsed_seconds)
14
+ @life_time_seconds += elapsed_seconds
15
+ unless @ping_request_sent
16
+ @member_pool.send_ping_request_to_k_members(@id)
17
+ @ping_request_sent = true
18
+ end
19
+ if @life_time_seconds > 60
20
+ Confirmed.new(@id, @member_pool, UpdateEntry.new(@id, :confirmed, @update_entry.incarnation_number, -2))
21
+ else
22
+ self
23
+ end
24
+ end
25
+
26
+ def can_be_pinged?
27
+ true
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end