breaker_machines 0.10.3 → 0.11.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/ext/breaker_machines_native/core/Cargo.toml +6 -3
- data/ext/breaker_machines_native/ffi/Cargo.toml +5 -3
- data/ext/breaker_machines_native/ffi/extconf.rb +3 -3
- data/lib/breaker_machines/version.rb +1 -1
- metadata +2 -27
- data/ext/breaker_machines_native/Cargo.toml +0 -8
- data/ext/breaker_machines_native/target/package/breaker-machines-0.7.1/Cargo.toml +0 -48
- data/ext/breaker_machines_native/target/package/breaker-machines-0.7.1/examples/basic.rs +0 -61
- data/ext/breaker_machines_native/target/package/breaker-machines-0.7.1/src/builder.rs +0 -232
- data/ext/breaker_machines_native/target/package/breaker-machines-0.7.1/src/bulkhead.rs +0 -223
- data/ext/breaker_machines_native/target/package/breaker-machines-0.7.1/src/callbacks.rs +0 -147
- data/ext/breaker_machines_native/target/package/breaker-machines-0.7.1/src/circuit.rs +0 -1436
- data/ext/breaker_machines_native/target/package/breaker-machines-0.7.1/src/classifier.rs +0 -177
- data/ext/breaker_machines_native/target/package/breaker-machines-0.7.1/src/errors.rs +0 -47
- data/ext/breaker_machines_native/target/package/breaker-machines-0.7.1/src/lib.rs +0 -62
- data/ext/breaker_machines_native/target/package/breaker-machines-0.7.1/src/storage.rs +0 -377
- data/ext/breaker_machines_native/target/package/breaker-machines-0.7.2/Cargo.toml +0 -48
- data/ext/breaker_machines_native/target/package/breaker-machines-0.7.2/examples/basic.rs +0 -61
- data/ext/breaker_machines_native/target/package/breaker-machines-0.7.2/src/builder.rs +0 -232
- data/ext/breaker_machines_native/target/package/breaker-machines-0.7.2/src/bulkhead.rs +0 -223
- data/ext/breaker_machines_native/target/package/breaker-machines-0.7.2/src/callbacks.rs +0 -147
- data/ext/breaker_machines_native/target/package/breaker-machines-0.7.2/src/circuit.rs +0 -1436
- data/ext/breaker_machines_native/target/package/breaker-machines-0.7.2/src/classifier.rs +0 -177
- data/ext/breaker_machines_native/target/package/breaker-machines-0.7.2/src/errors.rs +0 -47
- data/ext/breaker_machines_native/target/package/breaker-machines-0.7.2/src/lib.rs +0 -62
- data/ext/breaker_machines_native/target/package/breaker-machines-0.7.2/src/storage.rs +0 -377
- data/ext/breaker_machines_native/target/release/build/clang-sys-710ac1a8148f7790/out/common.rs +0 -355
- data/ext/breaker_machines_native/target/release/build/clang-sys-710ac1a8148f7790/out/dynamic.rs +0 -276
- data/ext/breaker_machines_native/target/release/build/clang-sys-710ac1a8148f7790/out/macros.rs +0 -49
- data/ext/breaker_machines_native/target/release/build/rb-sys-9ecc9c8203d58b3a/out/bindings-0.9.117-mri-arm64-darwin24-4.0.0.rs +0 -8974
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: fd34f2d2c0993335422fcb7c9a2495af660ec6c59dde5f57c7f9da0841cb51db
|
|
4
|
+
data.tar.gz: fc1ba508ecbaf43dbf451ef81bcacb9b586a0003ca5148d66d80f4d51a53f926
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 49eac0d2918c9cee306309f785009fe53ffbd4d4a26641132f2dfe8a069e0cb200a47a3d97a8748ae1bf52d4604969127d3c549db2e31cf6e25d0b95ce9eb65c
|
|
7
|
+
data.tar.gz: a08fa2490b8b2a3b1ac6bc44853540763875ce39ce439c860b682256f01ccfbdf5e5e63176f917d9c109a3593b573a32588dddb336c34d0a1023c00bbe4c15c5
|
|
@@ -1,18 +1,21 @@
|
|
|
1
1
|
[package]
|
|
2
2
|
name = "breaker-machines"
|
|
3
|
-
version = "0.7.
|
|
3
|
+
version = "0.7.6"
|
|
4
4
|
edition = "2024"
|
|
5
|
-
authors = ["Abdelkader Boudih <
|
|
5
|
+
authors = ["Abdelkader Boudih <oss@seuros.com>"]
|
|
6
6
|
license = "MIT"
|
|
7
7
|
description = "High-performance circuit breaker with fallback support and rate-based thresholds"
|
|
8
8
|
repository = "https://github.com/seuros/breaker_machines"
|
|
9
9
|
readme = "README.md"
|
|
10
|
-
keywords = ["circuit-breaker", "reliability", "fault-tolerance"]
|
|
10
|
+
keywords = ["circuit-breaker", "reliability", "fault-tolerance", "state-machine"]
|
|
11
11
|
categories = ["concurrency"]
|
|
12
12
|
|
|
13
13
|
[lib]
|
|
14
14
|
crate-type = ["lib"]
|
|
15
15
|
|
|
16
|
+
[features]
|
|
17
|
+
inspect = ["state-machines/inspect"]
|
|
18
|
+
|
|
16
19
|
[dependencies]
|
|
17
20
|
state-machines = { workspace = true }
|
|
18
21
|
chrono-machines = { workspace = true }
|
|
@@ -1,16 +1,18 @@
|
|
|
1
1
|
[package]
|
|
2
2
|
name = "breaker_machines_native"
|
|
3
|
-
version = "0.5.
|
|
3
|
+
version = "0.5.8"
|
|
4
4
|
edition = "2024"
|
|
5
|
-
authors = ["Abdelkader Boudih <
|
|
5
|
+
authors = ["Abdelkader Boudih <oss@seuros.com>"]
|
|
6
6
|
license = "MIT"
|
|
7
|
+
description = "Ruby FFI bindings for breaker-machines circuit breaker"
|
|
8
|
+
repository = "https://github.com/seuros/breaker_machines"
|
|
7
9
|
|
|
8
10
|
[lib]
|
|
9
11
|
crate-type = ["cdylib"]
|
|
10
12
|
|
|
11
13
|
[dependencies]
|
|
12
14
|
breaker-machines = { workspace = true }
|
|
13
|
-
magnus =
|
|
15
|
+
magnus = "0.8.2"
|
|
14
16
|
|
|
15
17
|
[build-dependencies]
|
|
16
18
|
rb-sys = "0.9"
|
|
@@ -12,9 +12,9 @@ def create_noop_makefile(message)
|
|
|
12
12
|
exit 0
|
|
13
13
|
end
|
|
14
14
|
|
|
15
|
-
# Skip native extension compilation
|
|
16
|
-
|
|
17
|
-
create_noop_makefile(
|
|
15
|
+
# Skip native extension compilation outside CRuby
|
|
16
|
+
unless RUBY_ENGINE == 'ruby'
|
|
17
|
+
create_noop_makefile("Skipping native extension on #{RUBY_ENGINE}")
|
|
18
18
|
end
|
|
19
19
|
|
|
20
20
|
# Check if Cargo is available
|
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.11.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Abdelkader Boudih
|
|
@@ -150,7 +150,6 @@ extra_rdoc_files: []
|
|
|
150
150
|
files:
|
|
151
151
|
- LICENSE.txt
|
|
152
152
|
- README.md
|
|
153
|
-
- ext/breaker_machines_native/Cargo.toml
|
|
154
153
|
- ext/breaker_machines_native/core/Cargo.toml
|
|
155
154
|
- ext/breaker_machines_native/core/examples/basic.rs
|
|
156
155
|
- ext/breaker_machines_native/core/src/builder.rs
|
|
@@ -165,30 +164,6 @@ files:
|
|
|
165
164
|
- ext/breaker_machines_native/ffi/Cargo.toml
|
|
166
165
|
- ext/breaker_machines_native/ffi/extconf.rb
|
|
167
166
|
- ext/breaker_machines_native/ffi/src/lib.rs
|
|
168
|
-
- ext/breaker_machines_native/target/package/breaker-machines-0.7.1/Cargo.toml
|
|
169
|
-
- ext/breaker_machines_native/target/package/breaker-machines-0.7.1/examples/basic.rs
|
|
170
|
-
- ext/breaker_machines_native/target/package/breaker-machines-0.7.1/src/builder.rs
|
|
171
|
-
- ext/breaker_machines_native/target/package/breaker-machines-0.7.1/src/bulkhead.rs
|
|
172
|
-
- ext/breaker_machines_native/target/package/breaker-machines-0.7.1/src/callbacks.rs
|
|
173
|
-
- ext/breaker_machines_native/target/package/breaker-machines-0.7.1/src/circuit.rs
|
|
174
|
-
- ext/breaker_machines_native/target/package/breaker-machines-0.7.1/src/classifier.rs
|
|
175
|
-
- ext/breaker_machines_native/target/package/breaker-machines-0.7.1/src/errors.rs
|
|
176
|
-
- ext/breaker_machines_native/target/package/breaker-machines-0.7.1/src/lib.rs
|
|
177
|
-
- ext/breaker_machines_native/target/package/breaker-machines-0.7.1/src/storage.rs
|
|
178
|
-
- ext/breaker_machines_native/target/package/breaker-machines-0.7.2/Cargo.toml
|
|
179
|
-
- ext/breaker_machines_native/target/package/breaker-machines-0.7.2/examples/basic.rs
|
|
180
|
-
- ext/breaker_machines_native/target/package/breaker-machines-0.7.2/src/builder.rs
|
|
181
|
-
- ext/breaker_machines_native/target/package/breaker-machines-0.7.2/src/bulkhead.rs
|
|
182
|
-
- ext/breaker_machines_native/target/package/breaker-machines-0.7.2/src/callbacks.rs
|
|
183
|
-
- ext/breaker_machines_native/target/package/breaker-machines-0.7.2/src/circuit.rs
|
|
184
|
-
- ext/breaker_machines_native/target/package/breaker-machines-0.7.2/src/classifier.rs
|
|
185
|
-
- ext/breaker_machines_native/target/package/breaker-machines-0.7.2/src/errors.rs
|
|
186
|
-
- ext/breaker_machines_native/target/package/breaker-machines-0.7.2/src/lib.rs
|
|
187
|
-
- ext/breaker_machines_native/target/package/breaker-machines-0.7.2/src/storage.rs
|
|
188
|
-
- ext/breaker_machines_native/target/release/build/clang-sys-710ac1a8148f7790/out/common.rs
|
|
189
|
-
- ext/breaker_machines_native/target/release/build/clang-sys-710ac1a8148f7790/out/dynamic.rs
|
|
190
|
-
- ext/breaker_machines_native/target/release/build/clang-sys-710ac1a8148f7790/out/macros.rs
|
|
191
|
-
- ext/breaker_machines_native/target/release/build/rb-sys-9ecc9c8203d58b3a/out/bindings-0.9.117-mri-arm64-darwin24-4.0.0.rs
|
|
192
167
|
- lib/breaker_machines.rb
|
|
193
168
|
- lib/breaker_machines/async_circuit.rb
|
|
194
169
|
- lib/breaker_machines/async_support.rb
|
|
@@ -266,7 +241,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
266
241
|
- !ruby/object:Gem::Version
|
|
267
242
|
version: '0'
|
|
268
243
|
requirements: []
|
|
269
|
-
rubygems_version:
|
|
244
|
+
rubygems_version: 3.6.9
|
|
270
245
|
specification_version: 4
|
|
271
246
|
summary: Circuit breaker implementation for Ruby with a clean DSL and state_machines
|
|
272
247
|
under the hood
|
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
|
|
2
|
-
#
|
|
3
|
-
# When uploading crates to the registry Cargo will automatically
|
|
4
|
-
# "normalize" Cargo.toml files for maximal compatibility
|
|
5
|
-
# with all versions of Cargo and also rewrite `path` dependencies
|
|
6
|
-
# to registry (e.g., crates.io) dependencies.
|
|
7
|
-
#
|
|
8
|
-
# If you are reading this file be aware that the original Cargo.toml
|
|
9
|
-
# will likely look very different (and much more reasonable).
|
|
10
|
-
# See Cargo.toml.orig for the original contents.
|
|
11
|
-
|
|
12
|
-
[package]
|
|
13
|
-
edition = "2024"
|
|
14
|
-
name = "breaker-machines"
|
|
15
|
-
version = "0.7.1"
|
|
16
|
-
authors = ["Abdelkader Boudih <terminale@gmail.com>"]
|
|
17
|
-
build = false
|
|
18
|
-
autolib = false
|
|
19
|
-
autobins = false
|
|
20
|
-
autoexamples = false
|
|
21
|
-
autotests = false
|
|
22
|
-
autobenches = false
|
|
23
|
-
description = "High-performance circuit breaker with fallback support and rate-based thresholds"
|
|
24
|
-
readme = "README.md"
|
|
25
|
-
keywords = [
|
|
26
|
-
"circuit-breaker",
|
|
27
|
-
"reliability",
|
|
28
|
-
"fault-tolerance",
|
|
29
|
-
]
|
|
30
|
-
categories = ["concurrency"]
|
|
31
|
-
license = "MIT"
|
|
32
|
-
repository = "https://github.com/seuros/breaker_machines"
|
|
33
|
-
resolver = "2"
|
|
34
|
-
|
|
35
|
-
[lib]
|
|
36
|
-
name = "breaker_machines"
|
|
37
|
-
crate-type = ["lib"]
|
|
38
|
-
path = "src/lib.rs"
|
|
39
|
-
|
|
40
|
-
[[example]]
|
|
41
|
-
name = "basic"
|
|
42
|
-
path = "examples/basic.rs"
|
|
43
|
-
|
|
44
|
-
[dependencies.chrono-machines]
|
|
45
|
-
version = "0.2.1"
|
|
46
|
-
|
|
47
|
-
[dependencies.state-machines]
|
|
48
|
-
version = "0.6"
|
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
//! Basic circuit breaker usage example
|
|
2
|
-
|
|
3
|
-
use breaker_machines::CircuitBreaker;
|
|
4
|
-
|
|
5
|
-
fn main() {
|
|
6
|
-
println!("=== Circuit Breaker Basic Example ===\n");
|
|
7
|
-
|
|
8
|
-
// Create a circuit with builder API
|
|
9
|
-
let mut circuit = CircuitBreaker::builder("payment_api")
|
|
10
|
-
.failure_threshold(3)
|
|
11
|
-
.failure_window_secs(10.0)
|
|
12
|
-
.half_open_timeout_secs(5.0)
|
|
13
|
-
.success_threshold(2)
|
|
14
|
-
.on_open(|name| println!("🔴 Circuit '{}' opened!", name))
|
|
15
|
-
.on_close(|name| println!("🟢 Circuit '{}' closed!", name))
|
|
16
|
-
.on_half_open(|name| println!("🟡 Circuit '{}' half-open, testing...", name))
|
|
17
|
-
.build();
|
|
18
|
-
|
|
19
|
-
println!("Initial state: {}\n", circuit.state_name());
|
|
20
|
-
|
|
21
|
-
// Simulate successful calls
|
|
22
|
-
println!("--- Successful calls ---");
|
|
23
|
-
for i in 1..=2 {
|
|
24
|
-
match circuit.call(move || Ok::<_, String>(format!("Payment {}", i))) {
|
|
25
|
-
Ok(result) => println!("✓ {}", result),
|
|
26
|
-
Err(e) => println!("✗ Error: {}", e),
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
println!("State: {}\n", circuit.state_name());
|
|
30
|
-
|
|
31
|
-
// Simulate failures
|
|
32
|
-
println!("--- Triggering failures ---");
|
|
33
|
-
for i in 1..=3 {
|
|
34
|
-
match circuit.call(move || Err::<String, _>(format!("Payment failed {}", i))) {
|
|
35
|
-
Ok(_) => println!("✓ Success"),
|
|
36
|
-
Err(e) => println!("✗ {}", e),
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
println!("State: {} (circuit opened)\n", circuit.state_name());
|
|
40
|
-
|
|
41
|
-
// Try calling while open
|
|
42
|
-
println!("--- Attempting call while open ---");
|
|
43
|
-
match circuit.call(|| Ok::<_, String>("Should be rejected")) {
|
|
44
|
-
Ok(_) => println!("✓ Success"),
|
|
45
|
-
Err(e) => println!("✗ {}", e),
|
|
46
|
-
}
|
|
47
|
-
println!();
|
|
48
|
-
|
|
49
|
-
// Reset and demonstrate recovery
|
|
50
|
-
println!("--- Resetting circuit ---");
|
|
51
|
-
circuit.reset();
|
|
52
|
-
println!("State after reset: {}\n", circuit.state_name());
|
|
53
|
-
|
|
54
|
-
// Successful calls after reset
|
|
55
|
-
println!("--- Calls after reset ---");
|
|
56
|
-
match circuit.call(|| Ok::<_, String>("Payment successful")) {
|
|
57
|
-
Ok(result) => println!("✓ {}", result),
|
|
58
|
-
Err(e) => println!("✗ {}", e),
|
|
59
|
-
}
|
|
60
|
-
println!("State: {}", circuit.state_name());
|
|
61
|
-
}
|
|
@@ -1,232 +0,0 @@
|
|
|
1
|
-
//! Builder API for ergonomic circuit breaker configuration
|
|
2
|
-
|
|
3
|
-
use crate::{
|
|
4
|
-
MemoryStorage, StorageBackend,
|
|
5
|
-
bulkhead::BulkheadSemaphore,
|
|
6
|
-
callbacks::Callbacks,
|
|
7
|
-
circuit::{CircuitBreaker, CircuitContext, Config},
|
|
8
|
-
classifier::FailureClassifier,
|
|
9
|
-
};
|
|
10
|
-
use std::sync::Arc;
|
|
11
|
-
|
|
12
|
-
/// Builder for creating circuit breakers with fluent API
|
|
13
|
-
pub struct CircuitBuilder {
|
|
14
|
-
name: String,
|
|
15
|
-
config: Config,
|
|
16
|
-
storage: Option<Arc<dyn StorageBackend>>,
|
|
17
|
-
failure_classifier: Option<Arc<dyn FailureClassifier>>,
|
|
18
|
-
bulkhead: Option<Arc<BulkheadSemaphore>>,
|
|
19
|
-
callbacks: Callbacks,
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
impl CircuitBuilder {
|
|
23
|
-
/// Create a new builder for a circuit with the given name
|
|
24
|
-
pub fn new(name: impl Into<String>) -> Self {
|
|
25
|
-
Self {
|
|
26
|
-
name: name.into(),
|
|
27
|
-
config: Config::default(),
|
|
28
|
-
storage: None,
|
|
29
|
-
failure_classifier: None,
|
|
30
|
-
bulkhead: None,
|
|
31
|
-
callbacks: Callbacks::new(),
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
/// Set the absolute failure threshold (number of failures to open circuit)
|
|
36
|
-
pub fn failure_threshold(mut self, threshold: usize) -> Self {
|
|
37
|
-
self.config.failure_threshold = Some(threshold);
|
|
38
|
-
self
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
/// Disable absolute failure threshold (use only rate-based)
|
|
42
|
-
pub fn disable_failure_threshold(mut self) -> Self {
|
|
43
|
-
self.config.failure_threshold = None;
|
|
44
|
-
self
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
/// Set the failure rate threshold (0.0-1.0)
|
|
48
|
-
/// Circuit opens when (failures / total_calls) >= this value
|
|
49
|
-
pub fn failure_rate(mut self, rate: f64) -> Self {
|
|
50
|
-
self.config.failure_rate_threshold = Some(rate.clamp(0.0, 1.0));
|
|
51
|
-
self
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
/// Set minimum number of calls before rate-based threshold is evaluated
|
|
55
|
-
pub fn minimum_calls(mut self, calls: usize) -> Self {
|
|
56
|
-
self.config.minimum_calls = calls;
|
|
57
|
-
self
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
/// Set the failure window in seconds
|
|
61
|
-
pub fn failure_window_secs(mut self, seconds: f64) -> Self {
|
|
62
|
-
self.config.failure_window_secs = seconds;
|
|
63
|
-
self
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
/// Set the half-open timeout in seconds
|
|
67
|
-
pub fn half_open_timeout_secs(mut self, seconds: f64) -> Self {
|
|
68
|
-
self.config.half_open_timeout_secs = seconds;
|
|
69
|
-
self
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
/// Set the success threshold (successes needed to close from half-open)
|
|
73
|
-
pub fn success_threshold(mut self, threshold: usize) -> Self {
|
|
74
|
-
self.config.success_threshold = threshold;
|
|
75
|
-
self
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
/// Set the jitter factor (0.0 = no jitter, 1.0 = full jitter)
|
|
79
|
-
/// Uses chrono-machines formula: timeout * (1 - jitter + rand * jitter)
|
|
80
|
-
pub fn jitter_factor(mut self, factor: f64) -> Self {
|
|
81
|
-
self.config.jitter_factor = factor;
|
|
82
|
-
self
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
/// Set custom storage backend
|
|
86
|
-
pub fn storage(mut self, storage: Arc<dyn StorageBackend>) -> Self {
|
|
87
|
-
self.storage = Some(storage);
|
|
88
|
-
self
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
/// Set a failure classifier to filter which errors should trip the circuit
|
|
92
|
-
///
|
|
93
|
-
/// The classifier determines whether a given error should count toward
|
|
94
|
-
/// opening the circuit. Use this to ignore "expected" errors like validation
|
|
95
|
-
/// failures or client errors (4xx), while still tripping on server errors (5xx).
|
|
96
|
-
///
|
|
97
|
-
/// # Examples
|
|
98
|
-
///
|
|
99
|
-
/// ```rust
|
|
100
|
-
/// use breaker_machines::{CircuitBreaker, PredicateClassifier};
|
|
101
|
-
/// use std::sync::Arc;
|
|
102
|
-
///
|
|
103
|
-
/// let circuit = CircuitBreaker::builder("api")
|
|
104
|
-
/// .failure_classifier(Arc::new(PredicateClassifier::new(|ctx| {
|
|
105
|
-
/// // Only trip on slow errors
|
|
106
|
-
/// ctx.duration > 1.0
|
|
107
|
-
/// })))
|
|
108
|
-
/// .build();
|
|
109
|
-
/// ```
|
|
110
|
-
pub fn failure_classifier(mut self, classifier: Arc<dyn FailureClassifier>) -> Self {
|
|
111
|
-
self.failure_classifier = Some(classifier);
|
|
112
|
-
self
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
/// Set maximum concurrency limit (bulkheading)
|
|
116
|
-
///
|
|
117
|
-
/// When set, the circuit breaker will reject calls with `BulkheadFull` error
|
|
118
|
-
/// if the number of concurrent calls exceeds this limit. This prevents
|
|
119
|
-
/// resource exhaustion by limiting how many operations can run simultaneously.
|
|
120
|
-
///
|
|
121
|
-
/// # Panics
|
|
122
|
-
///
|
|
123
|
-
/// Panics if `limit` is 0.
|
|
124
|
-
///
|
|
125
|
-
/// # Examples
|
|
126
|
-
///
|
|
127
|
-
/// ```rust
|
|
128
|
-
/// use breaker_machines::CircuitBreaker;
|
|
129
|
-
///
|
|
130
|
-
/// let mut circuit = CircuitBreaker::builder("api")
|
|
131
|
-
/// .max_concurrency(10) // Allow max 10 concurrent calls
|
|
132
|
-
/// .build();
|
|
133
|
-
///
|
|
134
|
-
/// // This will succeed until 10 calls are running concurrently
|
|
135
|
-
/// let result = circuit.call(|| Ok::<_, String>("success"));
|
|
136
|
-
/// ```
|
|
137
|
-
pub fn max_concurrency(mut self, limit: usize) -> Self {
|
|
138
|
-
self.bulkhead = Some(Arc::new(BulkheadSemaphore::new(limit)));
|
|
139
|
-
self
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
/// Set callback for when circuit opens
|
|
143
|
-
pub fn on_open<F>(mut self, f: F) -> Self
|
|
144
|
-
where
|
|
145
|
-
F: Fn(&str) + Send + Sync + 'static,
|
|
146
|
-
{
|
|
147
|
-
self.callbacks.on_open = Some(Arc::new(f));
|
|
148
|
-
self
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
/// Set callback for when circuit closes
|
|
152
|
-
pub fn on_close<F>(mut self, f: F) -> Self
|
|
153
|
-
where
|
|
154
|
-
F: Fn(&str) + Send + Sync + 'static,
|
|
155
|
-
{
|
|
156
|
-
self.callbacks.on_close = Some(Arc::new(f));
|
|
157
|
-
self
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
/// Set callback for when circuit enters half-open
|
|
161
|
-
pub fn on_half_open<F>(mut self, f: F) -> Self
|
|
162
|
-
where
|
|
163
|
-
F: Fn(&str) + Send + Sync + 'static,
|
|
164
|
-
{
|
|
165
|
-
self.callbacks.on_half_open = Some(Arc::new(f));
|
|
166
|
-
self
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
/// Build the circuit breaker
|
|
170
|
-
pub fn build(self) -> CircuitBreaker {
|
|
171
|
-
let storage = self
|
|
172
|
-
.storage
|
|
173
|
-
.unwrap_or_else(|| Arc::new(MemoryStorage::new()));
|
|
174
|
-
|
|
175
|
-
let context = CircuitContext {
|
|
176
|
-
name: self.name,
|
|
177
|
-
config: self.config,
|
|
178
|
-
storage,
|
|
179
|
-
failure_classifier: self.failure_classifier,
|
|
180
|
-
bulkhead: self.bulkhead,
|
|
181
|
-
};
|
|
182
|
-
|
|
183
|
-
CircuitBreaker::with_context_and_callbacks(context, self.callbacks)
|
|
184
|
-
}
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
#[cfg(test)]
|
|
188
|
-
mod tests {
|
|
189
|
-
use super::*;
|
|
190
|
-
|
|
191
|
-
#[test]
|
|
192
|
-
fn test_builder_defaults() {
|
|
193
|
-
let circuit = CircuitBuilder::new("test").build();
|
|
194
|
-
|
|
195
|
-
assert_eq!(circuit.state_name(), "Closed");
|
|
196
|
-
assert!(circuit.is_closed());
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
#[test]
|
|
200
|
-
fn test_builder_custom_config() {
|
|
201
|
-
let circuit = CircuitBuilder::new("test")
|
|
202
|
-
.failure_threshold(10)
|
|
203
|
-
.failure_window_secs(120.0)
|
|
204
|
-
.half_open_timeout_secs(60.0)
|
|
205
|
-
.success_threshold(3)
|
|
206
|
-
.build();
|
|
207
|
-
|
|
208
|
-
assert!(circuit.is_closed());
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
#[test]
|
|
212
|
-
fn test_builder_with_callbacks() {
|
|
213
|
-
use std::sync::atomic::{AtomicBool, Ordering};
|
|
214
|
-
|
|
215
|
-
let opened = Arc::new(AtomicBool::new(false));
|
|
216
|
-
let opened_clone = opened.clone();
|
|
217
|
-
|
|
218
|
-
let mut circuit = CircuitBuilder::new("test")
|
|
219
|
-
.failure_threshold(2)
|
|
220
|
-
.on_open(move |_name| {
|
|
221
|
-
opened_clone.store(true, Ordering::SeqCst);
|
|
222
|
-
})
|
|
223
|
-
.build();
|
|
224
|
-
|
|
225
|
-
// Trigger failures to open circuit
|
|
226
|
-
let _ = circuit.call(|| Err::<(), _>("error 1"));
|
|
227
|
-
let _ = circuit.call(|| Err::<(), _>("error 2"));
|
|
228
|
-
|
|
229
|
-
// Callback should have been triggered
|
|
230
|
-
assert!(opened.load(Ordering::SeqCst));
|
|
231
|
-
}
|
|
232
|
-
}
|