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,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
|
data/lib/rswim/logger.rb
ADDED
@@ -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,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
|