stoplight 5.5.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.
Files changed (84) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/lib/stoplight/admin/actions/remove.rb +23 -0
  4. data/lib/stoplight/admin/dependencies.rb +6 -1
  5. data/lib/stoplight/admin/helpers.rb +10 -5
  6. data/lib/stoplight/admin/lights_repository.rb +26 -14
  7. data/lib/stoplight/admin/views/_card.erb +13 -1
  8. data/lib/stoplight/admin.rb +9 -0
  9. data/lib/stoplight/common/deprecations.rb +11 -0
  10. data/lib/stoplight/domain/config.rb +5 -1
  11. data/lib/stoplight/domain/data_store.rb +58 -6
  12. data/lib/stoplight/domain/failure.rb +2 -0
  13. data/lib/stoplight/domain/light/configuration_builder_interface.rb +120 -16
  14. data/lib/stoplight/domain/light.rb +34 -24
  15. data/lib/stoplight/domain/metrics.rb +64 -0
  16. data/lib/stoplight/domain/recovery_lock_token.rb +15 -0
  17. data/lib/stoplight/domain/{metadata.rb → state_snapshot.rb} +29 -37
  18. data/lib/stoplight/domain/storage/metrics.rb +42 -0
  19. data/lib/stoplight/domain/storage/recovery_lock.rb +56 -0
  20. data/lib/stoplight/domain/storage/state.rb +87 -0
  21. data/lib/stoplight/domain/strategies/green_run_strategy.rb +2 -2
  22. data/lib/stoplight/domain/strategies/red_run_strategy.rb +3 -3
  23. data/lib/stoplight/domain/strategies/run_strategy.rb +2 -7
  24. data/lib/stoplight/domain/strategies/yellow_run_strategy.rb +63 -36
  25. data/lib/stoplight/domain/tracker/base.rb +0 -29
  26. data/lib/stoplight/domain/tracker/recovery_probe.rb +26 -22
  27. data/lib/stoplight/domain/tracker/request.rb +26 -21
  28. data/lib/stoplight/domain/traffic_control/base.rb +5 -5
  29. data/lib/stoplight/domain/traffic_control/consecutive_errors.rb +3 -7
  30. data/lib/stoplight/domain/traffic_control/error_rate.rb +3 -3
  31. data/lib/stoplight/domain/traffic_recovery/base.rb +5 -5
  32. data/lib/stoplight/domain/traffic_recovery/consecutive_successes.rb +4 -8
  33. data/lib/stoplight/domain/traffic_recovery.rb +0 -1
  34. data/lib/stoplight/infrastructure/data_store/fail_safe.rb +164 -0
  35. data/lib/stoplight/infrastructure/data_store/memory/metrics.rb +27 -0
  36. data/lib/stoplight/infrastructure/data_store/memory/recovery_lock_store.rb +54 -0
  37. data/lib/stoplight/infrastructure/data_store/memory/recovery_lock_token.rb +20 -0
  38. data/lib/stoplight/infrastructure/data_store/memory/state.rb +21 -0
  39. data/lib/stoplight/infrastructure/data_store/memory.rb +163 -132
  40. data/lib/stoplight/infrastructure/data_store/redis/lua_scripts/get_metrics.lua +26 -0
  41. data/lib/stoplight/infrastructure/data_store/redis/lua_scripts/record_recovery_probe_failure.lua +27 -0
  42. data/lib/stoplight/infrastructure/data_store/redis/lua_scripts/record_recovery_probe_success.lua +23 -0
  43. data/lib/stoplight/infrastructure/data_store/redis/lua_scripts/release_lock.lua +6 -0
  44. data/lib/stoplight/infrastructure/data_store/redis/recovery_lock_store.rb +73 -0
  45. data/lib/stoplight/infrastructure/data_store/redis/recovery_lock_token.rb +35 -0
  46. data/lib/stoplight/infrastructure/data_store/redis/scripting.rb +71 -0
  47. data/lib/stoplight/infrastructure/data_store/redis.rb +211 -165
  48. data/lib/stoplight/infrastructure/notifier/fail_safe.rb +62 -0
  49. data/lib/stoplight/infrastructure/storage/compatibility_metrics.rb +48 -0
  50. data/lib/stoplight/infrastructure/storage/compatibility_recovery_lock.rb +36 -0
  51. data/lib/stoplight/infrastructure/storage/compatibility_recovery_metrics.rb +55 -0
  52. data/lib/stoplight/infrastructure/storage/compatibility_state.rb +55 -0
  53. data/lib/stoplight/version.rb +1 -1
  54. data/lib/stoplight/wiring/data_store/base.rb +11 -0
  55. data/lib/stoplight/wiring/data_store/memory.rb +10 -0
  56. data/lib/stoplight/wiring/data_store/redis.rb +25 -0
  57. data/lib/stoplight/wiring/default.rb +1 -1
  58. data/lib/stoplight/wiring/default_configuration.rb +1 -1
  59. data/lib/stoplight/wiring/default_factory_builder.rb +1 -1
  60. data/lib/stoplight/wiring/light_builder.rb +185 -0
  61. data/lib/stoplight/wiring/light_factory/compatibility_validator.rb +55 -0
  62. data/lib/stoplight/wiring/light_factory/config_normalizer.rb +71 -0
  63. data/lib/stoplight/wiring/light_factory/configuration_pipeline.rb +72 -0
  64. data/lib/stoplight/wiring/light_factory/traffic_control_dsl.rb +26 -0
  65. data/lib/stoplight/wiring/light_factory/traffic_recovery_dsl.rb +21 -0
  66. data/lib/stoplight/wiring/light_factory.rb +45 -132
  67. data/lib/stoplight/wiring/notifier_factory.rb +26 -0
  68. data/lib/stoplight/wiring/public_api.rb +3 -2
  69. data/lib/stoplight.rb +18 -3
  70. metadata +55 -16
  71. data/lib/stoplight/infrastructure/data_store/redis/get_metadata.lua +0 -38
  72. data/lib/stoplight/infrastructure/data_store/redis/lua.rb +0 -25
  73. data/lib/stoplight/infrastructure/dependency_injection/container.rb +0 -249
  74. data/lib/stoplight/infrastructure/dependency_injection/unresolved_dependency_error.rb +0 -13
  75. data/lib/stoplight/wiring/container.rb +0 -80
  76. data/lib/stoplight/wiring/fail_safe_data_store.rb +0 -123
  77. data/lib/stoplight/wiring/fail_safe_notifier.rb +0 -79
  78. data/lib/stoplight/wiring/system_container.rb +0 -9
  79. data/lib/stoplight/wiring/system_light_factory.rb +0 -17
  80. /data/lib/stoplight/infrastructure/data_store/redis/{record_failure.lua → lua_scripts/record_failure.lua} +0 -0
  81. /data/lib/stoplight/infrastructure/data_store/redis/{record_success.lua → lua_scripts/record_success.lua} +0 -0
  82. /data/lib/stoplight/infrastructure/data_store/redis/{transition_to_green.lua → lua_scripts/transition_to_green.lua} +0 -0
  83. /data/lib/stoplight/infrastructure/data_store/redis/{transition_to_red.lua → lua_scripts/transition_to_red.lua} +0 -0
  84. /data/lib/stoplight/infrastructure/data_store/redis/{transition_to_yellow.lua → lua_scripts/transition_to_yellow.lua} +0 -0
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Stoplight
4
+ module Wiring
5
+ class LightFactory
6
+ TrafficControlDsl = proc do |value|
7
+ case value
8
+ in Domain::TrafficControl::Base
9
+ value
10
+ in :consecutive_errors
11
+ Domain::TrafficControl::ConsecutiveErrors.new
12
+ in :error_rate
13
+ Domain::TrafficControl::ErrorRate.new
14
+ in {error_rate: error_rate_settings}
15
+ Domain::TrafficControl::ErrorRate.new(**error_rate_settings)
16
+ else
17
+ raise Stoplight::Error::ConfigurationError, <<~ERROR
18
+ unsupported traffic_control strategy provided (`#{value}`). Supported options:
19
+ * :consecutive_errors
20
+ * :error_rate
21
+ ERROR
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Stoplight
4
+ module Wiring
5
+ class LightFactory
6
+ TrafficRecoveryDsl = proc do |value|
7
+ case value
8
+ in Domain::TrafficRecovery::Base
9
+ value
10
+ in :consecutive_successes
11
+ Domain::TrafficRecovery::ConsecutiveSuccesses.new
12
+ else
13
+ raise Domain::Error::ConfigurationError, <<~ERROR
14
+ unsupported traffic_recovery strategy provided (`#{value}`). Supported options:
15
+ * :consecutive_successes
16
+ ERROR
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -15,15 +15,29 @@ module Stoplight
15
15
  # @api private
16
16
 
17
17
  class LightFactory < Domain::LightFactory
18
- # @!attribute [r] container
19
- # The dependency injection container holding all component configurations.
20
- # Contains config, data_store, notifiers, strategies, etc.
21
- # @return [Stoplight::Wiring::Container]
22
- protected attr_reader :container
23
-
24
- # @param container [Stoplight::Wiring::Container]
25
- def initialize(container)
26
- @container = container
18
+ DEPENDENCY_KEYS = %i[data_store traffic_recovery traffic_control notifiers error_notifier].freeze
19
+ private_constant :DEPENDENCY_KEYS
20
+
21
+ CONFIG_KEYS = Domain::Config.members.freeze
22
+ private_constant :CONFIG_KEYS
23
+
24
+ # @!attribute [r] settings
25
+ # @return [Hash]
26
+ protected attr_reader :settings
27
+
28
+ def initialize(settings = {})
29
+ @settings = settings
30
+
31
+ validate_settings!
32
+ end
33
+
34
+ private def validate_settings!
35
+ recognized = CONFIG_KEYS + DEPENDENCY_KEYS
36
+ unknown = settings.keys - recognized
37
+
38
+ return if unknown.empty?
39
+
40
+ raise ArgumentError, "Unknown settings: #{unknown.join(", ")}", caller(2)
27
41
  end
28
42
 
29
43
  # @param settings [Hash] Settings to override in the new factory
@@ -31,16 +45,7 @@ module Stoplight
31
45
  # @return [Stoplight::Wiring::LightFactory]
32
46
  # @see Stoplight()
33
47
  def with(**settings)
34
- transformed_settings = transform_settings(settings)
35
- config_settings = extract_config_settings(transformed_settings)
36
- dependency_settings = extract_dependency_settings(transformed_settings)
37
-
38
- validate_settings!(transformed_settings, config_settings, dependency_settings)
39
-
40
- new_config = container.resolve(:config).with(**config_settings)
41
- new_container = container.with(config: new_config, **dependency_settings)
42
-
43
- self.class.new(new_container)
48
+ self.class.new(self.settings.merge(settings))
44
49
  end
45
50
 
46
51
  # Builds a fully-configured Light instance.
@@ -60,128 +65,36 @@ module Stoplight
60
65
  # light.run { api_call }
61
66
 
62
67
  def build
63
- validate!
64
-
65
- Stoplight::Domain::Light.new(
66
- container.resolve(:config),
67
- data_store: container.resolve(:data_store),
68
- green_run_strategy: container.resolve(:green_run_strategy),
69
- yellow_run_strategy: container.resolve(:yellow_run_strategy),
70
- red_run_strategy: container.resolve(:red_run_strategy),
71
- factory: self
72
- )
73
- end
74
-
75
- def ==(other)
76
- other.is_a?(self.class) && other.container == container
77
- end
78
-
79
- private def extract_config_settings(settings)
80
- settings.slice(*container.resolve(:config).members)
81
- end
68
+ config_settings = settings.slice(*CONFIG_KEYS)
69
+ dependency_settings = settings.slice(*DEPENDENCY_KEYS)
82
70
 
83
- private def extract_dependency_settings(settings)
84
- settings.slice(*container.keys)
85
- end
86
-
87
- private def validate_settings!(settings, config_settings, dependency_settings)
88
- recognized_keys = config_settings.keys + dependency_settings.keys
89
- unexpected_keys = settings.keys - recognized_keys
90
-
91
- return if unexpected_keys.empty?
92
- raise ArgumentError, "Unknown settings: #{unexpected_keys.join(", ")}"
93
- end
94
-
95
- private def transform_settings(settings)
96
- settings.dup.tap do |transformed_settings|
97
- transform_config_settings!(transformed_settings)
98
- transform_dependencies_settings!(transformed_settings)
99
- end
100
- end
101
-
102
- private def transform_config_settings!(settings)
103
- if settings.key?(:tracked_errors)
104
- settings[:tracked_errors] = normalize_array(settings[:tracked_errors])
105
- end
106
-
107
- if settings.key?(:skipped_errors)
108
- settings[:skipped_errors] = normalize_array(settings[:skipped_errors])
109
- end
110
-
111
- if settings.key?(:cool_off_time)
112
- settings[:cool_off_time] = normalize_cool_off_time(settings[:cool_off_time])
113
- end
114
- end
115
-
116
- private def transform_dependencies_settings!(settings)
117
- if settings.key?(:traffic_control)
118
- settings[:traffic_control] = apply_traffic_control_dsl(settings[:traffic_control])
119
- end
120
-
121
- if settings.key?(:traffic_recovery)
122
- settings[:traffic_recovery] = apply_traffic_recovery_dsl(settings[:traffic_recovery])
123
- end
71
+ config, dependencies = ConfigurationPipeline.process(
72
+ config_settings,
73
+ dependency_settings
74
+ )
75
+ LightBuilder.new(factory: self, config:, **dependencies).build
124
76
  end
125
77
 
126
- private def normalize_array(value) = Array(value)
127
- private def normalize_cool_off_time(value) = value.to_i
128
-
129
- private def apply_traffic_control_dsl(traffic_control)
130
- case traffic_control
131
- in Domain::TrafficControl::Base
132
- traffic_control
133
- in :consecutive_errors
134
- Domain::TrafficControl::ConsecutiveErrors.new
135
- in :error_rate
136
- Domain::TrafficControl::ErrorRate.new
137
- in {error_rate: error_rate_settings}
138
- Domain::TrafficControl::ErrorRate.new(**error_rate_settings)
139
- else
140
- raise Domain::Error::ConfigurationError, <<~ERROR
141
- unsupported traffic_control strategy provided (`#{traffic_control}`). Supported options:
142
- * :consecutive_errors
143
- * :error_rate
144
- ERROR
145
- end
146
- end
78
+ # @return [Stoplight::Error::ConfigurationError]
79
+ def validate_configuration!
80
+ config_settings = settings.slice(*CONFIG_KEYS)
81
+ dependency_settings = settings.slice(*DEPENDENCY_KEYS)
147
82
 
148
- def apply_traffic_recovery_dsl(traffic_recovery)
149
- case traffic_recovery
150
- in Domain::TrafficRecovery::Base
151
- traffic_recovery
152
- in :consecutive_successes
153
- Domain::TrafficRecovery::ConsecutiveSuccesses.new
154
- else
155
- raise Domain::Error::ConfigurationError, <<~ERROR
156
- unsupported traffic_recovery strategy provided (`#{traffic_recovery}`). Supported options:
157
- * :consecutive_successes
158
- ERROR
159
- end
83
+ ConfigurationPipeline.process(
84
+ config_settings,
85
+ dependency_settings
86
+ )
87
+ nil
160
88
  end
161
89
 
162
- private def validate!
163
- validate_traffic_control!(container.resolve(:traffic_control), container.resolve(:config))
164
- validate_traffic_recovery!(container.resolve(:traffic_recovery), container.resolve(:config))
90
+ def ==(other)
91
+ other.is_a?(self.class) && other.settings == settings
165
92
  end
166
93
 
167
- private def validate_traffic_control!(traffic_control, config)
168
- traffic_control.check_compatibility(config).then do |compatibility_result|
169
- if compatibility_result.incompatible?
170
- raise Domain::Error::ConfigurationError.new(
171
- "#{traffic_control.class.name} strategy is incompatible with the Stoplight configuration: #{compatibility_result.error_messages}"
172
- )
173
- end
174
- end
175
- end
94
+ alias_method :eql?, :==
176
95
 
177
- private def validate_traffic_recovery!(traffic_recovery, config)
178
- traffic_recovery.check_compatibility(config).then do |compatibility_result|
179
- if compatibility_result.incompatible?
180
- raise Domain::Error::ConfigurationError.new(
181
- "#{traffic_recovery.class.name} strategy is incompatible with the Stoplight configuration: #{compatibility_result.error_messages}"
182
- )
183
- end
184
- end
96
+ def hash
97
+ [self.class, settings].hash
185
98
  end
186
99
  end
187
100
  end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Stoplight
4
+ module Wiring
5
+ class NotifierFactory
6
+ class << self
7
+ # Wraps a notifier with fail-safe mechanisms.
8
+ #
9
+ # @param notifier [Stoplight::Domain::StateTransitionNotifier] The notifier to wrap.
10
+ # @param error_notifier [Proc] called when wrapped data store fails
11
+ # @return [Stoplight::Notifier::FailSafe] The original notifier if it is already
12
+ # a +FailSafe+ instance, otherwise a new +FailSafe+ instance.
13
+ def create(notifier:, error_notifier:)
14
+ case notifier
15
+ in Infrastructure::Notifier::FailSafe if notifier.error_notifier == error_notifier
16
+ notifier
17
+ in Infrastructure::Notifier::FailSafe
18
+ Infrastructure::Notifier::FailSafe.new(notifier: notifier.notifier, error_notifier:)
19
+ else
20
+ Infrastructure::Notifier::FailSafe.new(notifier:, error_notifier:)
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -12,8 +12,9 @@ module Stoplight
12
12
 
13
13
  # Namespace aliases for data stores
14
14
  module DataStore
15
- Redis = Infrastructure::DataStore::Redis
16
- Memory = Infrastructure::DataStore::Memory
15
+ Base = Stoplight::Wiring::DataStore::Base
16
+ Redis = Stoplight::Wiring::DataStore::Redis
17
+ Memory = Stoplight::Wiring::DataStore::Memory
17
18
  end
18
19
 
19
20
  # Namespace aliases for notifiers
data/lib/stoplight.rb CHANGED
@@ -55,8 +55,10 @@ module Stoplight # rubocop:disable Style/Documentation
55
55
  factory_builder = Wiring::DefaultFactoryBuilder.new
56
56
  yield factory_builder.configuration if block_given?
57
57
 
58
+ default_light_factory = factory_builder.build
59
+ default_light_factory.validate_configuration!
58
60
  @default_configuration = factory_builder.configuration
59
- @default_light_factory = factory_builder.build
61
+ @default_light_factory = default_light_factory
60
62
  end
61
63
  end
62
64
 
@@ -67,7 +69,7 @@ module Stoplight # rubocop:disable Style/Documentation
67
69
  # @return [Stoplight::Light]
68
70
  # @api private
69
71
  def system_light(name, **settings)
70
- Wiring::SystemLightFactory.build_with(name: "__stoplight__#{name}", **settings)
72
+ Wiring::LightFactory.new.with(name: "__stoplight__#{name}", **settings).build
71
73
  end
72
74
 
73
75
  # Create a Light with the user default configuration.
@@ -121,7 +123,6 @@ end
121
123
  # @param settings [Hash] Optional settings to configure the circuit breaker.
122
124
  # @option settings [Numeric] :cool_off_time The time to wait before resetting the circuit breaker.
123
125
  # @option settings [Stoplight::DataStore::Base] :data_store The data store to use for storing state.
124
- # @option settings [Proc] :error_notifier A proc to handle error notifications.
125
126
  # @option settings [Array<Stoplight::Notifier::Base>] :notifiers A list of notifiers to use.
126
127
  # @option settings [Numeric] :threshold The failure threshold to trip the circuit breaker.
127
128
  # @option settings [Numeric] :window_size The size of the rolling window for failure tracking.
@@ -161,5 +162,19 @@ end
161
162
  # light = Stoplight("Payment API", traffic_control: :error_rate, threshold: 0.666, window_size: 300)
162
163
  #
163
164
  def Stoplight(name, **settings) # rubocop:disable Naming/MethodName
165
+ Stoplight::Common::Deprecations.deprecate(<<~MSG) if settings.include?(:error_notifier)
166
+ Passing "error_notifier" to Stoplight('#{name}') is deprecated and will be removed in v6.0.0.
167
+
168
+ IMPORTANT: The `error_notifier` is NOT called for exceptions in your protected code.
169
+ It only reports internal Stoplight failures (e.g., Redis connection errors).
170
+
171
+ To fix: Move `error_notifier` to global configuration:
172
+
173
+ Stoplight.configure do |config|
174
+ config.error_notifier = ->(error) { Logger.warn(error) }
175
+ end
176
+
177
+ See: https://github.com/bolshakov/stoplight#error-notifiers
178
+ MSG
164
179
  Stoplight.light(name, **settings)
165
180
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: stoplight
3
3
  version: !ruby/object:Gem::Version
4
- version: 5.5.0
4
+ version: 5.7.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Cameron Desautels
@@ -25,6 +25,20 @@ dependencies:
25
25
  - - ">="
26
26
  - !ruby/object:Gem::Version
27
27
  version: '0'
28
+ - !ruby/object:Gem::Dependency
29
+ name: concurrent-ruby
30
+ requirement: !ruby/object:Gem::Requirement
31
+ requirements:
32
+ - - ">="
33
+ - !ruby/object:Gem::Version
34
+ version: '0'
35
+ type: :runtime
36
+ prerelease: false
37
+ version_requirements: !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - ">="
40
+ - !ruby/object:Gem::Version
41
+ version: '0'
28
42
  description: An implementation of the circuit breaker pattern.
29
43
  email:
30
44
  - camdez@gmail.com
@@ -47,6 +61,7 @@ files:
47
61
  - lib/stoplight/admin/actions/lock_all_green.rb
48
62
  - lib/stoplight/admin/actions/lock_green.rb
49
63
  - lib/stoplight/admin/actions/lock_red.rb
64
+ - lib/stoplight/admin/actions/remove.rb
50
65
  - lib/stoplight/admin/actions/stats.rb
51
66
  - lib/stoplight/admin/actions/unlock.rb
52
67
  - lib/stoplight/admin/dependencies.rb
@@ -57,6 +72,7 @@ files:
57
72
  - lib/stoplight/admin/views/_card.erb
58
73
  - lib/stoplight/admin/views/index.erb
59
74
  - lib/stoplight/admin/views/layout.erb
75
+ - lib/stoplight/common/deprecations.rb
60
76
  - lib/stoplight/domain/color.rb
61
77
  - lib/stoplight/domain/compatibility_result.rb
62
78
  - lib/stoplight/domain/config.rb
@@ -66,9 +82,14 @@ files:
66
82
  - lib/stoplight/domain/light.rb
67
83
  - lib/stoplight/domain/light/configuration_builder_interface.rb
68
84
  - lib/stoplight/domain/light_factory.rb
69
- - lib/stoplight/domain/metadata.rb
85
+ - lib/stoplight/domain/metrics.rb
86
+ - lib/stoplight/domain/recovery_lock_token.rb
70
87
  - lib/stoplight/domain/state.rb
88
+ - lib/stoplight/domain/state_snapshot.rb
71
89
  - lib/stoplight/domain/state_transition_notifier.rb
90
+ - lib/stoplight/domain/storage/metrics.rb
91
+ - lib/stoplight/domain/storage/recovery_lock.rb
92
+ - lib/stoplight/domain/storage/state.rb
72
93
  - lib/stoplight/domain/strategies/green_run_strategy.rb
73
94
  - lib/stoplight/domain/strategies/red_run_strategy.rb
74
95
  - lib/stoplight/domain/strategies/run_strategy.rb
@@ -82,36 +103,54 @@ files:
82
103
  - lib/stoplight/domain/traffic_recovery.rb
83
104
  - lib/stoplight/domain/traffic_recovery/base.rb
84
105
  - lib/stoplight/domain/traffic_recovery/consecutive_successes.rb
106
+ - lib/stoplight/infrastructure/data_store/fail_safe.rb
85
107
  - lib/stoplight/infrastructure/data_store/memory.rb
108
+ - lib/stoplight/infrastructure/data_store/memory/metrics.rb
109
+ - lib/stoplight/infrastructure/data_store/memory/recovery_lock_store.rb
110
+ - lib/stoplight/infrastructure/data_store/memory/recovery_lock_token.rb
86
111
  - lib/stoplight/infrastructure/data_store/memory/sliding_window.rb
112
+ - lib/stoplight/infrastructure/data_store/memory/state.rb
87
113
  - lib/stoplight/infrastructure/data_store/redis.rb
88
- - lib/stoplight/infrastructure/data_store/redis/get_metadata.lua
89
- - lib/stoplight/infrastructure/data_store/redis/lua.rb
90
- - lib/stoplight/infrastructure/data_store/redis/record_failure.lua
91
- - lib/stoplight/infrastructure/data_store/redis/record_success.lua
92
- - lib/stoplight/infrastructure/data_store/redis/transition_to_green.lua
93
- - lib/stoplight/infrastructure/data_store/redis/transition_to_red.lua
94
- - lib/stoplight/infrastructure/data_store/redis/transition_to_yellow.lua
95
- - lib/stoplight/infrastructure/dependency_injection/container.rb
96
- - lib/stoplight/infrastructure/dependency_injection/unresolved_dependency_error.rb
114
+ - lib/stoplight/infrastructure/data_store/redis/lua_scripts/get_metrics.lua
115
+ - lib/stoplight/infrastructure/data_store/redis/lua_scripts/record_failure.lua
116
+ - lib/stoplight/infrastructure/data_store/redis/lua_scripts/record_recovery_probe_failure.lua
117
+ - lib/stoplight/infrastructure/data_store/redis/lua_scripts/record_recovery_probe_success.lua
118
+ - lib/stoplight/infrastructure/data_store/redis/lua_scripts/record_success.lua
119
+ - lib/stoplight/infrastructure/data_store/redis/lua_scripts/release_lock.lua
120
+ - lib/stoplight/infrastructure/data_store/redis/lua_scripts/transition_to_green.lua
121
+ - lib/stoplight/infrastructure/data_store/redis/lua_scripts/transition_to_red.lua
122
+ - lib/stoplight/infrastructure/data_store/redis/lua_scripts/transition_to_yellow.lua
123
+ - lib/stoplight/infrastructure/data_store/redis/recovery_lock_store.rb
124
+ - lib/stoplight/infrastructure/data_store/redis/recovery_lock_token.rb
125
+ - lib/stoplight/infrastructure/data_store/redis/scripting.rb
126
+ - lib/stoplight/infrastructure/notifier/fail_safe.rb
97
127
  - lib/stoplight/infrastructure/notifier/generic.rb
98
128
  - lib/stoplight/infrastructure/notifier/io.rb
99
129
  - lib/stoplight/infrastructure/notifier/logger.rb
130
+ - lib/stoplight/infrastructure/storage/compatibility_metrics.rb
131
+ - lib/stoplight/infrastructure/storage/compatibility_recovery_lock.rb
132
+ - lib/stoplight/infrastructure/storage/compatibility_recovery_metrics.rb
133
+ - lib/stoplight/infrastructure/storage/compatibility_state.rb
100
134
  - lib/stoplight/rspec.rb
101
135
  - lib/stoplight/rspec/generic_notifier.rb
102
136
  - lib/stoplight/version.rb
103
- - lib/stoplight/wiring/container.rb
137
+ - lib/stoplight/wiring/data_store/base.rb
138
+ - lib/stoplight/wiring/data_store/memory.rb
139
+ - lib/stoplight/wiring/data_store/redis.rb
104
140
  - lib/stoplight/wiring/default.rb
105
141
  - lib/stoplight/wiring/default_configuration.rb
106
142
  - lib/stoplight/wiring/default_factory_builder.rb
107
- - lib/stoplight/wiring/fail_safe_data_store.rb
108
- - lib/stoplight/wiring/fail_safe_notifier.rb
109
143
  - lib/stoplight/wiring/light/default_config.rb
110
144
  - lib/stoplight/wiring/light/system_config.rb
145
+ - lib/stoplight/wiring/light_builder.rb
111
146
  - lib/stoplight/wiring/light_factory.rb
147
+ - lib/stoplight/wiring/light_factory/compatibility_validator.rb
148
+ - lib/stoplight/wiring/light_factory/config_normalizer.rb
149
+ - lib/stoplight/wiring/light_factory/configuration_pipeline.rb
150
+ - lib/stoplight/wiring/light_factory/traffic_control_dsl.rb
151
+ - lib/stoplight/wiring/light_factory/traffic_recovery_dsl.rb
152
+ - lib/stoplight/wiring/notifier_factory.rb
112
153
  - lib/stoplight/wiring/public_api.rb
113
- - lib/stoplight/wiring/system_container.rb
114
- - lib/stoplight/wiring/system_light_factory.rb
115
154
  homepage: https://github.com/bolshakov/stoplight
116
155
  licenses:
117
156
  - MIT
@@ -1,38 +0,0 @@
1
- local number_of_metric_buckets = tonumber(ARGV[1])
2
- local number_of_recovery_buckets = tonumber(ARGV[2])
3
- local window_start_ts = tonumber(ARGV[3])
4
- local window_end_ts = tonumber(ARGV[4])
5
- local recovery_window_start_ts = tonumber(ARGV[5])
6
-
7
- local metadata_key = KEYS[1]
8
-
9
- -- It possible that after a successful recovery, Stoplight still see metrics
10
- -- that are older than the recovery window. To prevent this from happening,
11
- -- we need to limit the start time of the window to the time of the last recovery.
12
- local recovered_at = redis.call('HGET', metadata_key, "recovered_at")
13
- if recovered_at then
14
- window_start_ts = math.max(window_start_ts, tonumber(recovered_at))
15
- end
16
-
17
- local function count_events(start_idx, bucket_count, start_ts)
18
- local total = 0
19
- for idx = start_idx, start_idx + bucket_count - 1 do
20
- total = total + tonumber(redis.call('ZCOUNT', KEYS[idx], start_ts, window_end_ts))
21
- end
22
- return total
23
- end
24
-
25
- local offset = 2
26
- local successes = count_events(2, number_of_metric_buckets, window_start_ts)
27
-
28
- offset = offset + number_of_metric_buckets
29
- local errors = count_events(offset, number_of_metric_buckets, window_start_ts)
30
-
31
- offset = offset + number_of_metric_buckets
32
- local recovery_probe_successes = count_events(offset, number_of_recovery_buckets, recovery_window_start_ts)
33
-
34
- offset = offset + number_of_recovery_buckets
35
- local recovery_probe_errors = count_events(offset, number_of_recovery_buckets, recovery_window_start_ts)
36
-
37
- local metadata = redis.call('HGETALL', metadata_key)
38
- return {successes, errors, recovery_probe_successes, recovery_probe_errors, metadata}
@@ -1,25 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Stoplight
4
- module Infrastructure
5
- module DataStore
6
- class Redis
7
- # @api private
8
- module Lua
9
- class << self
10
- def read_lua_file(name_without_extension)
11
- File.read(File.join(__dir__, "#{name_without_extension}.lua"))
12
- end
13
- end
14
-
15
- RECORD_FAILURE = read_lua_file("record_failure")
16
- RECORD_SUCCESS = read_lua_file("record_success")
17
- GET_METADATA = read_lua_file("get_metadata")
18
- TRANSITION_TO_YELLOW = read_lua_file("transition_to_yellow")
19
- TRANSITION_TO_RED = read_lua_file("transition_to_red")
20
- TRANSITION_TO_GREEN = read_lua_file("transition_to_green")
21
- end
22
- end
23
- end
24
- end
25
- end