stoplight 5.8.3 → 6.0.0.rc2

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 (323) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1 -1
  3. data/LICENSE.md +1 -0
  4. data/README.md +132 -79
  5. data/UPGRADING.md +265 -6
  6. data/lib/stoplight/admin/actions/action.rb +4 -12
  7. data/lib/stoplight/admin/actions/lock.rb +12 -9
  8. data/lib/stoplight/admin/actions/lock_all.rb +27 -0
  9. data/lib/stoplight/admin/actions/remove.rb +12 -9
  10. data/lib/stoplight/admin/actions/stats.rb +15 -12
  11. data/lib/stoplight/admin/actions/unlock.rb +10 -9
  12. data/lib/stoplight/admin/assets/apple-touch-icon.png +0 -0
  13. data/lib/stoplight/admin/assets/favicon.ico +0 -0
  14. data/lib/stoplight/admin/assets/flowbite.min.css +2 -0
  15. data/lib/stoplight/admin/assets/flowbite.min.js +1 -0
  16. data/lib/stoplight/admin/assets/icon.svg +7 -0
  17. data/lib/stoplight/admin/assets/turbo.es2017-esm.min.js +34 -0
  18. data/lib/stoplight/admin/config_registry.rb +40 -0
  19. data/lib/stoplight/admin/dependencies.rb +29 -28
  20. data/lib/stoplight/admin/helpers.rb +50 -17
  21. data/lib/stoplight/admin/light_view.rb +184 -0
  22. data/lib/stoplight/admin/lights_stats.rb +7 -9
  23. data/lib/stoplight/admin/views/_card.erb +19 -21
  24. data/lib/stoplight/admin/views/index.erb +9 -3
  25. data/lib/stoplight/admin/views/layout.erb +45 -13
  26. data/lib/stoplight/admin.rb +110 -21
  27. data/lib/stoplight/data_store.rb +18 -8
  28. data/lib/stoplight/domain/config.rb +4 -1
  29. data/lib/stoplight/domain/error_tracking_policy.rb +9 -0
  30. data/lib/stoplight/domain/failure.rb +4 -0
  31. data/lib/stoplight/domain/id.rb +13 -0
  32. data/lib/stoplight/domain/light.rb +35 -77
  33. data/lib/stoplight/domain/lock_control.rb +55 -0
  34. data/lib/stoplight/domain/matcher_validator.rb +24 -0
  35. data/lib/stoplight/domain/state_snapshot.rb +2 -6
  36. data/lib/stoplight/domain/strategies/green_run_strategy.rb +33 -19
  37. data/lib/stoplight/domain/strategies/red_run_strategy.rb +8 -2
  38. data/lib/stoplight/domain/strategies/yellow_run_strategy.rb +56 -32
  39. data/lib/stoplight/domain/telemetry/bus.rb +90 -0
  40. data/lib/stoplight/domain/telemetry/consumer.rb +18 -0
  41. data/lib/stoplight/domain/telemetry/emitter.rb +45 -0
  42. data/lib/stoplight/domain/telemetry/envelope.rb +15 -0
  43. data/lib/stoplight/domain/telemetry/failure.rb +13 -0
  44. data/lib/stoplight/domain/telemetry/light_registered.rb +12 -0
  45. data/lib/stoplight/domain/telemetry/lock_changed.rb +19 -0
  46. data/lib/stoplight/domain/telemetry/metrics.rb +15 -0
  47. data/lib/stoplight/domain/telemetry/recovery_failed.rb +20 -0
  48. data/lib/stoplight/domain/telemetry/recovery_probe_completed.rb +15 -0
  49. data/lib/stoplight/domain/telemetry/recovery_started.rb +18 -0
  50. data/lib/stoplight/domain/telemetry/recovery_succeeded.rb +19 -0
  51. data/lib/stoplight/domain/telemetry/run_completed.rb +17 -0
  52. data/lib/stoplight/domain/telemetry/run_recorder.rb +58 -0
  53. data/lib/stoplight/domain/telemetry/settings.rb +21 -0
  54. data/lib/stoplight/domain/telemetry/state_transitioned.rb +11 -0
  55. data/lib/stoplight/domain/telemetry/subscription.rb +11 -0
  56. data/lib/stoplight/domain/telemetry/traffic_breached.rb +20 -0
  57. data/lib/stoplight/domain/tracker/recovery_probe.rb +61 -17
  58. data/lib/stoplight/domain/tracker/request.rb +17 -8
  59. data/lib/stoplight/domain/traffic_control/consecutive_errors.rb +13 -3
  60. data/lib/stoplight/domain/traffic_control/error_rate.rb +16 -12
  61. data/lib/stoplight/domain/traffic_recovery/consecutive_successes.rb +9 -3
  62. data/lib/stoplight/error.rb +10 -1
  63. data/lib/stoplight/infrastructure/config_serializer.rb +50 -0
  64. data/lib/stoplight/infrastructure/fail_safe/error_notifier.rb +25 -0
  65. data/lib/stoplight/infrastructure/fail_safe/storage/registry.rb +68 -0
  66. data/lib/stoplight/infrastructure/memory/storage/registry.rb +18 -0
  67. data/lib/stoplight/infrastructure/memory/storage/unbounded_metrics.rb +29 -32
  68. data/lib/stoplight/infrastructure/memory/storage/window_metrics/sliding_window.rb +74 -0
  69. data/lib/stoplight/infrastructure/memory/storage/window_metrics.rb +25 -24
  70. data/lib/stoplight/infrastructure/notifier/fail_safe.rb +3 -8
  71. data/lib/stoplight/infrastructure/notifier/generic.rb +3 -3
  72. data/lib/stoplight/infrastructure/redis/key.rb +25 -0
  73. data/lib/stoplight/infrastructure/redis/storage/now.lua +9 -0
  74. data/lib/stoplight/infrastructure/redis/storage/recovery_lock.rb +2 -4
  75. data/lib/stoplight/infrastructure/redis/storage/recovery_metrics.rb +1 -1
  76. data/lib/stoplight/infrastructure/redis/storage/registry.rb +73 -0
  77. data/lib/stoplight/infrastructure/redis/storage/scripting.rb +54 -3
  78. data/lib/stoplight/infrastructure/redis/storage/state/transition_to_green.lua +3 -1
  79. data/lib/stoplight/infrastructure/redis/storage/state/transition_to_red.lua +4 -2
  80. data/lib/stoplight/infrastructure/redis/storage/state/transition_to_yellow.lua +3 -1
  81. data/lib/stoplight/infrastructure/redis/storage/state.rb +8 -8
  82. data/lib/stoplight/infrastructure/redis/storage/unbounded_metrics/record_failure.lua +20 -21
  83. data/lib/stoplight/infrastructure/redis/storage/unbounded_metrics/record_success.lua +11 -19
  84. data/lib/stoplight/infrastructure/redis/storage/unbounded_metrics.rb +20 -16
  85. data/lib/stoplight/infrastructure/redis/storage/window_metrics/_evict.lua +44 -0
  86. data/lib/stoplight/infrastructure/redis/storage/window_metrics/metrics_snapshot.lua +14 -21
  87. data/lib/stoplight/infrastructure/redis/storage/window_metrics/record_failure.lua +38 -36
  88. data/lib/stoplight/infrastructure/redis/storage/window_metrics/record_success.lua +23 -30
  89. data/lib/stoplight/infrastructure/redis/storage/window_metrics.rb +45 -135
  90. data/lib/stoplight/infrastructure/system_clock.rb +4 -0
  91. data/lib/stoplight/telemetry.rb +19 -0
  92. data/lib/stoplight/types.rb +15 -2
  93. data/lib/stoplight/version.rb +1 -1
  94. data/lib/stoplight/wiring/config_compatibility_validator.rb +25 -2
  95. data/lib/stoplight/wiring/default.rb +1 -1
  96. data/lib/stoplight/wiring/default_config.rb +2 -1
  97. data/lib/stoplight/wiring/default_configuration.rb +3 -2
  98. data/lib/stoplight/wiring/external_caller.rb +27 -0
  99. data/lib/stoplight/wiring/fail_safe_config.rb +11 -0
  100. data/lib/stoplight/wiring/global_state.rb +93 -0
  101. data/lib/stoplight/wiring/{light_factory → light_configuration_dsl}/traffic_control_dsl.rb +3 -5
  102. data/lib/stoplight/wiring/{light_factory → light_configuration_dsl}/traffic_recovery_dsl.rb +2 -2
  103. data/lib/stoplight/wiring/light_configuration_dsl.rb +97 -0
  104. data/lib/stoplight/wiring/light_factory.rb +154 -97
  105. data/lib/stoplight/wiring/notifier_bridge.rb +33 -0
  106. data/lib/stoplight/wiring/redis/backend.rb +1 -1
  107. data/lib/stoplight/wiring/system/storage.rb +69 -0
  108. data/lib/stoplight/wiring/system.rb +125 -56
  109. data/lib/stoplight/wiring/{configuration_dsl.rb → system_configuration_dsl.rb} +13 -43
  110. data/lib/stoplight.rb +50 -194
  111. data/sig/sinatra/base.rbs +6 -0
  112. data/sig/stdlib/securerandom.rbs +4 -0
  113. data/sig/stoplight/admin/actions/action.rbs +10 -0
  114. data/sig/stoplight/admin/actions/lock.rbs +17 -0
  115. data/sig/stoplight/admin/actions/lock_all.rbs +17 -0
  116. data/sig/stoplight/admin/actions/remove.rbs +22 -0
  117. data/sig/stoplight/admin/actions/stats.rbs +28 -0
  118. data/sig/stoplight/admin/actions/unlock.rbs +17 -0
  119. data/sig/stoplight/admin/config_registry.rbs +20 -0
  120. data/sig/stoplight/admin/dependencies.rbs +16 -0
  121. data/sig/stoplight/admin/helpers.rbs +17 -0
  122. data/sig/stoplight/admin/light_view.rbs +39 -0
  123. data/sig/stoplight/admin/lights_stats.rbs +30 -0
  124. data/sig/stoplight/admin.rbs +14 -0
  125. data/sig/stoplight/color.rbs +3 -3
  126. data/sig/{_private/stoplight/data_store.rbs → stoplight/data_store/base.rbs} +1 -0
  127. data/sig/stoplight/data_store/memory.rbs +9 -0
  128. data/sig/stoplight/data_store/redis.rbs +17 -0
  129. data/sig/{_private/stoplight → stoplight}/domain/config.rbs +28 -24
  130. data/sig/{_private/stoplight → stoplight}/domain/error_tracking_policy.rbs +4 -0
  131. data/sig/{_private/stoplight → stoplight}/domain/failure.rbs +2 -0
  132. data/sig/stoplight/domain/id.rbs +7 -0
  133. data/sig/stoplight/domain/light.rbs +39 -0
  134. data/sig/{_private/stoplight → stoplight}/domain/light_info.rbs +1 -5
  135. data/sig/stoplight/domain/lock_control.rbs +16 -0
  136. data/sig/stoplight/domain/matcher_validator.rbs +11 -0
  137. data/sig/{_private/stoplight → stoplight}/domain/ports/clock.rbs +6 -0
  138. data/sig/stoplight/domain/ports/data_store.rbs +9 -0
  139. data/sig/stoplight/domain/ports/error_notifier.rbs +12 -0
  140. data/sig/stoplight/domain/ports/exception_matcher.rbs +10 -0
  141. data/sig/{_private/stoplight → stoplight}/domain/ports/metrics_store.rbs +2 -2
  142. data/sig/stoplight/domain/ports/registry.rbs +13 -0
  143. data/sig/{_private/stoplight → stoplight}/domain/ports/run_strategy.rbs +2 -1
  144. data/sig/stoplight/domain/ports/state_transition_notifier.rbs +18 -0
  145. data/sig/stoplight/domain/ports/telemetry.rbs +28 -0
  146. data/sig/stoplight/domain/ports/telemetry_publisher.rbs +14 -0
  147. data/sig/{_private/stoplight → stoplight}/domain/ports/traffic_control.rbs +7 -0
  148. data/sig/{_private/stoplight → stoplight}/domain/ports/traffic_recovery.rbs +2 -3
  149. data/sig/stoplight/domain/strategies/green_run_strategy.rbs +22 -0
  150. data/sig/stoplight/domain/strategies/red_run_strategy.rbs +19 -0
  151. data/sig/{_private/stoplight → stoplight}/domain/strategies/yellow_run_strategy.rbs +23 -12
  152. data/sig/stoplight/domain/telemetry/bus.rbs +49 -0
  153. data/sig/stoplight/domain/telemetry/consumer.rbs +13 -0
  154. data/sig/stoplight/domain/telemetry/emitter.rbs +51 -0
  155. data/sig/stoplight/domain/telemetry/envelope.rbs +30 -0
  156. data/sig/stoplight/domain/telemetry/failure.rbs +23 -0
  157. data/sig/stoplight/domain/telemetry/light_registered.rbs +23 -0
  158. data/sig/stoplight/domain/telemetry/lock_changed.rbs +31 -0
  159. data/sig/stoplight/domain/telemetry/metrics.rbs +27 -0
  160. data/sig/stoplight/domain/telemetry/recovery_failed.rbs +36 -0
  161. data/sig/stoplight/domain/telemetry/recovery_probe_completed.rbs +29 -0
  162. data/sig/stoplight/domain/telemetry/recovery_started.rbs +26 -0
  163. data/sig/stoplight/domain/telemetry/recovery_succeeded.rbs +31 -0
  164. data/sig/stoplight/domain/telemetry/run_completed.rbs +38 -0
  165. data/sig/stoplight/domain/telemetry/run_recorder.rbs +27 -0
  166. data/sig/stoplight/domain/telemetry/settings.rbs +47 -0
  167. data/sig/stoplight/domain/telemetry/state_transitioned.rbs +11 -0
  168. data/sig/stoplight/domain/telemetry/subscription.rbs +9 -0
  169. data/sig/stoplight/domain/telemetry/traffic_breached.rbs +36 -0
  170. data/sig/stoplight/domain/telemetry/types.rbs +32 -0
  171. data/sig/{_private/stoplight → stoplight}/domain/tracker/recovery_probe.rbs +10 -6
  172. data/sig/{_private/stoplight → stoplight}/domain/tracker/request.rbs +3 -3
  173. data/sig/{_private/stoplight → stoplight}/domain/traffic_control/consecutive_errors.rbs +2 -0
  174. data/sig/{_private/stoplight → stoplight}/domain/traffic_control/error_rate.rbs +3 -2
  175. data/sig/{_private/stoplight → stoplight}/domain/traffic_recovery/consecutive_successes.rbs +2 -0
  176. data/sig/stoplight/domain/types.rbs +25 -0
  177. data/sig/stoplight/error.rbs +8 -0
  178. data/sig/stoplight/generators/rails_generators_base.rbs +11 -0
  179. data/sig/stoplight/infrastructure/config_serializer.rbs +12 -0
  180. data/sig/stoplight/infrastructure/fail_safe/error_notifier.rbs +13 -0
  181. data/sig/{_private/stoplight → stoplight}/infrastructure/fail_safe/storage/metrics.rbs +2 -2
  182. data/sig/{_private/stoplight → stoplight}/infrastructure/fail_safe/storage/recovery_lock.rbs +2 -2
  183. data/sig/stoplight/infrastructure/fail_safe/storage/registry.rbs +23 -0
  184. data/sig/{_private/stoplight → stoplight}/infrastructure/fail_safe/storage/state.rbs +4 -4
  185. data/sig/stoplight/infrastructure/memory/storage/registry.rbs +13 -0
  186. data/sig/{_private/stoplight → stoplight}/infrastructure/memory/storage/state.rbs +3 -3
  187. data/sig/{_private/stoplight → stoplight}/infrastructure/memory/storage/unbounded_metrics.rbs +7 -7
  188. data/sig/stoplight/infrastructure/memory/storage/window_metrics/sliding_window.rbs +27 -0
  189. data/sig/{_private/stoplight → stoplight}/infrastructure/memory/storage/window_metrics.rbs +7 -7
  190. data/sig/stoplight/infrastructure/notifier/fail_safe.rbs +15 -0
  191. data/sig/{_private/stoplight → stoplight}/infrastructure/notifier/generic.rbs +1 -1
  192. data/sig/{_private/stoplight → stoplight}/infrastructure/notifier/io.rbs +1 -1
  193. data/sig/{_private/stoplight → stoplight}/infrastructure/notifier/logger.rbs +1 -1
  194. data/sig/stoplight/infrastructure/redis/key.rbs +10 -0
  195. data/sig/{_private/stoplight → stoplight}/infrastructure/redis/storage/recovery_lock.rbs +2 -2
  196. data/sig/stoplight/infrastructure/redis/storage/registry.rbs +27 -0
  197. data/sig/stoplight/infrastructure/redis/storage/scripting.rbs +34 -0
  198. data/sig/{_private/stoplight → stoplight}/infrastructure/redis/storage/state.rbs +4 -4
  199. data/sig/{_private/stoplight → stoplight}/infrastructure/redis/storage/unbounded_metrics.rbs +8 -1
  200. data/sig/stoplight/infrastructure/redis/storage/window_metrics.rbs +35 -0
  201. data/sig/stoplight/infrastructure/types.rbs +6 -0
  202. data/sig/stoplight/notifier.rbs +1 -1
  203. data/sig/stoplight/ports/system.rbs +16 -10
  204. data/sig/stoplight/state.rbs +3 -3
  205. data/sig/stoplight/telemetry.rbs +17 -0
  206. data/sig/stoplight/types.rbs +9 -0
  207. data/sig/{_private/stoplight → stoplight}/wiring/config_compatibility_validator.rbs +2 -0
  208. data/sig/stoplight/wiring/default.rbs +26 -0
  209. data/sig/stoplight/wiring/default_configuration.rbs +39 -0
  210. data/sig/stoplight/wiring/external_caller.rbs +13 -0
  211. data/sig/stoplight/wiring/fail_safe_config.rbs +7 -0
  212. data/sig/stoplight/wiring/global_state.rbs +36 -0
  213. data/sig/{_private/stoplight/wiring/light_factory → stoplight/wiring/light_configuration_dsl}/traffic_control_dsl.rbs +1 -1
  214. data/sig/{_private/stoplight/wiring/light_factory → stoplight/wiring/light_configuration_dsl}/traffic_recovery_dsl.rbs +1 -1
  215. data/sig/stoplight/wiring/light_configuration_dsl.rbs +38 -0
  216. data/sig/stoplight/wiring/light_factory.rbs +78 -0
  217. data/sig/stoplight/wiring/notifier_bridge.rbs +15 -0
  218. data/sig/stoplight/wiring/notifier_factory.rbs +10 -0
  219. data/sig/{_private/stoplight → stoplight}/wiring/redis/backend.rbs +6 -6
  220. data/sig/stoplight/wiring/system/storage.rbs +35 -0
  221. data/sig/stoplight/wiring/system.rbs +30 -0
  222. data/sig/stoplight/wiring/system_configuration_dsl.rbs +39 -0
  223. data/sig/stoplight/wiring/types.rbs +6 -0
  224. data/sig/stoplight.rbs +37 -54
  225. metadata +190 -143
  226. data/lib/stoplight/admin/actions/lock_all_green.rb +0 -18
  227. data/lib/stoplight/admin/actions/lock_green.rb +0 -23
  228. data/lib/stoplight/admin/actions/lock_red.rb +0 -23
  229. data/lib/stoplight/admin/lights_repository/light.rb +0 -171
  230. data/lib/stoplight/admin/lights_repository.rb +0 -87
  231. data/lib/stoplight/domain/light/configuration_builder_interface.rb +0 -236
  232. data/lib/stoplight/infrastructure/fail_safe/data_store.rb +0 -152
  233. data/lib/stoplight/infrastructure/memory/data_store/metrics.rb +0 -27
  234. data/lib/stoplight/infrastructure/memory/data_store/recovery_lock_store.rb +0 -52
  235. data/lib/stoplight/infrastructure/memory/data_store/recovery_lock_token.rb +0 -17
  236. data/lib/stoplight/infrastructure/memory/data_store/sliding_window.rb +0 -74
  237. data/lib/stoplight/infrastructure/memory/data_store/state.rb +0 -21
  238. data/lib/stoplight/infrastructure/memory/data_store.rb +0 -342
  239. data/lib/stoplight/infrastructure/redis/data_store/lua_scripts/get_metrics.lua +0 -26
  240. data/lib/stoplight/infrastructure/redis/data_store/lua_scripts/record_failure.lua +0 -36
  241. data/lib/stoplight/infrastructure/redis/data_store/lua_scripts/record_recovery_probe_failure.lua +0 -27
  242. data/lib/stoplight/infrastructure/redis/data_store/lua_scripts/record_recovery_probe_success.lua +0 -23
  243. data/lib/stoplight/infrastructure/redis/data_store/lua_scripts/record_success.lua +0 -35
  244. data/lib/stoplight/infrastructure/redis/data_store/lua_scripts/release_lock.lua +0 -6
  245. data/lib/stoplight/infrastructure/redis/data_store/lua_scripts/transition_to_green.lua +0 -10
  246. data/lib/stoplight/infrastructure/redis/data_store/lua_scripts/transition_to_red.lua +0 -10
  247. data/lib/stoplight/infrastructure/redis/data_store/lua_scripts/transition_to_yellow.lua +0 -9
  248. data/lib/stoplight/infrastructure/redis/data_store/recovery_lock_store.rb +0 -60
  249. data/lib/stoplight/infrastructure/redis/data_store/recovery_lock_token.rb +0 -28
  250. data/lib/stoplight/infrastructure/redis/data_store/scripting.rb +0 -73
  251. data/lib/stoplight/infrastructure/redis/data_store.rb +0 -516
  252. data/lib/stoplight/infrastructure/redis/storage/key_space.rb +0 -51
  253. data/lib/stoplight/infrastructure/storage/compatibility_metrics.rb +0 -41
  254. data/lib/stoplight/infrastructure/storage/compatibility_recovery_lock.rb +0 -33
  255. data/lib/stoplight/infrastructure/storage/compatibility_recovery_metrics.rb +0 -47
  256. data/lib/stoplight/infrastructure/storage/compatibility_state.rb +0 -44
  257. data/lib/stoplight/wiring/light_builder.rb +0 -198
  258. data/lib/stoplight/wiring/notifier_factory.rb +0 -26
  259. data/lib/stoplight/wiring/system/light_builder.rb +0 -47
  260. data/lib/stoplight/wiring/system/light_factory.rb +0 -64
  261. data/sig/_private/stoplight/domain/light.rbs +0 -25
  262. data/sig/_private/stoplight/domain/ports/data_store.rbs +0 -76
  263. data/sig/_private/stoplight/domain/ports/light_factory.rbs +0 -80
  264. data/sig/_private/stoplight/domain/strategies/green_run_strategy.rbs +0 -17
  265. data/sig/_private/stoplight/domain/strategies/red_run_strategy.rbs +0 -17
  266. data/sig/_private/stoplight/domain/tracker/base.rbs +0 -8
  267. data/sig/_private/stoplight/infrastructure/fail_safe/data_store.rbs +0 -26
  268. data/sig/_private/stoplight/infrastructure/memory/data_store/metrics.rbs +0 -25
  269. data/sig/_private/stoplight/infrastructure/memory/data_store/recovery_lock_store.rbs +0 -19
  270. data/sig/_private/stoplight/infrastructure/memory/data_store/recovery_lock_token.rbs +0 -17
  271. data/sig/_private/stoplight/infrastructure/memory/data_store/sliding_window.rbs +0 -27
  272. data/sig/_private/stoplight/infrastructure/memory/data_store/state.rbs +0 -17
  273. data/sig/_private/stoplight/infrastructure/memory/data_store.rbs +0 -30
  274. data/sig/_private/stoplight/infrastructure/notifier/fail_safe.rbs +0 -17
  275. data/sig/_private/stoplight/infrastructure/redis/data_store/recovery_lock_store.rbs +0 -24
  276. data/sig/_private/stoplight/infrastructure/redis/data_store/recovery_lock_token.rbs +0 -21
  277. data/sig/_private/stoplight/infrastructure/redis/data_store/scripting.rbs +0 -34
  278. data/sig/_private/stoplight/infrastructure/redis/data_store.rbs +0 -67
  279. data/sig/_private/stoplight/infrastructure/redis/storage/key_space.rbs +0 -19
  280. data/sig/_private/stoplight/infrastructure/redis/storage/scripting.rbs +0 -13
  281. data/sig/_private/stoplight/infrastructure/redis/storage/window_metrics.rbs +0 -34
  282. data/sig/_private/stoplight/infrastructure/storage/compatibility_metrics.rbs +0 -17
  283. data/sig/_private/stoplight/infrastructure/storage/compatibility_recovery_lock.rbs +0 -13
  284. data/sig/_private/stoplight/infrastructure/storage/compatibility_recovery_metrics.rbs +0 -14
  285. data/sig/_private/stoplight/infrastructure/storage/compatibility_state.rbs +0 -14
  286. data/sig/_private/stoplight/system/light_builder.rbs +0 -23
  287. data/sig/_private/stoplight/system/light_factory.rbs +0 -17
  288. data/sig/_private/stoplight/types.rbs +0 -6
  289. data/sig/_private/stoplight/wiring/configuration_dsl.rbs +0 -43
  290. data/sig/_private/stoplight/wiring/default.rbs +0 -26
  291. data/sig/_private/stoplight/wiring/default_configuration.rbs +0 -29
  292. data/sig/_private/stoplight/wiring/light_builder.rbs +0 -48
  293. data/sig/_private/stoplight/wiring/light_factory.rbs +0 -16
  294. data/sig/_private/stoplight/wiring/notifier_factory.rbs +0 -10
  295. data/sig/_private/stoplight/wiring/system.rbs +0 -15
  296. data/sig/_private/stoplight.rbs +0 -48
  297. data/sig/stoplight/data_store.rbs +0 -19
  298. data/sig/stoplight/ports/configuration.rbs +0 -19
  299. data/sig/stoplight/ports/exception_matcher.rbs +0 -8
  300. data/sig/stoplight/ports/light.rbs +0 -12
  301. data/sig/stoplight/ports/light_info.rbs +0 -5
  302. data/sig/stoplight/ports/state_transition_notifier.rbs +0 -15
  303. /data/sig/{_private/stoplight → stoplight}/common/deprecations.rbs +0 -0
  304. /data/sig/{_private/stoplight → stoplight}/domain/compatibility_result.rbs +0 -0
  305. /data/sig/{_private/stoplight → stoplight}/domain/metrics_snapshot.rbs +0 -0
  306. /data/sig/{_private/stoplight → stoplight}/domain/ports/recovery_lock_store.rbs +0 -0
  307. /data/sig/{_private/stoplight → stoplight}/domain/ports/recovery_lock_token.rbs +0 -0
  308. /data/sig/{_private/stoplight → stoplight}/domain/ports/state_store.rbs +0 -0
  309. /data/sig/{_private/stoplight → stoplight}/domain/state_snapshot.rbs +0 -0
  310. /data/sig/{_private/stoplight → stoplight}/domain/storage/recovery_lock_token.rbs +0 -0
  311. /data/sig/{_private/stoplight → stoplight}/domain/traffic_recovery.rbs +0 -0
  312. /data/sig/{_private → stoplight}/generators/stoplight/install/install_generator.rbs +0 -0
  313. /data/sig/{_private/stoplight → stoplight}/infrastructure/fail_safe/storage/recovery_lock_token.rbs +0 -0
  314. /data/sig/{_private/stoplight → stoplight}/infrastructure/memory/storage/recovery_lock.rbs +0 -0
  315. /data/sig/{_private/stoplight → stoplight}/infrastructure/memory/storage/recovery_metrics.rbs +0 -0
  316. /data/sig/{_private/stoplight → stoplight}/infrastructure/redis/storage/metrics.rbs +0 -0
  317. /data/sig/{_private/stoplight → stoplight}/infrastructure/redis/storage/recovery_metrics.rbs +0 -0
  318. /data/sig/{_private/stoplight → stoplight}/infrastructure/system_clock.rbs +0 -0
  319. /data/sig/{_private/stoplight → stoplight}/wiring/data_store_backend.rbs +0 -0
  320. /data/sig/{_private/stoplight → stoplight}/wiring/default_config.rbs +0 -0
  321. /data/sig/{_private/stoplight → stoplight}/wiring/memory/backend.rbs +0 -0
  322. /data/sig/{_private/stoplight → stoplight}/wiring/storage_set.rbs +0 -0
  323. /data/sig/{_private/stoplight → stoplight}/wiring/storage_set_builder.rbs +0 -0
@@ -5,21 +5,31 @@ module Stoplight
5
5
  end
6
6
 
7
7
  class Redis < Base
8
+ KEY_SPACE = Infrastructure::Redis::Key.new("stoplight", "v6")
9
+ private_constant :KEY_SPACE
10
+
8
11
  # @!attribute redis
9
12
  # @return [::Redis, ConnectionPool<::Redis>]
10
13
  attr_reader :redis
11
14
 
12
- # @!attribute warn_on_clock_skew
13
- # @return [Boolean]
14
- attr_reader :warn_on_clock_skew
15
-
16
15
  # @param redis [::Redis, ConnectionPool<::Redis>]
17
- # @param warn_on_clock_skew [Boolean] (true) Whether to warn about clock skew between Redis and
18
- # the application server
19
- def initialize(redis, warn_on_clock_skew: true)
20
- @warn_on_clock_skew = warn_on_clock_skew
16
+ def initialize(redis)
21
17
  @redis = redis
22
18
  end
19
+
20
+ # Immutable key namespace for a light within a system.
21
+ #
22
+ # Produces keys following entity-first structure:
23
+ # stoplight:{version}:{system_id}:{{light_id}}:locks:recovery
24
+ # stoplight:{version}:{system_id}:{{light_id}}:metrics:successes
25
+ # stoplight:{version}:{system_id}:{{light_id}}:state
26
+ #
27
+ # Identifiers are derived from SHA-256 and truncated to 12 characters. Collisions are extremely
28
+ # unlikely at expected system scale.
29
+ #
30
+ # @see Stoplight::Infrastructure::Redis::Key
31
+ def self.key_space = KEY_SPACE
32
+ def key_space = self.class.key_space
23
33
  end
24
34
 
25
35
  class Memory < Base
@@ -6,6 +6,7 @@ module Stoplight
6
6
  #
7
7
  # @api private
8
8
  Config = Data.define(
9
+ :id,
9
10
  :name,
10
11
  :cool_off_time,
11
12
  :threshold,
@@ -21,10 +22,11 @@ module Stoplight
21
22
  )
22
23
  class Config
23
24
  def cool_off_time_in_milliseconds
24
- (cool_off_time * 1_000).to_i
25
+ cool_off_time * 1_000
25
26
  end
26
27
 
27
28
  def with(
29
+ id: T.undefined,
28
30
  name: T.undefined,
29
31
  cool_off_time: T.undefined,
30
32
  threshold: T.undefined,
@@ -39,6 +41,7 @@ module Stoplight
39
41
  data_store: T.undefined
40
42
  )
41
43
  super(
44
+ id: id.is_a?(Undefined) ? self.id : id,
42
45
  name: name.is_a?(Undefined) ? self.name : name,
43
46
  cool_off_time: cool_off_time.is_a?(Undefined) ? self.cool_off_time : cool_off_time,
44
47
  threshold: threshold.is_a?(Undefined) ? self.threshold : threshold,
@@ -9,6 +9,15 @@ module Stoplight
9
9
  @skipped = skipped
10
10
  end
11
11
 
12
+ def with(tracked: T.undefined, skipped: T.undefined)
13
+ return self if tracked.is_a?(Undefined) && skipped.is_a?(Undefined)
14
+
15
+ self.class.new(
16
+ tracked: tracked.is_a?(Undefined) ? @tracked : Array(tracked),
17
+ skipped: skipped.is_a?(Undefined) ? @skipped : Array(skipped)
18
+ )
19
+ end
20
+
12
21
  def track?(error)
13
22
  !skipped?(error) && tracked?(error)
14
23
  end
@@ -39,6 +39,10 @@ module Stoplight
39
39
  error_message == other.error_message &&
40
40
  time == other.time
41
41
  end
42
+
43
+ alias_method :eql?, :==
44
+
45
+ def hash = [self.class, error_class, error_message, time].hash
42
46
  end
43
47
  end
44
48
  end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "digest"
4
+
5
+ module Stoplight
6
+ module Domain
7
+ # Helper to generate light/systems ids
8
+ module Id
9
+ # Derives id from a string
10
+ def self.for(name) = T.must(Digest::SHA256.hexdigest(name)[0, 12])
11
+ end
12
+ end
13
+ end
@@ -5,24 +5,34 @@ module Stoplight
5
5
  #
6
6
  # @api private use +Stoplight()+ method instead
7
7
  class Light
8
- include Common::Deprecations
9
- include ConfigurationBuilderInterface # steep:ignore
10
-
8
+ # @api public
11
9
  attr_reader :name
12
-
10
+ # @api private
13
11
  attr_reader :green_run_strategy
12
+ # @api private
14
13
  attr_reader :yellow_run_strategy
14
+ # @api private
15
15
  attr_reader :red_run_strategy
16
- attr_reader :factory
16
+ # @api private
17
17
  attr_reader :state_store
18
18
 
19
- def initialize(name, green_run_strategy:, yellow_run_strategy:, red_run_strategy:, factory:, state_store:)
19
+ # @api private
20
+ def initialize(
21
+ name,
22
+ green_run_strategy:,
23
+ yellow_run_strategy:,
24
+ red_run_strategy:,
25
+ state_store:,
26
+ lock_control:,
27
+ error_tracking_policy:
28
+ )
20
29
  @name = name
21
30
  @green_run_strategy = green_run_strategy
22
31
  @yellow_run_strategy = yellow_run_strategy
23
32
  @red_run_strategy = red_run_strategy
24
- @factory = factory
25
33
  @state_store = state_store
34
+ @lock_control = lock_control
35
+ @error_tracking_policy = error_tracking_policy
26
36
  end
27
37
 
28
38
  # Returns the current state of the light:
@@ -30,6 +40,7 @@ module Stoplight
30
40
  # * +Stoplight::State::LOCKED_RED+ -- light is locked red and blocks all traffic
31
41
  # * +Stoplight::State::UNLOCKED+ -- light is not locked and follow the configured rules
32
42
  #
43
+ # @api public
33
44
  def state = state_snapshot.locked_state
34
45
 
35
46
  # Returns current color:
@@ -41,6 +52,7 @@ module Stoplight
41
52
  # light = Stoplight('example')
42
53
  # light.color #=> Color::GREEN
43
54
  #
55
+ # @api public
44
56
  def color = state_snapshot.color
45
57
 
46
58
  # Runs the given block of code with this circuit breaker
@@ -53,14 +65,22 @@ module Stoplight
53
65
  # light = Stoplight('example')
54
66
  # light.run(->(error) { 0 }) { 1 / 0 } #=> 0
55
67
  #
68
+ # @example Overriding tracked errors for one run
69
+ # light.run(tracked_errors: [Timeout::Error]) { fetch_data }
70
+ #
56
71
  # @param fallback fallback code to run if the circuit breaker is open
72
+ # @param tracked_errors errors to track for this run; replaces the configured list
73
+ # @param skipped_errors errors to skip for this run; replaces the configured list
57
74
  # @raise [Stoplight::Error::RedLight]
58
- def run(fallback = nil, &code)
75
+ #
76
+ # @api public
77
+ def run(fallback = nil, tracked_errors: T.undefined, skipped_errors: T.undefined, &code)
59
78
  raise ArgumentError, "nothing to run. Please, pass a block into `Light#run`" unless block_given?
60
79
 
61
80
  state_snapshot.then do |state_snapshot|
62
81
  strategy = state_strategy_factory(state_snapshot.color)
63
- strategy.execute(fallback, state_snapshot:, &code)
82
+ error_tracking_policy = @error_tracking_policy.with(tracked: tracked_errors, skipped: skipped_errors)
83
+ strategy.execute(fallback, state_snapshot:, error_tracking_policy:, &code)
64
84
  end
65
85
  end
66
86
 
@@ -72,14 +92,10 @@ module Stoplight
72
92
  #
73
93
  # @param color should be either +Color::RED+ or +Color::GREEN+
74
94
  # @return locked light
95
+ #
96
+ # @api public
75
97
  def lock(color)
76
- state = case color
77
- when Color::RED then State::LOCKED_RED
78
- when Color::GREEN then State::LOCKED_GREEN
79
- else raise Error::IncorrectColor
80
- end
81
-
82
- state_store.set_state(state)
98
+ @lock_control.lock(color)
83
99
 
84
100
  self
85
101
  end
@@ -92,72 +108,14 @@ module Stoplight
92
108
  # light.unlock
93
109
  #
94
110
  # @return returns unlocked light (circuit breaker)
111
+ #
112
+ # @api public
95
113
  def unlock
96
- state_store.set_state(State::UNLOCKED)
114
+ @lock_control.unlock
97
115
 
98
116
  self
99
117
  end
100
118
 
101
- # Two lights considered equal if they have the same configuration.
102
- def ==(other)
103
- other.is_a?(self.class) && factory == other.factory
104
- end
105
-
106
- # Reconfigures the light with updated settings and returns a new instance.
107
- #
108
- # This method allows you to modify the configuration of a +Stoplight::Light+ object
109
- # by providing a hash of settings. The original light remains unchanged, and a new
110
- # light instance with the updated configuration is returned.
111
- #
112
- # @param settings [Hash] A hash of configuration options to update.
113
- # @option settings [String] :name The name of the light.
114
- # @option settings [Numeric] :cool_off_time The cool-off time in seconds before the light attempts recovery.
115
- # @option settings [Numeric] :threshold The failure threshold to trigger the red state.
116
- # @option settings [Numeric] :window_size The time window in seconds for counting failures.
117
- # @option settings [Stoplight::DataStore::Base] :data_store The data store to use for persisting light state.
118
- # @option settings [Array<Stoplight::Domain::AbstractStateTransitionNotifier>] :notifiers A list of notifiers to handle light events.
119
- # @option settings [Proc] :error_notifier A custom error notifier to handle exceptions.
120
- # @option settings [Array<StandardError>] :tracked_errors A list of errors to track for failure counting.
121
- # @option settings [Array<StandardError>] :skipped_errors A list of errors to skip from failure counting.
122
- # @return [Stoplight::Light] A new `Stoplight::Light` instance with the updated configuration.
123
- #
124
- # @example Reconfiguring a light with custom settings
125
- # light = Stoplight('payment-api')
126
- #
127
- # # Create a light for invoices with a higher threshold
128
- # invoices_light = light.with(tracked_errors: [TimeoutError], threshold: 10)
129
- #
130
- # # Create a light for payments with a lower threshold
131
- # payment_light = light.with(threshold: 5)
132
- #
133
- # # Run the lights with their respective configurations
134
- # invoices_light.run(->(error) { [] }) { call_invoices_api }
135
- # payment_light.run(->(error) { nil }) { call_payment_api }
136
- # @deprecated
137
- # @see +Stoplight()+
138
- # steep:ignore:start
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)
157
- factory.build_with(**settings)
158
- end
159
- # steep:ignore:end
160
-
161
119
  private
162
120
 
163
121
  def state_strategy_factory(color)
@@ -0,0 +1,55 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Stoplight
4
+ module Domain
5
+ # Pins a light to a color (or releases the pin), writing the lock state and emitting +Telemetry::LockChanged+.
6
+ #
7
+ # @example
8
+ # lock_control = Stoplight::Domain::LockControl.new(state_store:, emitter:)
9
+ # lock_control.lock(Stoplight::Color::RED)
10
+ # lock_control.unlock
11
+ #
12
+ # @api private
13
+ class LockControl
14
+ def initialize(state_store:, emitter:)
15
+ @state_store = state_store
16
+ @emitter = emitter
17
+ end
18
+
19
+ # @param color - +Color::RED+ or +Color::GREEN+
20
+ # @raise [Stoplight::Error::IncorrectColor] for any other color
21
+ def lock(color)
22
+ state = case color
23
+ when Color::RED then State::LOCKED_RED
24
+ when Color::GREEN then State::LOCKED_GREEN
25
+ else raise Error::IncorrectColor
26
+ end
27
+
28
+ @emitter.emit(Telemetry::LockChanged) do
29
+ state_snapshot = @state_store.state_snapshot
30
+
31
+ Telemetry::LockChanged.new(from_color: state_snapshot.color, to_color: color,
32
+ from_state: state_snapshot.locked_state, to_state: state)
33
+ end
34
+
35
+ @state_store.set_state(state)
36
+ end
37
+
38
+ def unlock
39
+ @emitter.emit(Telemetry::LockChanged) do
40
+ state_snapshot = @state_store.state_snapshot
41
+ from_color = state_snapshot.color
42
+ from_state = state_snapshot.locked_state
43
+ to_state = State::UNLOCKED
44
+ # Reuses StateSnapshot#color instead of re-deriving it: unlocking can reveal a
45
+ # breach/recovery color that isn't RED or GREEN, unlike a lock's fixed color.
46
+ to_color = state_snapshot.with(locked_state: to_state).color
47
+
48
+ Telemetry::LockChanged.new(from_color:, to_color:, from_state:, to_state:)
49
+ end
50
+
51
+ @state_store.set_state(State::UNLOCKED)
52
+ end
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Stoplight
4
+ module Domain
5
+ class MatcherValidator
6
+ class << self
7
+ def call(error_matcher)
8
+ name = error_matcher.is_a?(Module) ? error_matcher.name : nil
9
+ raise ArgumentError, message(error_matcher) if name.nil?
10
+
11
+ name
12
+ end
13
+
14
+ private
15
+
16
+ def message(error_matcher)
17
+ "error matcher #{error_matcher.inspect} must be a named class or module (e.g. StandardError, " \
18
+ "or a custom class/module overriding `===`) - procs, anonymous classes, and instances have no " \
19
+ "stable name and cannot be used here."
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -29,7 +29,7 @@ module Stoplight
29
29
  Color::GREEN
30
30
  elsif locked_state == State::LOCKED_RED
31
31
  Color::RED
32
- elsif (recovery_scheduled_after && recovery_scheduled_after! < time) || recovery_started_at
32
+ elsif (recovery_scheduled_after && recovery_scheduled_after! < time) || recovery_started?
33
33
  Color::YELLOW
34
34
  elsif breached_at
35
35
  Color::RED
@@ -44,11 +44,7 @@ module Stoplight
44
44
  # This method indicates whether the recovery has already started explicitly
45
45
  #
46
46
  def recovery_started?
47
- if recovery_started_at.nil?
48
- false
49
- else
50
- recovery_started_at! <= time
51
- end
47
+ !!recovery_started_at
52
48
  end
53
49
 
54
50
  def recovery_started_at!
@@ -10,9 +10,10 @@ module Stoplight
10
10
  #
11
11
  # @api private
12
12
  class GreenRunStrategy
13
- def initialize(error_tracking_policy:, request_tracker:)
14
- @error_tracking_policy = error_tracking_policy
13
+ def initialize(request_tracker:, run_recorder:, clock:)
15
14
  @request_tracker = request_tracker
15
+ @run_recorder = run_recorder
16
+ @clock = clock
16
17
  end
17
18
 
18
19
  # Executes the provided code block when the light is in the green state.
@@ -22,37 +23,50 @@ module Stoplight
22
23
  # @yield The code block to execute.
23
24
  # @return The result of the code block if successful.
24
25
  # @raise re-raises the error if it is not tracked or no fallback is provided.
25
- def execute(fallback, state_snapshot:, &code)
26
- # TODO: Consider implementing sampling rate to limit the memory footprint
27
- result = code.call
28
- record_success
29
- result
30
- rescue => error
31
- if @error_tracking_policy.track?(error)
32
- record_error(error)
33
-
34
- if fallback
35
- fallback.call(error)
26
+ def execute(fallback, state_snapshot:, error_tracking_policy:, &code)
27
+ started_at = capture_started_at
28
+
29
+ begin
30
+ result = code.call
31
+ rescue => error
32
+ if error_tracking_policy.track?(error)
33
+ record_error(error, duration_ms: duration_since(started_at), fallback_used: !fallback.nil?)
34
+
35
+ if fallback
36
+ fallback.call(error)
37
+ else
38
+ raise
39
+ end
36
40
  else
41
+ # User chose to not track the error, so we record it as a success
42
+ record_success(duration_ms: duration_since(started_at), error: error)
37
43
  raise
38
44
  end
39
45
  else
40
- # User chose to not track the error, so we record it as a success
41
- record_success
42
- raise
46
+ record_success(duration_ms: duration_since(started_at))
47
+ result
43
48
  end
44
49
  end
45
50
 
46
51
  private
47
52
 
48
- attr_reader :config
49
53
  attr_reader :request_tracker
50
54
 
51
- def record_error(error)
55
+ def capture_started_at
56
+ @clock.monotonic_millis if @run_recorder.subscribed?
57
+ end
58
+
59
+ def duration_since(started_at)
60
+ @clock.monotonic_millis - started_at if started_at
61
+ end
62
+
63
+ def record_error(error, duration_ms:, fallback_used:)
64
+ @run_recorder.record_failure(error, duration_ms:, fallback_used:)
52
65
  request_tracker.record_failure(error)
53
66
  end
54
67
 
55
- def record_success
68
+ def record_success(duration_ms:, error: nil)
69
+ @run_recorder.record_success(duration_ms:, error:)
56
70
  request_tracker.record_success
57
71
  end
58
72
  end
@@ -10,9 +10,10 @@ module Stoplight
10
10
  #
11
11
  # @api private
12
12
  class RedRunStrategy
13
- def initialize(name:, cool_off_time:)
13
+ def initialize(name:, cool_off_time:, run_recorder:)
14
14
  @name = name
15
15
  @cool_off_time = cool_off_time
16
+ @run_recorder = run_recorder
16
17
  end
17
18
 
18
19
  # Executes the fallback proc when the light is in the red state.
@@ -21,7 +22,12 @@ module Stoplight
21
22
  # @param state_snapshot
22
23
  # @return The result of the fallback proc if provided.
23
24
  # @raise [Stoplight::Error::RedLight] Raises an error if no fallback is provided.
24
- def execute(fallback, state_snapshot:)
25
+ def execute(fallback, state_snapshot:, error_tracking_policy:)
26
+ @run_recorder.record_blocked(
27
+ fallback_used: !fallback.nil?,
28
+ retry_after: state_snapshot.recovery_scheduled_after
29
+ )
30
+
25
31
  if fallback
26
32
  fallback.call(nil)
27
33
  else
@@ -13,24 +13,24 @@ module Stoplight
13
13
  class YellowRunStrategy
14
14
  def initialize(
15
15
  name:,
16
- error_tracking_policy:,
17
- notifiers:,
18
16
  request_tracker:,
19
- red_run_strategy:,
20
17
  state_store:,
21
18
  metrics_store:,
22
19
  recovery_lock_store:,
23
- config: # FIXME: needed for backward compatibility, remove when notifier accepts light config
20
+ config:,
21
+ clock:,
22
+ run_recorder:,
23
+ emitter:
24
24
  )
25
- @notifiers = notifiers
26
25
  @request_tracker = request_tracker
27
- @red_run_strategy = red_run_strategy
28
26
  @state_store = state_store
29
27
  @metrics_store = metrics_store
30
28
  @recovery_lock_store = recovery_lock_store
31
29
  @name = name
32
- @error_tracking_policy = error_tracking_policy
33
30
  @config = config
31
+ @clock = clock
32
+ @run_recorder = run_recorder
33
+ @emitter = emitter
34
34
  end
35
35
 
36
36
  # Executes the provided code block when the light is in the yellow state.
@@ -40,62 +40,84 @@ module Stoplight
40
40
  # @yield The code block to execute.
41
41
  # @return The result of the code block if successful.
42
42
  # @raise Re-raises the error if it is not tracked or no fallback is provided.
43
- def execute(fallback, state_snapshot:, &code)
43
+ def execute(fallback, state_snapshot:, error_tracking_policy:, &code)
44
44
  # Everything withing this block executed exclusively:
45
45
  # - enter recovery
46
46
  # - execute user's code
47
47
  # - record outcome
48
48
  # - transition to green or red if needed
49
- with_recovery_lock(fallback:, state_snapshot:, code:) do
49
+ with_recovery_lock(fallback:, state_snapshot:) do |started_at|
50
50
  enter_recovery(state_snapshot)
51
51
 
52
- result = code.call
53
- record_recovery_probe_success
54
- result
55
- rescue => error
56
- if @error_tracking_policy.track?(error)
57
- record_recovery_probe_failure(error)
52
+ begin
53
+ result = code.call
54
+ rescue => error
55
+ if error_tracking_policy.track?(error)
56
+ record_recovery_probe_failure(error, duration_ms: duration_since(started_at), fallback_used: !fallback.nil?)
58
57
 
59
- if fallback
60
- fallback.call(error)
58
+ if fallback
59
+ fallback.call(error)
60
+ else
61
+ raise
62
+ end
61
63
  else
64
+ record_recovery_probe_success(duration_ms: duration_since(started_at), error:)
62
65
  raise
63
66
  end
64
67
  else
65
- record_recovery_probe_success
66
- raise
68
+ record_recovery_probe_success(duration_ms: duration_since(started_at))
69
+ result
67
70
  end
68
71
  end
69
72
  end
70
73
 
71
74
  private
72
75
 
73
- attr_reader :notifiers
74
76
  attr_reader :request_tracker
75
- attr_reader :red_run_strategy
76
77
  attr_reader :state_store
77
78
  attr_reader :metrics_store
78
79
  attr_reader :recovery_lock_store
79
80
 
80
- def with_recovery_lock(fallback:, state_snapshot:, code:)
81
+ def with_recovery_lock(fallback:, state_snapshot:)
81
82
  recovery_lock_token = recovery_lock_store.acquire_lock
82
83
  if recovery_lock_token.nil?
83
- return red_run_strategy.execute(fallback, state_snapshot:, &code)
84
+ @run_recorder.record_blocked(
85
+ fallback_used: !fallback.nil?,
86
+ retry_after: state_snapshot.recovery_scheduled_after
87
+ )
88
+
89
+ return fallback.call(nil) if fallback
90
+
91
+ raise Error::RedLight.new(
92
+ @name,
93
+ cool_off_time: @config.cool_off_time,
94
+ retry_after: state_snapshot.recovery_scheduled_after
95
+ )
84
96
  end
85
97
 
86
98
  begin
87
- yield
99
+ yield capture_started_at
88
100
  ensure
89
101
  recovery_lock_store.release_lock(recovery_lock_token)
90
102
  end
91
103
  end
92
104
 
93
- def record_recovery_probe_success
94
- request_tracker.record_success
105
+ def capture_started_at
106
+ @clock.monotonic_millis
107
+ end
108
+
109
+ def duration_since(started_at)
110
+ @clock.monotonic_millis - started_at
111
+ end
112
+
113
+ def record_recovery_probe_success(duration_ms:, error: nil)
114
+ @run_recorder.record_success(duration_ms:, error:)
115
+ request_tracker.record_success(duration_ms:)
95
116
  end
96
117
 
97
- def record_recovery_probe_failure(error)
98
- request_tracker.record_failure(error)
118
+ def record_recovery_probe_failure(error, duration_ms:, fallback_used:)
119
+ @run_recorder.record_failure(error, duration_ms:, fallback_used:)
120
+ request_tracker.record_failure(error, duration_ms:)
99
121
  end
100
122
 
101
123
  def enter_recovery(state_snapshot)
@@ -103,10 +125,12 @@ module Stoplight
103
125
 
104
126
  state_store.transition_to_color(Color::YELLOW)
105
127
  metrics_store.clear
106
- # FIXME: use light config instead of @_config
107
- # light_info = LightInfo.new(name: @name)
108
- notifiers.each do |notifier|
109
- notifier.notify(@config, Color::RED, Color::YELLOW, nil)
128
+ @emitter.emit(Telemetry::RecoveryStarted) do
129
+ Telemetry::RecoveryStarted.new(
130
+ from_color: Color::RED,
131
+ to_color: Color::YELLOW,
132
+ breached_at: T.must(state_snapshot.breached_at)
133
+ )
110
134
  end
111
135
  end
112
136
  end