stoplight 5.6.0 → 5.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/lib/stoplight/admin/dependencies.rb +1 -1
- data/lib/stoplight/admin/helpers.rb +10 -5
- data/lib/stoplight/admin/lights_repository.rb +18 -15
- data/lib/stoplight/admin.rb +2 -1
- data/lib/stoplight/common/deprecations.rb +11 -0
- data/lib/stoplight/domain/config.rb +5 -1
- data/lib/stoplight/domain/data_store.rb +17 -1
- data/lib/stoplight/domain/light/configuration_builder_interface.rb +120 -16
- data/lib/stoplight/domain/light.rb +31 -20
- data/lib/stoplight/domain/metrics.rb +6 -27
- data/lib/stoplight/domain/recovery_lock_token.rb +15 -0
- data/lib/stoplight/domain/storage/metrics.rb +42 -0
- data/lib/stoplight/domain/storage/recovery_lock.rb +56 -0
- data/lib/stoplight/domain/storage/state.rb +87 -0
- data/lib/stoplight/domain/strategies/run_strategy.rb +0 -5
- data/lib/stoplight/domain/strategies/yellow_run_strategy.rb +58 -32
- data/lib/stoplight/domain/tracker/base.rb +0 -29
- data/lib/stoplight/domain/tracker/recovery_probe.rb +23 -22
- data/lib/stoplight/domain/tracker/request.rb +23 -19
- data/lib/stoplight/domain/traffic_recovery/base.rb +1 -2
- data/lib/stoplight/domain/traffic_recovery/consecutive_successes.rb +2 -8
- data/lib/stoplight/domain/traffic_recovery.rb +0 -1
- data/lib/stoplight/infrastructure/data_store/fail_safe.rb +164 -0
- data/lib/stoplight/infrastructure/data_store/memory/recovery_lock_store.rb +54 -0
- data/lib/stoplight/infrastructure/data_store/memory/recovery_lock_token.rb +20 -0
- data/lib/stoplight/infrastructure/data_store/memory.rb +61 -32
- data/lib/stoplight/infrastructure/data_store/redis/lua_scripts/record_recovery_probe_failure.lua +27 -0
- data/lib/stoplight/infrastructure/data_store/redis/lua_scripts/record_recovery_probe_success.lua +23 -0
- data/lib/stoplight/infrastructure/data_store/redis/lua_scripts/release_lock.lua +6 -0
- data/lib/stoplight/infrastructure/data_store/redis/recovery_lock_store.rb +73 -0
- data/lib/stoplight/infrastructure/data_store/redis/recovery_lock_token.rb +35 -0
- data/lib/stoplight/infrastructure/data_store/redis/scripting.rb +71 -0
- data/lib/stoplight/infrastructure/data_store/redis.rb +133 -162
- data/lib/stoplight/infrastructure/notifier/fail_safe.rb +62 -0
- data/lib/stoplight/infrastructure/storage/compatibility_metrics.rb +48 -0
- data/lib/stoplight/infrastructure/storage/compatibility_recovery_lock.rb +36 -0
- data/lib/stoplight/infrastructure/storage/compatibility_recovery_metrics.rb +55 -0
- data/lib/stoplight/infrastructure/storage/compatibility_state.rb +55 -0
- data/lib/stoplight/version.rb +1 -1
- data/lib/stoplight/wiring/data_store/base.rb +11 -0
- data/lib/stoplight/wiring/data_store/memory.rb +10 -0
- data/lib/stoplight/wiring/data_store/redis.rb +25 -0
- data/lib/stoplight/wiring/default.rb +1 -1
- data/lib/stoplight/wiring/default_configuration.rb +1 -1
- data/lib/stoplight/wiring/default_factory_builder.rb +1 -1
- data/lib/stoplight/wiring/light_builder.rb +185 -0
- data/lib/stoplight/wiring/light_factory/compatibility_validator.rb +55 -0
- data/lib/stoplight/wiring/light_factory/config_normalizer.rb +71 -0
- data/lib/stoplight/wiring/light_factory/configuration_pipeline.rb +72 -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 +45 -132
- data/lib/stoplight/wiring/notifier_factory.rb +26 -0
- data/lib/stoplight/wiring/public_api.rb +3 -2
- data/lib/stoplight.rb +18 -3
- metadata +50 -15
- 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/fail_safe_data_store.rb +0 -147
- data/lib/stoplight/wiring/fail_safe_notifier.rb +0 -79
- 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/{get_metrics.lua → lua_scripts/get_metrics.lua} +0 -0
- /data/lib/stoplight/infrastructure/data_store/redis/{record_failure.lua → lua_scripts/record_failure.lua} +0 -0
- /data/lib/stoplight/infrastructure/data_store/redis/{record_success.lua → lua_scripts/record_success.lua} +0 -0
- /data/lib/stoplight/infrastructure/data_store/redis/{transition_to_green.lua → lua_scripts/transition_to_green.lua} +0 -0
- /data/lib/stoplight/infrastructure/data_store/redis/{transition_to_red.lua → lua_scripts/transition_to_red.lua} +0 -0
- /data/lib/stoplight/infrastructure/data_store/redis/{transition_to_yellow.lua → lua_scripts/transition_to_yellow.lua} +0 -0
|
@@ -1,249 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Stoplight
|
|
4
|
-
module Infrastructure
|
|
5
|
-
module DependencyInjection
|
|
6
|
-
# Generic dependency injection container for managing object dependencies.
|
|
7
|
-
#
|
|
8
|
-
# This is a low-level infrastructure component that provides the mechanism for
|
|
9
|
-
# dependency resolution. It has no knowledge of Stoplight's domain concepts and
|
|
10
|
-
# could theoretically be used in any Ruby application.
|
|
11
|
-
#
|
|
12
|
-
# The container supports three resolution strategies:
|
|
13
|
-
#
|
|
14
|
-
# 1. **Direct dependencies** - Pre-configured values stored in the container
|
|
15
|
-
# 2. **Lazy initialization** - Dependencies that are transformed on first access
|
|
16
|
-
# 3. **Factories** - Dependencies computed dynamically from other dependencies
|
|
17
|
-
#
|
|
18
|
-
# @example Basic usage with direct dependencies
|
|
19
|
-
# container = Container.new
|
|
20
|
-
# container.register(:logger, Logger.new)
|
|
21
|
-
# container.resolve(:logger) #=> #<Logger...>
|
|
22
|
-
#
|
|
23
|
-
# @example Using lazy initialization
|
|
24
|
-
# container.register(:redis, "redis://localhost:6379") do |url|
|
|
25
|
-
# Redis.new(url)
|
|
26
|
-
# end
|
|
27
|
-
# container.resolve(:redis) #=> #<Redis @connection_string="redis://localhost:6379">
|
|
28
|
-
#
|
|
29
|
-
# @example Using factories for computed dependencies
|
|
30
|
-
# container.register(:config, my_config)
|
|
31
|
-
# container.factory(:service) do
|
|
32
|
-
# MyService.new(config: resolve(:config))
|
|
33
|
-
# end
|
|
34
|
-
# container.resolve(:service) #=> #<MyService...>
|
|
35
|
-
#
|
|
36
|
-
# @example Building a container with DSL
|
|
37
|
-
# container = Container.define do
|
|
38
|
-
# register(:port, 3000)
|
|
39
|
-
# register(:host, "localhost")
|
|
40
|
-
#
|
|
41
|
-
# factory(:server) do
|
|
42
|
-
# Server.new(host: resolve(:host), port: resolve(:port))
|
|
43
|
-
# end
|
|
44
|
-
# end
|
|
45
|
-
#
|
|
46
|
-
# @api private
|
|
47
|
-
class Container
|
|
48
|
-
# @!attribute [r] dependencies
|
|
49
|
-
# Stores registered dependency values
|
|
50
|
-
# @return [Hash{Symbol => Object}]
|
|
51
|
-
protected attr_reader :dependencies
|
|
52
|
-
|
|
53
|
-
# @!attribute [r] factories
|
|
54
|
-
# Stores factory blocks for computed dependencies
|
|
55
|
-
# @return [Hash{Symbol => Proc}]
|
|
56
|
-
protected attr_reader :factories
|
|
57
|
-
|
|
58
|
-
# @!attribute [r] initializers
|
|
59
|
-
# Stores optional transformation blocks for dependencies
|
|
60
|
-
# @return [Hash{Symbol => Proc}]
|
|
61
|
-
protected attr_reader :initializers
|
|
62
|
-
|
|
63
|
-
class << self
|
|
64
|
-
# Define a container using a DSL block.
|
|
65
|
-
#
|
|
66
|
-
# This is a convenience method for creating and configuring a container
|
|
67
|
-
# in a single expression.
|
|
68
|
-
#
|
|
69
|
-
# @yield Block evaluated in the context of the new container
|
|
70
|
-
# @return [Stoplight::Infrastructure::DependencyInjection::Container] Configured container instance
|
|
71
|
-
#
|
|
72
|
-
# @example
|
|
73
|
-
# container = Container.define do
|
|
74
|
-
# register(:redis_url, "redis://localhost:6379")
|
|
75
|
-
# factory(:redis) { Redis.new(resolve(:redis_url)) }
|
|
76
|
-
# end
|
|
77
|
-
def define(&definition)
|
|
78
|
-
new.define(&definition)
|
|
79
|
-
end
|
|
80
|
-
end
|
|
81
|
-
|
|
82
|
-
# Creates a new dependency injection container.
|
|
83
|
-
#
|
|
84
|
-
# @param dependencies [Hash{Symbol => Object}]
|
|
85
|
-
# @param factories [Hash{Symbol => Proc}]
|
|
86
|
-
# @param initializers [Hash{Symbol => Proc}]
|
|
87
|
-
def initialize(dependencies: {}, factories: {}, initializers: {})
|
|
88
|
-
@dependencies = dependencies
|
|
89
|
-
@initializers = initializers
|
|
90
|
-
@factories = factories
|
|
91
|
-
end
|
|
92
|
-
|
|
93
|
-
# Evaluate a configuration block in the context of this container.
|
|
94
|
-
#
|
|
95
|
-
# @yield Block evaluated in the context of the container
|
|
96
|
-
# @return [Stoplight::Infrastructure::DependencyInjection::Container] self for method chaining
|
|
97
|
-
#
|
|
98
|
-
# @example
|
|
99
|
-
# container = Container.new
|
|
100
|
-
# container.define do
|
|
101
|
-
# register(:port, 8080)
|
|
102
|
-
# factory(:server) { Server.new(port: resolve(:port)) }
|
|
103
|
-
# end
|
|
104
|
-
|
|
105
|
-
def define(&definition)
|
|
106
|
-
instance_eval(&definition)
|
|
107
|
-
freeze
|
|
108
|
-
self
|
|
109
|
-
end
|
|
110
|
-
|
|
111
|
-
# Registers a dependency value with optional lazy initialization.
|
|
112
|
-
#
|
|
113
|
-
# The initializer block is called every time the dependency is resolved,
|
|
114
|
-
# receiving the registered value as an argument. This allows for delayed
|
|
115
|
-
# object construction or transformation of simple values into complex objects.
|
|
116
|
-
#
|
|
117
|
-
# @param name [Symbol] The dependency key
|
|
118
|
-
# @param value [Object] The dependency value (may be transformed by initializer)
|
|
119
|
-
# @yield [value] Optional transformation block
|
|
120
|
-
# @yieldparam value [Object] The registered value
|
|
121
|
-
# @yieldreturn [Object] The transformed value to return when resolving
|
|
122
|
-
# @return [Stoplight::Infrastructure::DependencyInjection::Container] self for method chaining
|
|
123
|
-
#
|
|
124
|
-
# @example Register a simple dependency
|
|
125
|
-
# container.register(:port, 3000)
|
|
126
|
-
# container.resolve(:port) #=> 3000
|
|
127
|
-
#
|
|
128
|
-
# @example Register with lazy initialization
|
|
129
|
-
# container.register(:redis, "redis://localhost:6379") do |url|
|
|
130
|
-
# Redis.new(url)
|
|
131
|
-
# end
|
|
132
|
-
# container.resolve(:redis) #=> #<Redis...>
|
|
133
|
-
#
|
|
134
|
-
# @example Initialization is applied on every resolution
|
|
135
|
-
# container.register(:timestamp, Time.now, &:to_i)
|
|
136
|
-
# container.resolve(:timestamp) #=> 1234567890
|
|
137
|
-
#
|
|
138
|
-
# # Update the value
|
|
139
|
-
# container.register(:timestamp, Time.now + 60)
|
|
140
|
-
# container.resolve(:timestamp) #=> 1234567950 (new timestamp)
|
|
141
|
-
#
|
|
142
|
-
def register(name, value, &initializer)
|
|
143
|
-
dependencies[name] = value
|
|
144
|
-
initializers[name] = initializer if block_given?
|
|
145
|
-
self
|
|
146
|
-
end
|
|
147
|
-
|
|
148
|
-
# Resolves a dependency by name.
|
|
149
|
-
#
|
|
150
|
-
# Resolution order:
|
|
151
|
-
# 1. Direct dependency with initializer (value is transformed)
|
|
152
|
-
# 2. Direct dependency without initializer (value returned as-is)
|
|
153
|
-
# 3. Factory (block evaluated in container context)
|
|
154
|
-
# 4. Raises {Stoplight::Infrastructure::DependencyInjection::UnresolvedDependencyError}
|
|
155
|
-
#
|
|
156
|
-
# @param name [Symbol] The dependency key
|
|
157
|
-
# @return [Object] The resolved dependency value
|
|
158
|
-
# @raise [Stoplight::Infrastructure::DependencyInjection::UnresolvedDependencyError] if dependency is not registered
|
|
159
|
-
#
|
|
160
|
-
def resolve(name)
|
|
161
|
-
if dependencies.key?(name)
|
|
162
|
-
value = dependencies[name]
|
|
163
|
-
if initializers.key?(name)
|
|
164
|
-
initializer = initializers[name]
|
|
165
|
-
instance_exec(value, &initializer)
|
|
166
|
-
else
|
|
167
|
-
value
|
|
168
|
-
end
|
|
169
|
-
elsif factories.key?(name)
|
|
170
|
-
factory = factories[name]
|
|
171
|
-
instance_eval(&factory)
|
|
172
|
-
else
|
|
173
|
-
raise UnresolvedDependencyError, name
|
|
174
|
-
end
|
|
175
|
-
end
|
|
176
|
-
|
|
177
|
-
# Registers a factory for computing dependencies dynamically.
|
|
178
|
-
#
|
|
179
|
-
# Factories are evaluated lazily when the dependency is resolved.
|
|
180
|
-
# The factory block is evaluated in the container's context, giving
|
|
181
|
-
# it access to the +#resolve+ method for accessing other dependencies.
|
|
182
|
-
#
|
|
183
|
-
# Unlike +#register+ with an initializer, factories are not cached -
|
|
184
|
-
# they are executed every time the dependency is resolved.
|
|
185
|
-
#
|
|
186
|
-
# @param key [Symbol] The dependency key
|
|
187
|
-
# @yield Factory block evaluated in container context
|
|
188
|
-
# @yieldreturn [Object] The computed dependency value
|
|
189
|
-
# @return [Stoplight::Infrastructure::DependencyInjection::Container] self for method chaining
|
|
190
|
-
#
|
|
191
|
-
def factory(key, &factory)
|
|
192
|
-
factories[key] = factory
|
|
193
|
-
self
|
|
194
|
-
end
|
|
195
|
-
|
|
196
|
-
# Returns all registered dependency keys.
|
|
197
|
-
#
|
|
198
|
-
# This includes both direct dependencies and factory definitions.
|
|
199
|
-
#
|
|
200
|
-
# @return [Array<Symbol>] All registered dependency keys
|
|
201
|
-
#
|
|
202
|
-
# @example
|
|
203
|
-
# container.register(:port, 3000)
|
|
204
|
-
# container.factory(:server) { Server.new }
|
|
205
|
-
# container.keys #=> [:port, :server]
|
|
206
|
-
#
|
|
207
|
-
def keys
|
|
208
|
-
factories.keys | dependencies.keys
|
|
209
|
-
end
|
|
210
|
-
|
|
211
|
-
# Creates a new container with merged dependencies.
|
|
212
|
-
#
|
|
213
|
-
# This is an immutable operation - the original container is not modified.
|
|
214
|
-
# Factories and initializers are copied to the new container.
|
|
215
|
-
#
|
|
216
|
-
# @param new_dependencies [Hash{Symbol => Object}] Dependencies to merge/override
|
|
217
|
-
# @return [Stoplight::Infrastructure::DependencyInjection::Container] New container with merged dependencies
|
|
218
|
-
#
|
|
219
|
-
# @example Creating specialized containers
|
|
220
|
-
# base = Container.define do
|
|
221
|
-
# register(:host, "localhost")
|
|
222
|
-
# register(:port, 3000)
|
|
223
|
-
# end
|
|
224
|
-
#
|
|
225
|
-
# production = base.with(host: "prod.example.com", port: 80)
|
|
226
|
-
# staging = base.with(host: "staging.example.com", port: 8080)
|
|
227
|
-
#
|
|
228
|
-
# base.resolve(:port) #=> 3000
|
|
229
|
-
# production.resolve(:port) #=> 80
|
|
230
|
-
# staging.resolve(:port) #=> 8080
|
|
231
|
-
|
|
232
|
-
def with(**new_dependencies)
|
|
233
|
-
self.class.new(
|
|
234
|
-
dependencies: {**dependencies, **new_dependencies},
|
|
235
|
-
factories:,
|
|
236
|
-
initializers:
|
|
237
|
-
)
|
|
238
|
-
end
|
|
239
|
-
|
|
240
|
-
def ==(other)
|
|
241
|
-
other.is_a?(self.class) &&
|
|
242
|
-
other.dependencies == dependencies &&
|
|
243
|
-
other.factories == factories &&
|
|
244
|
-
other.initializers == initializers
|
|
245
|
-
end
|
|
246
|
-
end
|
|
247
|
-
end
|
|
248
|
-
end
|
|
249
|
-
end
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Stoplight
|
|
4
|
-
module Infrastructure
|
|
5
|
-
module DependencyInjection
|
|
6
|
-
class UnresolvedDependencyError < Domain::Error::Base
|
|
7
|
-
def initialize(key)
|
|
8
|
-
super("Unable to resolve dependency: `#{key}`")
|
|
9
|
-
end
|
|
10
|
-
end
|
|
11
|
-
end
|
|
12
|
-
end
|
|
13
|
-
end
|
|
@@ -1,80 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Stoplight
|
|
4
|
-
module Wiring
|
|
5
|
-
# This container implements an instance of +Stoplight::Infrastructure::DependencyInjection::Container+
|
|
6
|
-
# with Stoplight-specific wiring knowledge. It defines how to construct and connect
|
|
7
|
-
# all the components needed for a circuit breaker to function.
|
|
8
|
-
#
|
|
9
|
-
# ## Default Configuration
|
|
10
|
-
#
|
|
11
|
-
# The container is pre-configured with sensible defaults:
|
|
12
|
-
# - Data Store - in-memory storage
|
|
13
|
-
# - STDERR notifier
|
|
14
|
-
# - No-op error notifier
|
|
15
|
-
# - Consecutive failure detection
|
|
16
|
-
# - Consecutive success recovery
|
|
17
|
-
#
|
|
18
|
-
# @see Infrastructure::DependencyInjection::Container Generic DI container
|
|
19
|
-
# @see Stoplight::Wiring::LightFactory Factory that uses this container
|
|
20
|
-
# @api private
|
|
21
|
-
Container = Infrastructure::DependencyInjection::Container.define do
|
|
22
|
-
register(:config, Light::DefaultConfig)
|
|
23
|
-
register(:error_notifier, Default::ERROR_NOTIFIER)
|
|
24
|
-
register(:traffic_control, Default::TRAFFIC_CONTROL)
|
|
25
|
-
register(:traffic_recovery, Default::TRAFFIC_RECOVERY)
|
|
26
|
-
|
|
27
|
-
register(:data_store, Default::DATA_STORE) do |data_store|
|
|
28
|
-
FailSafeDataStore.wrap(
|
|
29
|
-
data_store:,
|
|
30
|
-
error_notifier: resolve(:error_notifier)
|
|
31
|
-
)
|
|
32
|
-
end
|
|
33
|
-
|
|
34
|
-
register(:notifiers, Default::NOTIFIERS) do |notifiers|
|
|
35
|
-
error_notifier = resolve(:error_notifier)
|
|
36
|
-
notifiers.map { |notifier| Wiring::FailSafeNotifier.wrap(notifier:, error_notifier:) }
|
|
37
|
-
end
|
|
38
|
-
|
|
39
|
-
factory(:green_run_strategy) do
|
|
40
|
-
Domain::Strategies::GreenRunStrategy.new(
|
|
41
|
-
config: resolve(:config),
|
|
42
|
-
request_tracker: resolve(:request_tracker)
|
|
43
|
-
)
|
|
44
|
-
end
|
|
45
|
-
|
|
46
|
-
factory(:yellow_run_strategy) do
|
|
47
|
-
Domain::Strategies::YellowRunStrategy.new(
|
|
48
|
-
config: resolve(:config),
|
|
49
|
-
data_store: resolve(:data_store),
|
|
50
|
-
notifiers: resolve(:notifiers),
|
|
51
|
-
request_tracker: resolve(:recovery_probe_tracker)
|
|
52
|
-
)
|
|
53
|
-
end
|
|
54
|
-
|
|
55
|
-
factory(:red_run_strategy) do
|
|
56
|
-
Domain::Strategies::RedRunStrategy.new(
|
|
57
|
-
config: resolve(:config)
|
|
58
|
-
)
|
|
59
|
-
end
|
|
60
|
-
|
|
61
|
-
factory(:request_tracker) do
|
|
62
|
-
Domain::Tracker::Request.new(
|
|
63
|
-
data_store: resolve(:data_store),
|
|
64
|
-
traffic_control: resolve(:traffic_control),
|
|
65
|
-
notifiers: resolve(:notifiers),
|
|
66
|
-
config: resolve(:config)
|
|
67
|
-
)
|
|
68
|
-
end
|
|
69
|
-
|
|
70
|
-
factory(:recovery_probe_tracker) do
|
|
71
|
-
Domain::Tracker::RecoveryProbe.new(
|
|
72
|
-
data_store: resolve(:data_store),
|
|
73
|
-
traffic_recovery: resolve(:traffic_recovery),
|
|
74
|
-
notifiers: resolve(:notifiers),
|
|
75
|
-
config: resolve(:config)
|
|
76
|
-
)
|
|
77
|
-
end
|
|
78
|
-
end
|
|
79
|
-
end
|
|
80
|
-
end
|
|
@@ -1,147 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require "securerandom"
|
|
4
|
-
|
|
5
|
-
module Stoplight
|
|
6
|
-
module Wiring
|
|
7
|
-
# A wrapper around a data store that provides fail-safe mechanisms using a
|
|
8
|
-
# circuit breaker. It ensures that operations on the data store can gracefully
|
|
9
|
-
# handle failures by falling back to default values when necessary.
|
|
10
|
-
#
|
|
11
|
-
# @api private
|
|
12
|
-
class FailSafeDataStore < Domain::DataStore
|
|
13
|
-
class << self
|
|
14
|
-
# Wraps a data store with fail-safe mechanisms.
|
|
15
|
-
#
|
|
16
|
-
# @param data_store [Stoplight::DataStore::Base] The data store to wrap.
|
|
17
|
-
# @param error_notifier [Proc] called when wrapped data store fails
|
|
18
|
-
# @return [Stoplight::DataStore::Base, FailSafe] The original data store if it is already
|
|
19
|
-
# a +Memory+ or +FailSafe+ instance, otherwise a new +FailSafe+ instance.
|
|
20
|
-
def wrap(data_store:, error_notifier:)
|
|
21
|
-
case data_store
|
|
22
|
-
in Infrastructure::DataStore::Memory
|
|
23
|
-
data_store
|
|
24
|
-
in self if data_store.error_notifier == error_notifier
|
|
25
|
-
data_store
|
|
26
|
-
in self
|
|
27
|
-
new(data_store: data_store.data_store, error_notifier:)
|
|
28
|
-
else
|
|
29
|
-
new(data_store:, error_notifier:)
|
|
30
|
-
end
|
|
31
|
-
end
|
|
32
|
-
end
|
|
33
|
-
|
|
34
|
-
# @!attribute data_store
|
|
35
|
-
# @return [Stoplight::DataStore::Base] The underlying primary data store being used
|
|
36
|
-
attr_reader :data_store
|
|
37
|
-
|
|
38
|
-
# @!attribute error_notifier
|
|
39
|
-
# @return [Proc]
|
|
40
|
-
attr_reader :error_notifier
|
|
41
|
-
|
|
42
|
-
# @!attribute failover_data_store
|
|
43
|
-
# @return [Stoplight::DataStore::Base] The fallback data store used when the primary fails.
|
|
44
|
-
private attr_reader :failover_data_store
|
|
45
|
-
|
|
46
|
-
# @!attribute circuit_breaker
|
|
47
|
-
# @return [Stoplight::Light] The circuit breaker used to handle data store failures.
|
|
48
|
-
private attr_reader :circuit_breaker
|
|
49
|
-
|
|
50
|
-
# @param data_store [Stoplight::Domain::DataStore]
|
|
51
|
-
# @param error_notifier [Proc]
|
|
52
|
-
def initialize(data_store:, error_notifier:, failover_data_store: Wiring::Default::DATA_STORE)
|
|
53
|
-
@data_store = data_store
|
|
54
|
-
@error_notifier = error_notifier
|
|
55
|
-
@failover_data_store = failover_data_store
|
|
56
|
-
@circuit_breaker = Stoplight.system_light("data_store:fail_safe:#{data_store.class.name}")
|
|
57
|
-
end
|
|
58
|
-
|
|
59
|
-
def names
|
|
60
|
-
with_fallback(:names) do
|
|
61
|
-
data_store.names
|
|
62
|
-
end
|
|
63
|
-
end
|
|
64
|
-
|
|
65
|
-
def get_metrics(config, *args, **kwargs)
|
|
66
|
-
with_fallback(:get_metrics, config, *args, **kwargs) do
|
|
67
|
-
data_store.get_metrics(config, *args, **kwargs)
|
|
68
|
-
end
|
|
69
|
-
end
|
|
70
|
-
|
|
71
|
-
def get_recovery_metrics(config, *args, **kwargs)
|
|
72
|
-
with_fallback(:get_recovery_metrics, config, *args, **kwargs) do
|
|
73
|
-
data_store.get_recovery_metrics(config, *args, **kwargs)
|
|
74
|
-
end
|
|
75
|
-
end
|
|
76
|
-
|
|
77
|
-
def get_state_snapshot(config)
|
|
78
|
-
with_fallback(:get_state_snapshot, config) do
|
|
79
|
-
data_store.get_state_snapshot(config)
|
|
80
|
-
end
|
|
81
|
-
end
|
|
82
|
-
|
|
83
|
-
def clear_windowed_metrics(config)
|
|
84
|
-
with_fallback(:clear_windowed_metrics, config) do
|
|
85
|
-
data_store.clear_windowed_metrics(config)
|
|
86
|
-
end
|
|
87
|
-
end
|
|
88
|
-
|
|
89
|
-
def record_failure(config, *args, **kwargs)
|
|
90
|
-
with_fallback(:record_failure, config, *args, **kwargs) do
|
|
91
|
-
data_store.record_failure(config, *args, **kwargs)
|
|
92
|
-
end
|
|
93
|
-
end
|
|
94
|
-
|
|
95
|
-
def record_success(config, *args, **kwargs)
|
|
96
|
-
with_fallback(:record_success, config, *args, **kwargs) do
|
|
97
|
-
data_store.record_success(config, *args, **kwargs)
|
|
98
|
-
end
|
|
99
|
-
end
|
|
100
|
-
|
|
101
|
-
def record_recovery_probe_success(config, *args, **kwargs)
|
|
102
|
-
with_fallback(:record_recovery_probe_success, config, *args, **kwargs) do
|
|
103
|
-
data_store.record_recovery_probe_success(config, *args, **kwargs)
|
|
104
|
-
end
|
|
105
|
-
end
|
|
106
|
-
|
|
107
|
-
def record_recovery_probe_failure(config, *args, **kwargs)
|
|
108
|
-
with_fallback(:record_recovery_probe_failure, config, *args, **kwargs) do
|
|
109
|
-
data_store.record_recovery_probe_failure(config, *args, **kwargs)
|
|
110
|
-
end
|
|
111
|
-
end
|
|
112
|
-
|
|
113
|
-
def set_state(config, *args, **kwargs)
|
|
114
|
-
with_fallback(:set_state, config, *args, **kwargs) do
|
|
115
|
-
data_store.set_state(config, *args, **kwargs)
|
|
116
|
-
end
|
|
117
|
-
end
|
|
118
|
-
|
|
119
|
-
def transition_to_color(config, *args, **kwargs)
|
|
120
|
-
with_fallback(:transition_to_color, config, *args, **kwargs) do
|
|
121
|
-
data_store.transition_to_color(config, *args, **kwargs)
|
|
122
|
-
end
|
|
123
|
-
end
|
|
124
|
-
|
|
125
|
-
def delete_light(config, *args, **kwargs)
|
|
126
|
-
with_fallback(:delete_light, config, *args, **kwargs) do
|
|
127
|
-
data_store.delete_light(config, *args, **kwargs)
|
|
128
|
-
end
|
|
129
|
-
end
|
|
130
|
-
|
|
131
|
-
def ==(other)
|
|
132
|
-
other.is_a?(self.class) && other.data_store == data_store && other.error_notifier == error_notifier
|
|
133
|
-
end
|
|
134
|
-
|
|
135
|
-
# @param method_name [Symbol] protected method name
|
|
136
|
-
private def with_fallback(method_name, *args, **kwargs, &code)
|
|
137
|
-
fallback = proc do |error|
|
|
138
|
-
config = args.first
|
|
139
|
-
error_notifier.call(error) if config && error
|
|
140
|
-
@failover_data_store.public_send(method_name, *args, **kwargs)
|
|
141
|
-
end
|
|
142
|
-
|
|
143
|
-
circuit_breaker.run(fallback, &code)
|
|
144
|
-
end
|
|
145
|
-
end
|
|
146
|
-
end
|
|
147
|
-
end
|
|
@@ -1,79 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Stoplight
|
|
4
|
-
module Wiring
|
|
5
|
-
# A wrapper around a notifier that provides fail-safe mechanisms using a
|
|
6
|
-
# circuit breaker. It ensures that a notification can gracefully
|
|
7
|
-
# handle failures.
|
|
8
|
-
#
|
|
9
|
-
# @api private
|
|
10
|
-
class FailSafeNotifier < Domain::StateTransitionNotifier
|
|
11
|
-
# @!attribute [r] notifier
|
|
12
|
-
# @return [Stoplight::Domain::StateTransitionNotifier] The underlying notifier being wrapped.
|
|
13
|
-
attr_reader :notifier
|
|
14
|
-
|
|
15
|
-
# @!attribute [r] error_notifier
|
|
16
|
-
# @return [Stoplight::Domain::StateTransitionNotifier] The underlying notifier being wrapped.
|
|
17
|
-
attr_reader :error_notifier
|
|
18
|
-
|
|
19
|
-
class << self
|
|
20
|
-
# Wraps a notifier with fail-safe mechanisms.
|
|
21
|
-
#
|
|
22
|
-
# @param notifier [Stoplight::Domain::StateTransitionNotifier] The notifier to wrap.
|
|
23
|
-
# @param error_notifier [Proc] called when wrapped data store fails
|
|
24
|
-
# @return [Stoplight::Notifier::FailSafe] The original notifier if it is already
|
|
25
|
-
# a +FailSafe+ instance, otherwise a new +FailSafe+ instance.
|
|
26
|
-
def wrap(notifier:, error_notifier:)
|
|
27
|
-
case notifier
|
|
28
|
-
in self if notifier.error_notifier == error_notifier
|
|
29
|
-
notifier
|
|
30
|
-
in self
|
|
31
|
-
new(notifier: notifier.notifier, error_notifier:)
|
|
32
|
-
else
|
|
33
|
-
new(notifier:, error_notifier:)
|
|
34
|
-
end
|
|
35
|
-
end
|
|
36
|
-
end
|
|
37
|
-
|
|
38
|
-
# Initializes a new instance of the +FailSafe+ class.
|
|
39
|
-
#
|
|
40
|
-
# @param notifier [Stoplight::Domain::StateTransitionNotifier] The notifier to wrap.
|
|
41
|
-
# @param error_notifier [Proc] called when wrapped data store fails
|
|
42
|
-
def initialize(notifier:, error_notifier:)
|
|
43
|
-
@notifier = notifier
|
|
44
|
-
@error_notifier = error_notifier
|
|
45
|
-
end
|
|
46
|
-
|
|
47
|
-
# Sends a notification using the wrapped notifier with fail-safe mechanisms.
|
|
48
|
-
#
|
|
49
|
-
# @param config [Stoplight::Domain::Config] The light configuration.
|
|
50
|
-
# @param from_color [String] The initial color of the light.
|
|
51
|
-
# @param to_color [String] The target color of the light.
|
|
52
|
-
# @param error [Exception, nil] An optional error to include in the notification.
|
|
53
|
-
# @return [void]
|
|
54
|
-
def notify(config, from_color, to_color, error = nil)
|
|
55
|
-
fallback = proc do |exception|
|
|
56
|
-
error_notifier.call(exception) if exception
|
|
57
|
-
nil
|
|
58
|
-
end
|
|
59
|
-
|
|
60
|
-
circuit_breaker.run(fallback) do
|
|
61
|
-
notifier.notify(config, from_color, to_color, error)
|
|
62
|
-
end
|
|
63
|
-
end
|
|
64
|
-
|
|
65
|
-
# @return [Boolean]
|
|
66
|
-
def ==(other)
|
|
67
|
-
other.is_a?(self.class) && notifier == other.notifier
|
|
68
|
-
end
|
|
69
|
-
|
|
70
|
-
# @return [Stoplight::Light] The circuit breaker used to handle failures.
|
|
71
|
-
private def circuit_breaker
|
|
72
|
-
@circuit_breaker ||= Stoplight.system_light(
|
|
73
|
-
"stoplight:notifier:fail_safe:#{notifier.class.name}",
|
|
74
|
-
notifiers: []
|
|
75
|
-
)
|
|
76
|
-
end
|
|
77
|
-
end
|
|
78
|
-
end
|
|
79
|
-
end
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
# frozon_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Stoplight
|
|
4
|
-
module Wiring
|
|
5
|
-
# Factory for internal system lights used by the Stoplight itself.
|
|
6
|
-
#
|
|
7
|
-
# System lights are isolated from user configuration to prevent
|
|
8
|
-
# user settings from breaking the library's own circuit breakers.
|
|
9
|
-
# For example, the FailSafe data store wrapper uses a system light
|
|
10
|
-
# to protect against data store failures.
|
|
11
|
-
#
|
|
12
|
-
# @api private
|
|
13
|
-
SystemLightFactory = Wiring::LightFactory.new(
|
|
14
|
-
Wiring::SystemContainer.with(config: Light::SystemConfig)
|
|
15
|
-
)
|
|
16
|
-
end
|
|
17
|
-
end
|
/data/lib/stoplight/infrastructure/data_store/redis/{get_metrics.lua → lua_scripts/get_metrics.lua}
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|