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.
Files changed (238) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/UPGRADING.md +303 -0
  4. data/lib/generators/stoplight/install/install_generator.rb +6 -1
  5. data/lib/stoplight/admin/dependencies.rb +1 -1
  6. data/lib/stoplight/admin/helpers.rb +26 -5
  7. data/lib/stoplight/admin/lights_repository/light.rb +22 -6
  8. data/lib/stoplight/admin/lights_repository.rb +20 -16
  9. data/lib/stoplight/admin/views/_card.erb +8 -5
  10. data/lib/stoplight/admin.rb +2 -1
  11. data/lib/stoplight/color.rb +9 -0
  12. data/lib/stoplight/common/deprecations.rb +11 -0
  13. data/lib/stoplight/data_store.rb +28 -0
  14. data/lib/stoplight/domain/compatibility_result.rb +7 -7
  15. data/lib/stoplight/domain/config.rb +38 -35
  16. data/lib/stoplight/domain/error_tracking_policy.rb +27 -0
  17. data/lib/stoplight/domain/failure.rb +1 -1
  18. data/lib/stoplight/domain/light/configuration_builder_interface.rb +122 -16
  19. data/lib/stoplight/domain/light.rb +44 -64
  20. data/lib/stoplight/domain/light_info.rb +7 -0
  21. data/lib/stoplight/domain/metrics_snapshot.rb +58 -0
  22. data/lib/stoplight/domain/state_snapshot.rb +29 -23
  23. data/lib/stoplight/domain/storage/recovery_lock_token.rb +15 -0
  24. data/lib/stoplight/domain/strategies/green_run_strategy.rb +18 -26
  25. data/lib/stoplight/domain/strategies/red_run_strategy.rb +9 -12
  26. data/lib/stoplight/domain/strategies/yellow_run_strategy.rb +74 -58
  27. data/lib/stoplight/domain/tracker/recovery_probe.rb +27 -43
  28. data/lib/stoplight/domain/tracker/request.rb +24 -39
  29. data/lib/stoplight/domain/traffic_control/consecutive_errors.rb +8 -11
  30. data/lib/stoplight/domain/traffic_control/error_rate.rb +19 -15
  31. data/lib/stoplight/domain/traffic_recovery/consecutive_successes.rb +8 -18
  32. data/lib/stoplight/domain/traffic_recovery.rb +3 -5
  33. data/lib/stoplight/error.rb +46 -0
  34. data/lib/stoplight/infrastructure/fail_safe/data_store.rb +152 -0
  35. data/lib/stoplight/infrastructure/fail_safe/storage/metrics.rb +65 -0
  36. data/lib/stoplight/infrastructure/fail_safe/storage/recovery_lock.rb +69 -0
  37. data/lib/stoplight/infrastructure/fail_safe/storage/recovery_lock_token.rb +19 -0
  38. data/lib/stoplight/infrastructure/fail_safe/storage/state.rb +62 -0
  39. data/lib/stoplight/infrastructure/{data_store/memory → memory/data_store}/metrics.rb +2 -2
  40. data/lib/stoplight/infrastructure/memory/data_store/recovery_lock_store.rb +52 -0
  41. data/lib/stoplight/infrastructure/memory/data_store/recovery_lock_token.rb +17 -0
  42. data/lib/stoplight/infrastructure/{data_store/memory → memory/data_store}/sliding_window.rb +21 -26
  43. data/lib/stoplight/infrastructure/{data_store/memory → memory/data_store}/state.rb +3 -3
  44. data/lib/stoplight/infrastructure/{data_store/memory.rb → memory/data_store.rb} +90 -57
  45. data/lib/stoplight/infrastructure/memory/storage/recovery_lock.rb +35 -0
  46. data/lib/stoplight/infrastructure/memory/storage/recovery_metrics.rb +16 -0
  47. data/lib/stoplight/infrastructure/memory/storage/state.rb +155 -0
  48. data/lib/stoplight/infrastructure/memory/storage/unbounded_metrics.rb +103 -0
  49. data/lib/stoplight/infrastructure/memory/storage/window_metrics.rb +101 -0
  50. data/lib/stoplight/infrastructure/notifier/fail_safe.rb +50 -0
  51. data/lib/stoplight/infrastructure/notifier/generic.rb +4 -14
  52. data/lib/stoplight/infrastructure/notifier/io.rb +1 -2
  53. data/lib/stoplight/infrastructure/notifier/logger.rb +1 -2
  54. data/lib/stoplight/infrastructure/redis/data_store/lua_scripts/record_recovery_probe_failure.lua +27 -0
  55. data/lib/stoplight/infrastructure/redis/data_store/lua_scripts/record_recovery_probe_success.lua +23 -0
  56. data/lib/stoplight/infrastructure/redis/data_store/lua_scripts/release_lock.lua +6 -0
  57. data/lib/stoplight/infrastructure/redis/data_store/recovery_lock_store.rb +60 -0
  58. data/lib/stoplight/infrastructure/redis/data_store/recovery_lock_token.rb +28 -0
  59. data/lib/stoplight/infrastructure/redis/data_store/scripting.rb +73 -0
  60. data/lib/stoplight/infrastructure/{data_store/redis.rb → redis/data_store.rb} +173 -210
  61. data/lib/stoplight/infrastructure/redis/storage/key_space.rb +51 -0
  62. data/lib/stoplight/infrastructure/redis/storage/metrics.rb +40 -0
  63. data/lib/stoplight/infrastructure/redis/storage/recovery_lock/release_lock.lua +6 -0
  64. data/lib/stoplight/infrastructure/redis/storage/recovery_lock.rb +64 -0
  65. data/lib/stoplight/infrastructure/redis/storage/recovery_metrics.rb +20 -0
  66. data/lib/stoplight/infrastructure/redis/storage/scripting.rb +18 -0
  67. data/lib/stoplight/infrastructure/redis/storage/state/transition_to_green.lua +10 -0
  68. data/lib/stoplight/infrastructure/redis/storage/state/transition_to_red.lua +10 -0
  69. data/lib/stoplight/infrastructure/redis/storage/state/transition_to_yellow.lua +9 -0
  70. data/lib/stoplight/infrastructure/redis/storage/state.rb +141 -0
  71. data/lib/stoplight/infrastructure/redis/storage/unbounded_metrics/record_failure.lua +28 -0
  72. data/lib/stoplight/infrastructure/redis/storage/unbounded_metrics/record_success.lua +26 -0
  73. data/lib/stoplight/infrastructure/redis/storage/unbounded_metrics.rb +123 -0
  74. data/lib/stoplight/infrastructure/redis/storage/window_metrics/metrics_snapshot.lua +26 -0
  75. data/lib/stoplight/infrastructure/redis/storage/window_metrics/record_failure.lua +36 -0
  76. data/lib/stoplight/infrastructure/redis/storage/window_metrics/record_success.lua +35 -0
  77. data/lib/stoplight/infrastructure/redis/storage/window_metrics.rb +174 -0
  78. data/lib/stoplight/infrastructure/storage/compatibility_metrics.rb +41 -0
  79. data/lib/stoplight/infrastructure/storage/compatibility_recovery_lock.rb +33 -0
  80. data/lib/stoplight/infrastructure/storage/compatibility_recovery_metrics.rb +47 -0
  81. data/lib/stoplight/infrastructure/storage/compatibility_state.rb +44 -0
  82. data/lib/stoplight/infrastructure/system_clock.rb +16 -0
  83. data/lib/stoplight/notifier.rb +11 -0
  84. data/lib/stoplight/state.rb +9 -0
  85. data/lib/stoplight/types.rb +29 -0
  86. data/lib/stoplight/undefined.rb +16 -0
  87. data/lib/stoplight/version.rb +1 -1
  88. data/lib/stoplight/wiring/config_compatibility_validator.rb +54 -0
  89. data/lib/stoplight/wiring/configuration_dsl.rb +101 -0
  90. data/lib/stoplight/wiring/data_store_backend.rb +26 -0
  91. data/lib/stoplight/wiring/default.rb +2 -2
  92. data/lib/stoplight/wiring/default_config.rb +21 -0
  93. data/lib/stoplight/wiring/default_configuration.rb +70 -53
  94. data/lib/stoplight/wiring/light_builder.rb +198 -0
  95. data/lib/stoplight/wiring/light_factory/traffic_control_dsl.rb +26 -0
  96. data/lib/stoplight/wiring/light_factory/traffic_recovery_dsl.rb +21 -0
  97. data/lib/stoplight/wiring/light_factory.rb +74 -135
  98. data/lib/stoplight/wiring/memory/backend.rb +57 -0
  99. data/lib/stoplight/wiring/notifier_factory.rb +26 -0
  100. data/lib/stoplight/wiring/redis/backend.rb +116 -0
  101. data/lib/stoplight/wiring/storage_set.rb +12 -0
  102. data/lib/stoplight/wiring/storage_set_builder.rb +51 -0
  103. data/lib/stoplight/wiring/system/light_builder.rb +47 -0
  104. data/lib/stoplight/wiring/system/light_factory.rb +64 -0
  105. data/lib/stoplight/wiring/system.rb +129 -0
  106. data/lib/stoplight.rb +209 -23
  107. data/sig/_private/generators/stoplight/install/install_generator.rbs +22 -0
  108. data/sig/_private/stoplight/common/deprecations.rbs +9 -0
  109. data/sig/_private/stoplight/data_store.rbs +6 -0
  110. data/sig/_private/stoplight/domain/compatibility_result.rbs +18 -0
  111. data/sig/_private/stoplight/domain/config.rbs +65 -0
  112. data/sig/_private/stoplight/domain/error_tracking_policy.rbs +14 -0
  113. data/sig/_private/stoplight/domain/failure.rbs +16 -0
  114. data/sig/_private/stoplight/domain/light.rbs +25 -0
  115. data/sig/_private/stoplight/domain/light_info.rbs +19 -0
  116. data/sig/_private/stoplight/domain/metrics_snapshot.rbs +38 -0
  117. data/sig/_private/stoplight/domain/ports/clock.rbs +18 -0
  118. data/sig/_private/stoplight/domain/ports/data_store.rbs +76 -0
  119. data/{lib/stoplight/domain/light_factory.rb → sig/_private/stoplight/domain/ports/light_factory.rbs} +33 -28
  120. data/sig/_private/stoplight/domain/ports/metrics_store.rbs +29 -0
  121. data/sig/_private/stoplight/domain/ports/recovery_lock_store.rbs +52 -0
  122. data/sig/_private/stoplight/domain/ports/recovery_lock_token.rbs +6 -0
  123. data/sig/_private/stoplight/domain/ports/run_strategy.rbs +14 -0
  124. data/sig/_private/stoplight/domain/ports/state_store.rbs +79 -0
  125. data/sig/_private/stoplight/domain/ports/traffic_control.rbs +41 -0
  126. data/sig/_private/stoplight/domain/ports/traffic_recovery.rbs +47 -0
  127. data/sig/_private/stoplight/domain/state_snapshot.rbs +32 -0
  128. data/sig/_private/stoplight/domain/storage/recovery_lock_token.rbs +11 -0
  129. data/sig/_private/stoplight/domain/strategies/green_run_strategy.rbs +17 -0
  130. data/sig/_private/stoplight/domain/strategies/red_run_strategy.rbs +17 -0
  131. data/sig/_private/stoplight/domain/strategies/yellow_run_strategy.rbs +42 -0
  132. data/sig/_private/stoplight/domain/tracker/base.rbs +8 -0
  133. data/sig/_private/stoplight/domain/tracker/recovery_probe.rbs +25 -0
  134. data/sig/_private/stoplight/domain/tracker/request.rbs +26 -0
  135. data/sig/_private/stoplight/domain/traffic_control/consecutive_errors.rbs +9 -0
  136. data/sig/_private/stoplight/domain/traffic_control/error_rate.rbs +13 -0
  137. data/sig/_private/stoplight/domain/traffic_recovery/consecutive_successes.rbs +9 -0
  138. data/sig/_private/stoplight/domain/traffic_recovery.rbs +9 -0
  139. data/sig/_private/stoplight/infrastructure/fail_safe/data_store.rbs +26 -0
  140. data/sig/_private/stoplight/infrastructure/fail_safe/storage/metrics.rbs +25 -0
  141. data/sig/_private/stoplight/infrastructure/fail_safe/storage/recovery_lock.rbs +29 -0
  142. data/sig/_private/stoplight/infrastructure/fail_safe/storage/recovery_lock_token.rbs +19 -0
  143. data/sig/_private/stoplight/infrastructure/fail_safe/storage/state.rbs +25 -0
  144. data/sig/_private/stoplight/infrastructure/memory/data_store/metrics.rbs +25 -0
  145. data/sig/_private/stoplight/infrastructure/memory/data_store/recovery_lock_store.rbs +19 -0
  146. data/sig/_private/stoplight/infrastructure/memory/data_store/recovery_lock_token.rbs +17 -0
  147. data/sig/_private/stoplight/infrastructure/memory/data_store/sliding_window.rbs +27 -0
  148. data/sig/_private/stoplight/infrastructure/memory/data_store/state.rbs +17 -0
  149. data/sig/_private/stoplight/infrastructure/memory/data_store.rbs +30 -0
  150. data/sig/_private/stoplight/infrastructure/memory/storage/recovery_lock.rbs +15 -0
  151. data/sig/_private/stoplight/infrastructure/memory/storage/recovery_metrics.rbs +10 -0
  152. data/sig/_private/stoplight/infrastructure/memory/storage/state.rbs +28 -0
  153. data/sig/_private/stoplight/infrastructure/memory/storage/unbounded_metrics.rbs +25 -0
  154. data/sig/_private/stoplight/infrastructure/memory/storage/window_metrics.rbs +26 -0
  155. data/sig/_private/stoplight/infrastructure/notifier/fail_safe.rbs +17 -0
  156. data/sig/_private/stoplight/infrastructure/notifier/generic.rbs +18 -0
  157. data/sig/_private/stoplight/infrastructure/notifier/io.rbs +14 -0
  158. data/sig/_private/stoplight/infrastructure/notifier/logger.rbs +14 -0
  159. data/sig/_private/stoplight/infrastructure/redis/data_store/recovery_lock_store.rbs +24 -0
  160. data/sig/_private/stoplight/infrastructure/redis/data_store/recovery_lock_token.rbs +21 -0
  161. data/sig/_private/stoplight/infrastructure/redis/data_store/scripting.rbs +34 -0
  162. data/sig/_private/stoplight/infrastructure/redis/data_store.rbs +67 -0
  163. data/sig/_private/stoplight/infrastructure/redis/storage/key_space.rbs +19 -0
  164. data/sig/_private/stoplight/infrastructure/redis/storage/metrics.rbs +17 -0
  165. data/sig/_private/stoplight/infrastructure/redis/storage/recovery_lock.rbs +26 -0
  166. data/sig/_private/stoplight/infrastructure/redis/storage/recovery_metrics.rbs +10 -0
  167. data/sig/_private/stoplight/infrastructure/redis/storage/scripting.rbs +13 -0
  168. data/sig/_private/stoplight/infrastructure/redis/storage/state.rbs +32 -0
  169. data/sig/_private/stoplight/infrastructure/redis/storage/unbounded_metrics.rbs +21 -0
  170. data/sig/_private/stoplight/infrastructure/redis/storage/window_metrics.rbs +34 -0
  171. data/sig/_private/stoplight/infrastructure/storage/compatibility_metrics.rbs +17 -0
  172. data/sig/_private/stoplight/infrastructure/storage/compatibility_recovery_lock.rbs +13 -0
  173. data/sig/_private/stoplight/infrastructure/storage/compatibility_recovery_metrics.rbs +14 -0
  174. data/sig/_private/stoplight/infrastructure/storage/compatibility_state.rbs +14 -0
  175. data/sig/_private/stoplight/infrastructure/system_clock.rbs +7 -0
  176. data/sig/_private/stoplight/system/light_builder.rbs +23 -0
  177. data/sig/_private/stoplight/system/light_factory.rbs +17 -0
  178. data/sig/_private/stoplight/types.rbs +6 -0
  179. data/sig/_private/stoplight/wiring/config_compatibility_validator.rbs +19 -0
  180. data/sig/_private/stoplight/wiring/configuration_dsl.rbs +43 -0
  181. data/sig/_private/stoplight/wiring/data_store_backend.rbs +11 -0
  182. data/sig/_private/stoplight/wiring/default.rbs +26 -0
  183. data/sig/_private/stoplight/wiring/default_config.rbs +7 -0
  184. data/sig/_private/stoplight/wiring/default_configuration.rbs +29 -0
  185. data/sig/_private/stoplight/wiring/light_builder.rbs +48 -0
  186. data/sig/_private/stoplight/wiring/light_factory/traffic_control_dsl.rbs +7 -0
  187. data/sig/_private/stoplight/wiring/light_factory/traffic_recovery_dsl.rbs +7 -0
  188. data/sig/_private/stoplight/wiring/light_factory.rbs +16 -0
  189. data/sig/_private/stoplight/wiring/memory/backend.rbs +26 -0
  190. data/sig/_private/stoplight/wiring/notifier_factory.rbs +10 -0
  191. data/sig/_private/stoplight/wiring/redis/backend.rbs +38 -0
  192. data/sig/_private/stoplight/wiring/storage_set.rbs +38 -0
  193. data/sig/_private/stoplight/wiring/storage_set_builder.rbs +15 -0
  194. data/sig/_private/stoplight/wiring/system.rbs +15 -0
  195. data/sig/_private/stoplight.rbs +48 -0
  196. data/sig/stoplight/color.rbs +7 -0
  197. data/sig/stoplight/data_store.rbs +19 -0
  198. data/sig/stoplight/error.rbs +20 -0
  199. data/sig/stoplight/notifier.rbs +11 -0
  200. data/sig/stoplight/ports/configuration.rbs +19 -0
  201. data/sig/stoplight/ports/exception_matcher.rbs +8 -0
  202. data/sig/stoplight/ports/light.rbs +12 -0
  203. data/sig/stoplight/ports/light_info.rbs +5 -0
  204. data/sig/stoplight/ports/state_transition_notifier.rbs +15 -0
  205. data/sig/stoplight/ports/system.rbs +21 -0
  206. data/sig/stoplight/state.rbs +7 -0
  207. data/sig/stoplight/undefined.rbs +9 -0
  208. data/sig/stoplight/version.rbs +3 -0
  209. data/sig/stoplight.rbs +66 -0
  210. metadata +199 -36
  211. data/lib/stoplight/domain/color.rb +0 -11
  212. data/lib/stoplight/domain/data_store.rb +0 -130
  213. data/lib/stoplight/domain/error.rb +0 -42
  214. data/lib/stoplight/domain/metrics.rb +0 -85
  215. data/lib/stoplight/domain/state.rb +0 -11
  216. data/lib/stoplight/domain/state_transition_notifier.rb +0 -25
  217. data/lib/stoplight/domain/strategies/run_strategy.rb +0 -27
  218. data/lib/stoplight/domain/tracker/base.rb +0 -41
  219. data/lib/stoplight/domain/traffic_control/base.rb +0 -74
  220. data/lib/stoplight/domain/traffic_recovery/base.rb +0 -80
  221. data/lib/stoplight/infrastructure/data_store/redis/lua.rb +0 -25
  222. data/lib/stoplight/infrastructure/dependency_injection/container.rb +0 -249
  223. data/lib/stoplight/infrastructure/dependency_injection/unresolved_dependency_error.rb +0 -13
  224. data/lib/stoplight/wiring/container.rb +0 -80
  225. data/lib/stoplight/wiring/default_factory_builder.rb +0 -25
  226. data/lib/stoplight/wiring/fail_safe_data_store.rb +0 -147
  227. data/lib/stoplight/wiring/fail_safe_notifier.rb +0 -79
  228. data/lib/stoplight/wiring/light/default_config.rb +0 -18
  229. data/lib/stoplight/wiring/light/system_config.rb +0 -11
  230. data/lib/stoplight/wiring/public_api.rb +0 -28
  231. data/lib/stoplight/wiring/system_container.rb +0 -9
  232. data/lib/stoplight/wiring/system_light_factory.rb +0 -17
  233. /data/lib/stoplight/infrastructure/{data_store/redis → redis/data_store/lua_scripts}/get_metrics.lua +0 -0
  234. /data/lib/stoplight/infrastructure/{data_store/redis → redis/data_store/lua_scripts}/record_failure.lua +0 -0
  235. /data/lib/stoplight/infrastructure/{data_store/redis → redis/data_store/lua_scripts}/record_success.lua +0 -0
  236. /data/lib/stoplight/infrastructure/{data_store/redis → redis/data_store/lua_scripts}/transition_to_green.lua +0 -0
  237. /data/lib/stoplight/infrastructure/{data_store/redis → redis/data_store/lua_scripts}/transition_to_red.lua +0 -0
  238. /data/lib/stoplight/infrastructure/{data_store/redis → redis/data_store/lua_scripts}/transition_to_yellow.lua +0 -0
@@ -9,28 +9,28 @@ module Stoplight
9
9
  class << self
10
10
  # Creates a new +CompatibilityResult+ instance representing a compatible strategy.
11
11
  #
12
- # @return [CompatibilityResult] An instance with no errors.
12
+ # @return An instance with no errors.
13
13
  def compatible
14
14
  new(errors: [])
15
15
  end
16
16
 
17
17
  # Creates a new +CompatibilityResult+ instance representing an incompatible strategy.
18
18
  #
19
- # @param errors [Array<String>] List of error messages indicating incompatibility.
20
- # @return [CompatibilityResult] An instance with the provided errors.
19
+ # @param errors List of error messages indicating incompatibility.
20
+ # @return An instance with the provided errors.
21
21
  def incompatible(*errors)
22
22
  new(errors:)
23
23
  end
24
24
  end
25
25
 
26
26
  # Initializes a new `CompatibilityResult` instance.
27
- # @param errors [Array<String>] List of error messages if the strategy is not compatible.
27
+ # @param errors List of error messages if the strategy is not compatible.
28
28
  def initialize(errors: [])
29
29
  @errors = errors.freeze
30
30
  end
31
31
 
32
32
  # Checks if the strategy is compatible.
33
- # @return [Boolean] `true` if there are no errors, `false` otherwise.
33
+ # @return `true` if there are no errors, `false` otherwise.
34
34
  def compatible?
35
35
  @errors.empty?
36
36
  end
@@ -38,11 +38,11 @@ module Stoplight
38
38
  def incompatible? = !compatible?
39
39
 
40
40
  # Retrieves the list of error messages.
41
- # @return [Array<String>] The list of error messages.
41
+ # @return The list of error messages.
42
42
  attr_reader :errors
43
43
 
44
44
  # Retrieves a concatenated error message string.
45
- # @return [String, nil] A string containing all error messages joined by "; ",
45
+ # @return A string containing all error messages joined by "; ",
46
46
  # or `nil` if the strategy is compatible.
47
47
  def error_messages
48
48
  unless compatible?
@@ -4,24 +4,6 @@ module Stoplight
4
4
  module Domain
5
5
  # A +Stoplight::Light+ configuration object.
6
6
  #
7
- # # @!attribute [r] name
8
- # @return [String]
9
- #
10
- # @!attribute [r] cool_off_time
11
- # @return [Numeric]
12
- #
13
- # @!attribute [r] threshold
14
- # @return [Numeric]
15
- #
16
- # @!attribute [r] window_size
17
- # @return [Numeric]
18
- #
19
- # @!attribute [r] tracked_errors
20
- # @return [Array<StandardError>]
21
- #
22
- # @!attribute [r] skipped_errors
23
- # @return [Array<Exception>]
24
- #
25
7
  # @api private
26
8
  Config = Data.define(
27
9
  :name,
@@ -30,25 +12,46 @@ module Stoplight
30
12
  :recovery_threshold,
31
13
  :window_size,
32
14
  :tracked_errors,
33
- :skipped_errors
34
- ) do
35
- class << self
36
- # Creates a new NULL configuration object.
37
- # @return [Stoplight::Domain::Config]
38
- def empty
39
- new(**members.map { |key| [key, nil] }.to_h)
40
- end
15
+ :skipped_errors,
16
+ :traffic_control,
17
+ :traffic_recovery,
18
+ :error_notifier,
19
+ :notifiers,
20
+ :data_store
21
+ )
22
+ class Config
23
+ def cool_off_time_in_milliseconds
24
+ (cool_off_time * 1_000).to_i
41
25
  end
42
26
 
43
- # Checks if the given error should be tracked
44
- #
45
- # @param error [#==] The error to check, e.g. an Exception, Class or Proc
46
- # @return [Boolean]
47
- def track_error?(error)
48
- skip = skipped_errors.any? { |klass| klass === error }
49
- track = tracked_errors.any? { |klass| klass === error }
50
-
51
- !skip && track
27
+ def with(
28
+ name: T.undefined,
29
+ cool_off_time: T.undefined,
30
+ threshold: T.undefined,
31
+ recovery_threshold: T.undefined,
32
+ window_size: T.undefined,
33
+ skipped_errors: T.undefined,
34
+ tracked_errors: T.undefined,
35
+ traffic_control: T.undefined,
36
+ traffic_recovery: T.undefined,
37
+ error_notifier: T.undefined,
38
+ notifiers: T.undefined,
39
+ data_store: T.undefined
40
+ )
41
+ super(
42
+ name: name.is_a?(Undefined) ? self.name : name,
43
+ cool_off_time: cool_off_time.is_a?(Undefined) ? self.cool_off_time : cool_off_time,
44
+ threshold: threshold.is_a?(Undefined) ? self.threshold : threshold,
45
+ recovery_threshold: recovery_threshold.is_a?(Undefined) ? self.recovery_threshold : recovery_threshold,
46
+ window_size: window_size.is_a?(Undefined) ? self.window_size : window_size,
47
+ skipped_errors: skipped_errors.is_a?(Undefined) ? self.skipped_errors : skipped_errors,
48
+ tracked_errors: tracked_errors.is_a?(Undefined) ? self.tracked_errors : tracked_errors,
49
+ traffic_control: traffic_control.is_a?(Undefined) ? self.traffic_control : traffic_control,
50
+ traffic_recovery: traffic_recovery.is_a?(Undefined) ? self.traffic_recovery : traffic_recovery,
51
+ error_notifier: error_notifier.is_a?(Undefined) ? self.error_notifier : error_notifier,
52
+ notifiers: notifiers.is_a?(Undefined) ? self.notifiers : notifiers,
53
+ data_store: data_store.is_a?(Undefined) ? self.data_store : data_store,
54
+ )
52
55
  end
53
56
  end
54
57
  end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Stoplight
4
+ module Domain
5
+ # Determines which errors should be traced
6
+ class ErrorTrackingPolicy
7
+ def initialize(tracked:, skipped:)
8
+ @tracked = tracked
9
+ @skipped = skipped
10
+ end
11
+
12
+ def track?(error)
13
+ !skipped?(error) && tracked?(error)
14
+ end
15
+
16
+ private
17
+
18
+ def skipped?(error)
19
+ @skipped.any? { |matcher| matcher === error }
20
+ end
21
+
22
+ def tracked?(error)
23
+ @tracked.any? { |matcher| matcher === error }
24
+ end
25
+ end
26
+ end
27
+ end
@@ -16,7 +16,7 @@ module Stoplight
16
16
 
17
17
  # @param error [Exception]
18
18
  # @return (see #initialize)
19
- def self.from_error(error, time: Time.now)
19
+ def self.from_error(error, time:)
20
20
  new(error.class.name, error.message, time)
21
21
  end
22
22
 
@@ -6,6 +6,7 @@ module Stoplight
6
6
  module Domain
7
7
  class Light
8
8
  # Implements light configuration behavior
9
+ # steep:ignore:start
9
10
  module ConfigurationBuilderInterface
10
11
  # Configures data store to be used with this circuit breaker
11
12
  #
@@ -15,9 +16,22 @@ module Stoplight
15
16
  #
16
17
  # @param data_store [DataStore::Base]
17
18
  # @return [Stoplight::Light]
18
- # @deprecated consider using +Light#with+ for reconfiguration
19
+ # @deprecated
19
20
  def with_data_store(data_store)
20
- with(data_store:)
21
+ deprecate(<<~MSG)
22
+ Light#with_data_store is deprecated and will be removed in v6.0.0.
23
+
24
+ Circuit breakers should be configured once at creation, not cloned with
25
+ modifications.
26
+
27
+ Instead of:
28
+ light = Stoplight('api-call')
29
+ modified = light.with_data_store(data_stare)
30
+
31
+ Configure correctly from the start:
32
+ Stoplight('api-call', data_store:)
33
+ MSG
34
+ with_without_warning(data_store:)
21
35
  end
22
36
 
23
37
  # Configures cool off time. Stoplight automatically tries to recover
@@ -29,9 +43,22 @@ module Stoplight
29
43
  #
30
44
  # @param cool_off_time [Numeric] number of seconds
31
45
  # @return [Stoplight::Light]
32
- # @deprecated consider using +Light#with+ for reconfiguration
46
+ # @deprecated
33
47
  def with_cool_off_time(cool_off_time)
34
- with(cool_off_time:)
48
+ deprecate(<<~MSG)
49
+ Light#with_cool_off_time is deprecated and will be removed in v6.0.0.
50
+
51
+ Circuit breakers should be configured once at creation, not cloned with
52
+ modifications.
53
+
54
+ Instead of:
55
+ light = Stoplight('api-call')
56
+ modified = light.with_cool_off_time(cool_off_time)
57
+
58
+ Configure correctly from the start:
59
+ Stoplight('api-call', cool_off_time:)
60
+ MSG
61
+ with_without_warning(cool_off_time:)
35
62
  end
36
63
 
37
64
  # Configures custom threshold. After this number of failures Stoplight
@@ -43,9 +70,22 @@ module Stoplight
43
70
  #
44
71
  # @param threshold [Numeric]
45
72
  # @return [Stoplight::Light]
46
- # @deprecated consider using +Light#with+ for reconfiguration
73
+ # @deprecated
47
74
  def with_threshold(threshold)
48
- with(threshold:)
75
+ deprecate(<<~MSG)
76
+ Light#with_threshold is deprecated and will be removed in v6.0.0.
77
+
78
+ Circuit breakers should be configured once at creation, not cloned with
79
+ modifications.
80
+
81
+ Instead of:
82
+ light = Stoplight('api-call')
83
+ modified = light.with_threshold(threshold)
84
+
85
+ Configure correctly from the start:
86
+ Stoplight('api-call', threshold:)
87
+ MSG
88
+ with_without_warning(threshold:)
49
89
  end
50
90
 
51
91
  # Configures custom window size which Stoplight uses to count failures. For example,
@@ -60,9 +100,22 @@ module Stoplight
60
100
  #
61
101
  # @param window_size [Numeric] number of seconds
62
102
  # @return [Stoplight::Light]
63
- # @deprecated consider using +Light#with+ for reconfiguration
103
+ # @deprecated
64
104
  def with_window_size(window_size)
65
- with(window_size:)
105
+ deprecate(<<~MSG)
106
+ Light#with_window_size is deprecated and will be removed in v6.0.0.
107
+
108
+ Circuit breakers should be configured once at creation, not cloned with
109
+ modifications.
110
+
111
+ Instead of:
112
+ light = Stoplight('api-call')
113
+ modified = light.with_window_size(window_size)
114
+
115
+ Configure correctly from the start:
116
+ Stoplight('api-call', window_size:)
117
+ MSG
118
+ with_without_warning(window_size:)
66
119
  end
67
120
 
68
121
  # Configures custom notifier
@@ -75,17 +128,43 @@ module Stoplight
75
128
  #
76
129
  # @param notifiers [Array<Notifier::Base>]
77
130
  # @return [Stoplight::Light]
78
- # @deprecated consider using +Light#with+ for reconfiguration
131
+ # @deprecated
79
132
  def with_notifiers(notifiers)
80
- with(notifiers:)
133
+ deprecate(<<~MSG)
134
+ Light#with_notifiers is deprecated and will be removed in v6.0.0.
135
+
136
+ Circuit breakers should be configured once at creation, not cloned with
137
+ modifications.
138
+
139
+ Instead of:
140
+ light = Stoplight('api-call')
141
+ modified = light.with_notifiers(notifiers)
142
+
143
+ Configure correctly from the start:
144
+ Stoplight('api-call', notifiers:)
145
+ MSG
146
+ with_without_warning(notifiers:)
81
147
  end
82
148
 
83
149
  # @param error_notifier [Proc]
84
150
  # @return [Stoplight::Light]
85
151
  # @api private
86
- # @deprecated consider using +Light#with+ for reconfiguration
152
+ # @deprecated
87
153
  def with_error_notifier(&error_notifier)
88
- with(error_notifier: error_notifier)
154
+ deprecate(<<~MSG)
155
+ Light#with_error_notifier is deprecated and will be removed in v6.0.0.
156
+
157
+ Circuit breakers should be configured once at creation, not cloned with
158
+ modifications.
159
+
160
+ Instead of:
161
+ light = Stoplight('api-call')
162
+ modified = light.with_error_notifier { |error| warn error }
163
+
164
+ Configure correctly from the start:
165
+ Stoplight('api-call', error_notifier: ->(error) { warn error })
166
+ MSG
167
+ with_without_warning(error_notifier: error_notifier)
89
168
  end
90
169
 
91
170
  # Configures a custom list of tracked errors that counts toward the threshold.
@@ -101,9 +180,22 @@ module Stoplight
101
180
  #
102
181
  # @param tracked_errors [Array<StandardError>]
103
182
  # @return [Stoplight::Light]
104
- # @deprecated consider using +Light#with+ for reconfiguration
183
+ # @deprecated
105
184
  def with_tracked_errors(*tracked_errors)
106
- with(tracked_errors:)
185
+ deprecate(<<~MSG)
186
+ Light#with_tracked_errors is deprecated and will be removed in v6.0.0.
187
+
188
+ Circuit breakers should be configured once at creation, not cloned with
189
+ modifications.
190
+
191
+ Instead of:
192
+ light = Stoplight('api-call')
193
+ modified = light.with_tracked_errors(TimeoutError, NetworkError)
194
+
195
+ Configure correctly from the start:
196
+ Stoplight('api-call', tracked_errors: [TimeoutError, NetworkError])
197
+ MSG
198
+ with_without_warning(tracked_errors:)
107
199
  end
108
200
 
109
201
  # Configures a custom list of skipped errors that do not count toward the threshold.
@@ -120,11 +212,25 @@ module Stoplight
120
212
  #
121
213
  # @param skipped_errors [Array<Exception>]
122
214
  # @return [Stoplight::Light]
123
- # @deprecated consider using +Light#with+ for reconfiguration
215
+ # @deprecated
124
216
  def with_skipped_errors(*skipped_errors)
125
- with(skipped_errors:)
217
+ deprecate(<<~MSG)
218
+ Light#with_skipped_errors is deprecated and will be removed in v6.0.0.
219
+
220
+ Circuit breakers should be configured once at creation, not cloned with
221
+ modifications.
222
+
223
+ Instead of:
224
+ light = Stoplight('api-call')
225
+ modified = light.with_skipped_errors(ActiveRecord::RecordNotFound)
226
+
227
+ Configure correctly from the start:
228
+ Stoplight('api-call', skipped_errors: [ActiveRecord::RecordNotFound])
229
+ MSG
230
+ with_without_warning(skipped_errors:)
126
231
  end
127
232
  end
233
+ # steep:ignore:end
128
234
  end
129
235
  end
130
236
  end
@@ -1,53 +1,28 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "forwardable"
4
-
5
3
  module Stoplight
6
4
  module Domain
7
5
  #
8
6
  # @api private use +Stoplight()+ method instead
9
7
  class Light
10
- extend Forwardable
11
- include ConfigurationBuilderInterface
12
-
13
- # @!attribute [r] config
14
- # @return [Stoplight::Domain::Config]
15
- # @api private
16
- attr_reader :config
17
-
18
- # @!attribute [r] name
19
- # The name of the light.
20
- # @return [String]
21
- def_delegator :config, :name
22
-
23
- # @!attribute [r] green_run_strategy
24
- # @return [Stoplight::Domain::Strategies::GreenRunStrategy]
25
- protected attr_reader :green_run_strategy
26
-
27
- # @!attribute [r] yellow_run_strategy
28
- # @return [Stoplight::Domain::Strategies::YellowRunStrategy]
29
- protected attr_reader :yellow_run_strategy
30
-
31
- # @!attribute [r] red_run_strategy
32
- # @return [Stoplight::Domain::Strategies::RedRunStrategy]
33
- protected attr_reader :red_run_strategy
34
-
35
- # @!attribute [r] data_store
36
- # @return [Stoplight::Light::Base]
37
- protected attr_reader :data_store
38
-
39
- # @!attribute [r] factory
40
- # @return [Stoplight::Domain::LightFactory]
41
- protected attr_reader :factory
42
-
43
- # @param config [Stoplight::Domain::Config]
44
- def initialize(config, green_run_strategy:, yellow_run_strategy:, red_run_strategy:, data_store:, factory:)
45
- @config = config
46
- @data_store = data_store
8
+ include Common::Deprecations
9
+ include ConfigurationBuilderInterface # steep:ignore
10
+
11
+ attr_reader :name
12
+
13
+ attr_reader :green_run_strategy
14
+ attr_reader :yellow_run_strategy
15
+ attr_reader :red_run_strategy
16
+ attr_reader :factory
17
+ attr_reader :state_store
18
+
19
+ def initialize(name, green_run_strategy:, yellow_run_strategy:, red_run_strategy:, factory:, state_store:)
20
+ @name = name
47
21
  @green_run_strategy = green_run_strategy
48
22
  @yellow_run_strategy = yellow_run_strategy
49
23
  @red_run_strategy = red_run_strategy
50
24
  @factory = factory
25
+ @state_store = state_store
51
26
  end
52
27
 
53
28
  # Returns the current state of the light:
@@ -55,10 +30,7 @@ module Stoplight
55
30
  # * +Stoplight::State::LOCKED_RED+ -- light is locked red and blocks all traffic
56
31
  # * +Stoplight::State::UNLOCKED+ -- light is not locked and follow the configured rules
57
32
  #
58
- # @return [String]
59
- def state
60
- state_snapshot.locked_state
61
- end
33
+ def state = state_snapshot.locked_state
62
34
 
63
35
  # Returns current color:
64
36
  # * +Stoplight::Color::GREEN+ -- circuit breaker is closed
@@ -69,10 +41,7 @@ module Stoplight
69
41
  # light = Stoplight('example')
70
42
  # light.color #=> Color::GREEN
71
43
  #
72
- # @return [String] returns current light color
73
- def color
74
- state_snapshot.color
75
- end
44
+ def color = state_snapshot.color
76
45
 
77
46
  # Runs the given block of code with this circuit breaker
78
47
  #
@@ -84,9 +53,7 @@ module Stoplight
84
53
  # light = Stoplight('example')
85
54
  # light.run(->(error) { 0 }) { 1 / 0 } #=> 0
86
55
  #
87
- # @param fallback [Proc, nil] (nil) fallback code to run if the circuit breaker is open
88
- # @raise [Stoplight::Error::RedLight]
89
- # @return [any]
56
+ # @param fallback fallback code to run if the circuit breaker is open
90
57
  # @raise [Stoplight::Error::RedLight]
91
58
  def run(fallback = nil, &code)
92
59
  raise ArgumentError, "nothing to run. Please, pass a block into `Light#run`" unless block_given?
@@ -103,8 +70,8 @@ module Stoplight
103
70
  # light = Stoplight('example-locked')
104
71
  # light.lock(Stoplight::Color::RED)
105
72
  #
106
- # @param color [String] should be either +Color::RED+ or +Color::GREEN+
107
- # @return [Stoplight::Light] returns locked light (circuit breaker)
73
+ # @param color should be either +Color::RED+ or +Color::GREEN+
74
+ # @return locked light
108
75
  def lock(color)
109
76
  state = case color
110
77
  when Color::RED then State::LOCKED_RED
@@ -112,7 +79,7 @@ module Stoplight
112
79
  else raise Error::IncorrectColor
113
80
  end
114
81
 
115
- data_store.set_state(config, state)
82
+ state_store.set_state(state)
116
83
 
117
84
  self
118
85
  end
@@ -124,21 +91,16 @@ module Stoplight
124
91
  # light.lock(Stoplight::Color::RED)
125
92
  # light.unlock
126
93
  #
127
- # @return [Stoplight::Light] returns unlocked light (circuit breaker)
94
+ # @return returns unlocked light (circuit breaker)
128
95
  def unlock
129
- data_store.set_state(config, State::UNLOCKED)
96
+ state_store.set_state(State::UNLOCKED)
130
97
 
131
98
  self
132
99
  end
133
100
 
134
101
  # Two lights considered equal if they have the same configuration.
135
- #
136
- # @param other [any]
137
- # @return [Boolean]
138
102
  def ==(other)
139
- other.is_a?(self.class) && config == other.config && data_store == other.data_store &&
140
- green_run_strategy == other.green_run_strategy && yellow_run_strategy == other.yellow_run_strategy &&
141
- red_run_strategy == other.red_run_strategy && factory == other.factory
103
+ other.is_a?(self.class) && factory == other.factory
142
104
  end
143
105
 
144
106
  # Reconfigures the light with updated settings and returns a new instance.
@@ -171,10 +133,30 @@ module Stoplight
171
133
  # # Run the lights with their respective configurations
172
134
  # invoices_light.run(->(error) { [] }) { call_invoices_api }
173
135
  # payment_light.run(->(error) { nil }) { call_payment_api }
136
+ # @deprecated
174
137
  # @see +Stoplight()+
138
+ # steep:ignore:start
175
139
  def with(**settings)
140
+ deprecate(<<~MSG)
141
+ Light#with is deprecated and will be removed in v6.0.0.
142
+
143
+ Circuit breakers should be configured once at creation, not cloned with
144
+ modifications.
145
+
146
+ Instead of:
147
+ light = Stoplight('api-call', threshold: 5)
148
+ modified = light.with(threshold: 10)
149
+
150
+ Configure correctly from the start:
151
+ Stoplight('api-call', threshold: 10)
152
+ MSG
153
+ with_without_warning(**settings)
154
+ end
155
+
156
+ private def with_without_warning(**settings)
176
157
  factory.build_with(**settings)
177
158
  end
159
+ # steep:ignore:end
178
160
 
179
161
  private
180
162
 
@@ -189,9 +171,7 @@ module Stoplight
189
171
  end
190
172
  end
191
173
 
192
- def state_snapshot
193
- data_store.get_state_snapshot(config)
194
- end
174
+ def state_snapshot = state_store.state_snapshot
195
175
  end
196
176
  end
197
177
  end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Stoplight
4
+ module Domain
5
+ LightInfo = Data.define(:name)
6
+ end
7
+ end
@@ -0,0 +1,58 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Stoplight
4
+ module Domain
5
+ # Request metrics over a given window.
6
+ #
7
+ # @api private
8
+ MetricsSnapshot = Data.define(
9
+ :successes,
10
+ :errors,
11
+ :consecutive_errors,
12
+ :consecutive_successes,
13
+ :last_error,
14
+ :last_success_at
15
+ )
16
+
17
+ class MetricsSnapshot
18
+ # @!attribute successes
19
+ # A number of successes withing requested window. Zero for non-windowed metrics
20
+ #
21
+ # @!attribute errors
22
+ # A number of errors withing requested window. Zero for non-windowed metrics
23
+
24
+ # Calculates the error rate based on the number of successes and errors.
25
+ #
26
+ # @return [Float]
27
+ def error_rate
28
+ return unless requests # we effectively check if this is windowed metrics
29
+
30
+ if (successes! + errors!).zero?
31
+ 0.0
32
+ else
33
+ errors!.fdiv(successes! + errors!)
34
+ end
35
+ end
36
+
37
+ # @return [Integer]
38
+ def requests
39
+ if successes && errors # we effectively check if this is windowed metrics
40
+ successes! + errors!
41
+ end
42
+ end
43
+
44
+ # @return [Time, nil]
45
+ def last_error_at
46
+ last_error&.time
47
+ end
48
+
49
+ def successes!
50
+ successes or raise TypeError, "success must not be nil"
51
+ end
52
+
53
+ def errors!
54
+ errors or raise TypeError, "errors must not be nil"
55
+ end
56
+ end
57
+ end
58
+ end