evinrude 0.0.1
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/.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
|