evinrude 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.editorconfig +23 -0
- data/.gitignore +6 -0
- data/.yardopts +1 -0
- data/CODE_OF_CONDUCT.md +49 -0
- data/CONTRIBUTING.md +10 -0
- data/LICENCE +674 -0
- data/README.md +410 -0
- data/evinrude.gemspec +42 -0
- data/lib/evinrude.rb +1233 -0
- data/lib/evinrude/backoff.rb +19 -0
- data/lib/evinrude/cluster_configuration.rb +162 -0
- data/lib/evinrude/config_change_queue_entry.rb +19 -0
- data/lib/evinrude/config_change_queue_entry/add_node.rb +13 -0
- data/lib/evinrude/config_change_queue_entry/remove_node.rb +14 -0
- data/lib/evinrude/freedom_patches/range.rb +5 -0
- data/lib/evinrude/log.rb +102 -0
- data/lib/evinrude/log_entries.rb +3 -0
- data/lib/evinrude/log_entry.rb +13 -0
- data/lib/evinrude/log_entry/cluster_configuration.rb +15 -0
- data/lib/evinrude/log_entry/null.rb +6 -0
- data/lib/evinrude/log_entry/state_machine_command.rb +13 -0
- data/lib/evinrude/logging_helpers.rb +40 -0
- data/lib/evinrude/message.rb +19 -0
- data/lib/evinrude/message/append_entries_reply.rb +13 -0
- data/lib/evinrude/message/append_entries_request.rb +18 -0
- data/lib/evinrude/message/command_reply.rb +13 -0
- data/lib/evinrude/message/command_request.rb +18 -0
- data/lib/evinrude/message/install_snapshot_reply.rb +13 -0
- data/lib/evinrude/message/install_snapshot_request.rb +18 -0
- data/lib/evinrude/message/join_reply.rb +13 -0
- data/lib/evinrude/message/join_request.rb +18 -0
- data/lib/evinrude/message/node_removal_reply.rb +13 -0
- data/lib/evinrude/message/node_removal_request.rb +18 -0
- data/lib/evinrude/message/read_reply.rb +13 -0
- data/lib/evinrude/message/read_request.rb +18 -0
- data/lib/evinrude/message/vote_reply.rb +13 -0
- data/lib/evinrude/message/vote_request.rb +18 -0
- data/lib/evinrude/messages.rb +14 -0
- data/lib/evinrude/metrics.rb +50 -0
- data/lib/evinrude/network.rb +69 -0
- data/lib/evinrude/network/connection.rb +144 -0
- data/lib/evinrude/network/protocol.rb +69 -0
- data/lib/evinrude/node_info.rb +35 -0
- data/lib/evinrude/peer.rb +50 -0
- data/lib/evinrude/resolver.rb +96 -0
- data/lib/evinrude/snapshot.rb +9 -0
- data/lib/evinrude/state_machine.rb +15 -0
- data/lib/evinrude/state_machine/register.rb +25 -0
- data/smoke_tests/001_single_node_cluster.rb +20 -0
- data/smoke_tests/002_three_node_cluster.rb +43 -0
- data/smoke_tests/003_spill.rb +25 -0
- data/smoke_tests/004_stale_read.rb +67 -0
- data/smoke_tests/005_sleepy_master.rb +28 -0
- data/smoke_tests/006_join_via_follower.rb +26 -0
- data/smoke_tests/007_snapshot_madness.rb +97 -0
- data/smoke_tests/008_downsizing.rb +43 -0
- data/smoke_tests/009_disaster_recovery.rb +46 -0
- data/smoke_tests/999_final_smoke_test.rb +279 -0
- data/smoke_tests/run +22 -0
- data/smoke_tests/smoke_test_helper.rb +199 -0
- metadata +318 -0
@@ -0,0 +1,13 @@
|
|
1
|
+
require_relative "../message"
|
2
|
+
|
3
|
+
class Evinrude
|
4
|
+
class Message
|
5
|
+
class AppendEntriesReply < Message
|
6
|
+
attr_reader :term, :success, :last_index
|
7
|
+
|
8
|
+
def initialize(term:, success:, last_index: nil)
|
9
|
+
@term, @success, @last_index = term, success, last_index
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require_relative "../message"
|
2
|
+
require_relative "./append_entries_reply"
|
3
|
+
|
4
|
+
class Evinrude
|
5
|
+
class Message
|
6
|
+
class AppendEntriesRequest < Message
|
7
|
+
attr_reader :term, :leader_info, :leader_commit, :prev_log_index, :prev_log_term, :entries
|
8
|
+
|
9
|
+
def initialize(term:, leader_info:, leader_commit:, prev_log_index:, prev_log_term:, entries:)
|
10
|
+
@term, @leader_info, @leader_commit, @prev_log_index, @prev_log_term, @entries = term, leader_info, leader_commit, prev_log_index, prev_log_term, entries
|
11
|
+
end
|
12
|
+
|
13
|
+
def expected_reply_types
|
14
|
+
[AppendEntriesReply]
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
require_relative "../message"
|
2
|
+
|
3
|
+
class Evinrude
|
4
|
+
class Message
|
5
|
+
class CommandReply < Message
|
6
|
+
attr_reader :success, :leader_info
|
7
|
+
|
8
|
+
def initialize(success:, leader_info: nil)
|
9
|
+
@success, @leader_info = success, leader_info
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require_relative "../message"
|
2
|
+
require_relative "./command_reply"
|
3
|
+
|
4
|
+
class Evinrude
|
5
|
+
class Message
|
6
|
+
class CommandRequest < Message
|
7
|
+
attr_reader :command, :id, :node_name
|
8
|
+
|
9
|
+
def initialize(command:, id:, node_name:)
|
10
|
+
@command, @id, @node_name = command, id, node_name
|
11
|
+
end
|
12
|
+
|
13
|
+
def expected_reply_types
|
14
|
+
[CommandReply]
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require_relative "../message"
|
2
|
+
require_relative "./install_snapshot_reply"
|
3
|
+
|
4
|
+
class Evinrude
|
5
|
+
class Message
|
6
|
+
class InstallSnapshotRequest < Message
|
7
|
+
attr_reader :term, :leader_info, :last_included_index, :last_included_term, :data
|
8
|
+
|
9
|
+
def initialize(term:, leader_info:, last_included_index:, last_included_term:, data:)
|
10
|
+
@term, @leader_info, @last_included_index, @last_included_term, @data = term, leader_info, last_included_index, last_included_term, data
|
11
|
+
end
|
12
|
+
|
13
|
+
def expected_reply_types
|
14
|
+
[InstallSnapshotReply]
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require_relative "../message"
|
2
|
+
require_relative "./join_reply"
|
3
|
+
|
4
|
+
class Evinrude
|
5
|
+
class Message
|
6
|
+
class JoinRequest < Message
|
7
|
+
attr_reader :node_info
|
8
|
+
|
9
|
+
def initialize(node_info:)
|
10
|
+
@node_info = node_info
|
11
|
+
end
|
12
|
+
|
13
|
+
def expected_reply_types
|
14
|
+
[JoinReply]
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
require_relative "../message"
|
2
|
+
|
3
|
+
class Evinrude
|
4
|
+
class Message
|
5
|
+
class NodeRemovalReply < Message
|
6
|
+
attr_reader :success, :leader_info
|
7
|
+
|
8
|
+
def initialize(success:, leader_info: nil)
|
9
|
+
@success, @leader_info = success, leader_info
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require_relative "../message"
|
2
|
+
require_relative "./node_removal_reply"
|
3
|
+
|
4
|
+
class Evinrude
|
5
|
+
class Message
|
6
|
+
class NodeRemovalRequest < Message
|
7
|
+
attr_reader :node_info, :unsafe
|
8
|
+
|
9
|
+
def initialize(node_info:, unsafe: false)
|
10
|
+
@node_info, @unsafe = node_info, unsafe
|
11
|
+
end
|
12
|
+
|
13
|
+
def expected_reply_types
|
14
|
+
[NodeRemovalReply]
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require_relative "../message"
|
2
|
+
require_relative "./read_reply"
|
3
|
+
|
4
|
+
class Evinrude
|
5
|
+
class Message
|
6
|
+
class ReadRequest < Message
|
7
|
+
attr_reader :commit_index
|
8
|
+
|
9
|
+
def initialize(commit_index:)
|
10
|
+
@commit_index = commit_index
|
11
|
+
end
|
12
|
+
|
13
|
+
def expected_reply_types
|
14
|
+
[ReadReply]
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require_relative "../message"
|
2
|
+
require_relative "./vote_reply"
|
3
|
+
|
4
|
+
class Evinrude
|
5
|
+
class Message
|
6
|
+
class VoteRequest < Message
|
7
|
+
attr_reader :term, :candidate_info, :last_log_index, :last_log_term
|
8
|
+
|
9
|
+
def initialize(term:, candidate_info:, last_log_index:, last_log_term:)
|
10
|
+
@term, @candidate_info, @last_log_index, @last_log_term = term, candidate_info, last_log_index, last_log_term
|
11
|
+
end
|
12
|
+
|
13
|
+
def expected_reply_types
|
14
|
+
[VoteReply]
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require_relative "./message/append_entries_reply"
|
2
|
+
require_relative "./message/append_entries_request"
|
3
|
+
require_relative "./message/command_reply"
|
4
|
+
require_relative "./message/command_request"
|
5
|
+
require_relative "./message/install_snapshot_reply"
|
6
|
+
require_relative "./message/install_snapshot_request"
|
7
|
+
require_relative "./message/join_reply"
|
8
|
+
require_relative "./message/join_request"
|
9
|
+
require_relative "./message/node_removal_reply"
|
10
|
+
require_relative "./message/node_removal_request"
|
11
|
+
require_relative "./message/read_reply"
|
12
|
+
require_relative "./message/read_request"
|
13
|
+
require_relative "./message/vote_reply"
|
14
|
+
require_relative "./message/vote_request"
|
@@ -0,0 +1,50 @@
|
|
1
|
+
require "prometheus/client"
|
2
|
+
require "frankenstein"
|
3
|
+
require "frankenstein/remove_time_series"
|
4
|
+
|
5
|
+
class Evinrude
|
6
|
+
class Metrics
|
7
|
+
attr_reader :command_execution, :commit_index, :info, :joint_configuration,
|
8
|
+
:log_entries_persisted, :log_file_size, :log_loaded_from_disk,
|
9
|
+
:match_index, :messages_received, :next_index, :node_count,
|
10
|
+
:read_state, :remove_node, :replication_majority, :rpc,
|
11
|
+
:rpc_exception, :snapshot_file_size, :start_time, :state, :term
|
12
|
+
|
13
|
+
def initialize(registry)
|
14
|
+
@registry = registry
|
15
|
+
|
16
|
+
@command_execution = Frankenstein::Request.new(:evinrude_command, description: "state machine command", registry: @registry)
|
17
|
+
@commit_index = get_or_create(:gauge, :evinrude_commit_index, docstring: "The index of the last log entry committed to the state machine")
|
18
|
+
@info = get_or_create(:gauge, :evinrude_node_info, docstring: "Basic information about this Evinrude node in labels", labels: %i{node_name listen_address listen_port advertise_address advertise_port})
|
19
|
+
@joint_configuration = get_or_create(:gauge, :evinrude_joint_configuration_action, docstring: "Whether or not this node is currently using a 'joint' configuration for consensus")
|
20
|
+
@log_entries_persisted = get_or_create(:counter, :evinrude_log_entries_persisted_total, docstring: "How many log entries have been persisted to disk")
|
21
|
+
@log_file_size = get_or_create(:gauge, :evinrude_log_file_size_bytes, docstring: "The current size of the append-only log file")
|
22
|
+
@log_loaded_from_disk = get_or_create(:gauge, :evinrude_log_loaded_from_disk, docstring: "Whether (1) or not (0) this Evinrude node was initialized from data on disk")
|
23
|
+
@match_index = get_or_create(:gauge, :evinrude_follower_match_index, docstring: "The last log index known to be replicated to this follower", labels: %i{peer node_name})
|
24
|
+
@messages_received = get_or_create(:counter, :evinrude_messages_received_total, docstring: "How many unsolicited (RPC) messages have been received", labels: %i{type})
|
25
|
+
@next_index = get_or_create(:gauge, :evinrude_follower_next_index, docstring: "The index of the next log entry to be sent to a follower node", labels: %i{peer node_name})
|
26
|
+
@node_count = get_or_create(:gauge, :evinrude_node_count, docstring: "How many distinct nodes, active or otherwise, are currently in the cluster configuration")
|
27
|
+
@read_state = Frankenstein::Request.new(:evinrude_read_state, description: "read state check", registry: @registry)
|
28
|
+
@remove_node = Frankenstein::Request.new(:evinrude_remove_node, description: "remove node request", registry: @registry)
|
29
|
+
@replication_majority = get_or_create(:gauge, :evinrude_replication_majority_index, docstring: "The last log index that has been replicated to a majority of nodes")
|
30
|
+
@rpc = Frankenstein::Request.new(:evinrude_rpc, description: "remote procedure call", registry: @registry, labels: %i{target}, duration_labels: %i{target result})
|
31
|
+
@rpc_exception = get_or_create(:counter, :evinrude_rpc_to_leader_exceptions_total, docstring: "How many exceptions have been raised whilst trying to do RPCs to the cluster leader", labels: %i{node_name target class})
|
32
|
+
@snapshot_file_size = get_or_create(:gauge, :evinrude_snapshot_file_size_bytes, docstring: "The current size of the snapshot data file")
|
33
|
+
@start_time = get_or_create(:gauge, :evinrude_node_start_time, docstring: "The number of seconds since the Unix epoch at which this Evinrude node commenced operation")
|
34
|
+
@state = get_or_create(:gauge, :evinrude_node_state, docstring: "The current state of the node; 0=init, 1=candidate, 2=follower, 3=leader")
|
35
|
+
@term = get_or_create(:gauge, :evinrude_current_term, docstring: "The current Raft election 'term' that this node is operating in")
|
36
|
+
end
|
37
|
+
|
38
|
+
def clear_peer_metrics
|
39
|
+
[@match_index, @next_index, @replication_majority].each do |metric|
|
40
|
+
metric.values.keys.each { |ls| metric.remove(ls) }
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
def get_or_create(type, name, docstring:, labels: [])
|
47
|
+
@registry.get(name) || @registry.__send__(type, name, docstring: docstring, labels: labels)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
require "async/io"
|
2
|
+
require "socket"
|
3
|
+
|
4
|
+
require_relative "./message/join_request"
|
5
|
+
require_relative "./network/connection"
|
6
|
+
|
7
|
+
class Evinrude
|
8
|
+
class Network
|
9
|
+
class ConnectionTimeoutError < Error; end
|
10
|
+
|
11
|
+
include Evinrude::LoggingHelpers
|
12
|
+
|
13
|
+
def initialize(keys:, logger:, metrics:, listen:, advertise:)
|
14
|
+
@keys, @logger, @metrics, @advertise, @listen = keys, logger, metrics, advertise, listen
|
15
|
+
|
16
|
+
@endpoint = Async::IO::Endpoint.tcp(@listen[:address], @listen[:port])
|
17
|
+
end
|
18
|
+
|
19
|
+
def start
|
20
|
+
@socket = @endpoint.bind.first
|
21
|
+
@socket.listen(Socket::SOMAXCONN)
|
22
|
+
self
|
23
|
+
end
|
24
|
+
|
25
|
+
def advertised_address
|
26
|
+
@advertise[:address] ||
|
27
|
+
(
|
28
|
+
Socket.ip_address_list.select { |a| a.ipv6? && !a.ipv6_loopback? && !a.ipv6_linklocal? }.first ||
|
29
|
+
Socket.ip_address_list.select { |a| a.ipv4? && !a.ipv4_loopback? }.first
|
30
|
+
).ip_address
|
31
|
+
end
|
32
|
+
|
33
|
+
def advertised_port
|
34
|
+
@advertise[:port] ||
|
35
|
+
@socket&.instance_variable_get(:@io)&.local_address&.ip_port
|
36
|
+
end
|
37
|
+
|
38
|
+
def listen_address
|
39
|
+
@listen[:address]
|
40
|
+
end
|
41
|
+
|
42
|
+
def listen_port
|
43
|
+
@listen[:port] == 0 ? advertised_port : @listen[:port]
|
44
|
+
end
|
45
|
+
|
46
|
+
def each_message(&blk)
|
47
|
+
unless @socket
|
48
|
+
bind
|
49
|
+
end
|
50
|
+
|
51
|
+
@socket.listen(Socket::SOMAXCONN)
|
52
|
+
|
53
|
+
@socket.accept_each do |sock|
|
54
|
+
conn = Network::Connection.new(socket: sock, keys: @keys, logger: logger, metrics: @metrics)
|
55
|
+
|
56
|
+
conn.each_message do |msg|
|
57
|
+
blk.call(msg, conn)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def connect(address:, port:)
|
63
|
+
Connection.connect(address: address, port: port, keys: @keys, logger: logger, metrics: @metrics).tap do |conn|
|
64
|
+
logger.debug(logloc) { "New connection #{conn} to #{address}:#{port}" }
|
65
|
+
yield conn if block_given?
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,144 @@
|
|
1
|
+
require "async/semaphore"
|
2
|
+
require "digest/sha2"
|
3
|
+
require "yaml"
|
4
|
+
|
5
|
+
require_relative "./protocol"
|
6
|
+
|
7
|
+
class Evinrude
|
8
|
+
class Network
|
9
|
+
class Connection
|
10
|
+
include Evinrude::LoggingHelpers
|
11
|
+
include Evinrude::Network::Protocol
|
12
|
+
|
13
|
+
class ConnectionError < Error; end
|
14
|
+
|
15
|
+
attr_reader :peer_address, :peer_port
|
16
|
+
|
17
|
+
def self.connect(address:, port:, keys:, logger:, metrics:)
|
18
|
+
backoff = Evinrude::Backoff.new
|
19
|
+
|
20
|
+
begin
|
21
|
+
sock = Async::IO::Endpoint.tcp(address, port).connect
|
22
|
+
rescue Errno::ECONNRESET, Errno::ECONNREFUSED, Errno::ETIMEDOUT => ex
|
23
|
+
logger.info("Evinrude::Network::Connection.connect") { "Could not connect to #{address}:#{port}: #{ex.class}" }
|
24
|
+
raise ConnectionError,
|
25
|
+
"#{ex.class}"
|
26
|
+
end
|
27
|
+
|
28
|
+
new(socket: sock, logger: logger, metrics: metrics, keys: keys)
|
29
|
+
end
|
30
|
+
|
31
|
+
def initialize(socket:, logger:, metrics:, keys:)
|
32
|
+
@socket, @logger, @metrics = socket, logger, metrics
|
33
|
+
|
34
|
+
@keys = keys.map { |k| Digest::SHA256.digest(k) }
|
35
|
+
|
36
|
+
@sem = Async::Semaphore.new
|
37
|
+
|
38
|
+
@peer_address = @socket.remote_address.ip_address
|
39
|
+
@peer_port = @socket.remote_address.ip_port
|
40
|
+
end
|
41
|
+
|
42
|
+
def peer_info
|
43
|
+
"#{peer_address}:#{peer_port}"
|
44
|
+
end
|
45
|
+
|
46
|
+
def rpc(msg)
|
47
|
+
@metrics.rpc.measure(target: peer_info) do |labels|
|
48
|
+
begin
|
49
|
+
@sem.acquire do
|
50
|
+
logger.debug(logloc) { "Sending RPC request #{msg.inspect} to #{peer_info}" }
|
51
|
+
begin
|
52
|
+
@socket.write(frame(box(msg.to_yaml)))
|
53
|
+
rescue Errno::EPIPE, IOError, Errno::ECONNRESET => ex
|
54
|
+
logger.debug(logloc) { "Failed to send RPC request to #{peer_info}: #{ex.message} (#{ex.class})" }
|
55
|
+
labels[:result] = ex.class.to_s
|
56
|
+
return nil
|
57
|
+
end
|
58
|
+
|
59
|
+
logger.debug(logloc) { "Request sent; now we wait" }
|
60
|
+
|
61
|
+
begin
|
62
|
+
read_message
|
63
|
+
rescue Protocol::VersionError, Errno::ECONNRESET, Errno::EPIPE, IOError => ex
|
64
|
+
logger.debug(logloc) { "I/O exception #{ex.class} while reading RPC reply" }
|
65
|
+
labels[:result] = ex.class.to_s
|
66
|
+
@socket.close
|
67
|
+
nil
|
68
|
+
end
|
69
|
+
end.tap { labels[:result] = "success" }
|
70
|
+
rescue Async::Wrapper::Cancelled
|
71
|
+
labels[:result] = "cancelled"
|
72
|
+
nil
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def each_message
|
78
|
+
begin
|
79
|
+
loop do
|
80
|
+
@sem.acquire do
|
81
|
+
yield read_message
|
82
|
+
end
|
83
|
+
end
|
84
|
+
rescue Async::Wrapper::Cancelled
|
85
|
+
# This is fine
|
86
|
+
nil
|
87
|
+
rescue Evinrude::Error, SystemCallError, IOError => ex
|
88
|
+
# This is... not so fine, but there's not much we can do about it
|
89
|
+
log_exception(ex) { "Reading message" }
|
90
|
+
@socket.close
|
91
|
+
nil
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
def send_reply(msg)
|
96
|
+
@socket.write(frame(box(msg.to_yaml)))
|
97
|
+
end
|
98
|
+
|
99
|
+
def close
|
100
|
+
@socket.close
|
101
|
+
end
|
102
|
+
|
103
|
+
def inspect
|
104
|
+
"#<#{self.class}:0x#{object_id.to_s(16)} " +
|
105
|
+
instance_variables.map do |iv|
|
106
|
+
next nil if %i{@logger @metrics @socket}.include?(iv)
|
107
|
+
"#{iv}=#{instance_variable_get(iv).inspect}"
|
108
|
+
end.compact.join(" ")
|
109
|
+
end
|
110
|
+
|
111
|
+
private
|
112
|
+
|
113
|
+
def read_message
|
114
|
+
v = @socket.read(1)
|
115
|
+
|
116
|
+
if v.nil?
|
117
|
+
logger.debug(logloc) { "Connection to #{peer_info} closed" }
|
118
|
+
raise Async::Wrapper::Cancelled
|
119
|
+
end
|
120
|
+
|
121
|
+
unless v == "\x00"
|
122
|
+
raise Protocol::VersionError, "Expected 0, got #{v.inspect}"
|
123
|
+
end
|
124
|
+
|
125
|
+
lenlen = @socket.read(1).ord
|
126
|
+
|
127
|
+
if (lenlen & 0x80) == 0
|
128
|
+
len = lenlen
|
129
|
+
else
|
130
|
+
lenlen &= 0x7f
|
131
|
+
len = @socket.read(lenlen).split(//).inject(0) { |a, c| a * 256 + c.ord }
|
132
|
+
end
|
133
|
+
|
134
|
+
if len > max_message_size
|
135
|
+
raise MessageTooBigError
|
136
|
+
end
|
137
|
+
|
138
|
+
box = @socket.read(len)
|
139
|
+
|
140
|
+
YAML.safe_load(unbox(box), permitted_classes: Message.classes + LogEntry.classes + [NodeInfo, ClusterConfiguration, Symbol], aliases: true)
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|