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.
Files changed (31) hide show
  1. checksums.yaml +4 -4
  2. data/ext/breaker_machines_native/core/Cargo.toml +6 -3
  3. data/ext/breaker_machines_native/ffi/Cargo.toml +5 -3
  4. data/ext/breaker_machines_native/ffi/extconf.rb +3 -3
  5. data/lib/breaker_machines/version.rb +1 -1
  6. metadata +2 -27
  7. data/ext/breaker_machines_native/Cargo.toml +0 -8
  8. data/ext/breaker_machines_native/target/package/breaker-machines-0.7.1/Cargo.toml +0 -48
  9. data/ext/breaker_machines_native/target/package/breaker-machines-0.7.1/examples/basic.rs +0 -61
  10. data/ext/breaker_machines_native/target/package/breaker-machines-0.7.1/src/builder.rs +0 -232
  11. data/ext/breaker_machines_native/target/package/breaker-machines-0.7.1/src/bulkhead.rs +0 -223
  12. data/ext/breaker_machines_native/target/package/breaker-machines-0.7.1/src/callbacks.rs +0 -147
  13. data/ext/breaker_machines_native/target/package/breaker-machines-0.7.1/src/circuit.rs +0 -1436
  14. data/ext/breaker_machines_native/target/package/breaker-machines-0.7.1/src/classifier.rs +0 -177
  15. data/ext/breaker_machines_native/target/package/breaker-machines-0.7.1/src/errors.rs +0 -47
  16. data/ext/breaker_machines_native/target/package/breaker-machines-0.7.1/src/lib.rs +0 -62
  17. data/ext/breaker_machines_native/target/package/breaker-machines-0.7.1/src/storage.rs +0 -377
  18. data/ext/breaker_machines_native/target/package/breaker-machines-0.7.2/Cargo.toml +0 -48
  19. data/ext/breaker_machines_native/target/package/breaker-machines-0.7.2/examples/basic.rs +0 -61
  20. data/ext/breaker_machines_native/target/package/breaker-machines-0.7.2/src/builder.rs +0 -232
  21. data/ext/breaker_machines_native/target/package/breaker-machines-0.7.2/src/bulkhead.rs +0 -223
  22. data/ext/breaker_machines_native/target/package/breaker-machines-0.7.2/src/callbacks.rs +0 -147
  23. data/ext/breaker_machines_native/target/package/breaker-machines-0.7.2/src/circuit.rs +0 -1436
  24. data/ext/breaker_machines_native/target/package/breaker-machines-0.7.2/src/classifier.rs +0 -177
  25. data/ext/breaker_machines_native/target/package/breaker-machines-0.7.2/src/errors.rs +0 -47
  26. data/ext/breaker_machines_native/target/package/breaker-machines-0.7.2/src/lib.rs +0 -62
  27. data/ext/breaker_machines_native/target/package/breaker-machines-0.7.2/src/storage.rs +0 -377
  28. data/ext/breaker_machines_native/target/release/build/clang-sys-710ac1a8148f7790/out/common.rs +0 -355
  29. data/ext/breaker_machines_native/target/release/build/clang-sys-710ac1a8148f7790/out/dynamic.rs +0 -276
  30. data/ext/breaker_machines_native/target/release/build/clang-sys-710ac1a8148f7790/out/macros.rs +0 -49
  31. 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: '0888d588b03f049c46ec019c12a401648c1c2bed107bf62a72b15896de290af2'
4
- data.tar.gz: 902e776cd459cfa52c7fbc2f72e175666ee941db7d7c00a572759bb0daaebd08
3
+ metadata.gz: fd34f2d2c0993335422fcb7c9a2495af660ec6c59dde5f57c7f9da0841cb51db
4
+ data.tar.gz: fc1ba508ecbaf43dbf451ef81bcacb9b586a0003ca5148d66d80f4d51a53f926
5
5
  SHA512:
6
- metadata.gz: 3d375d5858d1c5dd435503bf782a802ab23a2cf8a1b6e93b7eb746bdcf4d51ea23df28186f34f86b223c7da53dd63c92128b915be72fa36812f0aebbfb9df1bc
7
- data.tar.gz: 1f30b69ee1c28efc09912dcdeb0bb28031230c9665187e59c94b6c4cdcf64910f3392393d7d95fbdb949adcf71bd8547a848c1bc8047e84510a5754fe0b2b70e
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.2"
3
+ version = "0.7.6"
4
4
  edition = "2024"
5
- authors = ["Abdelkader Boudih <terminale@gmail.com>"]
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.4"
3
+ version = "0.5.8"
4
4
  edition = "2024"
5
- authors = ["Abdelkader Boudih <terminale@gmail.com>"]
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 = { version = "0.8.2", features = ["embed"] }
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 on JRuby
16
- if RUBY_ENGINE == 'jruby'
17
- create_noop_makefile('Skipping native extension on JRuby')
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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module BreakerMachines
4
- VERSION = '0.10.3'
4
+ VERSION = '0.11.0'
5
5
  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.10.3
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: 4.0.1
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,8 +0,0 @@
1
- [workspace]
2
- members = ["core", "ffi"]
3
- resolver = "2"
4
-
5
- [workspace.dependencies]
6
- breaker-machines = { path = "./core" }
7
- state-machines = "0.6"
8
- chrono-machines = "0.2.1"
@@ -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
- }