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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +1 -1
- data/LICENSE.md +1 -0
- data/README.md +132 -79
- data/UPGRADING.md +265 -6
- data/lib/stoplight/admin/actions/action.rb +4 -12
- data/lib/stoplight/admin/actions/lock.rb +12 -9
- data/lib/stoplight/admin/actions/lock_all.rb +27 -0
- data/lib/stoplight/admin/actions/remove.rb +12 -9
- data/lib/stoplight/admin/actions/stats.rb +15 -12
- data/lib/stoplight/admin/actions/unlock.rb +10 -9
- data/lib/stoplight/admin/assets/apple-touch-icon.png +0 -0
- data/lib/stoplight/admin/assets/favicon.ico +0 -0
- data/lib/stoplight/admin/assets/flowbite.min.css +2 -0
- data/lib/stoplight/admin/assets/flowbite.min.js +1 -0
- data/lib/stoplight/admin/assets/icon.svg +7 -0
- data/lib/stoplight/admin/assets/turbo.es2017-esm.min.js +34 -0
- data/lib/stoplight/admin/config_registry.rb +40 -0
- data/lib/stoplight/admin/dependencies.rb +29 -28
- data/lib/stoplight/admin/helpers.rb +50 -17
- data/lib/stoplight/admin/light_view.rb +184 -0
- data/lib/stoplight/admin/lights_stats.rb +7 -9
- data/lib/stoplight/admin/views/_card.erb +19 -21
- data/lib/stoplight/admin/views/index.erb +9 -3
- data/lib/stoplight/admin/views/layout.erb +45 -13
- data/lib/stoplight/admin.rb +110 -21
- data/lib/stoplight/data_store.rb +18 -8
- data/lib/stoplight/domain/config.rb +4 -1
- data/lib/stoplight/domain/error_tracking_policy.rb +9 -0
- data/lib/stoplight/domain/failure.rb +4 -0
- data/lib/stoplight/domain/id.rb +13 -0
- data/lib/stoplight/domain/light.rb +35 -77
- data/lib/stoplight/domain/lock_control.rb +55 -0
- data/lib/stoplight/domain/matcher_validator.rb +24 -0
- data/lib/stoplight/domain/state_snapshot.rb +2 -6
- data/lib/stoplight/domain/strategies/green_run_strategy.rb +33 -19
- data/lib/stoplight/domain/strategies/red_run_strategy.rb +8 -2
- data/lib/stoplight/domain/strategies/yellow_run_strategy.rb +56 -32
- data/lib/stoplight/domain/telemetry/bus.rb +90 -0
- data/lib/stoplight/domain/telemetry/consumer.rb +18 -0
- data/lib/stoplight/domain/telemetry/emitter.rb +45 -0
- data/lib/stoplight/domain/telemetry/envelope.rb +15 -0
- data/lib/stoplight/domain/telemetry/failure.rb +13 -0
- data/lib/stoplight/domain/telemetry/light_registered.rb +12 -0
- data/lib/stoplight/domain/telemetry/lock_changed.rb +19 -0
- data/lib/stoplight/domain/telemetry/metrics.rb +15 -0
- data/lib/stoplight/domain/telemetry/recovery_failed.rb +20 -0
- data/lib/stoplight/domain/telemetry/recovery_probe_completed.rb +15 -0
- data/lib/stoplight/domain/telemetry/recovery_started.rb +18 -0
- data/lib/stoplight/domain/telemetry/recovery_succeeded.rb +19 -0
- data/lib/stoplight/domain/telemetry/run_completed.rb +17 -0
- data/lib/stoplight/domain/telemetry/run_recorder.rb +58 -0
- data/lib/stoplight/domain/telemetry/settings.rb +21 -0
- data/lib/stoplight/domain/telemetry/state_transitioned.rb +11 -0
- data/lib/stoplight/domain/telemetry/subscription.rb +11 -0
- data/lib/stoplight/domain/telemetry/traffic_breached.rb +20 -0
- data/lib/stoplight/domain/tracker/recovery_probe.rb +61 -17
- data/lib/stoplight/domain/tracker/request.rb +17 -8
- data/lib/stoplight/domain/traffic_control/consecutive_errors.rb +13 -3
- data/lib/stoplight/domain/traffic_control/error_rate.rb +16 -12
- data/lib/stoplight/domain/traffic_recovery/consecutive_successes.rb +9 -3
- data/lib/stoplight/error.rb +10 -1
- data/lib/stoplight/infrastructure/config_serializer.rb +50 -0
- data/lib/stoplight/infrastructure/fail_safe/error_notifier.rb +25 -0
- data/lib/stoplight/infrastructure/fail_safe/storage/registry.rb +68 -0
- data/lib/stoplight/infrastructure/memory/storage/registry.rb +18 -0
- data/lib/stoplight/infrastructure/memory/storage/unbounded_metrics.rb +29 -32
- data/lib/stoplight/infrastructure/memory/storage/window_metrics/sliding_window.rb +74 -0
- data/lib/stoplight/infrastructure/memory/storage/window_metrics.rb +25 -24
- data/lib/stoplight/infrastructure/notifier/fail_safe.rb +3 -8
- data/lib/stoplight/infrastructure/notifier/generic.rb +3 -3
- data/lib/stoplight/infrastructure/redis/key.rb +25 -0
- data/lib/stoplight/infrastructure/redis/storage/now.lua +9 -0
- data/lib/stoplight/infrastructure/redis/storage/recovery_lock.rb +2 -4
- data/lib/stoplight/infrastructure/redis/storage/recovery_metrics.rb +1 -1
- data/lib/stoplight/infrastructure/redis/storage/registry.rb +73 -0
- data/lib/stoplight/infrastructure/redis/storage/scripting.rb +54 -3
- data/lib/stoplight/infrastructure/redis/storage/state/transition_to_green.lua +3 -1
- data/lib/stoplight/infrastructure/redis/storage/state/transition_to_red.lua +4 -2
- data/lib/stoplight/infrastructure/redis/storage/state/transition_to_yellow.lua +3 -1
- data/lib/stoplight/infrastructure/redis/storage/state.rb +8 -8
- data/lib/stoplight/infrastructure/redis/storage/unbounded_metrics/record_failure.lua +20 -21
- data/lib/stoplight/infrastructure/redis/storage/unbounded_metrics/record_success.lua +11 -19
- data/lib/stoplight/infrastructure/redis/storage/unbounded_metrics.rb +20 -16
- data/lib/stoplight/infrastructure/redis/storage/window_metrics/_evict.lua +44 -0
- data/lib/stoplight/infrastructure/redis/storage/window_metrics/metrics_snapshot.lua +14 -21
- data/lib/stoplight/infrastructure/redis/storage/window_metrics/record_failure.lua +38 -36
- data/lib/stoplight/infrastructure/redis/storage/window_metrics/record_success.lua +23 -30
- data/lib/stoplight/infrastructure/redis/storage/window_metrics.rb +45 -135
- data/lib/stoplight/infrastructure/system_clock.rb +4 -0
- data/lib/stoplight/telemetry.rb +19 -0
- data/lib/stoplight/types.rb +15 -2
- data/lib/stoplight/version.rb +1 -1
- data/lib/stoplight/wiring/config_compatibility_validator.rb +25 -2
- data/lib/stoplight/wiring/default.rb +1 -1
- data/lib/stoplight/wiring/default_config.rb +2 -1
- data/lib/stoplight/wiring/default_configuration.rb +3 -2
- data/lib/stoplight/wiring/external_caller.rb +27 -0
- data/lib/stoplight/wiring/fail_safe_config.rb +11 -0
- data/lib/stoplight/wiring/global_state.rb +93 -0
- data/lib/stoplight/wiring/{light_factory → light_configuration_dsl}/traffic_control_dsl.rb +3 -5
- data/lib/stoplight/wiring/{light_factory → light_configuration_dsl}/traffic_recovery_dsl.rb +2 -2
- data/lib/stoplight/wiring/light_configuration_dsl.rb +97 -0
- data/lib/stoplight/wiring/light_factory.rb +154 -97
- data/lib/stoplight/wiring/notifier_bridge.rb +33 -0
- data/lib/stoplight/wiring/redis/backend.rb +1 -1
- data/lib/stoplight/wiring/system/storage.rb +69 -0
- data/lib/stoplight/wiring/system.rb +125 -56
- data/lib/stoplight/wiring/{configuration_dsl.rb → system_configuration_dsl.rb} +13 -43
- data/lib/stoplight.rb +50 -194
- data/sig/sinatra/base.rbs +6 -0
- data/sig/stdlib/securerandom.rbs +4 -0
- data/sig/stoplight/admin/actions/action.rbs +10 -0
- data/sig/stoplight/admin/actions/lock.rbs +17 -0
- data/sig/stoplight/admin/actions/lock_all.rbs +17 -0
- data/sig/stoplight/admin/actions/remove.rbs +22 -0
- data/sig/stoplight/admin/actions/stats.rbs +28 -0
- data/sig/stoplight/admin/actions/unlock.rbs +17 -0
- data/sig/stoplight/admin/config_registry.rbs +20 -0
- data/sig/stoplight/admin/dependencies.rbs +16 -0
- data/sig/stoplight/admin/helpers.rbs +17 -0
- data/sig/stoplight/admin/light_view.rbs +39 -0
- data/sig/stoplight/admin/lights_stats.rbs +30 -0
- data/sig/stoplight/admin.rbs +14 -0
- data/sig/stoplight/color.rbs +3 -3
- data/sig/{_private/stoplight/data_store.rbs → stoplight/data_store/base.rbs} +1 -0
- data/sig/stoplight/data_store/memory.rbs +9 -0
- data/sig/stoplight/data_store/redis.rbs +17 -0
- data/sig/{_private/stoplight → stoplight}/domain/config.rbs +28 -24
- data/sig/{_private/stoplight → stoplight}/domain/error_tracking_policy.rbs +4 -0
- data/sig/{_private/stoplight → stoplight}/domain/failure.rbs +2 -0
- data/sig/stoplight/domain/id.rbs +7 -0
- data/sig/stoplight/domain/light.rbs +39 -0
- data/sig/{_private/stoplight → stoplight}/domain/light_info.rbs +1 -5
- data/sig/stoplight/domain/lock_control.rbs +16 -0
- data/sig/stoplight/domain/matcher_validator.rbs +11 -0
- data/sig/{_private/stoplight → stoplight}/domain/ports/clock.rbs +6 -0
- data/sig/stoplight/domain/ports/data_store.rbs +9 -0
- data/sig/stoplight/domain/ports/error_notifier.rbs +12 -0
- data/sig/stoplight/domain/ports/exception_matcher.rbs +10 -0
- data/sig/{_private/stoplight → stoplight}/domain/ports/metrics_store.rbs +2 -2
- data/sig/stoplight/domain/ports/registry.rbs +13 -0
- data/sig/{_private/stoplight → stoplight}/domain/ports/run_strategy.rbs +2 -1
- data/sig/stoplight/domain/ports/state_transition_notifier.rbs +18 -0
- data/sig/stoplight/domain/ports/telemetry.rbs +28 -0
- data/sig/stoplight/domain/ports/telemetry_publisher.rbs +14 -0
- data/sig/{_private/stoplight → stoplight}/domain/ports/traffic_control.rbs +7 -0
- data/sig/{_private/stoplight → stoplight}/domain/ports/traffic_recovery.rbs +2 -3
- data/sig/stoplight/domain/strategies/green_run_strategy.rbs +22 -0
- data/sig/stoplight/domain/strategies/red_run_strategy.rbs +19 -0
- data/sig/{_private/stoplight → stoplight}/domain/strategies/yellow_run_strategy.rbs +23 -12
- data/sig/stoplight/domain/telemetry/bus.rbs +49 -0
- data/sig/stoplight/domain/telemetry/consumer.rbs +13 -0
- data/sig/stoplight/domain/telemetry/emitter.rbs +51 -0
- data/sig/stoplight/domain/telemetry/envelope.rbs +30 -0
- data/sig/stoplight/domain/telemetry/failure.rbs +23 -0
- data/sig/stoplight/domain/telemetry/light_registered.rbs +23 -0
- data/sig/stoplight/domain/telemetry/lock_changed.rbs +31 -0
- data/sig/stoplight/domain/telemetry/metrics.rbs +27 -0
- data/sig/stoplight/domain/telemetry/recovery_failed.rbs +36 -0
- data/sig/stoplight/domain/telemetry/recovery_probe_completed.rbs +29 -0
- data/sig/stoplight/domain/telemetry/recovery_started.rbs +26 -0
- data/sig/stoplight/domain/telemetry/recovery_succeeded.rbs +31 -0
- data/sig/stoplight/domain/telemetry/run_completed.rbs +38 -0
- data/sig/stoplight/domain/telemetry/run_recorder.rbs +27 -0
- data/sig/stoplight/domain/telemetry/settings.rbs +47 -0
- data/sig/stoplight/domain/telemetry/state_transitioned.rbs +11 -0
- data/sig/stoplight/domain/telemetry/subscription.rbs +9 -0
- data/sig/stoplight/domain/telemetry/traffic_breached.rbs +36 -0
- data/sig/stoplight/domain/telemetry/types.rbs +32 -0
- data/sig/{_private/stoplight → stoplight}/domain/tracker/recovery_probe.rbs +10 -6
- data/sig/{_private/stoplight → stoplight}/domain/tracker/request.rbs +3 -3
- data/sig/{_private/stoplight → stoplight}/domain/traffic_control/consecutive_errors.rbs +2 -0
- data/sig/{_private/stoplight → stoplight}/domain/traffic_control/error_rate.rbs +3 -2
- data/sig/{_private/stoplight → stoplight}/domain/traffic_recovery/consecutive_successes.rbs +2 -0
- data/sig/stoplight/domain/types.rbs +25 -0
- data/sig/stoplight/error.rbs +8 -0
- data/sig/stoplight/generators/rails_generators_base.rbs +11 -0
- data/sig/stoplight/infrastructure/config_serializer.rbs +12 -0
- data/sig/stoplight/infrastructure/fail_safe/error_notifier.rbs +13 -0
- data/sig/{_private/stoplight → stoplight}/infrastructure/fail_safe/storage/metrics.rbs +2 -2
- data/sig/{_private/stoplight → stoplight}/infrastructure/fail_safe/storage/recovery_lock.rbs +2 -2
- data/sig/stoplight/infrastructure/fail_safe/storage/registry.rbs +23 -0
- data/sig/{_private/stoplight → stoplight}/infrastructure/fail_safe/storage/state.rbs +4 -4
- data/sig/stoplight/infrastructure/memory/storage/registry.rbs +13 -0
- data/sig/{_private/stoplight → stoplight}/infrastructure/memory/storage/state.rbs +3 -3
- data/sig/{_private/stoplight → stoplight}/infrastructure/memory/storage/unbounded_metrics.rbs +7 -7
- data/sig/stoplight/infrastructure/memory/storage/window_metrics/sliding_window.rbs +27 -0
- data/sig/{_private/stoplight → stoplight}/infrastructure/memory/storage/window_metrics.rbs +7 -7
- data/sig/stoplight/infrastructure/notifier/fail_safe.rbs +15 -0
- data/sig/{_private/stoplight → stoplight}/infrastructure/notifier/generic.rbs +1 -1
- data/sig/{_private/stoplight → stoplight}/infrastructure/notifier/io.rbs +1 -1
- data/sig/{_private/stoplight → stoplight}/infrastructure/notifier/logger.rbs +1 -1
- data/sig/stoplight/infrastructure/redis/key.rbs +10 -0
- data/sig/{_private/stoplight → stoplight}/infrastructure/redis/storage/recovery_lock.rbs +2 -2
- data/sig/stoplight/infrastructure/redis/storage/registry.rbs +27 -0
- data/sig/stoplight/infrastructure/redis/storage/scripting.rbs +34 -0
- data/sig/{_private/stoplight → stoplight}/infrastructure/redis/storage/state.rbs +4 -4
- data/sig/{_private/stoplight → stoplight}/infrastructure/redis/storage/unbounded_metrics.rbs +8 -1
- data/sig/stoplight/infrastructure/redis/storage/window_metrics.rbs +35 -0
- data/sig/stoplight/infrastructure/types.rbs +6 -0
- data/sig/stoplight/notifier.rbs +1 -1
- data/sig/stoplight/ports/system.rbs +16 -10
- data/sig/stoplight/state.rbs +3 -3
- data/sig/stoplight/telemetry.rbs +17 -0
- data/sig/stoplight/types.rbs +9 -0
- data/sig/{_private/stoplight → stoplight}/wiring/config_compatibility_validator.rbs +2 -0
- data/sig/stoplight/wiring/default.rbs +26 -0
- data/sig/stoplight/wiring/default_configuration.rbs +39 -0
- data/sig/stoplight/wiring/external_caller.rbs +13 -0
- data/sig/stoplight/wiring/fail_safe_config.rbs +7 -0
- data/sig/stoplight/wiring/global_state.rbs +36 -0
- data/sig/{_private/stoplight/wiring/light_factory → stoplight/wiring/light_configuration_dsl}/traffic_control_dsl.rbs +1 -1
- data/sig/{_private/stoplight/wiring/light_factory → stoplight/wiring/light_configuration_dsl}/traffic_recovery_dsl.rbs +1 -1
- data/sig/stoplight/wiring/light_configuration_dsl.rbs +38 -0
- data/sig/stoplight/wiring/light_factory.rbs +78 -0
- data/sig/stoplight/wiring/notifier_bridge.rbs +15 -0
- data/sig/stoplight/wiring/notifier_factory.rbs +10 -0
- data/sig/{_private/stoplight → stoplight}/wiring/redis/backend.rbs +6 -6
- data/sig/stoplight/wiring/system/storage.rbs +35 -0
- data/sig/stoplight/wiring/system.rbs +30 -0
- data/sig/stoplight/wiring/system_configuration_dsl.rbs +39 -0
- data/sig/stoplight/wiring/types.rbs +6 -0
- data/sig/stoplight.rbs +37 -54
- metadata +190 -143
- data/lib/stoplight/admin/actions/lock_all_green.rb +0 -18
- data/lib/stoplight/admin/actions/lock_green.rb +0 -23
- data/lib/stoplight/admin/actions/lock_red.rb +0 -23
- data/lib/stoplight/admin/lights_repository/light.rb +0 -171
- data/lib/stoplight/admin/lights_repository.rb +0 -87
- data/lib/stoplight/domain/light/configuration_builder_interface.rb +0 -236
- data/lib/stoplight/infrastructure/fail_safe/data_store.rb +0 -152
- data/lib/stoplight/infrastructure/memory/data_store/metrics.rb +0 -27
- data/lib/stoplight/infrastructure/memory/data_store/recovery_lock_store.rb +0 -52
- data/lib/stoplight/infrastructure/memory/data_store/recovery_lock_token.rb +0 -17
- data/lib/stoplight/infrastructure/memory/data_store/sliding_window.rb +0 -74
- data/lib/stoplight/infrastructure/memory/data_store/state.rb +0 -21
- data/lib/stoplight/infrastructure/memory/data_store.rb +0 -342
- data/lib/stoplight/infrastructure/redis/data_store/lua_scripts/get_metrics.lua +0 -26
- data/lib/stoplight/infrastructure/redis/data_store/lua_scripts/record_failure.lua +0 -36
- data/lib/stoplight/infrastructure/redis/data_store/lua_scripts/record_recovery_probe_failure.lua +0 -27
- data/lib/stoplight/infrastructure/redis/data_store/lua_scripts/record_recovery_probe_success.lua +0 -23
- data/lib/stoplight/infrastructure/redis/data_store/lua_scripts/record_success.lua +0 -35
- data/lib/stoplight/infrastructure/redis/data_store/lua_scripts/release_lock.lua +0 -6
- data/lib/stoplight/infrastructure/redis/data_store/lua_scripts/transition_to_green.lua +0 -10
- data/lib/stoplight/infrastructure/redis/data_store/lua_scripts/transition_to_red.lua +0 -10
- data/lib/stoplight/infrastructure/redis/data_store/lua_scripts/transition_to_yellow.lua +0 -9
- data/lib/stoplight/infrastructure/redis/data_store/recovery_lock_store.rb +0 -60
- data/lib/stoplight/infrastructure/redis/data_store/recovery_lock_token.rb +0 -28
- data/lib/stoplight/infrastructure/redis/data_store/scripting.rb +0 -73
- data/lib/stoplight/infrastructure/redis/data_store.rb +0 -516
- data/lib/stoplight/infrastructure/redis/storage/key_space.rb +0 -51
- data/lib/stoplight/infrastructure/storage/compatibility_metrics.rb +0 -41
- data/lib/stoplight/infrastructure/storage/compatibility_recovery_lock.rb +0 -33
- data/lib/stoplight/infrastructure/storage/compatibility_recovery_metrics.rb +0 -47
- data/lib/stoplight/infrastructure/storage/compatibility_state.rb +0 -44
- data/lib/stoplight/wiring/light_builder.rb +0 -198
- data/lib/stoplight/wiring/notifier_factory.rb +0 -26
- data/lib/stoplight/wiring/system/light_builder.rb +0 -47
- data/lib/stoplight/wiring/system/light_factory.rb +0 -64
- data/sig/_private/stoplight/domain/light.rbs +0 -25
- data/sig/_private/stoplight/domain/ports/data_store.rbs +0 -76
- data/sig/_private/stoplight/domain/ports/light_factory.rbs +0 -80
- data/sig/_private/stoplight/domain/strategies/green_run_strategy.rbs +0 -17
- data/sig/_private/stoplight/domain/strategies/red_run_strategy.rbs +0 -17
- data/sig/_private/stoplight/domain/tracker/base.rbs +0 -8
- data/sig/_private/stoplight/infrastructure/fail_safe/data_store.rbs +0 -26
- data/sig/_private/stoplight/infrastructure/memory/data_store/metrics.rbs +0 -25
- data/sig/_private/stoplight/infrastructure/memory/data_store/recovery_lock_store.rbs +0 -19
- data/sig/_private/stoplight/infrastructure/memory/data_store/recovery_lock_token.rbs +0 -17
- data/sig/_private/stoplight/infrastructure/memory/data_store/sliding_window.rbs +0 -27
- data/sig/_private/stoplight/infrastructure/memory/data_store/state.rbs +0 -17
- data/sig/_private/stoplight/infrastructure/memory/data_store.rbs +0 -30
- data/sig/_private/stoplight/infrastructure/notifier/fail_safe.rbs +0 -17
- data/sig/_private/stoplight/infrastructure/redis/data_store/recovery_lock_store.rbs +0 -24
- data/sig/_private/stoplight/infrastructure/redis/data_store/recovery_lock_token.rbs +0 -21
- data/sig/_private/stoplight/infrastructure/redis/data_store/scripting.rbs +0 -34
- data/sig/_private/stoplight/infrastructure/redis/data_store.rbs +0 -67
- data/sig/_private/stoplight/infrastructure/redis/storage/key_space.rbs +0 -19
- data/sig/_private/stoplight/infrastructure/redis/storage/scripting.rbs +0 -13
- data/sig/_private/stoplight/infrastructure/redis/storage/window_metrics.rbs +0 -34
- data/sig/_private/stoplight/infrastructure/storage/compatibility_metrics.rbs +0 -17
- data/sig/_private/stoplight/infrastructure/storage/compatibility_recovery_lock.rbs +0 -13
- data/sig/_private/stoplight/infrastructure/storage/compatibility_recovery_metrics.rbs +0 -14
- data/sig/_private/stoplight/infrastructure/storage/compatibility_state.rbs +0 -14
- data/sig/_private/stoplight/system/light_builder.rbs +0 -23
- data/sig/_private/stoplight/system/light_factory.rbs +0 -17
- data/sig/_private/stoplight/types.rbs +0 -6
- data/sig/_private/stoplight/wiring/configuration_dsl.rbs +0 -43
- data/sig/_private/stoplight/wiring/default.rbs +0 -26
- data/sig/_private/stoplight/wiring/default_configuration.rbs +0 -29
- data/sig/_private/stoplight/wiring/light_builder.rbs +0 -48
- data/sig/_private/stoplight/wiring/light_factory.rbs +0 -16
- data/sig/_private/stoplight/wiring/notifier_factory.rbs +0 -10
- data/sig/_private/stoplight/wiring/system.rbs +0 -15
- data/sig/_private/stoplight.rbs +0 -48
- data/sig/stoplight/data_store.rbs +0 -19
- data/sig/stoplight/ports/configuration.rbs +0 -19
- data/sig/stoplight/ports/exception_matcher.rbs +0 -8
- data/sig/stoplight/ports/light.rbs +0 -12
- data/sig/stoplight/ports/light_info.rbs +0 -5
- data/sig/stoplight/ports/state_transition_notifier.rbs +0 -15
- /data/sig/{_private/stoplight → stoplight}/common/deprecations.rbs +0 -0
- /data/sig/{_private/stoplight → stoplight}/domain/compatibility_result.rbs +0 -0
- /data/sig/{_private/stoplight → stoplight}/domain/metrics_snapshot.rbs +0 -0
- /data/sig/{_private/stoplight → stoplight}/domain/ports/recovery_lock_store.rbs +0 -0
- /data/sig/{_private/stoplight → stoplight}/domain/ports/recovery_lock_token.rbs +0 -0
- /data/sig/{_private/stoplight → stoplight}/domain/ports/state_store.rbs +0 -0
- /data/sig/{_private/stoplight → stoplight}/domain/state_snapshot.rbs +0 -0
- /data/sig/{_private/stoplight → stoplight}/domain/storage/recovery_lock_token.rbs +0 -0
- /data/sig/{_private/stoplight → stoplight}/domain/traffic_recovery.rbs +0 -0
- /data/sig/{_private → stoplight}/generators/stoplight/install/install_generator.rbs +0 -0
- /data/sig/{_private/stoplight → stoplight}/infrastructure/fail_safe/storage/recovery_lock_token.rbs +0 -0
- /data/sig/{_private/stoplight → stoplight}/infrastructure/memory/storage/recovery_lock.rbs +0 -0
- /data/sig/{_private/stoplight → stoplight}/infrastructure/memory/storage/recovery_metrics.rbs +0 -0
- /data/sig/{_private/stoplight → stoplight}/infrastructure/redis/storage/metrics.rbs +0 -0
- /data/sig/{_private/stoplight → stoplight}/infrastructure/redis/storage/recovery_metrics.rbs +0 -0
- /data/sig/{_private/stoplight → stoplight}/infrastructure/system_clock.rbs +0 -0
- /data/sig/{_private/stoplight → stoplight}/wiring/data_store_backend.rbs +0 -0
- /data/sig/{_private/stoplight → stoplight}/wiring/default_config.rbs +0 -0
- /data/sig/{_private/stoplight → stoplight}/wiring/memory/backend.rbs +0 -0
- /data/sig/{_private/stoplight → stoplight}/wiring/storage_set.rbs +0 -0
- /data/sig/{_private/stoplight → stoplight}/wiring/storage_set_builder.rbs +0 -0
|
@@ -2,126 +2,183 @@
|
|
|
2
2
|
|
|
3
3
|
module Stoplight
|
|
4
4
|
module Wiring
|
|
5
|
-
#
|
|
5
|
+
# Wires together all infrastructure components (trackers, strategies, storage
|
|
6
|
+
# backend) needed for a functioning circuit breaker.
|
|
6
7
|
#
|
|
7
|
-
# This factory implements the +Stoplight::Domain::LightFactory+ protocol. It knows how to:
|
|
8
|
-
# 1. Parse and transform user-provided settings
|
|
9
|
-
# 2. Wire together all Light dependencies using a DI container
|
|
10
|
-
# 3. Validate configuration compatibility
|
|
11
|
-
# 4. Construct fully-functional Light instances
|
|
12
|
-
#
|
|
13
|
-
# @see Stoplight::Domain::LightFactory
|
|
14
|
-
# @see Stoplight()
|
|
15
8
|
# @api private
|
|
16
|
-
#
|
|
17
9
|
class LightFactory
|
|
18
|
-
def initialize(config:)
|
|
10
|
+
def initialize(system_id:, system_name:, config:, failover_system:, telemetry:)
|
|
11
|
+
@system_id = system_id
|
|
12
|
+
@system_name = system_name
|
|
13
|
+
@failover_system = failover_system
|
|
19
14
|
@config = config
|
|
15
|
+
|
|
16
|
+
@clock = Infrastructure::SystemClock.new
|
|
17
|
+
@name = T.must(config.name)
|
|
18
|
+
@cool_off_time = config.cool_off_time
|
|
19
|
+
|
|
20
|
+
@data_store_config = config.data_store
|
|
21
|
+
@error_notifier = Infrastructure::FailSafe::ErrorNotifier.new(
|
|
22
|
+
error_notifier: config.error_notifier
|
|
23
|
+
)
|
|
24
|
+
@traffic_recovery = config.traffic_recovery
|
|
25
|
+
@traffic_control = config.traffic_control
|
|
26
|
+
|
|
27
|
+
@error_tracking_policy = Domain::ErrorTrackingPolicy.new(
|
|
28
|
+
tracked: config.tracked_errors,
|
|
29
|
+
skipped: config.skipped_errors
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
@emitter = Domain::Telemetry::Emitter.new(
|
|
33
|
+
bus: telemetry,
|
|
34
|
+
system_name: @system_name,
|
|
35
|
+
light_name: config.name,
|
|
36
|
+
clock: @clock,
|
|
37
|
+
error_notifier: @error_notifier
|
|
38
|
+
)
|
|
20
39
|
end
|
|
21
40
|
|
|
22
|
-
def
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
error_notifier: T.undefined,
|
|
32
|
-
notifiers: T.undefined,
|
|
33
|
-
traffic_control: T.undefined,
|
|
34
|
-
traffic_recovery: T.undefined
|
|
35
|
-
)
|
|
36
|
-
self.class.new(
|
|
37
|
-
config: ConfigurationDsl.new(
|
|
38
|
-
name:,
|
|
39
|
-
cool_off_time:,
|
|
40
|
-
threshold:,
|
|
41
|
-
recovery_threshold:,
|
|
42
|
-
window_size:,
|
|
43
|
-
tracked_errors:,
|
|
44
|
-
skipped_errors:,
|
|
45
|
-
traffic_control:,
|
|
46
|
-
traffic_recovery:,
|
|
47
|
-
error_notifier:,
|
|
48
|
-
data_store:,
|
|
49
|
-
notifiers:
|
|
50
|
-
).configure!(config)
|
|
41
|
+
def build
|
|
42
|
+
light = Stoplight::Domain::Light.new(
|
|
43
|
+
@name,
|
|
44
|
+
state_store:,
|
|
45
|
+
green_run_strategy:,
|
|
46
|
+
yellow_run_strategy:,
|
|
47
|
+
red_run_strategy:,
|
|
48
|
+
lock_control:,
|
|
49
|
+
error_tracking_policy: @error_tracking_policy
|
|
51
50
|
)
|
|
51
|
+
|
|
52
|
+
@emitter.emit(Domain::Telemetry::LightRegistered) do
|
|
53
|
+
Domain::Telemetry::LightRegistered.new(settings: telemetry_settings)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
light
|
|
52
57
|
end
|
|
53
58
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
# +Stoplight::Light#with+ method to work for reconfiguration.
|
|
59
|
-
#
|
|
60
|
-
# @return [Stoplight::Light] Configured circuit breaker
|
|
61
|
-
# @raise [Stoplight::Error::ConfigurationError] If configuration is invalid
|
|
62
|
-
#
|
|
63
|
-
# @example
|
|
64
|
-
# factory = Stoplight::Wiring::LightFactory.new(container)
|
|
65
|
-
# light = factory.build
|
|
66
|
-
#
|
|
67
|
-
# # Light is ready to use
|
|
68
|
-
# light.run { api_call }
|
|
59
|
+
def state_store = storage_set.state_store
|
|
60
|
+
def metrics_store = storage_set.metrics_store
|
|
61
|
+
def lock_control = Domain::LockControl.new(state_store:, emitter: @emitter)
|
|
62
|
+
def recovery_metrics_store = storage_set.recovery_metrics_store
|
|
69
63
|
|
|
70
|
-
|
|
71
|
-
|
|
64
|
+
private
|
|
65
|
+
|
|
66
|
+
attr_reader :data_store_config
|
|
67
|
+
attr_reader :error_notifier
|
|
68
|
+
attr_reader :clock
|
|
69
|
+
attr_reader :traffic_control
|
|
70
|
+
attr_reader :traffic_recovery
|
|
71
|
+
attr_reader :config
|
|
72
|
+
attr_reader :system_name
|
|
73
|
+
|
|
74
|
+
def recovery_lock_store = storage_set.recovery_lock_store
|
|
75
|
+
def storage_scripting = Infrastructure::Redis::Storage::Scripting.new(redis:)
|
|
76
|
+
def failover_system = T.must(@failover_system)
|
|
77
|
+
|
|
78
|
+
def key_space
|
|
79
|
+
@key_space ||= case data_store_config
|
|
80
|
+
when DataStore::Redis
|
|
81
|
+
# We use keys like +stoplight:version:system_id:{light_id}:metrics+ prefixes,
|
|
82
|
+
# Putting +liht_id+ into curly braces make it a Redis Cluster hash-tag - this is a segment
|
|
83
|
+
# for slot colocation.
|
|
84
|
+
data_store_config.key_space.join(@system_id, "{#{@config.id}}")
|
|
85
|
+
else
|
|
86
|
+
raise T.absurd(data_store_config)
|
|
87
|
+
end
|
|
72
88
|
end
|
|
73
89
|
|
|
74
|
-
def
|
|
75
|
-
|
|
90
|
+
def storage_set
|
|
91
|
+
@storage_set ||= StorageSetBuilder.new(backend: build_backend, windowed: !config.window_size.nil?).build
|
|
76
92
|
end
|
|
77
93
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
def build_with(
|
|
81
|
-
name: T.undefined,
|
|
82
|
-
cool_off_time: T.undefined,
|
|
83
|
-
threshold: T.undefined,
|
|
84
|
-
recovery_threshold: T.undefined,
|
|
85
|
-
window_size: T.undefined,
|
|
86
|
-
tracked_errors: T.undefined,
|
|
87
|
-
skipped_errors: T.undefined,
|
|
88
|
-
data_store: T.undefined,
|
|
89
|
-
error_notifier: T.undefined,
|
|
90
|
-
notifiers: T.undefined,
|
|
91
|
-
traffic_control: T.undefined,
|
|
92
|
-
traffic_recovery: T.undefined
|
|
93
|
-
)
|
|
94
|
-
with(
|
|
95
|
-
name:,
|
|
96
|
-
cool_off_time:,
|
|
97
|
-
threshold:,
|
|
98
|
-
recovery_threshold:,
|
|
99
|
-
window_size:,
|
|
100
|
-
tracked_errors:,
|
|
101
|
-
skipped_errors:,
|
|
102
|
-
data_store:,
|
|
103
|
-
error_notifier:,
|
|
104
|
-
notifiers:,
|
|
105
|
-
traffic_control:,
|
|
106
|
-
traffic_recovery:
|
|
107
|
-
).build
|
|
94
|
+
def request_tracker
|
|
95
|
+
Domain::Tracker::Request.new(traffic_control:, config:, metrics_store:, state_store:, emitter: @emitter)
|
|
108
96
|
end
|
|
109
97
|
|
|
110
|
-
def
|
|
111
|
-
|
|
98
|
+
def recovery_probe_tracker
|
|
99
|
+
Domain::Tracker::RecoveryProbe.new(
|
|
100
|
+
traffic_recovery:,
|
|
101
|
+
config:,
|
|
102
|
+
metrics_store: recovery_metrics_store,
|
|
103
|
+
state_store:,
|
|
104
|
+
emitter: @emitter
|
|
105
|
+
)
|
|
112
106
|
end
|
|
113
107
|
|
|
114
|
-
|
|
108
|
+
def green_run_strategy
|
|
109
|
+
Domain::Strategies::GreenRunStrategy.new(
|
|
110
|
+
request_tracker:,
|
|
111
|
+
clock:,
|
|
112
|
+
run_recorder: run_recorder(Color::GREEN)
|
|
113
|
+
)
|
|
114
|
+
end
|
|
115
115
|
|
|
116
|
-
|
|
116
|
+
def yellow_run_strategy
|
|
117
|
+
Domain::Strategies::YellowRunStrategy.new(
|
|
118
|
+
name: @name,
|
|
119
|
+
request_tracker: recovery_probe_tracker,
|
|
120
|
+
state_store:,
|
|
121
|
+
metrics_store:,
|
|
122
|
+
recovery_lock_store:,
|
|
123
|
+
config: @config,
|
|
124
|
+
clock:,
|
|
125
|
+
run_recorder: run_recorder(Color::YELLOW),
|
|
126
|
+
emitter: @emitter
|
|
127
|
+
)
|
|
128
|
+
end
|
|
117
129
|
|
|
118
|
-
|
|
130
|
+
def red_run_strategy
|
|
131
|
+
Domain::Strategies::RedRunStrategy.new(
|
|
132
|
+
name: @name,
|
|
133
|
+
cool_off_time: @cool_off_time,
|
|
134
|
+
run_recorder: run_recorder(Color::RED)
|
|
135
|
+
)
|
|
136
|
+
end
|
|
119
137
|
|
|
120
|
-
def
|
|
121
|
-
|
|
138
|
+
def run_recorder(color)
|
|
139
|
+
Domain::Telemetry::RunRecorder.new(emitter: @emitter, color:)
|
|
122
140
|
end
|
|
123
141
|
|
|
124
|
-
def
|
|
142
|
+
def telemetry_settings
|
|
143
|
+
Domain::Telemetry::Settings.new(
|
|
144
|
+
cool_off_time: @config.cool_off_time,
|
|
145
|
+
threshold: @config.threshold,
|
|
146
|
+
recovery_threshold: @config.recovery_threshold,
|
|
147
|
+
window_size: @config.window_size,
|
|
148
|
+
tracked_errors: @config.tracked_errors.map { |matcher| Domain::MatcherValidator.call(matcher) },
|
|
149
|
+
skipped_errors: @config.skipped_errors.map { |matcher| Domain::MatcherValidator.call(matcher) },
|
|
150
|
+
traffic_control: @config.traffic_control.name,
|
|
151
|
+
traffic_recovery: @config.traffic_recovery.name,
|
|
152
|
+
# No strategy accepts constructor params yet; placeholder for forward compatibility.
|
|
153
|
+
traffic_control_params: {},
|
|
154
|
+
traffic_recovery_params: {}
|
|
155
|
+
)
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
def redis
|
|
159
|
+
case data_store_config
|
|
160
|
+
when DataStore::Redis
|
|
161
|
+
data_store_config.redis
|
|
162
|
+
else
|
|
163
|
+
raise T.absurd(data_store_config)
|
|
164
|
+
end
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
def build_backend
|
|
168
|
+
case data_store_config
|
|
169
|
+
in DataStore::Memory
|
|
170
|
+
Memory::Backend.new(clock:, config:)
|
|
171
|
+
in DataStore::Redis
|
|
172
|
+
Redis::Backend.new(
|
|
173
|
+
redis:, scripting: storage_scripting, key_space:, clock:, config:, error_notifier:,
|
|
174
|
+
failover_light: create_circuit_breaker("redis")
|
|
175
|
+
)
|
|
176
|
+
end
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
def create_circuit_breaker(name)
|
|
180
|
+
failover_system.register(name)
|
|
181
|
+
end
|
|
125
182
|
end
|
|
126
183
|
end
|
|
127
184
|
end
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Stoplight
|
|
4
|
+
module Wiring
|
|
5
|
+
# Subscribes the legacy, user-facing +Notifier+ collaborators to the telemetry bus, so
|
|
6
|
+
# +Domain::Light+'s collaborators never call notifiers directly.
|
|
7
|
+
#
|
|
8
|
+
# @api private
|
|
9
|
+
class NotifierBridge
|
|
10
|
+
def initialize(notifiers:)
|
|
11
|
+
@notifiers = notifiers
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def subscribe(bus)
|
|
15
|
+
bus.subscribe(Domain::Telemetry::TrafficBreached) { |envelope| notify(envelope, error: envelope.payload.failure&.exception) }
|
|
16
|
+
bus.subscribe(Domain::Telemetry::RecoveryStarted) { |envelope| notify(envelope, error: nil) }
|
|
17
|
+
bus.subscribe(Domain::Telemetry::RecoverySucceeded) { |envelope| notify(envelope, error: nil) }
|
|
18
|
+
bus.subscribe(Domain::Telemetry::RecoveryFailed) { |envelope| notify(envelope, error: envelope.payload.failure&.exception) }
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
private
|
|
22
|
+
|
|
23
|
+
def notify(envelope, error:)
|
|
24
|
+
info = Domain::LightInfo.new(name: envelope.light_name)
|
|
25
|
+
payload = envelope.payload
|
|
26
|
+
|
|
27
|
+
@notifiers.each do |notifier|
|
|
28
|
+
notifier.notify(info, payload.from_color, payload.to_color, error)
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
@@ -17,7 +17,7 @@ module Stoplight
|
|
|
17
17
|
# backend = Redis::Backend.new(
|
|
18
18
|
# redis: redis_connection,
|
|
19
19
|
# scripting: Scripting.new(redis:),
|
|
20
|
-
# key_space
|
|
20
|
+
# key_space:,
|
|
21
21
|
# config: light_config,
|
|
22
22
|
# error_notifier: ->(e) { Logger.error(e) },
|
|
23
23
|
# failover_light: Stoplight("redis-failover"),
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Stoplight
|
|
4
|
+
module Wiring
|
|
5
|
+
class System
|
|
6
|
+
# Only for admin panel use.
|
|
7
|
+
class Storage
|
|
8
|
+
def initialize(system_id:, system_name:, failover_system:, telemetry:)
|
|
9
|
+
@system_id = system_id
|
|
10
|
+
@system_name = system_name
|
|
11
|
+
@failover_system = failover_system
|
|
12
|
+
@factories = {}
|
|
13
|
+
@telemetry = telemetry
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def recovery_metrics_snapshot(config)
|
|
17
|
+
recovery_metrics_store(config).metrics_snapshot
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def state_snapshot(config)
|
|
21
|
+
state_store(config).state_snapshot
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def metrics_snapshot(config)
|
|
25
|
+
metrics_store(config).metrics_snapshot
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def delete(config)
|
|
29
|
+
state_store(config).clear
|
|
30
|
+
metrics_store(config).clear
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def lock(config, color)
|
|
34
|
+
lock_control(config).lock(color)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def unlock(config)
|
|
38
|
+
lock_control(config).unlock
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
private def recovery_metrics_store(config)
|
|
42
|
+
light_factory(config).recovery_metrics_store
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
private def state_store(config)
|
|
46
|
+
light_factory(config).state_store
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
private def metrics_store(config)
|
|
50
|
+
light_factory(config).metrics_store
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
private def lock_control(config)
|
|
54
|
+
light_factory(config).lock_control
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
private def light_factory(config)
|
|
58
|
+
@factories[config] ||= LightFactory.new(
|
|
59
|
+
system_id: @system_id,
|
|
60
|
+
system_name: @system_name,
|
|
61
|
+
config:,
|
|
62
|
+
failover_system: @failover_system,
|
|
63
|
+
telemetry: @telemetry
|
|
64
|
+
)
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
|
@@ -4,7 +4,6 @@ require "concurrent/map"
|
|
|
4
4
|
|
|
5
5
|
module Stoplight
|
|
6
6
|
module Wiring
|
|
7
|
-
# 🚧UNDER CONSTRUCTION 🚧
|
|
8
7
|
# System provides namespace isolation and shared configuration for related circuits.
|
|
9
8
|
#
|
|
10
9
|
# Systems enforce configuration consistency within their scope - creating the same
|
|
@@ -13,71 +12,97 @@ module Stoplight
|
|
|
13
12
|
# This prevents subtle bugs where circuits silently interfere with each other.
|
|
14
13
|
#
|
|
15
14
|
# @example Basic usage
|
|
16
|
-
# billing = Stoplight.
|
|
15
|
+
# billing = Stoplight.register_system(
|
|
16
|
+
# "Billing",
|
|
17
17
|
# data_store: billing_redis,
|
|
18
18
|
# threshold: 5,
|
|
19
19
|
# window_size: 300
|
|
20
20
|
# )
|
|
21
21
|
#
|
|
22
|
-
# billing.
|
|
23
|
-
# billing.
|
|
22
|
+
# billing.register("stripe")
|
|
23
|
+
# billing.register("paypal")
|
|
24
|
+
#
|
|
25
|
+
# billing.light("stripe").run { ... }
|
|
26
|
+
# billing.light("paypal").run { ... }
|
|
24
27
|
#
|
|
25
28
|
# @example Multi-tenancy
|
|
26
|
-
# tenant_a = Stoplight.
|
|
27
|
-
# tenant_b = Stoplight.
|
|
29
|
+
# tenant_a = Stoplight.register_system("Tenant A", data_store: tenant_a_redis)
|
|
30
|
+
# tenant_b = Stoplight.register_system("Tenant B", data_store: tenant_b_redis)
|
|
28
31
|
#
|
|
29
32
|
# # Same circuit name, completely isolated
|
|
30
|
-
# tenant_a.
|
|
31
|
-
# tenant_b.
|
|
33
|
+
# tenant_a.register("api")
|
|
34
|
+
# tenant_b.register("api")
|
|
32
35
|
#
|
|
33
36
|
# @example Configuration inheritance
|
|
34
|
-
# system = Stoplight.
|
|
35
|
-
#
|
|
36
|
-
# system.light("stripe") # Inherits threshold: 3
|
|
37
|
-
# system.light("paypal", threshold: 5) # Overrides threshold
|
|
37
|
+
# system = Stoplight.register_system("Payments", threshold: 3, cool_off_time: 600)
|
|
38
38
|
#
|
|
39
|
-
#
|
|
40
|
-
#
|
|
41
|
-
# matching works correctly across multiple system references.
|
|
39
|
+
# system.register("stripe") # Inherits threshold: 3
|
|
40
|
+
# system.register("paypal", threshold: 5) # Overrides threshold
|
|
42
41
|
#
|
|
43
|
-
# @note Light instances are cached within the system
|
|
44
|
-
#
|
|
45
|
-
#
|
|
46
|
-
# @api private
|
|
42
|
+
# @note Light instances are cached within the system once {#register}ed. {#light}
|
|
43
|
+
# returns that cached instance and raises +Stoplight::Error::UnregisteredLightError+
|
|
44
|
+
# if the name was never registered.
|
|
47
45
|
class System
|
|
46
|
+
REGISTRATION_FRAME_LIMIT = 5
|
|
47
|
+
private_constant :REGISTRATION_FRAME_LIMIT
|
|
48
|
+
|
|
48
49
|
attr_reader :name
|
|
49
|
-
#
|
|
50
|
-
|
|
51
|
-
attr_reader :system_config
|
|
50
|
+
# @api private
|
|
51
|
+
attr_reader :config
|
|
52
52
|
|
|
53
|
-
|
|
53
|
+
# Returns the consumer (subscribe-only) side of the telemetry bus.
|
|
54
|
+
def telemetry
|
|
55
|
+
Domain::Telemetry::Consumer.new(@telemetry)
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# @param failover_system is a system used to create lights that protects components
|
|
59
|
+
# of this system. For example if your system uses Redis data store, or notifiers that
|
|
60
|
+
# communicate with external systems, they could go off. Failover system hosts
|
|
61
|
+
# all the circuit breakers that are needed to protect these dependencies from failing.
|
|
62
|
+
# Failover system itself never uses external dependencies and therefore does not need
|
|
63
|
+
# external failover.
|
|
64
|
+
def initialize(config:, failover_system:, registry:)
|
|
54
65
|
@name = config.name
|
|
55
|
-
@
|
|
66
|
+
@config = config
|
|
56
67
|
@lights = Concurrent::Map.new
|
|
68
|
+
@failover_system = failover_system
|
|
69
|
+
@registry = registry
|
|
70
|
+
@telemetry = Domain::Telemetry::Bus.new(error_notifier: config.error_notifier)
|
|
71
|
+
|
|
72
|
+
unless config.notifiers.empty?
|
|
73
|
+
NotifierBridge.new(notifiers: wrapped_notifiers).subscribe(@telemetry)
|
|
74
|
+
end
|
|
57
75
|
end
|
|
58
76
|
|
|
59
|
-
|
|
77
|
+
def persistent?
|
|
78
|
+
case @config.data_store
|
|
79
|
+
when DataStore::Redis
|
|
80
|
+
true
|
|
81
|
+
when DataStore::Memory
|
|
82
|
+
false
|
|
83
|
+
else
|
|
84
|
+
raise T.absurd(@config.data_store)
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
# Registers and returns a light.
|
|
60
89
|
#
|
|
61
|
-
# If a light with this name already exists, returns
|
|
90
|
+
# If a light with this name already exists, returns it.
|
|
62
91
|
# If settings differ from the existing light, raises +Stoplight::Error::ConfigurationError+.
|
|
63
92
|
#
|
|
64
|
-
#
|
|
65
93
|
# @raise [Stoplight::Error::ConfigurationError] if light exists with different settings
|
|
66
94
|
#
|
|
67
|
-
# @example
|
|
68
|
-
#
|
|
69
|
-
#
|
|
70
|
-
# @example Retrieve existing light - both return cached light
|
|
71
|
-
# light = system.light("stripe", threshold: 5, window_size: 60)
|
|
72
|
-
# light = system.light("stripe")
|
|
95
|
+
# @example Register a light
|
|
96
|
+
# system.register("stripe", threshold: 5, window_size: 60)
|
|
97
|
+
# system.light("stripe") #=> returns named light
|
|
73
98
|
#
|
|
74
99
|
# @example Configuration conflict
|
|
75
|
-
# system.
|
|
76
|
-
# system.
|
|
100
|
+
# system.register("api", threshold: 5)
|
|
101
|
+
# system.register("api", threshold: 10) # Raises ConfigurationError
|
|
77
102
|
#
|
|
78
103
|
# @note Thread-safe: multiple threads can safely call this method concurrently
|
|
79
104
|
#
|
|
80
|
-
def
|
|
105
|
+
def register(
|
|
81
106
|
name,
|
|
82
107
|
cool_off_time: T.undefined,
|
|
83
108
|
threshold: T.undefined,
|
|
@@ -88,7 +113,7 @@ module Stoplight
|
|
|
88
113
|
traffic_control: T.undefined,
|
|
89
114
|
traffic_recovery: T.undefined
|
|
90
115
|
)
|
|
91
|
-
|
|
116
|
+
light_dsl = LightConfigurationDsl.new(
|
|
92
117
|
name:,
|
|
93
118
|
cool_off_time:,
|
|
94
119
|
threshold:,
|
|
@@ -98,32 +123,76 @@ module Stoplight
|
|
|
98
123
|
skipped_errors:,
|
|
99
124
|
traffic_control:,
|
|
100
125
|
traffic_recovery:
|
|
101
|
-
)
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
end
|
|
117
|
-
else
|
|
118
|
-
[LightFactory.new(system: self, config: light_config).build, light_config]
|
|
126
|
+
)
|
|
127
|
+
config_digest = light_dsl.digest
|
|
128
|
+
|
|
129
|
+
light, existing_digest, existing_backtrace = @lights[name] || begin
|
|
130
|
+
registered_at = ExternalCaller.backtrace.first(REGISTRATION_FRAME_LIMIT)
|
|
131
|
+
config = light_dsl.configure!(@config)
|
|
132
|
+
@lights.compute_if_absent(name) do
|
|
133
|
+
built = LightFactory.new(
|
|
134
|
+
system_id: @config.id,
|
|
135
|
+
system_name: @name, config:,
|
|
136
|
+
failover_system: @failover_system,
|
|
137
|
+
telemetry: @telemetry
|
|
138
|
+
).build
|
|
139
|
+
@registry.register(config)
|
|
140
|
+
[built, config_digest, registered_at]
|
|
119
141
|
end
|
|
120
142
|
end
|
|
143
|
+
|
|
144
|
+
if config_digest != existing_digest
|
|
145
|
+
original_site = existing_backtrace.map { |frame| " #{frame}" }.join("\n")
|
|
146
|
+
|
|
147
|
+
raise Stoplight::Error::ConfigurationError, <<~MSG, ExternalCaller.backtrace
|
|
148
|
+
Light `#{name}` already registered with different configuration.
|
|
149
|
+
|
|
150
|
+
Originally registered at:
|
|
151
|
+
#{original_site}
|
|
152
|
+
|
|
153
|
+
Lights must have consistent configuration across all call sites.
|
|
154
|
+
MSG
|
|
155
|
+
end
|
|
156
|
+
|
|
121
157
|
light
|
|
122
158
|
end
|
|
123
159
|
|
|
160
|
+
# @raise [Stoplight::Error::UnregisteredLightError] if no light was registered under +name+
|
|
161
|
+
def light(name)
|
|
162
|
+
@lights[name]&.first || raise(Stoplight::Error::UnregisteredLightError, <<~MSG)
|
|
163
|
+
Light `#{name}` was never registered on system `#{@name}`.
|
|
164
|
+
Call `.register(#{name.inspect}, ...)` at boot before using it.
|
|
165
|
+
MSG
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
# @api private
|
|
169
|
+
def __stoplight__storage
|
|
170
|
+
Storage.new(
|
|
171
|
+
system_id: @config.id,
|
|
172
|
+
system_name: @name,
|
|
173
|
+
failover_system: T.must(@failover_system), # works only with redis ds
|
|
174
|
+
telemetry: @telemetry
|
|
175
|
+
)
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
# @api private
|
|
179
|
+
def __stoplight__registry
|
|
180
|
+
@registry
|
|
181
|
+
end
|
|
182
|
+
|
|
124
183
|
private
|
|
125
184
|
|
|
126
|
-
|
|
185
|
+
def wrapped_notifiers
|
|
186
|
+
error_notifier = Infrastructure::FailSafe::ErrorNotifier.new(error_notifier: @config.error_notifier)
|
|
187
|
+
|
|
188
|
+
@config.notifiers.map do |notifier|
|
|
189
|
+
Infrastructure::Notifier::FailSafe.new(
|
|
190
|
+
notifier:,
|
|
191
|
+
error_notifier:,
|
|
192
|
+
circuit_breaker: T.must(@failover_system).register("notifier:#{notifier.class.name}")
|
|
193
|
+
)
|
|
194
|
+
end
|
|
195
|
+
end
|
|
127
196
|
end
|
|
128
197
|
end
|
|
129
198
|
end
|