breaker_machines 0.1.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.
@@ -0,0 +1,154 @@
1
+ module BreakerMachines
2
+ class Circuit
3
+ include StateManagement
4
+ include Configuration
5
+ include Execution
6
+ include Introspection
7
+ include Callbacks
8
+
9
+ # Instance variables from Configuration module
10
+ @name: String
11
+ @config: Hash[Symbol, untyped]
12
+ @storage: _StorageBackend?
13
+ @metrics: _MetricsRecorder?
14
+ @opened_at: Concurrent::AtomicReference[Float?]
15
+ @half_open_attempts: Concurrent::AtomicFixnum
16
+ @half_open_successes: Concurrent::AtomicFixnum
17
+ @mutex: Concurrent::ReentrantReadWriteLock
18
+ @last_failure_at: Concurrent::AtomicReference[Float?]
19
+ @last_error: Concurrent::AtomicReference[StandardError?]
20
+
21
+ # State machine status (from StateManagement)
22
+ attr_reader status: (:open | :closed | :half_open)
23
+ end
24
+
25
+ module StateManagement
26
+ interface _StateManagementState
27
+ def status: () -> (:open | :closed | :half_open)
28
+ def status=: (:open | :closed | :half_open value) -> void
29
+ def status_name: () -> (:open | :closed | :half_open)
30
+ def open?: () -> bool
31
+ def closed?: () -> bool
32
+ def half_open?: () -> bool
33
+ def trip: () -> bool
34
+ def attempt_recovery: () -> bool
35
+ def reset: () -> bool
36
+ def force_open: () -> bool
37
+ def force_close: () -> bool
38
+ end
39
+
40
+ def self.included: (singleton(Circuit) base) -> void
41
+
42
+ private
43
+
44
+ def on_circuit_open: () -> void
45
+ def on_circuit_close: () -> void
46
+ def on_circuit_half_open: () -> void
47
+ def restore_status_from_storage: () -> void
48
+ def reset_timeout_elapsed?: () -> bool
49
+ def monotonic_time: () -> Float
50
+ end
51
+
52
+ module Configuration
53
+ extend ActiveSupport::Concern
54
+
55
+ interface _ConfigurationMethods
56
+ def name: () -> String
57
+ def config: () -> Hash[Symbol, untyped]
58
+ def opened_at: () -> Concurrent::AtomicReference[Float?]
59
+ end
60
+
61
+ type storage_type = Storage::Base | Storage::Memory | Storage::BucketMemory | Storage::Null
62
+ type metrics_type = untyped
63
+ type callback_type = Proc | nil
64
+ type fallback_type = Proc | Array[Proc | untyped] | untyped
65
+
66
+ type circuit_config = {
67
+ failure_threshold: Integer,
68
+ failure_window: Integer,
69
+ success_threshold: Integer,
70
+ timeout: Integer?,
71
+ reset_timeout: Integer,
72
+ reset_timeout_jitter: Float,
73
+ half_open_calls: Integer,
74
+ storage: storage_type?,
75
+ metrics: metrics_type?,
76
+ fallback: fallback_type?,
77
+ on_open: callback_type,
78
+ on_close: callback_type,
79
+ on_half_open: callback_type,
80
+ on_reject: callback_type,
81
+ exceptions: Array[Class],
82
+ owner: untyped
83
+ }
84
+
85
+ def initialize: (String name, ?circuit_config options) -> void
86
+
87
+ private
88
+
89
+ def default_config: () -> circuit_config
90
+ def create_default_storage: () -> storage_type
91
+ end
92
+
93
+ module Execution
94
+ def call: [T] () { () -> T } -> untyped
95
+ def wrap: [T] () { () -> T } -> untyped
96
+
97
+ private
98
+
99
+ def handle_open_status: () { () -> untyped } -> untyped
100
+ def handle_half_open_status: () { () -> untyped } -> untyped
101
+ def handle_closed_status: () { () -> untyped } -> untyped
102
+ def execute_call: () { () -> untyped } -> untyped
103
+ def reject_call: () -> untyped
104
+ def handle_success: () -> void
105
+ def handle_failure: () -> void
106
+ def failure_threshold_exceeded?: () -> bool
107
+ def success_threshold_reached?: () -> bool
108
+ def record_success: (Float duration) -> void
109
+ def record_failure: (Float duration, ?StandardError? error) -> void
110
+ end
111
+
112
+ module Introspection
113
+ type stats_hash = {
114
+ state: (:open | :closed | :half_open),
115
+ failure_count: Integer,
116
+ success_count: Integer,
117
+ last_failure_at: Float?,
118
+ opened_at: Float?,
119
+ half_open_attempts: Integer,
120
+ half_open_successes: Integer
121
+ }
122
+
123
+ type error_info = {
124
+ class: String,
125
+ message: String,
126
+ occurred_at: Float
127
+ }
128
+
129
+ type circuit_hash = {
130
+ name: String,
131
+ state: (:open | :closed | :half_open),
132
+ stats: stats_hash,
133
+ config: Hash[Symbol, untyped],
134
+ event_log: Array[untyped],
135
+ last_error: error_info?
136
+ }
137
+
138
+ def stats: () -> stats_hash
139
+ def configuration: () -> Hash[Symbol, untyped]
140
+ def event_log: (?limit: Integer) -> Array[untyped]?
141
+ def last_error: () -> StandardError?
142
+ def to_h: () -> circuit_hash
143
+ def summary: () -> String
144
+ def last_error_info: () -> error_info?
145
+ end
146
+
147
+ module Callbacks
148
+ private
149
+
150
+ def invoke_callback: (Symbol callback_name) -> void
151
+ def invoke_fallback: (StandardError error) -> untyped
152
+ def invoke_single_fallback: (Proc | untyped fallback, StandardError error) -> untyped
153
+ end
154
+ end
@@ -0,0 +1,32 @@
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
@@ -0,0 +1,50 @@
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
@@ -0,0 +1,24 @@
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
@@ -0,0 +1,46 @@
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
@@ -0,0 +1,30 @@
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
@@ -0,0 +1,65 @@
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
@@ -0,0 +1,97 @@
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
@@ -0,0 +1,30 @@
1
+ module BreakerMachines
2
+ VERSION: String
3
+
4
+ # Global configuration
5
+ extend ActiveSupport::Configurable
6
+
7
+ # Configuration accessors
8
+ self.@config: untyped
9
+ def self.config: () -> ActiveSupport::Configurable::Configuration
10
+ def self.default_storage: () -> (:memory | :bucket_memory | :null | untyped)
11
+ def self.default_storage=: (:memory | :bucket_memory | :null | untyped value) -> void
12
+ def self.default_timeout: () -> Integer?
13
+ def self.default_timeout=: (Integer? value) -> void
14
+ def self.default_reset_timeout: () -> Integer
15
+ def self.default_reset_timeout=: (Integer value) -> void
16
+ def self.default_failure_threshold: () -> Integer
17
+ def self.default_failure_threshold=: (Integer value) -> void
18
+ def self.log_events: () -> bool
19
+ def self.log_events=: (bool value) -> void
20
+
21
+ # Class methods
22
+ def self.configure: () { (untyped config) -> void } -> void
23
+ def self.setup_notifications: () -> void
24
+ def self.logger: () -> ActiveSupport::Logger?
25
+ def self.logger=: (ActiveSupport::Logger? logger) -> void
26
+ def self.instrument: (String event, ?Hash[Symbol, untyped] payload) -> void
27
+ def self.console: () -> void
28
+ def self.registry: () -> Registry
29
+ def self.loader: () -> Zeitwerk::Loader
30
+ end
data/sig/manifest.yaml ADDED
@@ -0,0 +1,5 @@
1
+ dependencies:
2
+ - name: activesupport
3
+ - name: concurrent-ruby
4
+ - name: state_machines
5
+ - name: zeitwerk