Class: Evinrude::ClusterConfiguration

Inherits:
Object
  • Object
show all
Includes:
LoggingHelpers
Defined in:
lib/evinrude/cluster_configuration.rb

Defined Under Namespace

Classes: TransitionInProgressError

Instance Method Summary collapse

Constructor Details

#initialize(logger:, metrics:) ⇒ ClusterConfiguration

Returns a new instance of ClusterConfiguration.



7
8
9
10
11
12
13
14
# File 'lib/evinrude/cluster_configuration.rb', line 7

def initialize(logger:, metrics:)
	@logger, @metrics = logger, metrics
	@old = []
	@new = []
	@transition_in_progress = false
	@metrics.joint_configuration.set(0)
	@m = Mutex.new
end

Instance Method Details

#[](id) ⇒ Object



100
101
102
# File 'lib/evinrude/cluster_configuration.rb', line 100

def [](id)
	nodes.find { |n| n.id == id }
end

#add_node(node_info) ⇒ Object



32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
# File 'lib/evinrude/cluster_configuration.rb', line 32

def add_node(node_info)
	@m.synchronize do
		if @transition_in_progress
			raise TransitionInProgressError,
			      "Cannot add a node whilst a config transition is in progress (@old=#{@old.inspect}, @new=#{@new.inspect})"
		end

		logger.debug(logloc) { "Commencing addition of #{node_info.inspect} to cluster config" }

		# Adding a new node with the same name but, presumably, a different
		# address and/or port triggers a config change in which the old
		# address/port is removed and the new address/port is added.
		existing_node = @old.find { |n| n.name == node_info.name }

		@new = @old + [node_info] - [existing_node].compact
		if @metrics
			@metrics.node_count&.set(nodes.length)
			@metrics.joint_configuration.set(1)
		end
		@transition_in_progress = true
	end
end

#encode_with(coder) ⇒ Object



104
105
106
107
108
109
110
111
# File 'lib/evinrude/cluster_configuration.rb', line 104

def encode_with(coder)
	@m.synchronize do
		instance_variables.each do |iv|
			next if %i{@logger @metrics @m}.include?(iv)
			coder[iv.to_s.sub(/^@/, '')] = instance_variable_get(iv)
		end
	end
end

#init_with(coder) ⇒ Object



113
114
115
116
117
118
119
# File 'lib/evinrude/cluster_configuration.rb', line 113

def init_with(coder)
	@m = Mutex.new

	coder.map.each do |k, v|
		instance_variable_set(:"@#{k}", v)
	end
end

#inspectObject



121
122
123
124
125
126
127
128
129
# File 'lib/evinrude/cluster_configuration.rb', line 121

def inspect
	@m.synchronize do
		"#<#{self.class}:0x#{object_id.to_s(16)} " +
			instance_variables.map do |iv|
				next nil if iv == :@logger || iv == :@metrics
				"#{iv}=#{instance_variable_get(iv).inspect}"
			end.compact.join(" ")
	end
end

#joint_configuration_replicatedObject



77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
# File 'lib/evinrude/cluster_configuration.rb', line 77

def joint_configuration_replicated
	unlock = false

	unless @m.owned?
		@m.lock
		unlock = true
	end

	logger.debug(logloc) { "Joint configuration has been replicated" }
	@old = @new
	@new = []
	@transition_in_progress = false
	@metrics&.joint_configuration&.set(0)
ensure
	@m.unlock if unlock
end

#logger=(l) ⇒ Object



131
132
133
134
135
136
137
# File 'lib/evinrude/cluster_configuration.rb', line 131

def logger=(l)
	if @logger
		raise ArgumentError, "Logger cannot be changed once set"
	end

	@logger = l
end

#metrics=(m) ⇒ Object



139
140
141
142
143
144
145
# File 'lib/evinrude/cluster_configuration.rb', line 139

def metrics=(m)
	if @metrics
		raise ArgumentError, "Metrics cannot be changed once set"
	end

	@metrics = m
end

#nodesObject



20
21
22
23
24
25
26
27
28
29
30
# File 'lib/evinrude/cluster_configuration.rb', line 20

def nodes
	locked = false
	unless @m.owned?
		@m.lock
		locked = true
	end

	(@old + @new).uniq
ensure
	@m.unlock if locked
end

#quorum_met?(present_nodes) ⇒ Boolean

Returns:

  • (Boolean)


94
95
96
97
98
# File 'lib/evinrude/cluster_configuration.rb', line 94

def quorum_met?(present_nodes)
	@m.synchronize do
		group_quorum?(@old, present_nodes) && group_quorum?(@new, present_nodes)
	end
end

#remove_node(node_info, force: false) ⇒ Object



55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
# File 'lib/evinrude/cluster_configuration.rb', line 55

def remove_node(node_info, force: false)
	@m.synchronize do
		if @transition_in_progress && !force
			raise TransitionInProgressError,
			      "Cannot remove a node whilst a config transition is in progress"
		end

		logger.debug(logloc) { "Commencing #{force ? "forced " : ""}removal of #{node_info.inspect} from cluster config" }

		@new = @old - [node_info]
		if @metrics
			@metrics.node_count&.set(nodes.length)
			@metrics.joint_configuration.set(1)
		end
		@transition_in_progress = true

		if force
			joint_configuration_replicated
		end
	end
end

#transitioning?Boolean

Returns:

  • (Boolean)


16
17
18
# File 'lib/evinrude/cluster_configuration.rb', line 16

def transitioning?
	@transition_in_progress
end