breaker_machines 0.20.0 → 0.21.1
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/README.md +17 -0
- data/lib/breaker_machines/circuit/base.rb +1 -5
- data/lib/breaker_machines/circuit/native.rb +2 -0
- data/lib/breaker_machines/circuit/protection.rb +13 -0
- data/lib/breaker_machines/circuit/proxy.rb +27 -0
- data/lib/breaker_machines/circuit.rb +6 -0
- data/lib/breaker_machines/version.rb +1 -1
- metadata +3 -13
- data/sig/README.md +0 -74
- data/sig/all.rbs +0 -25
- data/sig/breaker_machines/circuit.rbs +0 -179
- data/sig/breaker_machines/console.rbs +0 -32
- data/sig/breaker_machines/dsl.rbs +0 -50
- data/sig/breaker_machines/errors.rbs +0 -24
- data/sig/breaker_machines/interfaces.rbs +0 -46
- data/sig/breaker_machines/registry.rbs +0 -30
- data/sig/breaker_machines/storage.rbs +0 -65
- data/sig/breaker_machines/types.rbs +0 -97
- data/sig/breaker_machines.rbs +0 -42
- data/sig/manifest.yaml +0 -5
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 62bbe5baf9608aefa0694d69b7e16be9d46d7a4285fddfca1cd1a1faeb89dc66
|
|
4
|
+
data.tar.gz: f16d0af67651ec2c4bfbd9018fc56a4746ad42161f2a73acbf1e1d91fe661304
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 3c72c822d6bb5dc20fe9d483e15dc06ee507133946d97b06cb8f35b99d1bb0d1aa2105f32cfbac362195b97b709bda28897047650cb5693038b3ffdf6acd1837
|
|
7
|
+
data.tar.gz: 07635f79e5de1b65e379dfc9a631a1a3ccce73a91d2a60f133d97ae140b9967f2ec374a74a92ebff9ee8ab5b0df860549a2d56d28e896353514d62bf539554fe
|
data/README.md
CHANGED
|
@@ -28,6 +28,23 @@ class PaymentService
|
|
|
28
28
|
end
|
|
29
29
|
```
|
|
30
30
|
|
|
31
|
+
### Protect an existing client
|
|
32
|
+
|
|
33
|
+
Use `protect` to route selected public methods through an existing circuit:
|
|
34
|
+
|
|
35
|
+
```ruby
|
|
36
|
+
circuit = BreakerMachines::Circuit.new(:teleporter, failure_threshold: 3)
|
|
37
|
+
protected_client = circuit.protect(teleporter, methods: %i[beam_up beam_down])
|
|
38
|
+
|
|
39
|
+
protected_client.beam_up('Schrödinger', destination: :sickbay)
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
The wrapper forwards arguments, keywords, blocks, and results through `circuit.call`, including its configured error handling and fallback. Other methods pass through without consulting the circuit. `methods:` must be nonempty and name public methods the client responds to; otherwise `protect` raises `ArgumentError`.
|
|
43
|
+
|
|
44
|
+
The original client stays unchanged. Each call through the wrapper counts as one operation, including any internal method calls. Returned objects stay unchanged too: later calls on a returned client or lazy stream need their own protection. Wrap the object that performs the external operation.
|
|
45
|
+
|
|
46
|
+
Available on Ruby and native circuits, including circuits obtained through the DSL. Multiple wrappers can share the same circuit.
|
|
47
|
+
|
|
31
48
|
## A Message to the Resistance
|
|
32
49
|
|
|
33
50
|
So AI took your job while you were waiting for Fireship to drop the next JavaScript framework?
|
|
@@ -12,6 +12,7 @@ module BreakerMachines
|
|
|
12
12
|
include Circuit::Configuration
|
|
13
13
|
include Circuit::Admission
|
|
14
14
|
include Circuit::Execution
|
|
15
|
+
include Circuit::Protection
|
|
15
16
|
include Circuit::HedgedExecution
|
|
16
17
|
include Circuit::Introspection
|
|
17
18
|
include Circuit::Callbacks
|
|
@@ -31,11 +32,6 @@ module BreakerMachines
|
|
|
31
32
|
@storage = @config[:storage] || create_default_storage
|
|
32
33
|
@metrics = @config[:metrics]
|
|
33
34
|
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
35
|
end
|
|
40
36
|
|
|
41
37
|
private
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module BreakerMachines
|
|
4
|
+
class Circuit
|
|
5
|
+
# Protection adds `protect`, which wraps selected public methods of an
|
|
6
|
+
# object so that each call runs through this circuit's `call`.
|
|
7
|
+
module Protection
|
|
8
|
+
def protect(object, methods:)
|
|
9
|
+
Proxy.new(object, circuit: self, methods:)
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'delegate'
|
|
4
|
+
|
|
5
|
+
module BreakerMachines
|
|
6
|
+
class Circuit
|
|
7
|
+
# Proxy delegates to a wrapped object and routes the configured methods
|
|
8
|
+
# through a circuit. Unlisted methods pass straight through.
|
|
9
|
+
class Proxy < SimpleDelegator
|
|
10
|
+
def initialize(object, circuit:, methods:)
|
|
11
|
+
raise ArgumentError, 'methods must not be empty' if methods.empty?
|
|
12
|
+
|
|
13
|
+
methods.each do |name|
|
|
14
|
+
raise ArgumentError, "Unknown public method: #{name}" unless object.respond_to?(name)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
super(object)
|
|
18
|
+
|
|
19
|
+
methods.each do |name|
|
|
20
|
+
define_singleton_method(name) do |*args, **kwargs, &block|
|
|
21
|
+
circuit.call { __getobj__.public_send(name, *args, **kwargs, &block) }
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -4,5 +4,11 @@ module BreakerMachines
|
|
|
4
4
|
class Circuit
|
|
5
5
|
include Circuit::Base
|
|
6
6
|
include StateManagement
|
|
7
|
+
|
|
8
|
+
def initialize(...)
|
|
9
|
+
super
|
|
10
|
+
restore_status_from_storage if @storage
|
|
11
|
+
BreakerMachines::Registry.instance.register(self) unless @config[:auto_register] == false
|
|
12
|
+
end
|
|
7
13
|
end
|
|
8
14
|
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.21.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Abdelkader Boudih
|
|
@@ -164,6 +164,8 @@ files:
|
|
|
164
164
|
- lib/breaker_machines/circuit/hedged_execution.rb
|
|
165
165
|
- lib/breaker_machines/circuit/introspection.rb
|
|
166
166
|
- lib/breaker_machines/circuit/native.rb
|
|
167
|
+
- lib/breaker_machines/circuit/protection.rb
|
|
168
|
+
- lib/breaker_machines/circuit/proxy.rb
|
|
167
169
|
- lib/breaker_machines/circuit/state_callbacks.rb
|
|
168
170
|
- lib/breaker_machines/circuit/state_machine_definition.rb
|
|
169
171
|
- lib/breaker_machines/circuit/state_management.rb
|
|
@@ -192,18 +194,6 @@ files:
|
|
|
192
194
|
- lib/breaker_machines/storage/null.rb
|
|
193
195
|
- lib/breaker_machines/types.rb
|
|
194
196
|
- lib/breaker_machines/version.rb
|
|
195
|
-
- sig/README.md
|
|
196
|
-
- sig/all.rbs
|
|
197
|
-
- sig/breaker_machines.rbs
|
|
198
|
-
- sig/breaker_machines/circuit.rbs
|
|
199
|
-
- sig/breaker_machines/console.rbs
|
|
200
|
-
- sig/breaker_machines/dsl.rbs
|
|
201
|
-
- sig/breaker_machines/errors.rbs
|
|
202
|
-
- sig/breaker_machines/interfaces.rbs
|
|
203
|
-
- sig/breaker_machines/registry.rbs
|
|
204
|
-
- sig/breaker_machines/storage.rbs
|
|
205
|
-
- sig/breaker_machines/types.rbs
|
|
206
|
-
- sig/manifest.yaml
|
|
207
197
|
homepage: https://github.com/seuros/breaker_machines
|
|
208
198
|
licenses:
|
|
209
199
|
- MIT
|
data/sig/README.md
DELETED
|
@@ -1,74 +0,0 @@
|
|
|
1
|
-
# BreakerMachines RBS Type Signatures
|
|
2
|
-
|
|
3
|
-
This directory contains RBS (Ruby type signatures) for the BreakerMachines gem.
|
|
4
|
-
|
|
5
|
-
## Structure
|
|
6
|
-
|
|
7
|
-
- `breaker_machines.rbs` - Main module and configuration
|
|
8
|
-
- `breaker_machines/` - Type signatures for all classes and modules
|
|
9
|
-
- `circuit.rbs` - Circuit class and its modules
|
|
10
|
-
- `errors.rbs` - Error classes
|
|
11
|
-
- `storage.rbs` - Storage backends
|
|
12
|
-
- `dsl.rbs` - DSL module for including in classes
|
|
13
|
-
- `registry.rbs` - Global circuit registry
|
|
14
|
-
- `console.rbs` - Interactive console
|
|
15
|
-
- `types.rbs` - Common type aliases
|
|
16
|
-
- `interfaces.rbs` - Interface definitions
|
|
17
|
-
|
|
18
|
-
## Usage
|
|
19
|
-
|
|
20
|
-
To use these type signatures in your project:
|
|
21
|
-
|
|
22
|
-
1. Add to your `Steepfile`:
|
|
23
|
-
```ruby
|
|
24
|
-
target :app do
|
|
25
|
-
signature "sig"
|
|
26
|
-
check "lib"
|
|
27
|
-
|
|
28
|
-
library "breaker_machines"
|
|
29
|
-
end
|
|
30
|
-
```
|
|
31
|
-
|
|
32
|
-
2. Or with RBS directly:
|
|
33
|
-
```bash
|
|
34
|
-
rbs validate
|
|
35
|
-
```
|
|
36
|
-
|
|
37
|
-
## Type Checking Examples
|
|
38
|
-
|
|
39
|
-
### Basic Circuit Usage
|
|
40
|
-
```ruby
|
|
41
|
-
circuit = BreakerMachines::Circuit.new("api",
|
|
42
|
-
failure_threshold: 5,
|
|
43
|
-
reset_timeout: 30
|
|
44
|
-
)
|
|
45
|
-
|
|
46
|
-
result = circuit.call { api.fetch_data }
|
|
47
|
-
```
|
|
48
|
-
|
|
49
|
-
### DSL Usage
|
|
50
|
-
```ruby
|
|
51
|
-
class MyService
|
|
52
|
-
include BreakerMachines::DSL
|
|
53
|
-
|
|
54
|
-
circuit :database do
|
|
55
|
-
threshold failures: 10, within: 60
|
|
56
|
-
reset_after 120
|
|
57
|
-
fallback { [] }
|
|
58
|
-
end
|
|
59
|
-
end
|
|
60
|
-
```
|
|
61
|
-
|
|
62
|
-
## Key Types
|
|
63
|
-
|
|
64
|
-
- `circuit_state` - `:open | :closed | :half_open`
|
|
65
|
-
- `storage_backend` - `:memory | :bucket_memory | :null | :redis`
|
|
66
|
-
- `circuit_options` - Configuration hash for circuits
|
|
67
|
-
- `event_record` - Structure for logged events
|
|
68
|
-
|
|
69
|
-
## Interfaces
|
|
70
|
-
|
|
71
|
-
The type signatures define several interfaces:
|
|
72
|
-
- `_StorageBackend` - For custom storage implementations
|
|
73
|
-
- `_MetricsRecorder` - For custom metrics recording
|
|
74
|
-
- `_CircuitLike` - For circuit-compatible objects
|
data/sig/all.rbs
DELETED
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
# Main entry point for BreakerMachines RBS types
|
|
2
|
-
#
|
|
3
|
-
# This file provides a complete type definition for the BreakerMachines gem,
|
|
4
|
-
# a Ruby implementation of the Circuit Breaker pattern.
|
|
5
|
-
#
|
|
6
|
-
# Usage:
|
|
7
|
-
# class MyService
|
|
8
|
-
# include BreakerMachines::DSL
|
|
9
|
-
#
|
|
10
|
-
# circuit :api_call do
|
|
11
|
-
# threshold failures: 5, within: 60
|
|
12
|
-
# reset_after 30
|
|
13
|
-
# fallback { |error| { error: "Service unavailable" } }
|
|
14
|
-
# end
|
|
15
|
-
#
|
|
16
|
-
# def fetch_data
|
|
17
|
-
# circuit(:api_call).call do
|
|
18
|
-
# # Your API call here
|
|
19
|
-
# end
|
|
20
|
-
# end
|
|
21
|
-
# end
|
|
22
|
-
|
|
23
|
-
# Import all type definitions
|
|
24
|
-
use BreakerMachines::*
|
|
25
|
-
use BreakerMachines::Storage::*
|
|
@@ -1,179 +0,0 @@
|
|
|
1
|
-
module BreakerMachines
|
|
2
|
-
class Circuit
|
|
3
|
-
include StateManagement
|
|
4
|
-
include Configuration
|
|
5
|
-
include Admission
|
|
6
|
-
include Execution
|
|
7
|
-
include Introspection
|
|
8
|
-
include Callbacks
|
|
9
|
-
|
|
10
|
-
# Instance variables from Configuration module
|
|
11
|
-
@name: String
|
|
12
|
-
@config: Hash[Symbol, untyped]
|
|
13
|
-
@storage: _StorageBackend?
|
|
14
|
-
@metrics: _MetricsRecorder?
|
|
15
|
-
@opened_at: Concurrent::AtomicReference[Float?]
|
|
16
|
-
@half_open_attempts: Concurrent::AtomicFixnum
|
|
17
|
-
@half_open_successes: Concurrent::AtomicFixnum
|
|
18
|
-
@state_epoch: Concurrent::AtomicFixnum
|
|
19
|
-
@mutex: Concurrent::ReentrantReadWriteLock
|
|
20
|
-
@last_failure_at: Concurrent::AtomicReference[Float?]
|
|
21
|
-
@last_error: Concurrent::AtomicReference[StandardError?]
|
|
22
|
-
|
|
23
|
-
# State machine status (from StateManagement)
|
|
24
|
-
attr_reader status: (:open | :closed | :half_open)
|
|
25
|
-
end
|
|
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
|
-
|
|
48
|
-
module StateManagement
|
|
49
|
-
interface _StateManagementState
|
|
50
|
-
def status: () -> (:open | :closed | :half_open)
|
|
51
|
-
def status=: (:open | :closed | :half_open value) -> void
|
|
52
|
-
def status_name: () -> (:open | :closed | :half_open)
|
|
53
|
-
def open?: () -> bool
|
|
54
|
-
def closed?: () -> bool
|
|
55
|
-
def half_open?: () -> bool
|
|
56
|
-
def trip: () -> bool
|
|
57
|
-
def attempt_recovery: () -> bool
|
|
58
|
-
def reset: () -> bool
|
|
59
|
-
def force_open: () -> bool
|
|
60
|
-
def force_close: () -> bool
|
|
61
|
-
end
|
|
62
|
-
|
|
63
|
-
def self.included: (singleton(Circuit) base) -> void
|
|
64
|
-
|
|
65
|
-
private
|
|
66
|
-
|
|
67
|
-
def on_circuit_open: () -> void
|
|
68
|
-
def on_circuit_close: () -> void
|
|
69
|
-
def on_circuit_half_open: () -> void
|
|
70
|
-
def restore_status_from_storage: () -> void
|
|
71
|
-
def reset_timeout_elapsed?: () -> bool
|
|
72
|
-
def monotonic_time: () -> Float
|
|
73
|
-
end
|
|
74
|
-
|
|
75
|
-
module Configuration
|
|
76
|
-
extend ActiveSupport::Concern
|
|
77
|
-
|
|
78
|
-
interface _ConfigurationMethods
|
|
79
|
-
def name: () -> String
|
|
80
|
-
def config: () -> Hash[Symbol, untyped]
|
|
81
|
-
def opened_at: () -> Concurrent::AtomicReference[Float?]
|
|
82
|
-
end
|
|
83
|
-
|
|
84
|
-
type storage_type = Storage::Base | Storage::Memory | Storage::BucketMemory | Storage::Null
|
|
85
|
-
type metrics_type = untyped
|
|
86
|
-
type callback_type = Proc | nil
|
|
87
|
-
type fallback_type = Proc | Array[Proc | untyped] | untyped
|
|
88
|
-
|
|
89
|
-
type circuit_config = {
|
|
90
|
-
failure_threshold: Integer,
|
|
91
|
-
failure_window: Integer,
|
|
92
|
-
success_threshold: Integer,
|
|
93
|
-
timeout: Integer?,
|
|
94
|
-
reset_timeout: Integer,
|
|
95
|
-
reset_timeout_jitter: Float,
|
|
96
|
-
half_open_calls: Integer,
|
|
97
|
-
storage: storage_type?,
|
|
98
|
-
metrics: metrics_type?,
|
|
99
|
-
fallback: fallback_type?,
|
|
100
|
-
on_open: callback_type,
|
|
101
|
-
on_close: callback_type,
|
|
102
|
-
on_half_open: callback_type,
|
|
103
|
-
on_reject: callback_type,
|
|
104
|
-
exceptions: Array[Class],
|
|
105
|
-
owner: untyped
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
def initialize: (String name, ?circuit_config options) -> void
|
|
109
|
-
|
|
110
|
-
private
|
|
111
|
-
|
|
112
|
-
def default_config: () -> circuit_config
|
|
113
|
-
def create_default_storage: () -> storage_type
|
|
114
|
-
end
|
|
115
|
-
|
|
116
|
-
module Execution
|
|
117
|
-
def call: [T] () { () -> T } -> untyped
|
|
118
|
-
def wrap: [T] () { () -> T } -> untyped
|
|
119
|
-
|
|
120
|
-
private
|
|
121
|
-
|
|
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
|
|
129
|
-
def reject_call: () -> untyped
|
|
130
|
-
def reject_call_bulkhead: () -> untyped
|
|
131
|
-
def failure_threshold_exceeded?: () -> bool
|
|
132
|
-
def success_threshold_reached?: () -> bool
|
|
133
|
-
def record_success: (Float duration) -> void
|
|
134
|
-
def record_failure: (Float duration, ?StandardError? error) -> void
|
|
135
|
-
end
|
|
136
|
-
|
|
137
|
-
module Introspection
|
|
138
|
-
type stats_hash = {
|
|
139
|
-
state: (:open | :closed | :half_open),
|
|
140
|
-
failure_count: Integer,
|
|
141
|
-
success_count: Integer,
|
|
142
|
-
last_failure_at: Float?,
|
|
143
|
-
opened_at: Float?,
|
|
144
|
-
half_open_attempts: Integer,
|
|
145
|
-
half_open_successes: Integer
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
type error_info = {
|
|
149
|
-
class: String,
|
|
150
|
-
message: String,
|
|
151
|
-
occurred_at: Float
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
type circuit_hash = {
|
|
155
|
-
name: String,
|
|
156
|
-
state: (:open | :closed | :half_open),
|
|
157
|
-
stats: stats_hash,
|
|
158
|
-
config: Hash[Symbol, untyped],
|
|
159
|
-
event_log: Array[untyped],
|
|
160
|
-
last_error: error_info?
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
def stats: () -> stats_hash
|
|
164
|
-
def configuration: () -> Hash[Symbol, untyped]
|
|
165
|
-
def event_log: (?limit: Integer) -> Array[untyped]?
|
|
166
|
-
def last_error: () -> StandardError?
|
|
167
|
-
def to_h: () -> circuit_hash
|
|
168
|
-
def summary: () -> String
|
|
169
|
-
def last_error_info: () -> error_info?
|
|
170
|
-
end
|
|
171
|
-
|
|
172
|
-
module Callbacks
|
|
173
|
-
private
|
|
174
|
-
|
|
175
|
-
def invoke_callback: (Symbol callback_name) -> void
|
|
176
|
-
def invoke_fallback: (StandardError error) -> untyped
|
|
177
|
-
def invoke_single_fallback: (Proc | untyped fallback, StandardError error) -> untyped
|
|
178
|
-
end
|
|
179
|
-
end
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
module BreakerMachines
|
|
2
|
-
class Console
|
|
3
|
-
@running: bool
|
|
4
|
-
|
|
5
|
-
def self.start: () -> void
|
|
6
|
-
|
|
7
|
-
def initialize: () -> void
|
|
8
|
-
def run: () -> void
|
|
9
|
-
|
|
10
|
-
private
|
|
11
|
-
|
|
12
|
-
def print_header: () -> void
|
|
13
|
-
def print_help: () -> void
|
|
14
|
-
def print_prompt: () -> void
|
|
15
|
-
def process_command: (String command) -> void
|
|
16
|
-
|
|
17
|
-
# Command methods
|
|
18
|
-
def list_circuits: () -> void
|
|
19
|
-
def show_stats: () -> void
|
|
20
|
-
def show_circuit: (String? name) -> void
|
|
21
|
-
def show_events: (String? name, Integer limit) -> void
|
|
22
|
-
def reset_circuit: (String? name) -> void
|
|
23
|
-
def force_open_circuit: (String? name) -> void
|
|
24
|
-
def force_close_circuit: (String? name) -> void
|
|
25
|
-
def generate_report: () -> void
|
|
26
|
-
def cleanup_registry: () -> void
|
|
27
|
-
|
|
28
|
-
# Utility methods
|
|
29
|
-
def colorize_state: (Symbol state) -> String
|
|
30
|
-
def colorize_event_type: (Symbol type) -> String
|
|
31
|
-
end
|
|
32
|
-
end
|
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
module BreakerMachines
|
|
2
|
-
module DSL
|
|
3
|
-
extend ActiveSupport::Concern
|
|
4
|
-
|
|
5
|
-
@instance_registries: Concurrent::Map[Class, Concurrent::Array[WeakRef[untyped]]]
|
|
6
|
-
|
|
7
|
-
module ClassMethods
|
|
8
|
-
@circuits: Hash[Symbol, Hash[Symbol, untyped]]
|
|
9
|
-
|
|
10
|
-
def instance_registry: () -> Concurrent::Array[WeakRef[untyped]]
|
|
11
|
-
def cleanup_instance_registry: () -> void
|
|
12
|
-
def circuit: (Symbol name) ?{ (CircuitBuilder) -> void } -> Hash[Symbol, untyped]
|
|
13
|
-
def circuits: () -> Hash[Symbol, Hash[Symbol, untyped]]
|
|
14
|
-
def circuit_definitions: () -> Hash[Symbol, Hash[Symbol, untyped]]
|
|
15
|
-
def reset_all_circuits: () -> void
|
|
16
|
-
def circuit_stats: () -> Hash[Symbol, { total: Integer, by_state: Hash[Symbol, Integer] }]
|
|
17
|
-
end
|
|
18
|
-
|
|
19
|
-
def circuit: (Symbol name) -> Circuit
|
|
20
|
-
def circuit_instances: () -> Hash[Symbol, Circuit]
|
|
21
|
-
def circuits_summary: () -> Hash[Symbol, String]
|
|
22
|
-
def circuits_report: () -> Hash[Symbol, untyped]
|
|
23
|
-
def reset_all_circuits: () -> void
|
|
24
|
-
|
|
25
|
-
class CircuitBuilder
|
|
26
|
-
attr_reader config: Hash[Symbol, untyped]
|
|
27
|
-
|
|
28
|
-
def initialize: () -> void
|
|
29
|
-
|
|
30
|
-
# Configuration methods
|
|
31
|
-
def threshold: (?failures: Integer?, ?within: Integer, ?successes: Integer?) -> void
|
|
32
|
-
def reset_after: (Integer duration, ?jitter: Float?) -> void
|
|
33
|
-
def timeout: (Integer duration) -> void
|
|
34
|
-
def half_open_requests: (Integer count) -> void
|
|
35
|
-
def storage: ((:memory | :bucket_memory | :redis | Class | Storage::Base) backend, **untyped) -> void
|
|
36
|
-
def metrics: (?untyped recorder) ?{ () -> void } -> void
|
|
37
|
-
def fallback: (?untyped value) ?{ (StandardError) -> untyped } -> void
|
|
38
|
-
def on_open: () { () -> void } -> void
|
|
39
|
-
def on_close: () { () -> void } -> void
|
|
40
|
-
def on_half_open: () { () -> void } -> void
|
|
41
|
-
def on_reject: () { () -> void } -> void
|
|
42
|
-
def notify: (Symbol service, ?String? url, ?events: Array[Symbol], **untyped options) -> void
|
|
43
|
-
def handle: (*Class exceptions) -> void
|
|
44
|
-
|
|
45
|
-
# Advanced features
|
|
46
|
-
def backends: (Array[untyped] list) -> void
|
|
47
|
-
def parallel_calls: (Integer count, ?timeout: Integer?) -> void
|
|
48
|
-
end
|
|
49
|
-
end
|
|
50
|
-
end
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
module BreakerMachines
|
|
2
|
-
class Error < StandardError
|
|
3
|
-
end
|
|
4
|
-
|
|
5
|
-
class CircuitOpenError < Error
|
|
6
|
-
attr_reader circuit_name: String
|
|
7
|
-
attr_reader opened_at: Float?
|
|
8
|
-
|
|
9
|
-
def initialize: (String circuit_name, ?Float? opened_at) -> void
|
|
10
|
-
end
|
|
11
|
-
|
|
12
|
-
class CircuitTimeoutError < Error
|
|
13
|
-
attr_reader circuit_name: String
|
|
14
|
-
attr_reader timeout: Numeric
|
|
15
|
-
|
|
16
|
-
def initialize: (String circuit_name, Numeric timeout) -> void
|
|
17
|
-
end
|
|
18
|
-
|
|
19
|
-
class ConfigurationError < Error
|
|
20
|
-
end
|
|
21
|
-
|
|
22
|
-
class StorageError < Error
|
|
23
|
-
end
|
|
24
|
-
end
|
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
module BreakerMachines
|
|
2
|
-
# Interface for objects that can record metrics
|
|
3
|
-
interface _MetricsRecorder
|
|
4
|
-
def record_success: (String circuit_name, Float duration) -> void
|
|
5
|
-
def record_failure: (String circuit_name, Float duration) -> void
|
|
6
|
-
def record_rejection: (String circuit_name) -> void
|
|
7
|
-
end
|
|
8
|
-
|
|
9
|
-
# Interface for storage backends
|
|
10
|
-
interface _StorageBackend
|
|
11
|
-
def get_status: (String circuit_name) -> status_record?
|
|
12
|
-
def set_status: (String circuit_name, circuit_state status, ?Float? opened_at) -> void
|
|
13
|
-
def record_success: (String circuit_name, Float duration) -> void
|
|
14
|
-
def record_failure: (String circuit_name, Float duration) -> void
|
|
15
|
-
def success_count: (String circuit_name, Integer window_seconds) -> Integer
|
|
16
|
-
def failure_count: (String circuit_name, Integer window_seconds) -> Integer
|
|
17
|
-
def clear: (String circuit_name) -> void
|
|
18
|
-
def clear_all: () -> void
|
|
19
|
-
end
|
|
20
|
-
|
|
21
|
-
# Interface for advanced storage backends with event logging
|
|
22
|
-
interface _AdvancedStorageBackend
|
|
23
|
-
def record_success: (String circuit_name, ?Float? duration) -> void
|
|
24
|
-
def record_failure: (String circuit_name, ?Float? duration) -> void
|
|
25
|
-
def success_count: (String circuit_name, ?Integer? window) -> Integer
|
|
26
|
-
def failure_count: (String circuit_name, ?Integer? window) -> Integer
|
|
27
|
-
def get_status: (String circuit_name) -> Hash[Symbol, untyped]?
|
|
28
|
-
def set_status: (String circuit_name, circuit_state status, ?Float? opened_at) -> void
|
|
29
|
-
def clear: (String circuit_name) -> void
|
|
30
|
-
def record_event_with_details: (String circuit_name, event_type event_type, Float duration, ?Hash[Symbol, untyped] details) -> void
|
|
31
|
-
def event_log: (String circuit_name, ?Integer limit) -> Array[event_record]
|
|
32
|
-
end
|
|
33
|
-
|
|
34
|
-
# Interface for circuit-like objects
|
|
35
|
-
interface _CircuitLike
|
|
36
|
-
def name: () -> String
|
|
37
|
-
def call: () { () -> untyped } -> untyped
|
|
38
|
-
def wrap: () { () -> untyped } -> untyped
|
|
39
|
-
def status_name: () -> circuit_state
|
|
40
|
-
def open?: () -> bool
|
|
41
|
-
def closed?: () -> bool
|
|
42
|
-
def half_open?: () -> bool
|
|
43
|
-
def reset: () -> bool
|
|
44
|
-
def stats: () -> Hash[Symbol, untyped]
|
|
45
|
-
end
|
|
46
|
-
end
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
module BreakerMachines
|
|
2
|
-
class Registry
|
|
3
|
-
include Singleton
|
|
4
|
-
|
|
5
|
-
@circuits: Concurrent::Map[Integer, WeakRef[Circuit]]
|
|
6
|
-
@mutex: Mutex
|
|
7
|
-
@registration_count: Integer
|
|
8
|
-
@cleanup_interval: Integer
|
|
9
|
-
|
|
10
|
-
def initialize: () -> void
|
|
11
|
-
|
|
12
|
-
# Circuit management
|
|
13
|
-
def register: (Circuit circuit) -> void
|
|
14
|
-
def unregister: (Circuit circuit) -> void
|
|
15
|
-
def all_circuits: () -> Array[Circuit]
|
|
16
|
-
def find_by_name: (Symbol name) -> Array[Circuit]
|
|
17
|
-
|
|
18
|
-
# Reporting
|
|
19
|
-
def stats_summary: () -> { total: Integer, by_state: Hash[Symbol, Integer], by_name: Hash[Symbol, Integer] }
|
|
20
|
-
def detailed_report: () -> Array[Hash[Symbol, untyped]]
|
|
21
|
-
|
|
22
|
-
# Maintenance
|
|
23
|
-
def clear: () -> void
|
|
24
|
-
def cleanup_dead_references: () -> void
|
|
25
|
-
|
|
26
|
-
private
|
|
27
|
-
|
|
28
|
-
def cleanup_dead_references_unsafe: () -> void
|
|
29
|
-
end
|
|
30
|
-
end
|
|
@@ -1,65 +0,0 @@
|
|
|
1
|
-
module BreakerMachines
|
|
2
|
-
module Storage
|
|
3
|
-
class Base
|
|
4
|
-
@options: Hash[Symbol, untyped]
|
|
5
|
-
|
|
6
|
-
def initialize: (**untyped options) -> void
|
|
7
|
-
|
|
8
|
-
# Status management
|
|
9
|
-
def get_status: (String circuit_name) -> { status: (:open | :closed | :half_open), opened_at: Float? }?
|
|
10
|
-
def set_status: (String circuit_name, (:open | :closed | :half_open) status, ?Float? opened_at) -> void
|
|
11
|
-
|
|
12
|
-
# Metrics tracking
|
|
13
|
-
def record_success: (String circuit_name, Float duration) -> void
|
|
14
|
-
def record_failure: (String circuit_name, Float duration) -> void
|
|
15
|
-
def success_count: (String circuit_name, Integer window_seconds) -> Integer
|
|
16
|
-
def failure_count: (String circuit_name, Integer window_seconds) -> Integer
|
|
17
|
-
|
|
18
|
-
# Cleanup
|
|
19
|
-
def clear: (String circuit_name) -> void
|
|
20
|
-
def clear_all: () -> void
|
|
21
|
-
end
|
|
22
|
-
|
|
23
|
-
class Memory < Base
|
|
24
|
-
@circuits: Concurrent::Map[String, untyped]
|
|
25
|
-
@events: Concurrent::Map[String, Concurrent::Array[untyped]]
|
|
26
|
-
@event_logs: Concurrent::Map[String, Concurrent::Array[untyped]]
|
|
27
|
-
@max_events: Integer
|
|
28
|
-
|
|
29
|
-
def initialize: (**untyped options) -> void
|
|
30
|
-
def record_event_with_details: (String circuit_name, (:success | :failure | :state_change) type, Float duration, ?error: StandardError?, ?new_state: (:open | :closed | :half_open)?) -> void
|
|
31
|
-
def event_log: (String circuit_name, Integer limit) -> Array[event_record]
|
|
32
|
-
|
|
33
|
-
private
|
|
34
|
-
|
|
35
|
-
def record_event: (String circuit_name, (:success | :failure) type, Float duration) -> void
|
|
36
|
-
def count_events: (String circuit_name, (:success | :failure) type, Integer window_seconds) -> Integer
|
|
37
|
-
def monotonic_time: () -> Float
|
|
38
|
-
end
|
|
39
|
-
|
|
40
|
-
class BucketMemory < Base
|
|
41
|
-
@buckets: Concurrent::Map[String, untyped]
|
|
42
|
-
@bucket_duration: Integer
|
|
43
|
-
@bucket_count: Integer
|
|
44
|
-
@lock: Concurrent::ReentrantReadWriteLock
|
|
45
|
-
@last_cleanup: Concurrent::AtomicReference[Float]
|
|
46
|
-
@cleanup_interval: Integer
|
|
47
|
-
|
|
48
|
-
def record_event_with_details: (String circuit_name, (:success | :failure | :state_change) event_type, Float duration, ?Hash[Symbol, untyped] details) -> void
|
|
49
|
-
def event_log: (String circuit_name, ?Integer limit) -> Array[event_record]
|
|
50
|
-
|
|
51
|
-
private
|
|
52
|
-
|
|
53
|
-
def circuit_buckets: (String circuit_name) -> Hash[Symbol, untyped]
|
|
54
|
-
def current_bucket_index: () -> Integer
|
|
55
|
-
def bucket_indices_for_window: (Integer window_seconds) -> Array[Integer]
|
|
56
|
-
def cleanup_old_buckets: (Hash[Symbol, untyped] buckets) -> void
|
|
57
|
-
def maybe_cleanup_all_buckets: () -> void
|
|
58
|
-
end
|
|
59
|
-
|
|
60
|
-
class Null < Base
|
|
61
|
-
def record_event_with_details: (String circuit_name, Symbol event_type, Float duration, ?Hash[Symbol, untyped] metadata) -> void
|
|
62
|
-
def event_log: (String circuit_name, ?Integer limit) -> Array[untyped]
|
|
63
|
-
end
|
|
64
|
-
end
|
|
65
|
-
end
|
|
@@ -1,97 +0,0 @@
|
|
|
1
|
-
# Common type aliases used throughout BreakerMachines
|
|
2
|
-
|
|
3
|
-
# External dependencies type declarations not included in their gems
|
|
4
|
-
module Concurrent
|
|
5
|
-
class AtomicReference[T]
|
|
6
|
-
def initialize: (?T initial_value) -> void
|
|
7
|
-
def value: () -> T
|
|
8
|
-
def value=: (T value) -> T
|
|
9
|
-
def get: () -> T
|
|
10
|
-
def set: (T value) -> T
|
|
11
|
-
end
|
|
12
|
-
|
|
13
|
-
class AtomicFixnum
|
|
14
|
-
def initialize: (?Integer initial_value) -> void
|
|
15
|
-
def value: () -> Integer
|
|
16
|
-
def value=: (Integer value) -> Integer
|
|
17
|
-
def increment: (?Integer delta) -> Integer
|
|
18
|
-
def decrement: (?Integer delta) -> Integer
|
|
19
|
-
end
|
|
20
|
-
|
|
21
|
-
class ReentrantReadWriteLock
|
|
22
|
-
def initialize: () -> void
|
|
23
|
-
def with_read_lock: [T] () { () -> T } -> T
|
|
24
|
-
def with_write_lock: [T] () { () -> T } -> T
|
|
25
|
-
end
|
|
26
|
-
end
|
|
27
|
-
|
|
28
|
-
class WeakRef[T]
|
|
29
|
-
def initialize: (T object) -> void
|
|
30
|
-
def weakref_alive?: () -> bool
|
|
31
|
-
def __getobj__: () -> T
|
|
32
|
-
end
|
|
33
|
-
|
|
34
|
-
module Zeitwerk
|
|
35
|
-
class Loader
|
|
36
|
-
def push_dir: (String path) -> void
|
|
37
|
-
def setup: () -> void
|
|
38
|
-
def eager_load: () -> void
|
|
39
|
-
end
|
|
40
|
-
end
|
|
41
|
-
|
|
42
|
-
module BreakerMachines
|
|
43
|
-
# Circuit states
|
|
44
|
-
type circuit_state = :open | :closed | :half_open
|
|
45
|
-
|
|
46
|
-
# Event types
|
|
47
|
-
type event_type = :success | :failure | :state_change | :rejection
|
|
48
|
-
|
|
49
|
-
# Callback names
|
|
50
|
-
type callback_name = :on_open | :on_close | :on_half_open | :on_reject
|
|
51
|
-
|
|
52
|
-
# Storage backend symbols
|
|
53
|
-
type storage_backend = :memory | :bucket_memory | :null | :redis
|
|
54
|
-
|
|
55
|
-
# Generic callback proc
|
|
56
|
-
type callback_proc = Proc | nil
|
|
57
|
-
|
|
58
|
-
# Fallback can be a proc, array of procs/values, or any static value
|
|
59
|
-
type fallback_value = Proc | Array[Proc | untyped] | untyped
|
|
60
|
-
|
|
61
|
-
# Options hash for circuits
|
|
62
|
-
type circuit_options = {
|
|
63
|
-
failure_threshold: Integer?,
|
|
64
|
-
failure_window: Integer?,
|
|
65
|
-
success_threshold: Integer?,
|
|
66
|
-
timeout: Integer?,
|
|
67
|
-
reset_timeout: Integer?,
|
|
68
|
-
reset_timeout_jitter: Float?,
|
|
69
|
-
half_open_calls: Integer?,
|
|
70
|
-
storage: (Storage::Base | storage_backend | Class)?,
|
|
71
|
-
metrics: untyped,
|
|
72
|
-
fallback: fallback_value?,
|
|
73
|
-
on_open: callback_proc,
|
|
74
|
-
on_close: callback_proc,
|
|
75
|
-
on_half_open: callback_proc,
|
|
76
|
-
on_reject: callback_proc,
|
|
77
|
-
exceptions: Array[Class]?,
|
|
78
|
-
owner: untyped
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
# Event record structure
|
|
82
|
-
type event_record = {
|
|
83
|
-
timestamp: Float,
|
|
84
|
-
type: event_type,
|
|
85
|
-
duration: Float,
|
|
86
|
-
duration_ms: Float?,
|
|
87
|
-
error_class: String?,
|
|
88
|
-
new_state: circuit_state?,
|
|
89
|
-
details: Hash[Symbol, untyped]?
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
# Status record from storage
|
|
93
|
-
type status_record = {
|
|
94
|
-
status: circuit_state,
|
|
95
|
-
opened_at: Float?
|
|
96
|
-
}
|
|
97
|
-
end
|
data/sig/breaker_machines.rbs
DELETED
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
module BreakerMachines
|
|
2
|
-
VERSION: String
|
|
3
|
-
|
|
4
|
-
class Configuration
|
|
5
|
-
attr_accessor default_storage: (:memory | :bucket_memory | :null | untyped)
|
|
6
|
-
attr_accessor default_timeout: Integer?
|
|
7
|
-
attr_accessor default_reset_timeout: Integer
|
|
8
|
-
attr_accessor default_failure_threshold: Integer
|
|
9
|
-
attr_accessor log_events: bool
|
|
10
|
-
attr_accessor fiber_safe: bool
|
|
11
|
-
|
|
12
|
-
def initialize: () -> void
|
|
13
|
-
end
|
|
14
|
-
|
|
15
|
-
# Global configuration
|
|
16
|
-
self.@config: Configuration?
|
|
17
|
-
def self.config: () -> Configuration
|
|
18
|
-
|
|
19
|
-
# Class methods
|
|
20
|
-
def self.configure: () { (Configuration config) -> void } -> void
|
|
21
|
-
|
|
22
|
-
# Delegated config accessors for backward compatibility
|
|
23
|
-
def self.default_storage: () -> (:memory | :bucket_memory | :null | untyped)
|
|
24
|
-
def self.default_storage=: ((:memory | :bucket_memory | :null | untyped) value) -> void
|
|
25
|
-
def self.default_timeout: () -> Integer?
|
|
26
|
-
def self.default_timeout=: (Integer? value) -> void
|
|
27
|
-
def self.default_reset_timeout: () -> Integer
|
|
28
|
-
def self.default_reset_timeout=: (Integer value) -> void
|
|
29
|
-
def self.default_failure_threshold: () -> Integer
|
|
30
|
-
def self.default_failure_threshold=: (Integer value) -> void
|
|
31
|
-
def self.log_events: () -> bool
|
|
32
|
-
def self.log_events=: (bool value) -> void
|
|
33
|
-
def self.fiber_safe: () -> bool
|
|
34
|
-
def self.fiber_safe=: (bool value) -> void
|
|
35
|
-
def self.setup_notifications: () -> void
|
|
36
|
-
def self.logger: () -> ActiveSupport::Logger?
|
|
37
|
-
def self.logger=: (ActiveSupport::Logger? logger) -> void
|
|
38
|
-
def self.instrument: (String event, ?Hash[Symbol, untyped] payload) -> void
|
|
39
|
-
def self.console: () -> void
|
|
40
|
-
def self.registry: () -> Registry
|
|
41
|
-
def self.loader: () -> Zeitwerk::Loader
|
|
42
|
-
end
|