breaker_machines 0.6.0 → 0.7.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/extconf.rb +1 -38
- data/ext/breaker_machines_native/ffi/extconf.rb +67 -0
- data/lib/breaker_machines/native_extension.rb +8 -0
- data/lib/breaker_machines/native_speedup.rb +5 -1
- data/lib/breaker_machines/storage/native.rb +22 -19
- data/lib/breaker_machines/version.rb +1 -1
- data/lib/breaker_machines.rb +7 -7
- metadata +22 -6
- data/lib/breaker_machines_native/breaker_machines_native.bundle +0 -0
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 5137f3692641c76411b86e725df03e901220a09d18a9bc1ba64fc1834d93b811
|
|
4
|
+
data.tar.gz: f5f0c05289c82dfe6aa1fe23322a531fbfd9faa1b8e9f29575eb5c801aacf712
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 1150d77d3823b038277fb4bd7ee2c28bb39ea0307fefe227f2346c220ceb961f89349bfadc0d8b7bbcea01d7c0bd319ad96177c2de0eb24b6df82f57ee8001f6
|
|
7
|
+
data.tar.gz: 3673de4be4d9784ea7135da34e0d02aae91c4b2f8a0c04bc3e058cb01e7f250feb9e6526c4f638d555cc7a3e35a4d3ffb75ffcd770db867442e234ecf43f58a7
|
|
@@ -1,40 +1,3 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
if RUBY_ENGINE == 'jruby'
|
|
5
|
-
puts 'Skipping native extension compilation on JRuby'
|
|
6
|
-
puts 'BreakerMachines will use pure Ruby backend'
|
|
7
|
-
makefile_content = "all:\n\t@echo 'Skipping native extension on JRuby'\n" \
|
|
8
|
-
"install:\n\t@echo 'Skipping native extension on JRuby'\n"
|
|
9
|
-
File.write('Makefile', makefile_content)
|
|
10
|
-
exit 0
|
|
11
|
-
end
|
|
12
|
-
|
|
13
|
-
# Check if Cargo is available
|
|
14
|
-
def cargo_available?
|
|
15
|
-
system('cargo --version > /dev/null 2>&1')
|
|
16
|
-
end
|
|
17
|
-
|
|
18
|
-
unless cargo_available?
|
|
19
|
-
warn 'WARNING: Cargo (Rust toolchain) not found!'
|
|
20
|
-
warn 'BreakerMachines will fall back to pure Ruby backend.'
|
|
21
|
-
warn 'To enable native performance, install Rust from https://rustup.rs'
|
|
22
|
-
|
|
23
|
-
# Create a dummy Makefile that does nothing
|
|
24
|
-
makefile_content = "all:\n\t@echo 'Skipping native extension (Cargo not found)'\n" \
|
|
25
|
-
"install:\n\t@echo 'Skipping native extension (Cargo not found)'\n"
|
|
26
|
-
File.write('Makefile', makefile_content)
|
|
27
|
-
exit 0
|
|
28
|
-
end
|
|
29
|
-
|
|
30
|
-
# Use rb_sys to compile the Rust extension
|
|
31
|
-
require 'mkmf'
|
|
32
|
-
require 'rb_sys/mkmf'
|
|
33
|
-
|
|
34
|
-
create_rust_makefile('breaker_machines_native/breaker_machines_native') do |r|
|
|
35
|
-
# Set the path to the FFI crate (relative to current directory)
|
|
36
|
-
r.ext_dir = 'ffi'
|
|
37
|
-
|
|
38
|
-
# Profile configuration
|
|
39
|
-
r.profile = ENV.fetch('RB_SYS_CARGO_PROFILE', :release).to_sym
|
|
40
|
-
end
|
|
3
|
+
require_relative 'ffi/extconf'
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Skip native extension compilation on JRuby
|
|
4
|
+
if RUBY_ENGINE == 'jruby'
|
|
5
|
+
puts 'Skipping native extension compilation on JRuby'
|
|
6
|
+
puts 'BreakerMachines will use pure Ruby backend'
|
|
7
|
+
makefile_content = "all:\n\t@echo 'Skipping native extension on JRuby'\n" \
|
|
8
|
+
"install:\n\t@echo 'Skipping native extension on JRuby'\n"
|
|
9
|
+
File.write('Makefile', makefile_content)
|
|
10
|
+
exit 0
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
# Check if Cargo is available
|
|
14
|
+
def cargo_available?
|
|
15
|
+
system('cargo --version > /dev/null 2>&1')
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def create_noop_makefile(message)
|
|
19
|
+
warn message
|
|
20
|
+
warn 'BreakerMachines will fall back to pure Ruby backend.'
|
|
21
|
+
File.write('Makefile', <<~MAKE)
|
|
22
|
+
all:
|
|
23
|
+
@echo '#{message}'
|
|
24
|
+
install:
|
|
25
|
+
@echo '#{message}'
|
|
26
|
+
MAKE
|
|
27
|
+
exit 0
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
unless cargo_available?
|
|
31
|
+
create_noop_makefile('Skipping native extension (Cargo not found)')
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# Use rb_sys to compile the Rust extension
|
|
35
|
+
require 'mkmf'
|
|
36
|
+
|
|
37
|
+
# Wrap entire compilation process in error handling to ensure gem install never fails
|
|
38
|
+
begin
|
|
39
|
+
require 'rb_sys/mkmf'
|
|
40
|
+
|
|
41
|
+
require 'pathname'
|
|
42
|
+
|
|
43
|
+
create_rust_makefile('breaker_machines_native/breaker_machines_native') do |r|
|
|
44
|
+
ffi_dir = Pathname(__dir__)
|
|
45
|
+
r.ext_dir = begin
|
|
46
|
+
ffi_dir.relative_path_from(Pathname(Dir.pwd)).to_s
|
|
47
|
+
rescue ArgumentError
|
|
48
|
+
ffi_dir.expand_path.to_s
|
|
49
|
+
end
|
|
50
|
+
# Profile configuration
|
|
51
|
+
r.profile = ENV.fetch('RB_SYS_CARGO_PROFILE', :release).to_sym
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
makefile_path = File.join(Dir.pwd, 'Makefile')
|
|
55
|
+
if File.exist?(makefile_path)
|
|
56
|
+
manifest_path = File.expand_path(__dir__)
|
|
57
|
+
contents = File.read(makefile_path)
|
|
58
|
+
contents.gsub!(%r{^RB_SYS_CARGO_MANIFEST_DIR \?=.*$}, "RB_SYS_CARGO_MANIFEST_DIR ?= #{manifest_path}")
|
|
59
|
+
File.write(makefile_path, contents)
|
|
60
|
+
end
|
|
61
|
+
rescue LoadError => e
|
|
62
|
+
# rb_sys not available
|
|
63
|
+
create_noop_makefile("Skipping native extension (rb_sys gem not available: #{e.message})")
|
|
64
|
+
rescue StandardError => e
|
|
65
|
+
# Any other compilation setup failure (Rust compilation errors, Makefile generation, etc.)
|
|
66
|
+
create_noop_makefile("Skipping native extension (compilation setup failed: #{e.message})")
|
|
67
|
+
end
|
|
@@ -9,6 +9,14 @@ module BreakerMachines
|
|
|
9
9
|
def load!
|
|
10
10
|
return @loaded if defined?(@loaded)
|
|
11
11
|
|
|
12
|
+
# Respect explicit ENV flag to disable native (useful for testing)
|
|
13
|
+
if ENV['BREAKER_MACHINES_NATIVE'] == '0'
|
|
14
|
+
@loaded = false
|
|
15
|
+
BreakerMachines.instance_variable_set(:@native_available, false)
|
|
16
|
+
BreakerMachines.log(:info, 'Native extension disabled via ENV')
|
|
17
|
+
return false
|
|
18
|
+
end
|
|
19
|
+
|
|
12
20
|
@loaded = true
|
|
13
21
|
require 'breaker_machines_native/breaker_machines_native'
|
|
14
22
|
BreakerMachines.instance_variable_set(:@native_available, true)
|
|
@@ -3,4 +3,8 @@
|
|
|
3
3
|
require_relative 'native_extension'
|
|
4
4
|
|
|
5
5
|
# Load the native extension if available
|
|
6
|
-
BreakerMachines::NativeExtension.load!
|
|
6
|
+
if BreakerMachines::NativeExtension.load!
|
|
7
|
+
# Only load Storage::Native if native extension loaded successfully
|
|
8
|
+
# This prevents referencing BreakerMachinesNative::Storage when not available
|
|
9
|
+
require_relative 'storage/native'
|
|
10
|
+
end
|
|
@@ -2,31 +2,34 @@
|
|
|
2
2
|
|
|
3
3
|
module BreakerMachines
|
|
4
4
|
module Storage
|
|
5
|
-
# Native extension storage backend
|
|
5
|
+
# Native extension storage backend with graceful fallback to pure Ruby
|
|
6
6
|
#
|
|
7
7
|
# This backend provides identical functionality to Memory storage but with
|
|
8
|
-
# significantly better performance for sliding window calculations
|
|
9
|
-
#
|
|
10
|
-
#
|
|
8
|
+
# significantly better performance for sliding window calculations when the
|
|
9
|
+
# native extension is available. If the native extension isn't available
|
|
10
|
+
# (e.g., on JRuby or if Rust wasn't installed), it automatically falls back
|
|
11
|
+
# to the pure Ruby Memory storage backend.
|
|
11
12
|
#
|
|
12
|
-
# Performance: ~63x faster than Memory storage
|
|
13
|
+
# Performance: ~63x faster than Memory storage when native extension is available
|
|
13
14
|
#
|
|
14
15
|
# Usage:
|
|
15
16
|
# BreakerMachines.configure do |config|
|
|
16
17
|
# config.default_storage = :native
|
|
17
18
|
# end
|
|
18
19
|
#
|
|
19
|
-
#
|
|
20
|
-
#
|
|
21
|
-
# Use Storage::Memory as a fallback in such cases.
|
|
20
|
+
# FFI Hybrid Pattern: This class is only loaded when native extension is available
|
|
21
|
+
# Fallback happens at load time (native_speedup.rb), not at runtime
|
|
22
22
|
class Native < Base
|
|
23
23
|
def initialize(**options)
|
|
24
24
|
super
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
25
|
+
# Native extension is guaranteed to be available when this class is loaded
|
|
26
|
+
@backend = BreakerMachinesNative::Storage.new
|
|
27
|
+
end
|
|
28
28
|
|
|
29
|
-
|
|
29
|
+
# Check if using native backend
|
|
30
|
+
# @return [Boolean] always true - this class only exists when native is available
|
|
31
|
+
def native?
|
|
32
|
+
true
|
|
30
33
|
end
|
|
31
34
|
|
|
32
35
|
def get_status(_circuit_name)
|
|
@@ -41,27 +44,27 @@ module BreakerMachines
|
|
|
41
44
|
end
|
|
42
45
|
|
|
43
46
|
def record_success(circuit_name, duration)
|
|
44
|
-
@
|
|
47
|
+
@backend.record_success(circuit_name.to_s, duration)
|
|
45
48
|
end
|
|
46
49
|
|
|
47
50
|
def record_failure(circuit_name, duration)
|
|
48
|
-
@
|
|
51
|
+
@backend.record_failure(circuit_name.to_s, duration)
|
|
49
52
|
end
|
|
50
53
|
|
|
51
54
|
def success_count(circuit_name, window_seconds)
|
|
52
|
-
@
|
|
55
|
+
@backend.success_count(circuit_name.to_s, window_seconds)
|
|
53
56
|
end
|
|
54
57
|
|
|
55
58
|
def failure_count(circuit_name, window_seconds)
|
|
56
|
-
@
|
|
59
|
+
@backend.failure_count(circuit_name.to_s, window_seconds)
|
|
57
60
|
end
|
|
58
61
|
|
|
59
62
|
def clear(circuit_name)
|
|
60
|
-
@
|
|
63
|
+
@backend.clear(circuit_name.to_s)
|
|
61
64
|
end
|
|
62
65
|
|
|
63
66
|
def clear_all
|
|
64
|
-
@
|
|
67
|
+
@backend.clear_all
|
|
65
68
|
end
|
|
66
69
|
|
|
67
70
|
def record_event_with_details(circuit_name, type, duration, error: nil, new_state: nil)
|
|
@@ -78,7 +81,7 @@ module BreakerMachines
|
|
|
78
81
|
end
|
|
79
82
|
|
|
80
83
|
def event_log(circuit_name, limit)
|
|
81
|
-
@
|
|
84
|
+
@backend.event_log(circuit_name.to_s, limit)
|
|
82
85
|
end
|
|
83
86
|
|
|
84
87
|
def with_timeout(_timeout_ms)
|
data/lib/breaker_machines.rb
CHANGED
|
@@ -17,6 +17,7 @@ loader.ignore("#{__dir__}/breaker_machines/hedged_async_support.rb")
|
|
|
17
17
|
loader.ignore("#{__dir__}/breaker_machines/circuit/async_state_management.rb")
|
|
18
18
|
loader.ignore("#{__dir__}/breaker_machines/native_speedup.rb")
|
|
19
19
|
loader.ignore("#{__dir__}/breaker_machines/native_extension.rb")
|
|
20
|
+
loader.ignore("#{__dir__}/breaker_machines/storage/native.rb")
|
|
20
21
|
loader.setup
|
|
21
22
|
|
|
22
23
|
# BreakerMachines provides a thread-safe implementation of the Circuit Breaker pattern
|
|
@@ -190,11 +191,10 @@ module BreakerMachines
|
|
|
190
191
|
end
|
|
191
192
|
|
|
192
193
|
# Load optional native speedup after core is loaded
|
|
193
|
-
#
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
end
|
|
194
|
+
# Automatically loads if available, gracefully falls back to pure Ruby if not
|
|
195
|
+
begin
|
|
196
|
+
require_relative 'breaker_machines/native_speedup'
|
|
197
|
+
rescue LoadError
|
|
198
|
+
# Native extension not available, using pure Ruby backend
|
|
199
|
+
# This is expected on JRuby or when Cargo is not available
|
|
200
200
|
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.7.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Abdelkader Boudih
|
|
@@ -79,6 +79,20 @@ dependencies:
|
|
|
79
79
|
- - "~>"
|
|
80
80
|
- !ruby/object:Gem::Version
|
|
81
81
|
version: '2.7'
|
|
82
|
+
- !ruby/object:Gem::Dependency
|
|
83
|
+
name: rb_sys
|
|
84
|
+
requirement: !ruby/object:Gem::Requirement
|
|
85
|
+
requirements:
|
|
86
|
+
- - "~>"
|
|
87
|
+
- !ruby/object:Gem::Version
|
|
88
|
+
version: '0.9'
|
|
89
|
+
type: :runtime
|
|
90
|
+
prerelease: false
|
|
91
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
92
|
+
requirements:
|
|
93
|
+
- - "~>"
|
|
94
|
+
- !ruby/object:Gem::Version
|
|
95
|
+
version: '0.9'
|
|
82
96
|
- !ruby/object:Gem::Dependency
|
|
83
97
|
name: minitest
|
|
84
98
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -108,19 +122,19 @@ dependencies:
|
|
|
108
122
|
- !ruby/object:Gem::Version
|
|
109
123
|
version: '13.0'
|
|
110
124
|
- !ruby/object:Gem::Dependency
|
|
111
|
-
name:
|
|
125
|
+
name: rake-compiler
|
|
112
126
|
requirement: !ruby/object:Gem::Requirement
|
|
113
127
|
requirements:
|
|
114
128
|
- - "~>"
|
|
115
129
|
- !ruby/object:Gem::Version
|
|
116
|
-
version: '
|
|
130
|
+
version: '1.3'
|
|
117
131
|
type: :development
|
|
118
132
|
prerelease: false
|
|
119
133
|
version_requirements: !ruby/object:Gem::Requirement
|
|
120
134
|
requirements:
|
|
121
135
|
- - "~>"
|
|
122
136
|
- !ruby/object:Gem::Version
|
|
123
|
-
version: '
|
|
137
|
+
version: '1.3'
|
|
124
138
|
description: |
|
|
125
139
|
BreakerMachines is a production-ready circuit breaker implementation for Ruby that prevents
|
|
126
140
|
cascade failures in distributed systems. Built on the battle-tested state_machines gem, it
|
|
@@ -149,6 +163,7 @@ files:
|
|
|
149
163
|
- ext/breaker_machines_native/core/src/storage.rs
|
|
150
164
|
- ext/breaker_machines_native/extconf.rb
|
|
151
165
|
- ext/breaker_machines_native/ffi/Cargo.toml
|
|
166
|
+
- ext/breaker_machines_native/ffi/extconf.rb
|
|
152
167
|
- ext/breaker_machines_native/ffi/src/lib.rs
|
|
153
168
|
- ext/breaker_machines_native/target/debug/build/clang-sys-d961dfabd5f43fba/out/common.rs
|
|
154
169
|
- ext/breaker_machines_native/target/debug/build/clang-sys-d961dfabd5f43fba/out/dynamic.rs
|
|
@@ -235,7 +250,6 @@ files:
|
|
|
235
250
|
- lib/breaker_machines/storage/null.rb
|
|
236
251
|
- lib/breaker_machines/types.rb
|
|
237
252
|
- lib/breaker_machines/version.rb
|
|
238
|
-
- lib/breaker_machines_native/breaker_machines_native.bundle
|
|
239
253
|
- sig/README.md
|
|
240
254
|
- sig/all.rbs
|
|
241
255
|
- sig/breaker_machines.rbs
|
|
@@ -258,6 +272,8 @@ metadata:
|
|
|
258
272
|
bug_tracker_uri: https://github.com/seuros/breaker_machines/issues
|
|
259
273
|
documentation_uri: https://github.com/seuros/breaker_machines#readme
|
|
260
274
|
rubygems_mfa_required: 'true'
|
|
275
|
+
cargo_crate_name: breaker_machines_native
|
|
276
|
+
cargo_manifest_path: ext/breaker_machines_native/ffi/Cargo.toml
|
|
261
277
|
rdoc_options: []
|
|
262
278
|
require_paths:
|
|
263
279
|
- lib
|
|
@@ -265,7 +281,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
265
281
|
requirements:
|
|
266
282
|
- - ">="
|
|
267
283
|
- !ruby/object:Gem::Version
|
|
268
|
-
version: 3.
|
|
284
|
+
version: 3.3.0
|
|
269
285
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
270
286
|
requirements:
|
|
271
287
|
- - ">="
|
|
Binary file
|