Class: Stoplight::Infrastructure::Memory::DataStore

Inherits:
Object
  • Object
show all
Includes:
MonitorMixin
Defined in:
lib/stoplight/infrastructure/memory/data_store.rb,
lib/stoplight/infrastructure/memory/data_store/state.rb,
lib/stoplight/infrastructure/memory/data_store/metrics.rb,
lib/stoplight/infrastructure/memory/data_store/sliding_window.rb,
lib/stoplight/infrastructure/memory/data_store/recovery_lock_store.rb,
lib/stoplight/infrastructure/memory/data_store/recovery_lock_token.rb

Overview

steep:ignore:start

See Also:

  • +Domain+Domain::_DataStore+

Defined Under Namespace

Classes: Metrics, RecoveryLockStore, RecoveryLockToken, SlidingWindow, State

Constant Summary collapse

KEY_SEPARATOR =
":"

Instance Method Summary collapse

Constructor Details

#initialize(recovery_lock_store:, clock:) ⇒ DataStore

Returns a new instance of DataStore.

Parameters:



28
29
30
31
32
33
34
35
36
37
38
39
40
# File 'lib/stoplight/infrastructure/memory/data_store.rb', line 28

def initialize(recovery_lock_store:, clock:)
  @clock = clock
  @recovery_lock_store = recovery_lock_store
  @errors = Hash.new { |errors, light_name| errors[light_name] = SlidingWindow.new(clock:) }
  @successes = Hash.new { |successes, light_name| successes[light_name] = SlidingWindow.new(clock:) }
  @metrics = Hash.new { |metrics, light_name| metrics[light_name] = Metrics.new }

  @recovery_metrics = Hash.new { |metrics, light_name| metrics[light_name] = Metrics.new }

  @states = Hash.new { |states, light_name| states[light_name] = State.new }

  super() # MonitorMixin
end

Instance Method Details

#acquire_recovery_lock(config) ⇒ Stoplight::Infrastructure::Memory::DataStore::RecoveryLockToken?



257
258
259
# File 'lib/stoplight/infrastructure/memory/data_store.rb', line 257

def acquire_recovery_lock(config)
  recovery_lock_store.acquire_lock(config.name)
end

#clear_metrics(config) ⇒ Object



132
133
134
135
136
137
138
139
140
141
# File 'lib/stoplight/infrastructure/memory/data_store.rb', line 132

def clear_metrics(config)
  light_name = config.name
  synchronize do
    if config.window_size
      @errors[light_name] = SlidingWindow.new(clock:)
      @successes[light_name] = SlidingWindow.new(clock:)
    end
    @metrics[light_name] = Metrics.new
  end
end

#clear_recovery_metrics(config) ⇒ Object



143
144
145
146
147
# File 'lib/stoplight/infrastructure/memory/data_store.rb', line 143

def clear_recovery_metrics(config)
  synchronize do
    @recovery_metrics[config.name] = Metrics.new
  end
end

#delete_light(config) ⇒ void

This method returns an undefined value.

Parameters:



225
226
227
228
229
230
231
232
233
234
235
# File 'lib/stoplight/infrastructure/memory/data_store.rb', line 225

def delete_light(config)
  light_name = config.name

  synchronize do
    @states.delete(light_name)
    @recovery_metrics.delete(light_name)
    @metrics.delete(light_name)
    @errors.delete(light_name)
    @successes.delete(light_name)
  end
end

#get_metrics(config) ⇒ Stoplight::Domain::MetricsSnapshot



49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
# File 'lib/stoplight/infrastructure/memory/data_store.rb', line 49

def get_metrics(config)
  light_name = config.name

  synchronize do
    current_time = clock.current_time
    window_start = if config.window_size
      (current_time - config.window_size)
    else
      current_time
    end

    metrics = @metrics[light_name]

    errors = @errors[light_name].sum_in_window(window_start) if config.window_size
    successes = @successes[light_name].sum_in_window(window_start) if config.window_size
    consecutive_errors = config.window_size ? [metrics.consecutive_errors, errors].min : metrics.consecutive_errors
    consecutive_successes = config.window_size ? [metrics.consecutive_successes.to_i, successes].min : metrics.consecutive_successes.to_i

    Domain::MetricsSnapshot.new(
      errors:,
      successes:,
      consecutive_errors:,
      consecutive_successes:,
      last_error: metrics.last_error,
      last_success_at: metrics.last_success_at
    )
  end
end

#get_recovery_metrics(config) ⇒ Stoplight::Domain::MetricsSnapshot



79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
# File 'lib/stoplight/infrastructure/memory/data_store.rb', line 79

def get_recovery_metrics(config)
  light_name = config.name

  synchronize do
    metrics = @recovery_metrics[light_name]

    Domain::MetricsSnapshot.new(
      errors: nil, successes: nil,
      consecutive_errors: metrics.consecutive_errors,
      consecutive_successes: metrics.consecutive_successes,
      last_error: metrics.last_error,
      last_success_at: metrics.last_success_at
    )
  end
end

#get_state_snapshot(config) ⇒ Stoplight::Domain::StateSnapshot



96
97
98
99
100
101
102
103
104
105
106
107
108
# File 'lib/stoplight/infrastructure/memory/data_store.rb', line 96

def get_state_snapshot(config)
  time, state = synchronize do
    [clock.current_time, @states[config.name]]
  end

  Domain::StateSnapshot.new(
    time:,
    locked_state: state.locked_state,
    recovery_scheduled_after: state.recovery_scheduled_after,
    recovery_started_at: state.recovery_started_at,
    breached_at: state.breached_at
  )
end

#inspectString

Returns:

  • (String)


219
220
221
# File 'lib/stoplight/infrastructure/memory/data_store.rb', line 219

def inspect
  "#<#{self.class.name}>"
end

#namesArray<String>

Returns:

  • (Array<String>)


43
44
45
# File 'lib/stoplight/infrastructure/memory/data_store.rb', line 43

def names
  synchronize { @metrics.keys | @states.keys | @recovery_metrics.keys }
end

#record_failure(config, exception) ⇒ void

This method returns an undefined value.

Parameters:



113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
# File 'lib/stoplight/infrastructure/memory/data_store.rb', line 113

def record_failure(config, exception)
  current_time = clock.current_time
  light_name = config.name
  failure = Domain::Failure.from_error(exception, time: current_time)

  synchronize do
    @errors[light_name].increment if config.window_size

    metrics = @metrics[light_name]

    if metrics.last_error_at.nil? || failure.occurred_at > metrics.last_error_at
      metrics.last_error = failure
    end

    metrics.consecutive_errors += 1
    metrics.consecutive_successes = 0
  end
end

#record_recovery_probe_failure(config, exception) ⇒ void

This method returns an undefined value.

Parameters:



172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
# File 'lib/stoplight/infrastructure/memory/data_store.rb', line 172

def record_recovery_probe_failure(config, exception)
  light_name = config.name
  current_time = clock.current_time
  failure = Domain::Failure.from_error(exception, time: current_time)

  synchronize do
    metrics = @recovery_metrics[light_name]

    if metrics.last_error_at.nil? || failure.occurred_at > metrics.last_error_at
      metrics.last_error = failure
    end

    metrics.consecutive_errors += 1
    metrics.consecutive_successes = 0
  end
end

#record_recovery_probe_success(config) ⇒ void

This method returns an undefined value.

Parameters:



191
192
193
194
195
196
197
198
199
200
201
202
203
204
# File 'lib/stoplight/infrastructure/memory/data_store.rb', line 191

def record_recovery_probe_success(config)
  light_name = config.name
  current_time = clock.current_time

  synchronize do
    metrics = @recovery_metrics[light_name]
    if metrics.last_success_at.nil? || current_time > metrics.last_success_at
      metrics.last_success_at = current_time
    end

    metrics.consecutive_errors = 0
    metrics.consecutive_successes += 1
  end
end

#record_success(config) ⇒ void

This method returns an undefined value.

Parameters:



151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
# File 'lib/stoplight/infrastructure/memory/data_store.rb', line 151

def record_success(config)
  light_name = config.name
  current_time = clock.current_time

  synchronize do
    @successes[light_name].increment if config.window_size

    metrics = @metrics[light_name]

    if metrics.last_success_at.nil? || current_time > metrics.last_success_at
      metrics.last_success_at = current_time
    end

    metrics.consecutive_errors = 0
    metrics.consecutive_successes += 1
  end
end

#release_recovery_lock(lock) ⇒ void

This method returns an undefined value.



263
264
265
# File 'lib/stoplight/infrastructure/memory/data_store.rb', line 263

def release_recovery_lock(lock)
  recovery_lock_store.release_lock(lock)
end

#set_state(config, state) ⇒ String

Parameters:

Returns:

  • (String)


209
210
211
212
213
214
215
216
# File 'lib/stoplight/infrastructure/memory/data_store.rb', line 209

def set_state(config, state)
  light_name = config.name

  synchronize do
    @states[light_name].locked_state = state
  end
  state
end

#transition_to_color(config, color) ⇒ Boolean

Combined method that performs the state transition based on color

Parameters:

  • config (Stoplight::Domain::Config)

    The light configuration

  • color (String)

    The color to transition to (“GREEN”, “YELLOW”, or “RED”)

Returns:

  • (Boolean)

    true if this is the first instance to detect this transition



242
243
244
245
246
247
248
249
250
251
252
253
# File 'lib/stoplight/infrastructure/memory/data_store.rb', line 242

def transition_to_color(config, color)
  case color
  when Color::GREEN
    transition_to_green(config)
  when Color::YELLOW
    transition_to_yellow(config)
  when Color::RED
    transition_to_red(config)
  else
    raise ArgumentError, "Invalid color: #{color}"
  end
end