stoplight 5.6.0 → 5.8.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/README.md +1 -1
- data/UPGRADING.md +303 -0
- data/lib/generators/stoplight/install/install_generator.rb +6 -1
- data/lib/stoplight/admin/dependencies.rb +1 -1
- data/lib/stoplight/admin/helpers.rb +26 -5
- data/lib/stoplight/admin/lights_repository/light.rb +22 -6
- data/lib/stoplight/admin/lights_repository.rb +20 -16
- data/lib/stoplight/admin/views/_card.erb +8 -5
- data/lib/stoplight/admin.rb +2 -1
- data/lib/stoplight/color.rb +9 -0
- data/lib/stoplight/common/deprecations.rb +11 -0
- data/lib/stoplight/data_store.rb +28 -0
- data/lib/stoplight/domain/compatibility_result.rb +7 -7
- data/lib/stoplight/domain/config.rb +38 -35
- data/lib/stoplight/domain/error_tracking_policy.rb +27 -0
- data/lib/stoplight/domain/failure.rb +1 -1
- data/lib/stoplight/domain/light/configuration_builder_interface.rb +122 -16
- data/lib/stoplight/domain/light.rb +44 -64
- data/lib/stoplight/domain/light_info.rb +7 -0
- data/lib/stoplight/domain/metrics_snapshot.rb +58 -0
- data/lib/stoplight/domain/state_snapshot.rb +29 -23
- data/lib/stoplight/domain/storage/recovery_lock_token.rb +15 -0
- data/lib/stoplight/domain/strategies/green_run_strategy.rb +18 -26
- data/lib/stoplight/domain/strategies/red_run_strategy.rb +9 -12
- data/lib/stoplight/domain/strategies/yellow_run_strategy.rb +74 -58
- data/lib/stoplight/domain/tracker/recovery_probe.rb +27 -43
- data/lib/stoplight/domain/tracker/request.rb +24 -39
- data/lib/stoplight/domain/traffic_control/consecutive_errors.rb +8 -11
- data/lib/stoplight/domain/traffic_control/error_rate.rb +19 -15
- data/lib/stoplight/domain/traffic_recovery/consecutive_successes.rb +8 -18
- data/lib/stoplight/domain/traffic_recovery.rb +3 -5
- data/lib/stoplight/error.rb +46 -0
- data/lib/stoplight/infrastructure/fail_safe/data_store.rb +152 -0
- data/lib/stoplight/infrastructure/fail_safe/storage/metrics.rb +65 -0
- data/lib/stoplight/infrastructure/fail_safe/storage/recovery_lock.rb +69 -0
- data/lib/stoplight/infrastructure/fail_safe/storage/recovery_lock_token.rb +19 -0
- data/lib/stoplight/infrastructure/fail_safe/storage/state.rb +62 -0
- data/lib/stoplight/infrastructure/{data_store/memory → memory/data_store}/metrics.rb +2 -2
- data/lib/stoplight/infrastructure/memory/data_store/recovery_lock_store.rb +52 -0
- data/lib/stoplight/infrastructure/memory/data_store/recovery_lock_token.rb +17 -0
- data/lib/stoplight/infrastructure/{data_store/memory → memory/data_store}/sliding_window.rb +21 -26
- data/lib/stoplight/infrastructure/{data_store/memory → memory/data_store}/state.rb +3 -3
- data/lib/stoplight/infrastructure/{data_store/memory.rb → memory/data_store.rb} +90 -57
- data/lib/stoplight/infrastructure/memory/storage/recovery_lock.rb +35 -0
- data/lib/stoplight/infrastructure/memory/storage/recovery_metrics.rb +16 -0
- data/lib/stoplight/infrastructure/memory/storage/state.rb +155 -0
- data/lib/stoplight/infrastructure/memory/storage/unbounded_metrics.rb +103 -0
- data/lib/stoplight/infrastructure/memory/storage/window_metrics.rb +101 -0
- data/lib/stoplight/infrastructure/notifier/fail_safe.rb +50 -0
- data/lib/stoplight/infrastructure/notifier/generic.rb +4 -14
- data/lib/stoplight/infrastructure/notifier/io.rb +1 -2
- data/lib/stoplight/infrastructure/notifier/logger.rb +1 -2
- data/lib/stoplight/infrastructure/redis/data_store/lua_scripts/record_recovery_probe_failure.lua +27 -0
- data/lib/stoplight/infrastructure/redis/data_store/lua_scripts/record_recovery_probe_success.lua +23 -0
- data/lib/stoplight/infrastructure/redis/data_store/lua_scripts/release_lock.lua +6 -0
- data/lib/stoplight/infrastructure/redis/data_store/recovery_lock_store.rb +60 -0
- data/lib/stoplight/infrastructure/redis/data_store/recovery_lock_token.rb +28 -0
- data/lib/stoplight/infrastructure/redis/data_store/scripting.rb +73 -0
- data/lib/stoplight/infrastructure/{data_store/redis.rb → redis/data_store.rb} +173 -210
- data/lib/stoplight/infrastructure/redis/storage/key_space.rb +51 -0
- data/lib/stoplight/infrastructure/redis/storage/metrics.rb +40 -0
- data/lib/stoplight/infrastructure/redis/storage/recovery_lock/release_lock.lua +6 -0
- data/lib/stoplight/infrastructure/redis/storage/recovery_lock.rb +64 -0
- data/lib/stoplight/infrastructure/redis/storage/recovery_metrics.rb +20 -0
- data/lib/stoplight/infrastructure/redis/storage/scripting.rb +18 -0
- data/lib/stoplight/infrastructure/redis/storage/state/transition_to_green.lua +10 -0
- data/lib/stoplight/infrastructure/redis/storage/state/transition_to_red.lua +10 -0
- data/lib/stoplight/infrastructure/redis/storage/state/transition_to_yellow.lua +9 -0
- data/lib/stoplight/infrastructure/redis/storage/state.rb +141 -0
- data/lib/stoplight/infrastructure/redis/storage/unbounded_metrics/record_failure.lua +28 -0
- data/lib/stoplight/infrastructure/redis/storage/unbounded_metrics/record_success.lua +26 -0
- data/lib/stoplight/infrastructure/redis/storage/unbounded_metrics.rb +123 -0
- data/lib/stoplight/infrastructure/redis/storage/window_metrics/metrics_snapshot.lua +26 -0
- data/lib/stoplight/infrastructure/redis/storage/window_metrics/record_failure.lua +36 -0
- data/lib/stoplight/infrastructure/redis/storage/window_metrics/record_success.lua +35 -0
- data/lib/stoplight/infrastructure/redis/storage/window_metrics.rb +174 -0
- data/lib/stoplight/infrastructure/storage/compatibility_metrics.rb +41 -0
- data/lib/stoplight/infrastructure/storage/compatibility_recovery_lock.rb +33 -0
- data/lib/stoplight/infrastructure/storage/compatibility_recovery_metrics.rb +47 -0
- data/lib/stoplight/infrastructure/storage/compatibility_state.rb +44 -0
- data/lib/stoplight/infrastructure/system_clock.rb +16 -0
- data/lib/stoplight/notifier.rb +11 -0
- data/lib/stoplight/state.rb +9 -0
- data/lib/stoplight/types.rb +29 -0
- data/lib/stoplight/undefined.rb +16 -0
- data/lib/stoplight/version.rb +1 -1
- data/lib/stoplight/wiring/config_compatibility_validator.rb +54 -0
- data/lib/stoplight/wiring/configuration_dsl.rb +101 -0
- data/lib/stoplight/wiring/data_store_backend.rb +26 -0
- data/lib/stoplight/wiring/default.rb +2 -2
- data/lib/stoplight/wiring/default_config.rb +21 -0
- data/lib/stoplight/wiring/default_configuration.rb +70 -53
- data/lib/stoplight/wiring/light_builder.rb +198 -0
- data/lib/stoplight/wiring/light_factory/traffic_control_dsl.rb +26 -0
- data/lib/stoplight/wiring/light_factory/traffic_recovery_dsl.rb +21 -0
- data/lib/stoplight/wiring/light_factory.rb +74 -135
- data/lib/stoplight/wiring/memory/backend.rb +57 -0
- data/lib/stoplight/wiring/notifier_factory.rb +26 -0
- data/lib/stoplight/wiring/redis/backend.rb +116 -0
- data/lib/stoplight/wiring/storage_set.rb +12 -0
- data/lib/stoplight/wiring/storage_set_builder.rb +51 -0
- data/lib/stoplight/wiring/system/light_builder.rb +47 -0
- data/lib/stoplight/wiring/system/light_factory.rb +64 -0
- data/lib/stoplight/wiring/system.rb +129 -0
- data/lib/stoplight.rb +209 -23
- data/sig/_private/generators/stoplight/install/install_generator.rbs +22 -0
- data/sig/_private/stoplight/common/deprecations.rbs +9 -0
- data/sig/_private/stoplight/data_store.rbs +6 -0
- data/sig/_private/stoplight/domain/compatibility_result.rbs +18 -0
- data/sig/_private/stoplight/domain/config.rbs +65 -0
- data/sig/_private/stoplight/domain/error_tracking_policy.rbs +14 -0
- data/sig/_private/stoplight/domain/failure.rbs +16 -0
- data/sig/_private/stoplight/domain/light.rbs +25 -0
- data/sig/_private/stoplight/domain/light_info.rbs +19 -0
- data/sig/_private/stoplight/domain/metrics_snapshot.rbs +38 -0
- data/sig/_private/stoplight/domain/ports/clock.rbs +18 -0
- data/sig/_private/stoplight/domain/ports/data_store.rbs +76 -0
- data/{lib/stoplight/domain/light_factory.rb → sig/_private/stoplight/domain/ports/light_factory.rbs} +33 -28
- data/sig/_private/stoplight/domain/ports/metrics_store.rbs +29 -0
- data/sig/_private/stoplight/domain/ports/recovery_lock_store.rbs +52 -0
- data/sig/_private/stoplight/domain/ports/recovery_lock_token.rbs +6 -0
- data/sig/_private/stoplight/domain/ports/run_strategy.rbs +14 -0
- data/sig/_private/stoplight/domain/ports/state_store.rbs +79 -0
- data/sig/_private/stoplight/domain/ports/traffic_control.rbs +41 -0
- data/sig/_private/stoplight/domain/ports/traffic_recovery.rbs +47 -0
- data/sig/_private/stoplight/domain/state_snapshot.rbs +32 -0
- data/sig/_private/stoplight/domain/storage/recovery_lock_token.rbs +11 -0
- data/sig/_private/stoplight/domain/strategies/green_run_strategy.rbs +17 -0
- data/sig/_private/stoplight/domain/strategies/red_run_strategy.rbs +17 -0
- data/sig/_private/stoplight/domain/strategies/yellow_run_strategy.rbs +42 -0
- data/sig/_private/stoplight/domain/tracker/base.rbs +8 -0
- data/sig/_private/stoplight/domain/tracker/recovery_probe.rbs +25 -0
- data/sig/_private/stoplight/domain/tracker/request.rbs +26 -0
- data/sig/_private/stoplight/domain/traffic_control/consecutive_errors.rbs +9 -0
- data/sig/_private/stoplight/domain/traffic_control/error_rate.rbs +13 -0
- data/sig/_private/stoplight/domain/traffic_recovery/consecutive_successes.rbs +9 -0
- data/sig/_private/stoplight/domain/traffic_recovery.rbs +9 -0
- data/sig/_private/stoplight/infrastructure/fail_safe/data_store.rbs +26 -0
- data/sig/_private/stoplight/infrastructure/fail_safe/storage/metrics.rbs +25 -0
- data/sig/_private/stoplight/infrastructure/fail_safe/storage/recovery_lock.rbs +29 -0
- data/sig/_private/stoplight/infrastructure/fail_safe/storage/recovery_lock_token.rbs +19 -0
- data/sig/_private/stoplight/infrastructure/fail_safe/storage/state.rbs +25 -0
- data/sig/_private/stoplight/infrastructure/memory/data_store/metrics.rbs +25 -0
- data/sig/_private/stoplight/infrastructure/memory/data_store/recovery_lock_store.rbs +19 -0
- data/sig/_private/stoplight/infrastructure/memory/data_store/recovery_lock_token.rbs +17 -0
- data/sig/_private/stoplight/infrastructure/memory/data_store/sliding_window.rbs +27 -0
- data/sig/_private/stoplight/infrastructure/memory/data_store/state.rbs +17 -0
- data/sig/_private/stoplight/infrastructure/memory/data_store.rbs +30 -0
- data/sig/_private/stoplight/infrastructure/memory/storage/recovery_lock.rbs +15 -0
- data/sig/_private/stoplight/infrastructure/memory/storage/recovery_metrics.rbs +10 -0
- data/sig/_private/stoplight/infrastructure/memory/storage/state.rbs +28 -0
- data/sig/_private/stoplight/infrastructure/memory/storage/unbounded_metrics.rbs +25 -0
- data/sig/_private/stoplight/infrastructure/memory/storage/window_metrics.rbs +26 -0
- data/sig/_private/stoplight/infrastructure/notifier/fail_safe.rbs +17 -0
- data/sig/_private/stoplight/infrastructure/notifier/generic.rbs +18 -0
- data/sig/_private/stoplight/infrastructure/notifier/io.rbs +14 -0
- data/sig/_private/stoplight/infrastructure/notifier/logger.rbs +14 -0
- data/sig/_private/stoplight/infrastructure/redis/data_store/recovery_lock_store.rbs +24 -0
- data/sig/_private/stoplight/infrastructure/redis/data_store/recovery_lock_token.rbs +21 -0
- data/sig/_private/stoplight/infrastructure/redis/data_store/scripting.rbs +34 -0
- data/sig/_private/stoplight/infrastructure/redis/data_store.rbs +67 -0
- data/sig/_private/stoplight/infrastructure/redis/storage/key_space.rbs +19 -0
- data/sig/_private/stoplight/infrastructure/redis/storage/metrics.rbs +17 -0
- data/sig/_private/stoplight/infrastructure/redis/storage/recovery_lock.rbs +26 -0
- data/sig/_private/stoplight/infrastructure/redis/storage/recovery_metrics.rbs +10 -0
- data/sig/_private/stoplight/infrastructure/redis/storage/scripting.rbs +13 -0
- data/sig/_private/stoplight/infrastructure/redis/storage/state.rbs +32 -0
- data/sig/_private/stoplight/infrastructure/redis/storage/unbounded_metrics.rbs +21 -0
- data/sig/_private/stoplight/infrastructure/redis/storage/window_metrics.rbs +34 -0
- data/sig/_private/stoplight/infrastructure/storage/compatibility_metrics.rbs +17 -0
- data/sig/_private/stoplight/infrastructure/storage/compatibility_recovery_lock.rbs +13 -0
- data/sig/_private/stoplight/infrastructure/storage/compatibility_recovery_metrics.rbs +14 -0
- data/sig/_private/stoplight/infrastructure/storage/compatibility_state.rbs +14 -0
- data/sig/_private/stoplight/infrastructure/system_clock.rbs +7 -0
- data/sig/_private/stoplight/system/light_builder.rbs +23 -0
- data/sig/_private/stoplight/system/light_factory.rbs +17 -0
- data/sig/_private/stoplight/types.rbs +6 -0
- data/sig/_private/stoplight/wiring/config_compatibility_validator.rbs +19 -0
- data/sig/_private/stoplight/wiring/configuration_dsl.rbs +43 -0
- data/sig/_private/stoplight/wiring/data_store_backend.rbs +11 -0
- data/sig/_private/stoplight/wiring/default.rbs +26 -0
- data/sig/_private/stoplight/wiring/default_config.rbs +7 -0
- data/sig/_private/stoplight/wiring/default_configuration.rbs +29 -0
- data/sig/_private/stoplight/wiring/light_builder.rbs +48 -0
- data/sig/_private/stoplight/wiring/light_factory/traffic_control_dsl.rbs +7 -0
- data/sig/_private/stoplight/wiring/light_factory/traffic_recovery_dsl.rbs +7 -0
- data/sig/_private/stoplight/wiring/light_factory.rbs +16 -0
- data/sig/_private/stoplight/wiring/memory/backend.rbs +26 -0
- data/sig/_private/stoplight/wiring/notifier_factory.rbs +10 -0
- data/sig/_private/stoplight/wiring/redis/backend.rbs +38 -0
- data/sig/_private/stoplight/wiring/storage_set.rbs +38 -0
- data/sig/_private/stoplight/wiring/storage_set_builder.rbs +15 -0
- data/sig/_private/stoplight/wiring/system.rbs +15 -0
- data/sig/_private/stoplight.rbs +48 -0
- data/sig/stoplight/color.rbs +7 -0
- data/sig/stoplight/data_store.rbs +19 -0
- data/sig/stoplight/error.rbs +20 -0
- data/sig/stoplight/notifier.rbs +11 -0
- data/sig/stoplight/ports/configuration.rbs +19 -0
- data/sig/stoplight/ports/exception_matcher.rbs +8 -0
- data/sig/stoplight/ports/light.rbs +12 -0
- data/sig/stoplight/ports/light_info.rbs +5 -0
- data/sig/stoplight/ports/state_transition_notifier.rbs +15 -0
- data/sig/stoplight/ports/system.rbs +21 -0
- data/sig/stoplight/state.rbs +7 -0
- data/sig/stoplight/undefined.rbs +9 -0
- data/sig/stoplight/version.rbs +3 -0
- data/sig/stoplight.rbs +66 -0
- metadata +199 -36
- data/lib/stoplight/domain/color.rb +0 -11
- data/lib/stoplight/domain/data_store.rb +0 -130
- data/lib/stoplight/domain/error.rb +0 -42
- data/lib/stoplight/domain/metrics.rb +0 -85
- data/lib/stoplight/domain/state.rb +0 -11
- data/lib/stoplight/domain/state_transition_notifier.rb +0 -25
- data/lib/stoplight/domain/strategies/run_strategy.rb +0 -27
- data/lib/stoplight/domain/tracker/base.rb +0 -41
- data/lib/stoplight/domain/traffic_control/base.rb +0 -74
- data/lib/stoplight/domain/traffic_recovery/base.rb +0 -80
- data/lib/stoplight/infrastructure/data_store/redis/lua.rb +0 -25
- data/lib/stoplight/infrastructure/dependency_injection/container.rb +0 -249
- data/lib/stoplight/infrastructure/dependency_injection/unresolved_dependency_error.rb +0 -13
- data/lib/stoplight/wiring/container.rb +0 -80
- data/lib/stoplight/wiring/default_factory_builder.rb +0 -25
- data/lib/stoplight/wiring/fail_safe_data_store.rb +0 -147
- data/lib/stoplight/wiring/fail_safe_notifier.rb +0 -79
- data/lib/stoplight/wiring/light/default_config.rb +0 -18
- data/lib/stoplight/wiring/light/system_config.rb +0 -11
- data/lib/stoplight/wiring/public_api.rb +0 -28
- data/lib/stoplight/wiring/system_container.rb +0 -9
- data/lib/stoplight/wiring/system_light_factory.rb +0 -17
- /data/lib/stoplight/infrastructure/{data_store/redis → redis/data_store/lua_scripts}/get_metrics.lua +0 -0
- /data/lib/stoplight/infrastructure/{data_store/redis → redis/data_store/lua_scripts}/record_failure.lua +0 -0
- /data/lib/stoplight/infrastructure/{data_store/redis → redis/data_store/lua_scripts}/record_success.lua +0 -0
- /data/lib/stoplight/infrastructure/{data_store/redis → redis/data_store/lua_scripts}/transition_to_green.lua +0 -0
- /data/lib/stoplight/infrastructure/{data_store/redis → redis/data_store/lua_scripts}/transition_to_red.lua +0 -0
- /data/lib/stoplight/infrastructure/{data_store/redis → redis/data_store/lua_scripts}/transition_to_yellow.lua +0 -0
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Stoplight
|
|
4
|
+
module Infrastructure
|
|
5
|
+
module Redis
|
|
6
|
+
module Storage
|
|
7
|
+
# Distributed storage for time-windowed light metrics using Redis.
|
|
8
|
+
#
|
|
9
|
+
# This class implements sliding window metrics using Redis sorted sets (ZSETs)
|
|
10
|
+
# for efficient time-range queries. Events are bucketed by hour to bound memory
|
|
11
|
+
# usage and enable automatic expiration via Redis TTLs.
|
|
12
|
+
#
|
|
13
|
+
# == Storage Structure
|
|
14
|
+
#
|
|
15
|
+
# Events are stored in hourly buckets as ZSETs:
|
|
16
|
+
# stoplight:{version}:{system}:{light}:window_metrics:success:1696154400
|
|
17
|
+
# stoplight:{version}:{system}:{light}:window_metrics:failure:1696154400
|
|
18
|
+
#
|
|
19
|
+
# Each ZSET member is a unique event ID with its timestamp as the score,
|
|
20
|
+
# enabling O(log N) range queries via ZCOUNT.
|
|
21
|
+
#
|
|
22
|
+
# Metadata (consecutive counters, last error) is stored in a hash:
|
|
23
|
+
# stoplight:{version}:{system}:{light}:window_metrics
|
|
24
|
+
#
|
|
25
|
+
# == Bucket Strategy
|
|
26
|
+
#
|
|
27
|
+
# Fixed 1-hour buckets provide a balance between:
|
|
28
|
+
# - Query efficiency: At most ~25 buckets for a 24-hour window
|
|
29
|
+
# - Memory efficiency: Natural expiration without manual cleanup
|
|
30
|
+
# - Precision: Sub-bucket accuracy via ZSET scores
|
|
31
|
+
#
|
|
32
|
+
# == Atomicity
|
|
33
|
+
#
|
|
34
|
+
# All operations use Lua scripts to ensure atomicity:
|
|
35
|
+
# - record_success: Increments counter and updates metadata in one round-trip
|
|
36
|
+
# - record_failure: Same, plus stores serialized error details
|
|
37
|
+
# - metrics_snapshot: Aggregates across buckets atomically
|
|
38
|
+
#
|
|
39
|
+
class WindowMetrics < Metrics
|
|
40
|
+
def initialize(redis:, scripting:, config:, clock:, key_space:)
|
|
41
|
+
@clock = clock
|
|
42
|
+
@scripting = scripting
|
|
43
|
+
@redis = redis
|
|
44
|
+
@config = config
|
|
45
|
+
@key_space = key_space
|
|
46
|
+
@metrics_key = key_space.key(:window_metrics)
|
|
47
|
+
@window_size = T.must(config.window_size).to_i
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# Get metrics for the current light
|
|
51
|
+
#
|
|
52
|
+
# @return [Stoplight::Domain::Metrics]
|
|
53
|
+
def metrics_snapshot
|
|
54
|
+
window_end_ts = clock.current_time.to_f
|
|
55
|
+
window_start_ts = window_end_ts - @window_size
|
|
56
|
+
failure_keys = failure_bucket_keys(window_end_ts)
|
|
57
|
+
success_keys = success_bucket_keys(window_end_ts)
|
|
58
|
+
|
|
59
|
+
successes, errors, last_success_at, last_error_json, consecutive_errors, consecutive_successes = scripting.call(
|
|
60
|
+
:"window_metrics/metrics_snapshot",
|
|
61
|
+
args: [
|
|
62
|
+
failure_keys.count,
|
|
63
|
+
window_start_ts,
|
|
64
|
+
window_end_ts,
|
|
65
|
+
"last_success_at", "last_error_json", "consecutive_errors", "consecutive_successes"
|
|
66
|
+
],
|
|
67
|
+
keys: [
|
|
68
|
+
metrics_key,
|
|
69
|
+
*success_keys,
|
|
70
|
+
*failure_keys
|
|
71
|
+
]
|
|
72
|
+
)
|
|
73
|
+
Domain::MetricsSnapshot.new(
|
|
74
|
+
successes:, errors:,
|
|
75
|
+
consecutive_errors: [consecutive_errors.to_i, errors].min,
|
|
76
|
+
consecutive_successes: [consecutive_successes.to_i, successes].min,
|
|
77
|
+
last_error: deserialize_failure(last_error_json),
|
|
78
|
+
last_success_at: (clock.at(last_success_at.to_f) if last_success_at)
|
|
79
|
+
)
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
# Records successful circuit breaker execution
|
|
83
|
+
#
|
|
84
|
+
# @return [void]
|
|
85
|
+
def record_success
|
|
86
|
+
timestamp = clock.current_time.to_f
|
|
87
|
+
|
|
88
|
+
scripting.call(
|
|
89
|
+
:"window_metrics/record_success",
|
|
90
|
+
args: [timestamp, SecureRandom.hex(12), bucket_ttl, metrics_ttl],
|
|
91
|
+
keys: [
|
|
92
|
+
metrics_key,
|
|
93
|
+
successes_key(time: timestamp)
|
|
94
|
+
]
|
|
95
|
+
)
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
# Records failed circuit breaker execution
|
|
99
|
+
#
|
|
100
|
+
# @param exception [StandardError]
|
|
101
|
+
# @return [void]
|
|
102
|
+
def record_failure(exception)
|
|
103
|
+
timestamp = clock.current_time.to_f
|
|
104
|
+
|
|
105
|
+
scripting.call(
|
|
106
|
+
:"window_metrics/record_failure",
|
|
107
|
+
args: [timestamp, SecureRandom.hex(12), serialize_exception(exception, timestamp:), bucket_ttl, metrics_ttl],
|
|
108
|
+
keys: [metrics_key, errors_key(time: timestamp)]
|
|
109
|
+
)
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
def clear
|
|
113
|
+
window_end_ts = clock.current_time.to_f
|
|
114
|
+
failure_keys = failure_bucket_keys(window_end_ts)
|
|
115
|
+
success_keys = success_bucket_keys(window_end_ts)
|
|
116
|
+
|
|
117
|
+
redis.with do |client|
|
|
118
|
+
client.del(metrics_key, *failure_keys, *success_keys)
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
# Generates a Redis key for a specific metric and time.
|
|
123
|
+
#
|
|
124
|
+
# @param metric [Symbol] The metric type (e.g., "errors").
|
|
125
|
+
# @param time [Time, Numeric] The time for which to generate the key.
|
|
126
|
+
# @return [String] The generated Redis key.
|
|
127
|
+
def bucket_key(metric:, time:)
|
|
128
|
+
key_space.key(:window_metrics, metric, (time.to_i / bucket_size) * bucket_size)
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
# Retrieves the list of Redis bucket keys required to cover a specific time window.
|
|
132
|
+
#
|
|
133
|
+
# @param metric The metric type (e.g., "errors").
|
|
134
|
+
# @param window_end The end time of the window (can be a Time object or a numeric timestamp).
|
|
135
|
+
# @return A list of Redis keys for the buckets that cover the time window.
|
|
136
|
+
# @api private
|
|
137
|
+
def buckets_for_window(metric:, window_end:)
|
|
138
|
+
window_end_ts = window_end.to_i
|
|
139
|
+
window_start_ts = window_end_ts - @window_size
|
|
140
|
+
|
|
141
|
+
# Find bucket timestamps that contain any part of the window
|
|
142
|
+
start_bucket = (window_start_ts / bucket_size) * bucket_size
|
|
143
|
+
|
|
144
|
+
# End bucket is the last bucket that contains data within our window
|
|
145
|
+
end_bucket = ((window_end_ts - 1) / bucket_size) * bucket_size
|
|
146
|
+
|
|
147
|
+
(start_bucket..end_bucket).step(bucket_size).map do |bucket_start|
|
|
148
|
+
bucket_key(metric: metric, time: bucket_start)
|
|
149
|
+
end
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
private
|
|
153
|
+
|
|
154
|
+
attr_reader :redis
|
|
155
|
+
attr_reader :scripting
|
|
156
|
+
attr_reader :metrics_key
|
|
157
|
+
attr_reader :clock
|
|
158
|
+
attr_reader :key_space
|
|
159
|
+
|
|
160
|
+
def bucket_size = 3600 # 1 hour
|
|
161
|
+
def bucket_ttl = @window_size + bucket_size
|
|
162
|
+
|
|
163
|
+
def successes_key(time:) = bucket_key(metric: :success, time:)
|
|
164
|
+
|
|
165
|
+
def errors_key(time:) = bucket_key(metric: :failure, time:)
|
|
166
|
+
|
|
167
|
+
def failure_bucket_keys(window_end) = buckets_for_window(metric: :failure, window_end:)
|
|
168
|
+
|
|
169
|
+
def success_bucket_keys(window_end) = buckets_for_window(metric: :success, window_end:)
|
|
170
|
+
end
|
|
171
|
+
end
|
|
172
|
+
end
|
|
173
|
+
end
|
|
174
|
+
end
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Stoplight
|
|
4
|
+
module Infrastructure
|
|
5
|
+
module Storage
|
|
6
|
+
# Temporary adapter that bridges Domain::Storage::Metrics to existing DataStore.
|
|
7
|
+
#
|
|
8
|
+
# This compatibility layer allows the metrics abstraction to be introduced
|
|
9
|
+
# without breaking existing data store implementations. It delegates all
|
|
10
|
+
# operations to the data store's original methods.
|
|
11
|
+
#
|
|
12
|
+
# This class will be removed in a future versions once all data stores
|
|
13
|
+
# have native metrics implementations.
|
|
14
|
+
#
|
|
15
|
+
# @example Creating metrics for a circuit
|
|
16
|
+
# metrics = CompatibilityMetrics.new(
|
|
17
|
+
# data_store: redis_store,
|
|
18
|
+
# config: config
|
|
19
|
+
# )
|
|
20
|
+
# metrics.record_success
|
|
21
|
+
#
|
|
22
|
+
class CompatibilityMetrics
|
|
23
|
+
attr_reader :data_store
|
|
24
|
+
attr_reader :config
|
|
25
|
+
|
|
26
|
+
def initialize(data_store:, config:)
|
|
27
|
+
@data_store = data_store
|
|
28
|
+
@config = config
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def metrics_snapshot = data_store.get_metrics(config)
|
|
32
|
+
|
|
33
|
+
def record_success = data_store.record_success(config)
|
|
34
|
+
|
|
35
|
+
def record_failure(error) = data_store.record_failure(config, error)
|
|
36
|
+
|
|
37
|
+
def clear = data_store.clear_metrics(config)
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Stoplight
|
|
4
|
+
module Infrastructure
|
|
5
|
+
module Storage
|
|
6
|
+
# Temporary adapter that bridges +Domain::Storage::RecoveryLock+ to existing DataStore.
|
|
7
|
+
#
|
|
8
|
+
# This compatibility layer allows the recovery lock abstraction to be
|
|
9
|
+
# introduced without breaking existing data store implementations. It
|
|
10
|
+
# delegates all lock operations to the data store's original methods.
|
|
11
|
+
#
|
|
12
|
+
# This adapter will be removed in a future versions once all
|
|
13
|
+
# data stores have native recovery lock implementations.
|
|
14
|
+
#
|
|
15
|
+
# @see Stoplight::Domain::_RecoveryLockStore
|
|
16
|
+
class CompatibilityRecoveryLock
|
|
17
|
+
def initialize(data_store:, config:)
|
|
18
|
+
@data_store = data_store
|
|
19
|
+
@config = config
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def acquire_lock = data_store.acquire_recovery_lock(config) #: Domain::Storage::RecoveryLockToken?
|
|
23
|
+
|
|
24
|
+
def release_lock(lock) = data_store.release_recovery_lock(lock)
|
|
25
|
+
|
|
26
|
+
private
|
|
27
|
+
|
|
28
|
+
attr_reader :data_store
|
|
29
|
+
attr_reader :config
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Stoplight
|
|
4
|
+
module Infrastructure
|
|
5
|
+
module Storage
|
|
6
|
+
# When a circuit is RED (open), Stoplight periodically sends "recovery probes"
|
|
7
|
+
# to test whether the protected service has recovered. These test requests have
|
|
8
|
+
# different semantics than normal requests and their metrics are tracked separately.
|
|
9
|
+
#
|
|
10
|
+
# Like +CompatibilityMetrics+, this adapter will be replaced with purpose-built
|
|
11
|
+
# recovery metrics implementations (e.g., +ConsecutiveSuccessMetrics+) once the
|
|
12
|
+
# metrics extraction is complete.
|
|
13
|
+
#
|
|
14
|
+
# @example Recovery probe flow
|
|
15
|
+
# # Circuit is RED, start probing
|
|
16
|
+
# recovery_metrics = CompatibilityRecoveryMetrics.new(
|
|
17
|
+
# data_store: redis_store,
|
|
18
|
+
# config: circuit_config
|
|
19
|
+
# )
|
|
20
|
+
#
|
|
21
|
+
# recovery_metrics.record_success
|
|
22
|
+
# recovery_metrics.metrics_snapshot # => 1 success, 0 failures
|
|
23
|
+
#
|
|
24
|
+
class CompatibilityRecoveryMetrics
|
|
25
|
+
def initialize(data_store:, config:)
|
|
26
|
+
@data_store = data_store
|
|
27
|
+
@config = config
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def metrics_snapshot = data_store.get_recovery_metrics(config)
|
|
31
|
+
|
|
32
|
+
# Tracks successful circuit breaker execution
|
|
33
|
+
def record_success = data_store.record_recovery_probe_success(config)
|
|
34
|
+
|
|
35
|
+
# Tracks failed circuit breaker execution
|
|
36
|
+
def record_failure(error) = data_store.record_recovery_probe_failure(config, error)
|
|
37
|
+
|
|
38
|
+
def clear = data_store.clear_recovery_metrics(config)
|
|
39
|
+
|
|
40
|
+
private
|
|
41
|
+
|
|
42
|
+
attr_reader :data_store
|
|
43
|
+
attr_reader :config
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Stoplight
|
|
4
|
+
module Infrastructure
|
|
5
|
+
module Storage
|
|
6
|
+
# Temporary adapter that bridges Domain::Storage::State to existing DataStore.
|
|
7
|
+
#
|
|
8
|
+
# This compatibility layer allows the state abstraction to be introduced
|
|
9
|
+
# without breaking existing data store implementations. It delegates all
|
|
10
|
+
# state operations to the data store's original methods.
|
|
11
|
+
#
|
|
12
|
+
# This adapter will be removed in a future versions once all
|
|
13
|
+
# data stores have native state storage implementations.
|
|
14
|
+
#
|
|
15
|
+
# @example Creating state storage for a circuit
|
|
16
|
+
# state = CompatibilityState.new(
|
|
17
|
+
# data_store: redis_store,
|
|
18
|
+
# config: circuit_config
|
|
19
|
+
# )
|
|
20
|
+
# state.set_state(State::LOCKED_RED)
|
|
21
|
+
# snapshot = state.state_snapshot
|
|
22
|
+
#
|
|
23
|
+
class CompatibilityState
|
|
24
|
+
def initialize(data_store:, config:)
|
|
25
|
+
@data_store = data_store
|
|
26
|
+
@config = config
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def state_snapshot = data_store.get_state_snapshot(config)
|
|
30
|
+
|
|
31
|
+
def set_state(state) = data_store.set_state(config, state)
|
|
32
|
+
|
|
33
|
+
def transition_to_color(color) = data_store.transition_to_color(config, color)
|
|
34
|
+
|
|
35
|
+
def clear = data_store.delete_light(config)
|
|
36
|
+
|
|
37
|
+
private
|
|
38
|
+
|
|
39
|
+
attr_reader :data_store
|
|
40
|
+
attr_reader :config
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Stoplight
|
|
4
|
+
module Infrastructure
|
|
5
|
+
# Production clock implementation using Ruby's Time class.
|
|
6
|
+
#
|
|
7
|
+
# Default clock for all Stoplight time-dependent operations including
|
|
8
|
+
# bucket calculation, window boundaries, and state transition timestamps.
|
|
9
|
+
#
|
|
10
|
+
class SystemClock
|
|
11
|
+
def current_time = Time.now.utc
|
|
12
|
+
|
|
13
|
+
def at(timestamp) = Time.at(timestamp).utc
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "singleton"
|
|
4
|
+
|
|
5
|
+
module Stoplight
|
|
6
|
+
module Types
|
|
7
|
+
def self.undefined = Undefined.instance
|
|
8
|
+
|
|
9
|
+
# Asserts a value is non-nil, returning it with a narrowed type.
|
|
10
|
+
#
|
|
11
|
+
# Use this to satisfy Steep's flow typing when you know a nilable value
|
|
12
|
+
# must be present. Prefer this over type assertions (#: Type) since it
|
|
13
|
+
# provides runtime validation.
|
|
14
|
+
#
|
|
15
|
+
# @example Validating required configuration
|
|
16
|
+
# @window_size = T.must(config.window_size)
|
|
17
|
+
#
|
|
18
|
+
# @raise [TypeError] if value is nil
|
|
19
|
+
# @return [T] the non-nil value
|
|
20
|
+
#
|
|
21
|
+
def self.must(value)
|
|
22
|
+
if value.nil?
|
|
23
|
+
raise TypeError, "must not have nil value"
|
|
24
|
+
else
|
|
25
|
+
value
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Stoplight
|
|
4
|
+
# Singleton representing an undefined/not-provided argument.
|
|
5
|
+
#
|
|
6
|
+
# Distinct from nil, which may be a valid configured value.
|
|
7
|
+
# Used with keyword arguments to detect when a parameter
|
|
8
|
+
# wasn't passed vs. explicitly set to nil.
|
|
9
|
+
# @api private
|
|
10
|
+
class Undefined
|
|
11
|
+
include Singleton
|
|
12
|
+
|
|
13
|
+
def inspect = "UNDEFINED"
|
|
14
|
+
alias_method :to_s, :inspect
|
|
15
|
+
end
|
|
16
|
+
end
|
data/lib/stoplight/version.rb
CHANGED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Stoplight
|
|
4
|
+
module Wiring
|
|
5
|
+
# Validates that traffic control and recovery strategies are
|
|
6
|
+
# compatible with the provided configuration.
|
|
7
|
+
#
|
|
8
|
+
# Different strategies have different configuration requirements:
|
|
9
|
+
# - ErrorRate requires window_size and threshold ∈ [0,1]
|
|
10
|
+
# - ConsecutiveErrors requires threshold > 0
|
|
11
|
+
# - ConsecutiveSuccesses requires recovery_threshold > 0
|
|
12
|
+
#
|
|
13
|
+
# @raise [Stoplight::Error::ConfigurationError] if incompatible
|
|
14
|
+
class ConfigCompatibilityValidator
|
|
15
|
+
private attr_reader :config
|
|
16
|
+
|
|
17
|
+
class << self
|
|
18
|
+
def call(config:) = new(config:).call
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def initialize(config:)
|
|
22
|
+
@config = config
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def call
|
|
26
|
+
validate_traffic_control!
|
|
27
|
+
validate_traffic_recovery!
|
|
28
|
+
config
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
private def validate_traffic_control!
|
|
32
|
+
traffic_control = config.traffic_control
|
|
33
|
+
traffic_control.check_compatibility(config).then do |compatibility_result|
|
|
34
|
+
if compatibility_result.incompatible?
|
|
35
|
+
raise Error::ConfigurationError,
|
|
36
|
+
"#{traffic_control} incompatible with config: #{compatibility_result.error_messages}",
|
|
37
|
+
caller(8)
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def validate_traffic_recovery!
|
|
43
|
+
traffic_recovery = config.traffic_recovery
|
|
44
|
+
traffic_recovery.check_compatibility(config).then do |compatibility_result|
|
|
45
|
+
if compatibility_result.incompatible?
|
|
46
|
+
raise Error::ConfigurationError,
|
|
47
|
+
"#{traffic_recovery} incompatible with config: #{compatibility_result.error_messages}",
|
|
48
|
+
caller(8)
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Stoplight
|
|
4
|
+
module Wiring
|
|
5
|
+
class ConfigurationDsl
|
|
6
|
+
def initialize(
|
|
7
|
+
name: T.undefined,
|
|
8
|
+
cool_off_time: T.undefined,
|
|
9
|
+
threshold: T.undefined,
|
|
10
|
+
recovery_threshold: T.undefined,
|
|
11
|
+
window_size: T.undefined,
|
|
12
|
+
tracked_errors: T.undefined,
|
|
13
|
+
skipped_errors: T.undefined,
|
|
14
|
+
data_store: T.undefined,
|
|
15
|
+
error_notifier: T.undefined,
|
|
16
|
+
notifiers: T.undefined,
|
|
17
|
+
traffic_control: T.undefined,
|
|
18
|
+
traffic_recovery: T.undefined
|
|
19
|
+
)
|
|
20
|
+
@name = name
|
|
21
|
+
@cool_off_time = cool_off_time
|
|
22
|
+
@threshold = threshold
|
|
23
|
+
@recovery_threshold = recovery_threshold
|
|
24
|
+
@window_size = window_size
|
|
25
|
+
@tracked_errors = tracked_errors
|
|
26
|
+
@skipped_errors = skipped_errors
|
|
27
|
+
@traffic_control = traffic_control
|
|
28
|
+
@traffic_recovery = traffic_recovery
|
|
29
|
+
@error_notifier = error_notifier
|
|
30
|
+
@data_store = data_store
|
|
31
|
+
@notifiers = notifiers
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def configure!(default_config)
|
|
35
|
+
ConfigCompatibilityValidator.call(
|
|
36
|
+
config: default_config.with(
|
|
37
|
+
name:,
|
|
38
|
+
cool_off_time:,
|
|
39
|
+
threshold:,
|
|
40
|
+
recovery_threshold:,
|
|
41
|
+
window_size:,
|
|
42
|
+
tracked_errors:,
|
|
43
|
+
skipped_errors:,
|
|
44
|
+
traffic_control:,
|
|
45
|
+
traffic_recovery:,
|
|
46
|
+
error_notifier:,
|
|
47
|
+
data_store:,
|
|
48
|
+
notifiers:
|
|
49
|
+
)
|
|
50
|
+
)
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
private
|
|
54
|
+
|
|
55
|
+
attr_reader :name
|
|
56
|
+
attr_reader :cool_off_time
|
|
57
|
+
attr_reader :threshold
|
|
58
|
+
attr_reader :recovery_threshold
|
|
59
|
+
attr_reader :window_size
|
|
60
|
+
attr_reader :error_notifier
|
|
61
|
+
attr_reader :data_store
|
|
62
|
+
attr_reader :notifiers
|
|
63
|
+
|
|
64
|
+
def tracked_errors
|
|
65
|
+
value = @tracked_errors
|
|
66
|
+
if value.is_a?(Undefined)
|
|
67
|
+
value
|
|
68
|
+
else
|
|
69
|
+
Array(value)
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def skipped_errors
|
|
74
|
+
value = @skipped_errors
|
|
75
|
+
if value.is_a?(Undefined)
|
|
76
|
+
value
|
|
77
|
+
else
|
|
78
|
+
Array(value)
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def traffic_control
|
|
83
|
+
value = @traffic_control
|
|
84
|
+
if value.is_a?(Undefined)
|
|
85
|
+
value
|
|
86
|
+
else
|
|
87
|
+
LightFactory::TrafficControlDsl.call(value)
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def traffic_recovery
|
|
92
|
+
value = @traffic_recovery
|
|
93
|
+
if value.is_a?(Undefined)
|
|
94
|
+
value
|
|
95
|
+
else
|
|
96
|
+
LightFactory::TrafficRecoveryDsl.call(value)
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
end
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Stoplight
|
|
4
|
+
module Wiring
|
|
5
|
+
# Abstract base class defining the storage backend interface.
|
|
6
|
+
#
|
|
7
|
+
# A backend encapsulates all storage construction for a specific data store type
|
|
8
|
+
# (Memory or Redis). Backends handle infrastructure concerns like connection
|
|
9
|
+
# management and failover wrapping, exposing a uniform interface to StorageSetBuilder.
|
|
10
|
+
#
|
|
11
|
+
# Each method returns a memoized storage instance. Backends are designed to be
|
|
12
|
+
# instantiated once per Light and reused.
|
|
13
|
+
#
|
|
14
|
+
# @abstract Subclass and implement all methods
|
|
15
|
+
# @see Memory::Backend
|
|
16
|
+
# @see Redis::Backend
|
|
17
|
+
# @api private
|
|
18
|
+
class DataStoreBackend
|
|
19
|
+
def state_store = raise ArgumentError
|
|
20
|
+
def recovery_lock_store = raise ArgumentError
|
|
21
|
+
def recovery_metrics_store = raise ArgumentError
|
|
22
|
+
def windowed_metrics_store = raise ArgumentError
|
|
23
|
+
def unbounded_metrics_store = raise ArgumentError
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
@@ -5,7 +5,7 @@ module Stoplight
|
|
|
5
5
|
module Default
|
|
6
6
|
COOL_OFF_TIME = 60.0
|
|
7
7
|
|
|
8
|
-
DATA_STORE =
|
|
8
|
+
DATA_STORE = Stoplight::DataStore::Memory.new
|
|
9
9
|
|
|
10
10
|
ERROR_NOTIFIER = ->(error) { warn error }
|
|
11
11
|
|
|
@@ -19,7 +19,7 @@ module Stoplight
|
|
|
19
19
|
WINDOW_SIZE = nil
|
|
20
20
|
|
|
21
21
|
TRACKED_ERRORS = [StandardError].freeze
|
|
22
|
-
SKIPPED_ERRORS = [].freeze
|
|
22
|
+
SKIPPED_ERRORS = [].freeze # steep:ignore
|
|
23
23
|
|
|
24
24
|
TRAFFIC_CONTROL = Domain::TrafficControl::ConsecutiveErrors.new
|
|
25
25
|
TRAFFIC_RECOVERY = Domain::TrafficRecovery::ConsecutiveSuccesses.new
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Stoplight
|
|
4
|
+
module Wiring
|
|
5
|
+
# Provides default settings for the Stoplight library.
|
|
6
|
+
DefaultConfig = Domain::Config.new(
|
|
7
|
+
name: "DEFAULT_CONFIG",
|
|
8
|
+
cool_off_time: Default::COOL_OFF_TIME,
|
|
9
|
+
threshold: Default::THRESHOLD,
|
|
10
|
+
recovery_threshold: Default::RECOVERY_THRESHOLD,
|
|
11
|
+
window_size: Default::WINDOW_SIZE,
|
|
12
|
+
tracked_errors: Default::TRACKED_ERRORS,
|
|
13
|
+
skipped_errors: Default::SKIPPED_ERRORS,
|
|
14
|
+
traffic_control: Default::TRAFFIC_CONTROL,
|
|
15
|
+
traffic_recovery: Default::TRAFFIC_RECOVERY,
|
|
16
|
+
error_notifier: Default::ERROR_NOTIFIER,
|
|
17
|
+
notifiers: Default::NOTIFIERS,
|
|
18
|
+
data_store: Default::DATA_STORE
|
|
19
|
+
)
|
|
20
|
+
end
|
|
21
|
+
end
|