breaker_machines 0.12.0 → 0.13.2
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 +4 -4
- data/lib/breaker_machines/cascading_circuit.rb +3 -11
- data/lib/breaker_machines/circuit/async_state_management.rb +1 -35
- data/lib/breaker_machines/circuit/coordinated_state_management.rb +1 -34
- data/lib/breaker_machines/circuit/state_machine_definition.rb +51 -0
- data/lib/breaker_machines/circuit/state_management.rb +1 -34
- data/lib/breaker_machines/storage/bucket_memory.rb +2 -65
- data/lib/breaker_machines/storage/memory.rb +2 -65
- data/lib/breaker_machines/storage/memory_support.rb +80 -0
- data/lib/breaker_machines/version.rb +1 -1
- metadata +5 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: dab65770123fb6626a9112608ddeb784b1f623c6dd61ba59bd4f2094f8c73bb4
|
|
4
|
+
data.tar.gz: 572686fc3febc21058ea20c300667d09b466d427c0bcab98af4230e5e14c7ee3
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: ba432f7aeda81bd88dd93ea8d1c0cb1c843cbc964bc3a31c6baf1359b4fb0a2c4873cd0e5d19ed0543ef5f3ba728f7e2de78a022f395abec3164465f8d562169
|
|
7
|
+
data.tar.gz: 43f4c0da0445a1c7e91a9a1ba24e8892d4ba10cf1891ab75c44704834a1026b20e23fa5830c23029b3dee4a04ca9bba51c2319f069b0b92bbf7abd910a445ad1
|
|
@@ -95,17 +95,9 @@ module BreakerMachines
|
|
|
95
95
|
@cascade_triggered_at.value = BreakerMachines.monotonic_time
|
|
96
96
|
|
|
97
97
|
@dependent_circuits.each do |circuit_name|
|
|
98
|
-
#
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
# If not found and we have an owner, try to get it from the owner
|
|
102
|
-
if !circuit && @config[:owner]
|
|
103
|
-
owner = @config[:owner]
|
|
104
|
-
# Handle WeakRef if present
|
|
105
|
-
owner = owner.__getobj__ if owner.is_a?(WeakRef)
|
|
106
|
-
|
|
107
|
-
circuit = owner.circuit(circuit_name) if owner.respond_to?(:circuit)
|
|
108
|
-
end
|
|
98
|
+
# Resolve via registry, falling back to the owner (inherited from
|
|
99
|
+
# CoordinatedStateManagement).
|
|
100
|
+
circuit = find_dependent_circuit(circuit_name)
|
|
109
101
|
|
|
110
102
|
next unless circuit
|
|
111
103
|
next unless circuit.closed? || circuit.half_open?
|
|
@@ -14,11 +14,6 @@ module BreakerMachines
|
|
|
14
14
|
# - Fiber-safe execution
|
|
15
15
|
# - Concurrent transition handling
|
|
16
16
|
state_machine :status, initial: :closed, async: true do
|
|
17
|
-
event :trip do
|
|
18
|
-
transition closed: :open
|
|
19
|
-
transition half_open: :open
|
|
20
|
-
end
|
|
21
|
-
|
|
22
17
|
event :attempt_recovery do
|
|
23
18
|
transition open: :half_open
|
|
24
19
|
end
|
|
@@ -28,36 +23,7 @@ module BreakerMachines
|
|
|
28
23
|
transition closed: :closed
|
|
29
24
|
end
|
|
30
25
|
|
|
31
|
-
|
|
32
|
-
transition any => :open
|
|
33
|
-
end
|
|
34
|
-
|
|
35
|
-
event :force_close do
|
|
36
|
-
transition any => :closed
|
|
37
|
-
end
|
|
38
|
-
|
|
39
|
-
event :hard_reset do
|
|
40
|
-
transition any => :closed
|
|
41
|
-
end
|
|
42
|
-
|
|
43
|
-
before_transition on: :hard_reset do |circuit|
|
|
44
|
-
circuit.storage&.clear(circuit.name)
|
|
45
|
-
circuit.half_open_attempts.value = 0
|
|
46
|
-
circuit.half_open_successes.value = 0
|
|
47
|
-
end
|
|
48
|
-
|
|
49
|
-
# Async-safe callbacks using modern API
|
|
50
|
-
after_transition to: :open do |circuit|
|
|
51
|
-
circuit.send(:on_circuit_open)
|
|
52
|
-
end
|
|
53
|
-
|
|
54
|
-
after_transition to: :closed do |circuit|
|
|
55
|
-
circuit.send(:on_circuit_close)
|
|
56
|
-
end
|
|
57
|
-
|
|
58
|
-
after_transition from: :open, to: :half_open do |circuit|
|
|
59
|
-
circuit.send(:on_circuit_half_open)
|
|
60
|
-
end
|
|
26
|
+
instance_eval(&StateMachineDefinition::COMMON)
|
|
61
27
|
end
|
|
62
28
|
|
|
63
29
|
# Additional async event methods are automatically generated:
|
|
@@ -10,11 +10,6 @@ module BreakerMachines
|
|
|
10
10
|
included do
|
|
11
11
|
# Override the state machine to add coordinated guards
|
|
12
12
|
state_machine :status, initial: :closed do
|
|
13
|
-
event :trip do
|
|
14
|
-
transition closed: :open
|
|
15
|
-
transition half_open: :open
|
|
16
|
-
end
|
|
17
|
-
|
|
18
13
|
event :attempt_recovery do
|
|
19
14
|
transition open: :half_open,
|
|
20
15
|
if: lambda(&:recovery_allowed?)
|
|
@@ -26,35 +21,7 @@ module BreakerMachines
|
|
|
26
21
|
transition closed: :closed
|
|
27
22
|
end
|
|
28
23
|
|
|
29
|
-
|
|
30
|
-
transition any => :open
|
|
31
|
-
end
|
|
32
|
-
|
|
33
|
-
event :force_close do
|
|
34
|
-
transition any => :closed
|
|
35
|
-
end
|
|
36
|
-
|
|
37
|
-
event :hard_reset do
|
|
38
|
-
transition any => :closed
|
|
39
|
-
end
|
|
40
|
-
|
|
41
|
-
before_transition on: :hard_reset do |circuit|
|
|
42
|
-
circuit.storage&.clear(circuit.name)
|
|
43
|
-
circuit.half_open_attempts.value = 0
|
|
44
|
-
circuit.half_open_successes.value = 0
|
|
45
|
-
end
|
|
46
|
-
|
|
47
|
-
after_transition to: :open do |circuit|
|
|
48
|
-
circuit.send(:on_circuit_open)
|
|
49
|
-
end
|
|
50
|
-
|
|
51
|
-
after_transition to: :closed do |circuit|
|
|
52
|
-
circuit.send(:on_circuit_close)
|
|
53
|
-
end
|
|
54
|
-
|
|
55
|
-
after_transition from: :open, to: :half_open do |circuit|
|
|
56
|
-
circuit.send(:on_circuit_half_open)
|
|
57
|
-
end
|
|
24
|
+
instance_eval(&StateMachineDefinition::COMMON)
|
|
58
25
|
end
|
|
59
26
|
end
|
|
60
27
|
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module BreakerMachines
|
|
4
|
+
class Circuit
|
|
5
|
+
# Shared state machine definition for circuit breakers.
|
|
6
|
+
#
|
|
7
|
+
# The trip/force_open/force_close/hard_reset events and the transition
|
|
8
|
+
# callbacks are identical across the sync, async, and coordinated state
|
|
9
|
+
# management modules. Only the +attempt_recovery+ and +reset+ events differ
|
|
10
|
+
# (coordinated circuits add guard conditions), so those stay in each module
|
|
11
|
+
# while everything common is spliced in via instance_eval(&COMMON).
|
|
12
|
+
module StateMachineDefinition
|
|
13
|
+
COMMON = proc do
|
|
14
|
+
event :trip do
|
|
15
|
+
transition closed: :open
|
|
16
|
+
transition half_open: :open
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
event :force_open do
|
|
20
|
+
transition any => :open
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
event :force_close do
|
|
24
|
+
transition any => :closed
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
event :hard_reset do
|
|
28
|
+
transition any => :closed
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
before_transition on: :hard_reset do |circuit|
|
|
32
|
+
circuit.storage&.clear(circuit.name)
|
|
33
|
+
circuit.half_open_attempts.value = 0
|
|
34
|
+
circuit.half_open_successes.value = 0
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
after_transition to: :open do |circuit|
|
|
38
|
+
circuit.send(:on_circuit_open)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
after_transition to: :closed do |circuit|
|
|
42
|
+
circuit.send(:on_circuit_close)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
after_transition from: :open, to: :half_open do |circuit|
|
|
46
|
+
circuit.send(:on_circuit_half_open)
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
@@ -9,11 +9,6 @@ module BreakerMachines
|
|
|
9
9
|
|
|
10
10
|
included do
|
|
11
11
|
state_machine :status, initial: :closed do
|
|
12
|
-
event :trip do
|
|
13
|
-
transition closed: :open
|
|
14
|
-
transition half_open: :open
|
|
15
|
-
end
|
|
16
|
-
|
|
17
12
|
event :attempt_recovery do
|
|
18
13
|
transition open: :half_open
|
|
19
14
|
end
|
|
@@ -23,35 +18,7 @@ module BreakerMachines
|
|
|
23
18
|
transition closed: :closed
|
|
24
19
|
end
|
|
25
20
|
|
|
26
|
-
|
|
27
|
-
transition any => :open
|
|
28
|
-
end
|
|
29
|
-
|
|
30
|
-
event :force_close do
|
|
31
|
-
transition any => :closed
|
|
32
|
-
end
|
|
33
|
-
|
|
34
|
-
event :hard_reset do
|
|
35
|
-
transition any => :closed
|
|
36
|
-
end
|
|
37
|
-
|
|
38
|
-
before_transition on: :hard_reset do |circuit|
|
|
39
|
-
circuit.storage&.clear(circuit.name)
|
|
40
|
-
circuit.half_open_attempts.value = 0
|
|
41
|
-
circuit.half_open_successes.value = 0
|
|
42
|
-
end
|
|
43
|
-
|
|
44
|
-
after_transition to: :open do |circuit|
|
|
45
|
-
circuit.send(:on_circuit_open)
|
|
46
|
-
end
|
|
47
|
-
|
|
48
|
-
after_transition to: :closed do |circuit|
|
|
49
|
-
circuit.send(:on_circuit_close)
|
|
50
|
-
end
|
|
51
|
-
|
|
52
|
-
after_transition from: :open, to: :half_open do |circuit|
|
|
53
|
-
circuit.send(:on_circuit_half_open)
|
|
54
|
-
end
|
|
21
|
+
instance_eval(&StateMachineDefinition::COMMON)
|
|
55
22
|
end
|
|
56
23
|
end
|
|
57
24
|
end
|
|
@@ -12,6 +12,8 @@ module BreakerMachines
|
|
|
12
12
|
# environments as memory is not shared between processes. Use Cache backend
|
|
13
13
|
# with an external cache store (Redis, Memcached) for distributed setups.
|
|
14
14
|
class BucketMemory < Base
|
|
15
|
+
include MemorySupport
|
|
16
|
+
|
|
15
17
|
BUCKET_SIZE = 1 # 1 second per bucket
|
|
16
18
|
|
|
17
19
|
def initialize(**options)
|
|
@@ -25,40 +27,6 @@ module BreakerMachines
|
|
|
25
27
|
@start_time = BreakerMachines.monotonic_time
|
|
26
28
|
end
|
|
27
29
|
|
|
28
|
-
def get_status(circuit_name)
|
|
29
|
-
circuit_data = @circuits[circuit_name]
|
|
30
|
-
return nil unless circuit_data
|
|
31
|
-
|
|
32
|
-
BreakerMachines::Status.new(
|
|
33
|
-
status: circuit_data[:status],
|
|
34
|
-
opened_at: circuit_data[:opened_at]
|
|
35
|
-
)
|
|
36
|
-
end
|
|
37
|
-
|
|
38
|
-
def set_status(circuit_name, status, opened_at = nil)
|
|
39
|
-
@circuits[circuit_name] = {
|
|
40
|
-
status: status,
|
|
41
|
-
opened_at: opened_at,
|
|
42
|
-
updated_at: monotonic_time
|
|
43
|
-
}
|
|
44
|
-
end
|
|
45
|
-
|
|
46
|
-
def record_success(circuit_name, duration)
|
|
47
|
-
record_event(circuit_name, :success, duration)
|
|
48
|
-
end
|
|
49
|
-
|
|
50
|
-
def record_failure(circuit_name, duration)
|
|
51
|
-
record_event(circuit_name, :failure, duration)
|
|
52
|
-
end
|
|
53
|
-
|
|
54
|
-
def success_count(circuit_name, window_seconds)
|
|
55
|
-
count_events(circuit_name, :success, window_seconds)
|
|
56
|
-
end
|
|
57
|
-
|
|
58
|
-
def failure_count(circuit_name, window_seconds)
|
|
59
|
-
count_events(circuit_name, :failure, window_seconds)
|
|
60
|
-
end
|
|
61
|
-
|
|
62
30
|
def clear(circuit_name)
|
|
63
31
|
@circuits.delete(circuit_name)
|
|
64
32
|
@circuit_buckets.delete(circuit_name)
|
|
@@ -71,32 +39,6 @@ module BreakerMachines
|
|
|
71
39
|
@event_logs.clear
|
|
72
40
|
end
|
|
73
41
|
|
|
74
|
-
def record_event_with_details(circuit_name, type, duration, error: nil, new_state: nil)
|
|
75
|
-
events = @event_logs.compute_if_absent(circuit_name) { Concurrent::Array.new }
|
|
76
|
-
|
|
77
|
-
event = {
|
|
78
|
-
type: type,
|
|
79
|
-
timestamp: monotonic_time,
|
|
80
|
-
duration_ms: (duration * 1000).round(2)
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
event[:error_class] = error.class.name if error
|
|
84
|
-
event[:error_message] = error.message if error
|
|
85
|
-
event[:new_state] = new_state if new_state
|
|
86
|
-
|
|
87
|
-
events << event
|
|
88
|
-
|
|
89
|
-
# Keep only the most recent events
|
|
90
|
-
events.shift while events.size > @max_events
|
|
91
|
-
end
|
|
92
|
-
|
|
93
|
-
def event_log(circuit_name, limit)
|
|
94
|
-
events = @event_logs[circuit_name]
|
|
95
|
-
return [] unless events
|
|
96
|
-
|
|
97
|
-
events.last(limit).map(&:dup)
|
|
98
|
-
end
|
|
99
|
-
|
|
100
42
|
private
|
|
101
43
|
|
|
102
44
|
def record_event(circuit_name, type, _duration)
|
|
@@ -161,11 +103,6 @@ module BreakerMachines
|
|
|
161
103
|
(monotonic_time / BUCKET_SIZE).to_i
|
|
162
104
|
end
|
|
163
105
|
|
|
164
|
-
def monotonic_time
|
|
165
|
-
# Return time relative to storage creation (matches Rust implementation)
|
|
166
|
-
BreakerMachines.monotonic_time - @start_time
|
|
167
|
-
end
|
|
168
|
-
|
|
169
106
|
def with_timeout(_timeout_ms)
|
|
170
107
|
# BucketMemory operations should be instant, but we'll still respect the timeout
|
|
171
108
|
# This is more for consistency and to catch any potential deadlocks
|
|
@@ -11,6 +11,8 @@ module BreakerMachines
|
|
|
11
11
|
# environments as memory is not shared between processes. Use Cache backend
|
|
12
12
|
# with an external cache store (Redis, Memcached) for distributed setups.
|
|
13
13
|
class Memory < Base
|
|
14
|
+
include MemorySupport
|
|
15
|
+
|
|
14
16
|
def initialize(**options)
|
|
15
17
|
super
|
|
16
18
|
@circuits = Concurrent::Map.new
|
|
@@ -21,40 +23,6 @@ module BreakerMachines
|
|
|
21
23
|
@start_time = BreakerMachines.monotonic_time
|
|
22
24
|
end
|
|
23
25
|
|
|
24
|
-
def get_status(circuit_name)
|
|
25
|
-
circuit_data = @circuits[circuit_name]
|
|
26
|
-
return nil unless circuit_data
|
|
27
|
-
|
|
28
|
-
BreakerMachines::Status.new(
|
|
29
|
-
status: circuit_data[:status],
|
|
30
|
-
opened_at: circuit_data[:opened_at]
|
|
31
|
-
)
|
|
32
|
-
end
|
|
33
|
-
|
|
34
|
-
def set_status(circuit_name, status, opened_at = nil)
|
|
35
|
-
@circuits[circuit_name] = {
|
|
36
|
-
status: status,
|
|
37
|
-
opened_at: opened_at,
|
|
38
|
-
updated_at: monotonic_time
|
|
39
|
-
}
|
|
40
|
-
end
|
|
41
|
-
|
|
42
|
-
def record_success(circuit_name, duration)
|
|
43
|
-
record_event(circuit_name, :success, duration)
|
|
44
|
-
end
|
|
45
|
-
|
|
46
|
-
def record_failure(circuit_name, duration)
|
|
47
|
-
record_event(circuit_name, :failure, duration)
|
|
48
|
-
end
|
|
49
|
-
|
|
50
|
-
def success_count(circuit_name, window_seconds)
|
|
51
|
-
count_events(circuit_name, :success, window_seconds)
|
|
52
|
-
end
|
|
53
|
-
|
|
54
|
-
def failure_count(circuit_name, window_seconds)
|
|
55
|
-
count_events(circuit_name, :failure, window_seconds)
|
|
56
|
-
end
|
|
57
|
-
|
|
58
26
|
def clear(circuit_name)
|
|
59
27
|
@circuits.delete(circuit_name)
|
|
60
28
|
@events.delete(circuit_name)
|
|
@@ -67,32 +35,6 @@ module BreakerMachines
|
|
|
67
35
|
@event_logs.clear
|
|
68
36
|
end
|
|
69
37
|
|
|
70
|
-
def record_event_with_details(circuit_name, type, duration, error: nil, new_state: nil)
|
|
71
|
-
events = @event_logs.compute_if_absent(circuit_name) { Concurrent::Array.new }
|
|
72
|
-
|
|
73
|
-
event = {
|
|
74
|
-
type: type,
|
|
75
|
-
timestamp: monotonic_time,
|
|
76
|
-
duration_ms: (duration * 1000).round(2)
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
event[:error_class] = error.class.name if error
|
|
80
|
-
event[:error_message] = error.message if error
|
|
81
|
-
event[:new_state] = new_state if new_state
|
|
82
|
-
|
|
83
|
-
events << event
|
|
84
|
-
|
|
85
|
-
# Keep only the most recent events
|
|
86
|
-
events.shift while events.size > @max_events
|
|
87
|
-
end
|
|
88
|
-
|
|
89
|
-
def event_log(circuit_name, limit)
|
|
90
|
-
events = @event_logs[circuit_name]
|
|
91
|
-
return [] unless events
|
|
92
|
-
|
|
93
|
-
events.last(limit).map(&:dup)
|
|
94
|
-
end
|
|
95
|
-
|
|
96
38
|
def with_timeout(_timeout_ms)
|
|
97
39
|
# Memory operations should be instant, but we'll still respect the timeout
|
|
98
40
|
# This is more for consistency and to catch any potential deadlocks
|
|
@@ -130,11 +72,6 @@ module BreakerMachines
|
|
|
130
72
|
cutoff_time = monotonic_time - window_seconds
|
|
131
73
|
events.count { |e| e[:type] == type && e[:timestamp] >= cutoff_time }
|
|
132
74
|
end
|
|
133
|
-
|
|
134
|
-
def monotonic_time
|
|
135
|
-
# Return time relative to storage creation (matches Rust implementation)
|
|
136
|
-
BreakerMachines.monotonic_time - @start_time
|
|
137
|
-
end
|
|
138
75
|
end
|
|
139
76
|
end
|
|
140
77
|
end
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module BreakerMachines
|
|
4
|
+
module Storage
|
|
5
|
+
# Methods shared by the in-memory storage backends (Memory and BucketMemory).
|
|
6
|
+
#
|
|
7
|
+
# Status storage, event-detail logging and the relative monotonic clock are
|
|
8
|
+
# identical between the two backends; only the event counting strategy
|
|
9
|
+
# (raw event arrays vs. fixed-size buckets) differs, so those methods stay
|
|
10
|
+
# in each backend.
|
|
11
|
+
module MemorySupport
|
|
12
|
+
def get_status(circuit_name)
|
|
13
|
+
circuit_data = @circuits[circuit_name]
|
|
14
|
+
return nil unless circuit_data
|
|
15
|
+
|
|
16
|
+
BreakerMachines::Status.new(
|
|
17
|
+
status: circuit_data[:status],
|
|
18
|
+
opened_at: circuit_data[:opened_at]
|
|
19
|
+
)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def set_status(circuit_name, status, opened_at = nil)
|
|
23
|
+
@circuits[circuit_name] = {
|
|
24
|
+
status: status,
|
|
25
|
+
opened_at: opened_at,
|
|
26
|
+
updated_at: monotonic_time
|
|
27
|
+
}
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def record_success(circuit_name, duration)
|
|
31
|
+
record_event(circuit_name, :success, duration)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def record_failure(circuit_name, duration)
|
|
35
|
+
record_event(circuit_name, :failure, duration)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def success_count(circuit_name, window_seconds)
|
|
39
|
+
count_events(circuit_name, :success, window_seconds)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def failure_count(circuit_name, window_seconds)
|
|
43
|
+
count_events(circuit_name, :failure, window_seconds)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def record_event_with_details(circuit_name, type, duration, error: nil, new_state: nil)
|
|
47
|
+
events = @event_logs.compute_if_absent(circuit_name) { Concurrent::Array.new }
|
|
48
|
+
|
|
49
|
+
event = {
|
|
50
|
+
type: type,
|
|
51
|
+
timestamp: monotonic_time,
|
|
52
|
+
duration_ms: (duration * 1000).round(2)
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
event[:error_class] = error.class.name if error
|
|
56
|
+
event[:error_message] = error.message if error
|
|
57
|
+
event[:new_state] = new_state if new_state
|
|
58
|
+
|
|
59
|
+
events << event
|
|
60
|
+
|
|
61
|
+
# Keep only the most recent events
|
|
62
|
+
events.shift while events.size > @max_events
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def event_log(circuit_name, limit)
|
|
66
|
+
events = @event_logs[circuit_name]
|
|
67
|
+
return [] unless events
|
|
68
|
+
|
|
69
|
+
events.last(limit).map(&:dup)
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
private
|
|
73
|
+
|
|
74
|
+
def monotonic_time
|
|
75
|
+
# Return time relative to storage creation (matches Rust implementation)
|
|
76
|
+
BreakerMachines.monotonic_time - @start_time
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: breaker_machines
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.13.2
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Abdelkader Boudih
|
|
@@ -57,14 +57,14 @@ dependencies:
|
|
|
57
57
|
requirements:
|
|
58
58
|
- - ">="
|
|
59
59
|
- !ruby/object:Gem::Version
|
|
60
|
-
version: 0.
|
|
60
|
+
version: 0.200.0
|
|
61
61
|
type: :runtime
|
|
62
62
|
prerelease: false
|
|
63
63
|
version_requirements: !ruby/object:Gem::Requirement
|
|
64
64
|
requirements:
|
|
65
65
|
- - ">="
|
|
66
66
|
- !ruby/object:Gem::Version
|
|
67
|
-
version: 0.
|
|
67
|
+
version: 0.200.0
|
|
68
68
|
- !ruby/object:Gem::Dependency
|
|
69
69
|
name: zeitwerk
|
|
70
70
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -164,6 +164,7 @@ files:
|
|
|
164
164
|
- lib/breaker_machines/circuit/introspection.rb
|
|
165
165
|
- lib/breaker_machines/circuit/native.rb
|
|
166
166
|
- lib/breaker_machines/circuit/state_callbacks.rb
|
|
167
|
+
- lib/breaker_machines/circuit/state_machine_definition.rb
|
|
167
168
|
- lib/breaker_machines/circuit/state_management.rb
|
|
168
169
|
- lib/breaker_machines/circuit_group.rb
|
|
169
170
|
- lib/breaker_machines/console.rb
|
|
@@ -185,6 +186,7 @@ files:
|
|
|
185
186
|
- lib/breaker_machines/storage/cache.rb
|
|
186
187
|
- lib/breaker_machines/storage/fallback_chain.rb
|
|
187
188
|
- lib/breaker_machines/storage/memory.rb
|
|
189
|
+
- lib/breaker_machines/storage/memory_support.rb
|
|
188
190
|
- lib/breaker_machines/storage/native.rb
|
|
189
191
|
- lib/breaker_machines/storage/null.rb
|
|
190
192
|
- lib/breaker_machines/types.rb
|