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