breaker_machines 0.13.2 → 0.17.0
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/async_support.rb +26 -19
- data/lib/breaker_machines/circuit/admission.rb +96 -0
- data/lib/breaker_machines/circuit/base.rb +13 -7
- data/lib/breaker_machines/circuit/execution.rb +56 -101
- data/lib/breaker_machines/circuit/state_callbacks.rb +3 -0
- data/lib/breaker_machines/version.rb +1 -1
- data/sig/breaker_machines/circuit.rbs +31 -6
- metadata +2 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 4116461b731bfc94e0773301a5ce27d8a118e4b8d8adf033e1b53b304f2538e5
|
|
4
|
+
data.tar.gz: 6cf9ed874a11f2f33db5c92f13875686260b65267c933dcb1d2cc1d994264a3f
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: fb362d48c18c9706cdffc79634af898403d9cd349a4bef73da399cbe177eaab435f83a8143b33d7c420745b4f4969c682a184269b8849893123244b98b7b2eea
|
|
7
|
+
data.tar.gz: afd84f6bc4ac312d7bc7cbbcc1c9710fdb550694e0e32c4598bf04d0658af2fe5c23a399fbdfa2801e99dbfe610c3fd44458df8b33569e948b77757bb5fd7208
|
|
@@ -18,32 +18,36 @@ module BreakerMachines
|
|
|
18
18
|
end
|
|
19
19
|
|
|
20
20
|
# Execute a call with async support (fiber-safe mode)
|
|
21
|
-
def execute_call_async(&)
|
|
21
|
+
def execute_call_async(admission, &)
|
|
22
|
+
completed = false
|
|
22
23
|
start_time = BreakerMachines.monotonic_time
|
|
23
24
|
|
|
24
25
|
begin
|
|
25
|
-
|
|
26
|
-
result = if @config[:hedged_requests] || @config[:backends]
|
|
27
|
-
execute_hedged(&)
|
|
28
|
-
else
|
|
29
|
-
execute_with_async_timeout(@config[:timeout], &)
|
|
30
|
-
end
|
|
26
|
+
result = execute_async_operation(&)
|
|
31
27
|
|
|
32
|
-
|
|
33
|
-
|
|
28
|
+
complete_call_success(admission, start_time)
|
|
29
|
+
completed = true
|
|
34
30
|
result
|
|
35
31
|
rescue StandardError => e
|
|
36
32
|
# Re-raise if it's not an async timeout or configured exception
|
|
37
33
|
raise unless e.is_a?(async_timeout_error_class) || @config[:exceptions].any? { |klass| e.is_a?(klass) }
|
|
38
34
|
|
|
39
|
-
|
|
40
|
-
|
|
35
|
+
complete_call_failure(admission, start_time, e)
|
|
36
|
+
completed = true
|
|
41
37
|
raise unless @config[:fallback]
|
|
42
38
|
|
|
43
39
|
invoke_fallback_with_async(e)
|
|
40
|
+
ensure
|
|
41
|
+
release_abandoned_admission(admission) unless completed
|
|
44
42
|
end
|
|
45
43
|
end
|
|
46
44
|
|
|
45
|
+
def execute_async_operation(&)
|
|
46
|
+
return execute_hedged(&) if @config[:hedged_requests] || @config[:backends]
|
|
47
|
+
|
|
48
|
+
execute_with_async_timeout(@config[:timeout], &)
|
|
49
|
+
end
|
|
50
|
+
|
|
47
51
|
# Execute a block with optional timeout using modern Async API
|
|
48
52
|
def execute_with_async_timeout(timeout, &)
|
|
49
53
|
if timeout
|
|
@@ -60,14 +64,7 @@ module BreakerMachines
|
|
|
60
64
|
when BreakerMachines::DSL::ParallelFallbackWrapper
|
|
61
65
|
invoke_parallel_fallbacks(@config[:fallback].fallbacks, error)
|
|
62
66
|
when Proc
|
|
63
|
-
|
|
64
|
-
@config[:owner].instance_exec(error, &@config[:fallback])
|
|
65
|
-
else
|
|
66
|
-
@config[:fallback].call(error)
|
|
67
|
-
end
|
|
68
|
-
|
|
69
|
-
# If the fallback returns an Async::Task, wait for it
|
|
70
|
-
result.is_a?(::Async::Task) ? result.wait : result
|
|
67
|
+
invoke_proc_fallback_async(@config[:fallback], error)
|
|
71
68
|
when Array
|
|
72
69
|
# Try each fallback in order until one succeeds
|
|
73
70
|
last_error = error
|
|
@@ -85,6 +82,16 @@ module BreakerMachines
|
|
|
85
82
|
|
|
86
83
|
private
|
|
87
84
|
|
|
85
|
+
def invoke_proc_fallback_async(fallback, error)
|
|
86
|
+
result = if @config[:owner]
|
|
87
|
+
@config[:owner].instance_exec(error, &fallback)
|
|
88
|
+
else
|
|
89
|
+
fallback.call(error)
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
result.is_a?(::Async::Task) ? result.wait : result
|
|
93
|
+
end
|
|
94
|
+
|
|
88
95
|
def invoke_single_fallback_async(fallback, error)
|
|
89
96
|
case fallback
|
|
90
97
|
when Proc
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module BreakerMachines
|
|
4
|
+
class Circuit
|
|
5
|
+
# Admission reserves calls under short locks and tracks their state generation.
|
|
6
|
+
module Admission
|
|
7
|
+
extend ActiveSupport::Concern
|
|
8
|
+
|
|
9
|
+
Call = Data.define(:state, :state_epoch) do
|
|
10
|
+
def closed?
|
|
11
|
+
state == :closed
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def half_open?
|
|
15
|
+
state == :half_open
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
private
|
|
20
|
+
|
|
21
|
+
def attempt_recovery_if_ready
|
|
22
|
+
return unless open? && reset_timeout_elapsed?
|
|
23
|
+
|
|
24
|
+
@mutex.with_write_lock do
|
|
25
|
+
attempt_recovery if open? && reset_timeout_elapsed?
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def admit_call
|
|
30
|
+
@mutex.with_read_lock do
|
|
31
|
+
case status_name
|
|
32
|
+
when :closed
|
|
33
|
+
Call.new(state: :closed, state_epoch: @state_epoch.value)
|
|
34
|
+
when :half_open
|
|
35
|
+
admit_half_open_call
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def admit_half_open_call
|
|
41
|
+
state_epoch = @state_epoch.value
|
|
42
|
+
new_attempts = @half_open_attempts.increment
|
|
43
|
+
|
|
44
|
+
return Call.new(state: :half_open, state_epoch:) if new_attempts <= @config[:half_open_calls]
|
|
45
|
+
|
|
46
|
+
# This caller lost the race for a probe slot.
|
|
47
|
+
@half_open_attempts.decrement
|
|
48
|
+
nil
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def admission_current?(admission)
|
|
52
|
+
admission.state_epoch == @state_epoch.value
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def handle_success(admission)
|
|
56
|
+
return unless admission.half_open?
|
|
57
|
+
|
|
58
|
+
@mutex.with_write_lock do
|
|
59
|
+
return unless admission_current?(admission) && half_open?
|
|
60
|
+
|
|
61
|
+
successful_attempts = @half_open_successes.increment
|
|
62
|
+
next unless successful_attempts >= @config[:half_open_calls] || success_threshold_reached?
|
|
63
|
+
|
|
64
|
+
@half_open_attempts.value = 0
|
|
65
|
+
@half_open_successes.value = 0
|
|
66
|
+
reset
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def handle_failure(admission)
|
|
71
|
+
@mutex.with_write_lock do
|
|
72
|
+
return unless admission_current?(admission)
|
|
73
|
+
|
|
74
|
+
if admission.closed? && closed? && failure_threshold_exceeded?
|
|
75
|
+
trip
|
|
76
|
+
elsif admission.half_open? && half_open?
|
|
77
|
+
@half_open_attempts.value = 0
|
|
78
|
+
@half_open_successes.value = 0
|
|
79
|
+
trip
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def release_abandoned_admission(admission)
|
|
85
|
+
return unless admission.half_open?
|
|
86
|
+
|
|
87
|
+
@mutex.with_write_lock do
|
|
88
|
+
next unless admission_current?(admission) && half_open?
|
|
89
|
+
next unless @half_open_attempts.value.positive?
|
|
90
|
+
|
|
91
|
+
@half_open_attempts.decrement
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
end
|
|
@@ -10,6 +10,7 @@ module BreakerMachines
|
|
|
10
10
|
|
|
11
11
|
included do
|
|
12
12
|
include Circuit::Configuration
|
|
13
|
+
include Circuit::Admission
|
|
13
14
|
include Circuit::Execution
|
|
14
15
|
include Circuit::HedgedExecution
|
|
15
16
|
include Circuit::Introspection
|
|
@@ -29,24 +30,29 @@ module BreakerMachines
|
|
|
29
30
|
# Use global default storage if not specified
|
|
30
31
|
@storage = @config[:storage] || create_default_storage
|
|
31
32
|
@metrics = @config[:metrics]
|
|
33
|
+
initialize_runtime_state
|
|
34
|
+
|
|
35
|
+
restore_status_from_storage if @storage
|
|
36
|
+
|
|
37
|
+
# Register with global registry unless auto_register is disabled
|
|
38
|
+
BreakerMachines::Registry.instance.register(self) unless @config[:auto_register] == false
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
private
|
|
42
|
+
|
|
43
|
+
def initialize_runtime_state
|
|
32
44
|
@opened_at = Concurrent::AtomicReference.new(nil)
|
|
33
45
|
@half_open_attempts = Concurrent::AtomicFixnum.new(0)
|
|
34
46
|
@half_open_successes = Concurrent::AtomicFixnum.new(0)
|
|
47
|
+
@state_epoch = Concurrent::AtomicFixnum.new(0)
|
|
35
48
|
@mutex = Concurrent::ReentrantReadWriteLock.new
|
|
36
49
|
@last_failure_at = Concurrent::AtomicReference.new(nil)
|
|
37
50
|
@last_error = Concurrent::AtomicReference.new(nil)
|
|
38
51
|
|
|
39
52
|
# Initialize semaphore for bulkheading if max_concurrent is set
|
|
40
53
|
@semaphore = (Concurrent::Semaphore.new(@config[:max_concurrent]) if @config[:max_concurrent])
|
|
41
|
-
|
|
42
|
-
restore_status_from_storage if @storage
|
|
43
|
-
|
|
44
|
-
# Register with global registry unless auto_register is disabled
|
|
45
|
-
BreakerMachines::Registry.instance.register(self) unless @config[:auto_register] == false
|
|
46
54
|
end
|
|
47
55
|
|
|
48
|
-
private
|
|
49
|
-
|
|
50
56
|
def restore_status_from_storage
|
|
51
57
|
stored_status = @storage.get_status(@name)
|
|
52
58
|
return unless stored_status
|
|
@@ -31,100 +31,92 @@ module BreakerMachines
|
|
|
31
31
|
|
|
32
32
|
private
|
|
33
33
|
|
|
34
|
-
def execute_with_state_check(&
|
|
35
|
-
|
|
36
|
-
if open? && reset_timeout_elapsed?
|
|
37
|
-
@mutex.with_write_lock do
|
|
38
|
-
attempt_recovery if open? # Double-check after acquiring lock
|
|
39
|
-
end
|
|
40
|
-
end
|
|
34
|
+
def execute_with_state_check(&)
|
|
35
|
+
attempt_recovery_if_ready
|
|
41
36
|
|
|
42
37
|
# Apply bulkheading first, outside of any locks
|
|
38
|
+
acquired = false
|
|
43
39
|
if @semaphore
|
|
44
40
|
acquired = @semaphore.try_acquire
|
|
45
|
-
unless acquired
|
|
46
|
-
# Reject immediately if we can't acquire semaphore
|
|
47
|
-
return reject_call_bulkhead
|
|
48
|
-
end
|
|
41
|
+
return reject_call_bulkhead unless acquired
|
|
49
42
|
end
|
|
50
43
|
|
|
51
44
|
begin
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
handle_closed_status(&block)
|
|
60
|
-
end
|
|
45
|
+
admission = admit_call
|
|
46
|
+
unless admission
|
|
47
|
+
# An open-circuit fallback is user code and may yield. It must not
|
|
48
|
+
# retain a bulkhead permit or circuit lock while it runs.
|
|
49
|
+
@semaphore&.release if acquired
|
|
50
|
+
acquired = false
|
|
51
|
+
return reject_call
|
|
61
52
|
end
|
|
53
|
+
|
|
54
|
+
execute_call(admission, &)
|
|
62
55
|
ensure
|
|
63
56
|
@semaphore&.release if @semaphore && acquired
|
|
64
57
|
end
|
|
65
58
|
end
|
|
66
59
|
|
|
67
|
-
def
|
|
68
|
-
# Atomically increment and get the new value
|
|
69
|
-
new_attempts = @half_open_attempts.increment
|
|
70
|
-
|
|
71
|
-
if new_attempts <= @config[:half_open_calls]
|
|
72
|
-
execute_call(&)
|
|
73
|
-
else
|
|
74
|
-
# This thread lost the race, decrement back and reject
|
|
75
|
-
@half_open_attempts.decrement
|
|
76
|
-
reject_call
|
|
77
|
-
end
|
|
78
|
-
end
|
|
79
|
-
|
|
80
|
-
def handle_closed_status(&)
|
|
81
|
-
execute_call(&)
|
|
82
|
-
end
|
|
83
|
-
|
|
84
|
-
def execute_call(&block)
|
|
60
|
+
def execute_call(admission, &)
|
|
85
61
|
# Use async version if fiber_safe is enabled
|
|
86
62
|
if @config[:fiber_safe]
|
|
87
63
|
# Ensure async is loaded and included
|
|
88
64
|
Execution.load_async_support unless respond_to?(:execute_call_async)
|
|
89
|
-
return execute_call_async(&
|
|
65
|
+
return execute_call_async(admission, &)
|
|
90
66
|
end
|
|
91
67
|
|
|
68
|
+
execute_call_sync(admission, &)
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def execute_call_sync(admission, &)
|
|
72
|
+
completed = false
|
|
92
73
|
start_time = BreakerMachines.monotonic_time
|
|
93
74
|
|
|
94
75
|
begin
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
# (e.g., HTTP client timeouts, database statement timeouts, etc.)
|
|
99
|
-
# Log a warning if timeout is configured
|
|
100
|
-
if @config[:timeout] && BreakerMachines.logger && BreakerMachines.config.log_events
|
|
101
|
-
BreakerMachines.logger.warn(
|
|
102
|
-
"[BreakerMachines] Circuit '#{@name}' has timeout configured but " \
|
|
103
|
-
'forceful timeouts are not implemented for safety. ' \
|
|
104
|
-
'Please use timeout mechanisms provided by your libraries ' \
|
|
105
|
-
'(e.g., Net::HTTP read_timeout, ActiveRecord statement_timeout).'
|
|
106
|
-
)
|
|
107
|
-
end
|
|
108
|
-
|
|
109
|
-
# Execute with hedged requests if enabled
|
|
110
|
-
result = if @config[:hedged_requests] || @config[:backends]
|
|
111
|
-
execute_hedged(&block)
|
|
112
|
-
else
|
|
113
|
-
block.call
|
|
114
|
-
end
|
|
115
|
-
|
|
116
|
-
record_success(BreakerMachines.monotonic_time - start_time)
|
|
117
|
-
handle_success
|
|
76
|
+
result = execute_sync_operation(&)
|
|
77
|
+
complete_call_success(admission, start_time)
|
|
78
|
+
completed = true
|
|
118
79
|
result
|
|
119
80
|
rescue *@config[:exceptions] => e
|
|
120
|
-
|
|
121
|
-
|
|
81
|
+
complete_call_failure(admission, start_time, e)
|
|
82
|
+
completed = true
|
|
122
83
|
raise unless @config[:fallback]
|
|
123
84
|
|
|
124
85
|
invoke_fallback(e)
|
|
86
|
+
ensure
|
|
87
|
+
release_abandoned_admission(admission) unless completed
|
|
125
88
|
end
|
|
126
89
|
end
|
|
127
90
|
|
|
91
|
+
def execute_sync_operation(&)
|
|
92
|
+
warn_about_sync_timeout
|
|
93
|
+
return execute_hedged(&) if @config[:hedged_requests] || @config[:backends]
|
|
94
|
+
|
|
95
|
+
yield
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def warn_about_sync_timeout
|
|
99
|
+
return unless @config[:timeout] && BreakerMachines.logger && BreakerMachines.config.log_events
|
|
100
|
+
|
|
101
|
+
# Forceful Ruby timeouts can interrupt code while it holds resources.
|
|
102
|
+
BreakerMachines.logger.warn(
|
|
103
|
+
"[BreakerMachines] Circuit '#{@name}' has timeout configured but " \
|
|
104
|
+
'forceful timeouts are not implemented for safety. ' \
|
|
105
|
+
'Please use timeout mechanisms provided by your libraries ' \
|
|
106
|
+
'(e.g., Net::HTTP read_timeout, ActiveRecord statement_timeout).'
|
|
107
|
+
)
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def complete_call_success(admission, start_time)
|
|
111
|
+
record_success(BreakerMachines.monotonic_time - start_time)
|
|
112
|
+
handle_success(admission)
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
def complete_call_failure(admission, start_time, error)
|
|
116
|
+
record_failure(BreakerMachines.monotonic_time - start_time, error)
|
|
117
|
+
handle_failure(admission)
|
|
118
|
+
end
|
|
119
|
+
|
|
128
120
|
def reject_call
|
|
129
121
|
@metrics&.record_rejection(@name)
|
|
130
122
|
invoke_callback(:on_reject)
|
|
@@ -144,43 +136,6 @@ module BreakerMachines
|
|
|
144
136
|
invoke_fallback(error)
|
|
145
137
|
end
|
|
146
138
|
|
|
147
|
-
def handle_success
|
|
148
|
-
return unless half_open?
|
|
149
|
-
|
|
150
|
-
@mutex.with_write_lock do
|
|
151
|
-
if half_open?
|
|
152
|
-
# Check if all allowed half-open calls have succeeded
|
|
153
|
-
# This ensures the circuit can close even if success_threshold > half_open_calls
|
|
154
|
-
successful_attempts = @half_open_successes.increment
|
|
155
|
-
|
|
156
|
-
# Fast-close logic: Circuit closes if EITHER:
|
|
157
|
-
# 1. All allowed half-open calls succeeded (conservative approach)
|
|
158
|
-
# 2. Success threshold is reached (aggressive approach for quick recovery)
|
|
159
|
-
# This allows flexible configuration - set success_threshold=1 for fast recovery
|
|
160
|
-
# or success_threshold=half_open_calls for cautious recovery
|
|
161
|
-
if successful_attempts >= @config[:half_open_calls] || success_threshold_reached?
|
|
162
|
-
@half_open_attempts.value = 0
|
|
163
|
-
@half_open_successes.value = 0
|
|
164
|
-
reset
|
|
165
|
-
end
|
|
166
|
-
end
|
|
167
|
-
end
|
|
168
|
-
end
|
|
169
|
-
|
|
170
|
-
def handle_failure
|
|
171
|
-
return unless closed? || half_open?
|
|
172
|
-
|
|
173
|
-
@mutex.with_write_lock do
|
|
174
|
-
if closed? && failure_threshold_exceeded?
|
|
175
|
-
trip
|
|
176
|
-
elsif half_open?
|
|
177
|
-
@half_open_attempts.value = 0
|
|
178
|
-
@half_open_successes.value = 0
|
|
179
|
-
trip
|
|
180
|
-
end
|
|
181
|
-
end
|
|
182
|
-
end
|
|
183
|
-
|
|
184
139
|
def failure_threshold_exceeded?
|
|
185
140
|
if @config[:use_rate_threshold]
|
|
186
141
|
# Rate-based threshold
|
|
@@ -9,6 +9,7 @@ module BreakerMachines
|
|
|
9
9
|
private
|
|
10
10
|
|
|
11
11
|
def on_circuit_open
|
|
12
|
+
@state_epoch.increment
|
|
12
13
|
@opened_at.value = BreakerMachines.monotonic_time
|
|
13
14
|
@storage&.set_status(@name, :open, @opened_at.value)
|
|
14
15
|
if @storage.respond_to?(:record_event_with_details)
|
|
@@ -20,6 +21,7 @@ module BreakerMachines
|
|
|
20
21
|
end
|
|
21
22
|
|
|
22
23
|
def on_circuit_close
|
|
24
|
+
@state_epoch.increment
|
|
23
25
|
@opened_at.value = nil
|
|
24
26
|
@last_error.value = nil
|
|
25
27
|
@last_failure_at.value = nil
|
|
@@ -33,6 +35,7 @@ module BreakerMachines
|
|
|
33
35
|
end
|
|
34
36
|
|
|
35
37
|
def on_circuit_half_open
|
|
38
|
+
@state_epoch.increment
|
|
36
39
|
@half_open_attempts.value = 0
|
|
37
40
|
@half_open_successes.value = 0
|
|
38
41
|
@storage&.set_status(@name, :half_open)
|
|
@@ -2,6 +2,7 @@ module BreakerMachines
|
|
|
2
2
|
class Circuit
|
|
3
3
|
include StateManagement
|
|
4
4
|
include Configuration
|
|
5
|
+
include Admission
|
|
5
6
|
include Execution
|
|
6
7
|
include Introspection
|
|
7
8
|
include Callbacks
|
|
@@ -14,6 +15,7 @@ module BreakerMachines
|
|
|
14
15
|
@opened_at: Concurrent::AtomicReference[Float?]
|
|
15
16
|
@half_open_attempts: Concurrent::AtomicFixnum
|
|
16
17
|
@half_open_successes: Concurrent::AtomicFixnum
|
|
18
|
+
@state_epoch: Concurrent::AtomicFixnum
|
|
17
19
|
@mutex: Concurrent::ReentrantReadWriteLock
|
|
18
20
|
@last_failure_at: Concurrent::AtomicReference[Float?]
|
|
19
21
|
@last_error: Concurrent::AtomicReference[StandardError?]
|
|
@@ -22,6 +24,27 @@ module BreakerMachines
|
|
|
22
24
|
attr_reader status: (:open | :closed | :half_open)
|
|
23
25
|
end
|
|
24
26
|
|
|
27
|
+
module Admission
|
|
28
|
+
class Call
|
|
29
|
+
attr_reader state: (:closed | :half_open)
|
|
30
|
+
attr_reader state_epoch: Integer
|
|
31
|
+
|
|
32
|
+
def self.new: (state: (:closed | :half_open), state_epoch: Integer) -> Call
|
|
33
|
+
def closed?: () -> bool
|
|
34
|
+
def half_open?: () -> bool
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
private
|
|
38
|
+
|
|
39
|
+
def attempt_recovery_if_ready: () -> void
|
|
40
|
+
def admit_call: () -> Call?
|
|
41
|
+
def admit_half_open_call: () -> Call?
|
|
42
|
+
def admission_current?: (Call admission) -> bool
|
|
43
|
+
def handle_success: (Call admission) -> void
|
|
44
|
+
def handle_failure: (Call admission) -> void
|
|
45
|
+
def release_abandoned_admission: (Call admission) -> void
|
|
46
|
+
end
|
|
47
|
+
|
|
25
48
|
module StateManagement
|
|
26
49
|
interface _StateManagementState
|
|
27
50
|
def status: () -> (:open | :closed | :half_open)
|
|
@@ -96,13 +119,15 @@ module BreakerMachines
|
|
|
96
119
|
|
|
97
120
|
private
|
|
98
121
|
|
|
99
|
-
def
|
|
100
|
-
def
|
|
101
|
-
def
|
|
102
|
-
def
|
|
122
|
+
def execute_with_state_check: [T] () { () -> T } -> untyped
|
|
123
|
+
def execute_call: [T] (Admission::Call admission) { () -> T } -> untyped
|
|
124
|
+
def execute_call_sync: [T] (Admission::Call admission) { () -> T } -> untyped
|
|
125
|
+
def execute_sync_operation: [T] () { () -> T } -> T
|
|
126
|
+
def warn_about_sync_timeout: () -> void
|
|
127
|
+
def complete_call_success: (Admission::Call admission, Float start_time) -> void
|
|
128
|
+
def complete_call_failure: (Admission::Call admission, Float start_time, Exception error) -> void
|
|
103
129
|
def reject_call: () -> untyped
|
|
104
|
-
def
|
|
105
|
-
def handle_failure: () -> void
|
|
130
|
+
def reject_call_bulkhead: () -> untyped
|
|
106
131
|
def failure_threshold_exceeded?: () -> bool
|
|
107
132
|
def success_threshold_reached?: () -> bool
|
|
108
133
|
def record_success: (Float duration) -> void
|
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.17.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Abdelkader Boudih
|
|
@@ -154,6 +154,7 @@ files:
|
|
|
154
154
|
- lib/breaker_machines/async_support.rb
|
|
155
155
|
- lib/breaker_machines/cascading_circuit.rb
|
|
156
156
|
- lib/breaker_machines/circuit.rb
|
|
157
|
+
- lib/breaker_machines/circuit/admission.rb
|
|
157
158
|
- lib/breaker_machines/circuit/async_state_management.rb
|
|
158
159
|
- lib/breaker_machines/circuit/base.rb
|
|
159
160
|
- lib/breaker_machines/circuit/callbacks.rb
|