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
|
@@ -6,167 +6,77 @@ module Stoplight
|
|
|
6
6
|
module Storage
|
|
7
7
|
# Distributed storage for time-windowed light metrics using Redis.
|
|
8
8
|
#
|
|
9
|
-
# This class implements sliding window metrics using Redis sorted sets (ZSETs)
|
|
10
|
-
# for efficient time-range queries. Events are bucketed by hour to bound memory
|
|
11
|
-
# usage and enable automatic expiration via Redis TTLs.
|
|
12
|
-
#
|
|
13
9
|
# == Storage Structure
|
|
14
10
|
#
|
|
15
|
-
#
|
|
16
|
-
#
|
|
17
|
-
#
|
|
18
|
-
#
|
|
19
|
-
# Each ZSET member is a unique event ID with its timestamp as the score,
|
|
20
|
-
# enabling O(log N) range queries via ZCOUNT.
|
|
21
|
-
#
|
|
22
|
-
# Metadata (consecutive counters, last error) is stored in a hash:
|
|
23
|
-
# stoplight:{version}:{system}:{light}:window_metrics
|
|
24
|
-
#
|
|
25
|
-
# == Bucket Strategy
|
|
26
|
-
#
|
|
27
|
-
# Fixed 1-hour buckets provide a balance between:
|
|
28
|
-
# - Query efficiency: At most ~25 buckets for a 24-hour window
|
|
29
|
-
# - Memory efficiency: Natural expiration without manual cleanup
|
|
30
|
-
# - Precision: Sub-bucket accuracy via ZSET scores
|
|
31
|
-
#
|
|
32
|
-
# == Atomicity
|
|
33
|
-
#
|
|
34
|
-
# All operations use Lua scripts to ensure atomicity:
|
|
35
|
-
# - record_success: Increments counter and updates metadata in one round-trip
|
|
36
|
-
# - record_failure: Same, plus stores serialized error details
|
|
37
|
-
# - metrics_snapshot: Aggregates across buckets atomically
|
|
11
|
+
# Maintains a running sum and stores per-second counts in a redis Hash. Eviction reads
|
|
12
|
+
# only the out-of-window entries from the sorted set index, subtracts their counts from
|
|
13
|
+
# the running totals, then deletes both the hash fields and the sorted-set members.
|
|
38
14
|
#
|
|
39
15
|
class WindowMetrics < Metrics
|
|
16
|
+
METRICS_FIELDS = %w[last_success_at last_error_json consecutive_errors consecutive_successes].freeze
|
|
17
|
+
private_constant :METRICS_FIELDS
|
|
18
|
+
|
|
40
19
|
def initialize(redis:, scripting:, config:, clock:, key_space:)
|
|
41
20
|
@clock = clock
|
|
42
|
-
@scripting = scripting
|
|
43
21
|
@redis = redis
|
|
44
|
-
@
|
|
45
|
-
@
|
|
46
|
-
@
|
|
47
|
-
@window_size = T.must(config.window_size)
|
|
22
|
+
@scripting = scripting
|
|
23
|
+
@metrics_key = key_space.join("window_metrics")
|
|
24
|
+
@ts_index_key = key_space.join("window_metrics", "ts_idx")
|
|
25
|
+
@window_size = T.must(config.window_size)
|
|
48
26
|
end
|
|
49
27
|
|
|
50
|
-
# Get metrics for the current light
|
|
51
|
-
#
|
|
52
|
-
# @return [Stoplight::Domain::Metrics]
|
|
53
28
|
def metrics_snapshot
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
window_start_ts,
|
|
64
|
-
window_end_ts,
|
|
65
|
-
"last_success_at", "last_error_json", "consecutive_errors", "consecutive_successes"
|
|
66
|
-
],
|
|
67
|
-
keys: [
|
|
68
|
-
metrics_key,
|
|
69
|
-
*success_keys,
|
|
70
|
-
*failure_keys
|
|
71
|
-
]
|
|
72
|
-
)
|
|
73
|
-
Domain::MetricsSnapshot.new(
|
|
74
|
-
successes:, errors:,
|
|
75
|
-
consecutive_errors: [consecutive_errors.to_i, errors].min,
|
|
76
|
-
consecutive_successes: [consecutive_successes.to_i, successes].min,
|
|
77
|
-
last_error: deserialize_failure(last_error_json),
|
|
78
|
-
last_success_at: (clock.at(last_success_at.to_f) if last_success_at)
|
|
79
|
-
)
|
|
29
|
+
successes, errors, last_success_at, last_error_json, consecutive_errors, consecutive_successes =
|
|
30
|
+
@scripting.call(
|
|
31
|
+
"window_metrics/metrics_snapshot",
|
|
32
|
+
args: [@window_size, *METRICS_FIELDS],
|
|
33
|
+
keys: [@metrics_key, @ts_index_key]
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
build_metrics_snapshot(successes:, errors:, consecutive_errors:, consecutive_successes:,
|
|
37
|
+
last_error_json:, last_success_at:)
|
|
80
38
|
end
|
|
81
39
|
|
|
82
|
-
# Records successful circuit breaker execution
|
|
83
|
-
#
|
|
84
|
-
# @return [void]
|
|
85
40
|
def record_success
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
:
|
|
90
|
-
args: [timestamp, SecureRandom.hex(12), bucket_ttl, metrics_ttl],
|
|
91
|
-
keys: [
|
|
92
|
-
metrics_key,
|
|
93
|
-
successes_key(time: timestamp)
|
|
94
|
-
]
|
|
41
|
+
@scripting.call(
|
|
42
|
+
"window_metrics/record_success",
|
|
43
|
+
args: [@window_size, metrics_ttl],
|
|
44
|
+
keys: [@metrics_key, @ts_index_key]
|
|
95
45
|
)
|
|
96
46
|
end
|
|
97
47
|
|
|
98
|
-
# Records failed circuit breaker execution
|
|
99
|
-
#
|
|
100
|
-
# @param exception [StandardError]
|
|
101
|
-
# @return [void]
|
|
102
48
|
def record_failure(exception)
|
|
103
|
-
timestamp = clock.current_time.to_f
|
|
104
|
-
|
|
105
|
-
scripting.call(
|
|
106
|
-
:"window_metrics/record_failure",
|
|
107
|
-
args: [timestamp, SecureRandom.hex(12), serialize_exception(exception, timestamp:), bucket_ttl, metrics_ttl],
|
|
108
|
-
keys: [metrics_key, errors_key(time: timestamp)]
|
|
109
|
-
)
|
|
110
|
-
end
|
|
111
|
-
|
|
112
|
-
def clear
|
|
113
|
-
window_end_ts = clock.current_time.to_f
|
|
114
|
-
failure_keys = failure_bucket_keys(window_end_ts)
|
|
115
|
-
success_keys = success_bucket_keys(window_end_ts)
|
|
49
|
+
timestamp = @clock.current_time.to_f
|
|
116
50
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
51
|
+
successes, errors, last_success_at, last_error_json, consecutive_errors, consecutive_successes =
|
|
52
|
+
@scripting.call(
|
|
53
|
+
"window_metrics/record_failure",
|
|
54
|
+
args: [serialize_exception(exception, timestamp:), @window_size, metrics_ttl],
|
|
55
|
+
keys: [@metrics_key, @ts_index_key]
|
|
56
|
+
)
|
|
121
57
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
# @param metric [Symbol] The metric type (e.g., "errors").
|
|
125
|
-
# @param time [Time, Numeric] The time for which to generate the key.
|
|
126
|
-
# @return [String] The generated Redis key.
|
|
127
|
-
def bucket_key(metric:, time:)
|
|
128
|
-
key_space.key(:window_metrics, metric, (time.to_i / bucket_size) * bucket_size)
|
|
58
|
+
build_metrics_snapshot(successes:, errors:, consecutive_errors:, consecutive_successes:,
|
|
59
|
+
last_error_json:, last_success_at:)
|
|
129
60
|
end
|
|
130
61
|
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
# @param window_end The end time of the window (can be a Time object or a numeric timestamp).
|
|
135
|
-
# @return A list of Redis keys for the buckets that cover the time window.
|
|
136
|
-
# @api private
|
|
137
|
-
def buckets_for_window(metric:, window_end:)
|
|
138
|
-
window_end_ts = window_end.to_i
|
|
139
|
-
window_start_ts = window_end_ts - @window_size
|
|
140
|
-
|
|
141
|
-
# Find bucket timestamps that contain any part of the window
|
|
142
|
-
start_bucket = (window_start_ts / bucket_size) * bucket_size
|
|
143
|
-
|
|
144
|
-
# End bucket is the last bucket that contains data within our window
|
|
145
|
-
end_bucket = ((window_end_ts - 1) / bucket_size) * bucket_size
|
|
146
|
-
|
|
147
|
-
(start_bucket..end_bucket).step(bucket_size).map do |bucket_start|
|
|
148
|
-
bucket_key(metric: metric, time: bucket_start)
|
|
62
|
+
def clear
|
|
63
|
+
@redis.with do |client|
|
|
64
|
+
client.del(@metrics_key, @ts_index_key)
|
|
149
65
|
end
|
|
150
66
|
end
|
|
151
67
|
|
|
152
68
|
private
|
|
153
69
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
def errors_key(time:) = bucket_key(metric: :failure, time:)
|
|
166
|
-
|
|
167
|
-
def failure_bucket_keys(window_end) = buckets_for_window(metric: :failure, window_end:)
|
|
168
|
-
|
|
169
|
-
def success_bucket_keys(window_end) = buckets_for_window(metric: :success, window_end:)
|
|
70
|
+
def build_metrics_snapshot(successes:, errors:, consecutive_errors:, consecutive_successes:,
|
|
71
|
+
last_error_json:, last_success_at:)
|
|
72
|
+
Domain::MetricsSnapshot.new(
|
|
73
|
+
successes:, errors:,
|
|
74
|
+
consecutive_errors: [consecutive_errors.to_i, errors].min,
|
|
75
|
+
consecutive_successes: [consecutive_successes.to_i, successes].min,
|
|
76
|
+
last_error: deserialize_failure(last_error_json),
|
|
77
|
+
last_success_at: (@clock.at(last_success_at.to_f) if last_success_at)
|
|
78
|
+
)
|
|
79
|
+
end
|
|
170
80
|
end
|
|
171
81
|
end
|
|
172
82
|
end
|
|
@@ -10,6 +10,10 @@ module Stoplight
|
|
|
10
10
|
class SystemClock
|
|
11
11
|
def current_time = Time.now.utc
|
|
12
12
|
|
|
13
|
+
def monotonic_millis = Process.clock_gettime(Process::CLOCK_MONOTONIC, :float_millisecond)
|
|
14
|
+
|
|
15
|
+
def monotonic_seconds = Process.clock_gettime(Process::CLOCK_MONOTONIC, :float_second)
|
|
16
|
+
|
|
13
17
|
def at(timestamp) = Time.at(timestamp).utc
|
|
14
18
|
end
|
|
15
19
|
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Stoplight
|
|
4
|
+
# Public face of the telemetry bus. The bus and its events live in the domain layer
|
|
5
|
+
# (+Domain::Telemetry+); this namespace re-exports them so callers never reference the
|
|
6
|
+
# +Domain::+ boundary - mirroring +Stoplight::Notifier+ and +Stoplight::DataStore+.
|
|
7
|
+
module Telemetry
|
|
8
|
+
RunCompleted = Domain::Telemetry::RunCompleted
|
|
9
|
+
TrafficBreached = Domain::Telemetry::TrafficBreached
|
|
10
|
+
RecoveryStarted = Domain::Telemetry::RecoveryStarted
|
|
11
|
+
RecoverySucceeded = Domain::Telemetry::RecoverySucceeded
|
|
12
|
+
RecoveryFailed = Domain::Telemetry::RecoveryFailed
|
|
13
|
+
LockChanged = Domain::Telemetry::LockChanged
|
|
14
|
+
RecoveryProbeCompleted = Domain::Telemetry::RecoveryProbeCompleted
|
|
15
|
+
LightRegistered = Domain::Telemetry::LightRegistered
|
|
16
|
+
|
|
17
|
+
StateTransitioned = Domain::Telemetry::StateTransitioned
|
|
18
|
+
end
|
|
19
|
+
end
|
data/lib/stoplight/types.rb
CHANGED
|
@@ -4,7 +4,19 @@ require "singleton"
|
|
|
4
4
|
|
|
5
5
|
module Stoplight
|
|
6
6
|
module Types
|
|
7
|
-
|
|
7
|
+
UNDEFINED = Undefined.instance
|
|
8
|
+
def self.undefined = UNDEFINED
|
|
9
|
+
|
|
10
|
+
def self.absurd(value = nil)
|
|
11
|
+
msg = "Control flow reached T.absurd."
|
|
12
|
+
|
|
13
|
+
case value
|
|
14
|
+
when Kernel
|
|
15
|
+
msg += " Got value: #{value}"
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
TypeError.new(msg)
|
|
19
|
+
end
|
|
8
20
|
|
|
9
21
|
# Asserts a value is non-nil, returning it with a narrowed type.
|
|
10
22
|
#
|
|
@@ -19,7 +31,8 @@ module Stoplight
|
|
|
19
31
|
# @return [T] the non-nil value
|
|
20
32
|
#
|
|
21
33
|
def self.must(value)
|
|
22
|
-
|
|
34
|
+
case value
|
|
35
|
+
when nil
|
|
23
36
|
raise TypeError, "must not have nil value"
|
|
24
37
|
else
|
|
25
38
|
value
|
data/lib/stoplight/version.rb
CHANGED
|
@@ -23,18 +23,41 @@ module Stoplight
|
|
|
23
23
|
end
|
|
24
24
|
|
|
25
25
|
def call
|
|
26
|
+
validate_window_size!
|
|
27
|
+
validate_cool_off_time!
|
|
26
28
|
validate_traffic_control!
|
|
27
29
|
validate_traffic_recovery!
|
|
28
30
|
config
|
|
29
31
|
end
|
|
30
32
|
|
|
33
|
+
# A window shorter than one bucket cannot be measured, and a fractional one silently
|
|
34
|
+
# rounds to a different span than requested.
|
|
35
|
+
private def validate_window_size!
|
|
36
|
+
window_size = config.window_size
|
|
37
|
+
return if window_size.nil?
|
|
38
|
+
return if window_size.is_a?(Integer) && window_size >= 1
|
|
39
|
+
|
|
40
|
+
raise Error::ConfigurationError,
|
|
41
|
+
"`window_size` should be a whole number of seconds, at least 1, got #{window_size.inspect}",
|
|
42
|
+
ExternalCaller.backtrace
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
private def validate_cool_off_time!
|
|
46
|
+
cool_off_time = config.cool_off_time
|
|
47
|
+
return if cool_off_time.is_a?(Integer) && cool_off_time >= 1
|
|
48
|
+
|
|
49
|
+
raise Error::ConfigurationError,
|
|
50
|
+
"`cool_off_time` should be a whole number of seconds, at least 1, got #{cool_off_time.inspect}",
|
|
51
|
+
ExternalCaller.backtrace
|
|
52
|
+
end
|
|
53
|
+
|
|
31
54
|
private def validate_traffic_control!
|
|
32
55
|
traffic_control = config.traffic_control
|
|
33
56
|
traffic_control.check_compatibility(config).then do |compatibility_result|
|
|
34
57
|
if compatibility_result.incompatible?
|
|
35
58
|
raise Error::ConfigurationError,
|
|
36
59
|
"#{traffic_control} incompatible with config: #{compatibility_result.error_messages}",
|
|
37
|
-
|
|
60
|
+
ExternalCaller.backtrace
|
|
38
61
|
end
|
|
39
62
|
end
|
|
40
63
|
end
|
|
@@ -45,7 +68,7 @@ module Stoplight
|
|
|
45
68
|
if compatibility_result.incompatible?
|
|
46
69
|
raise Error::ConfigurationError,
|
|
47
70
|
"#{traffic_recovery} incompatible with config: #{compatibility_result.error_messages}",
|
|
48
|
-
|
|
71
|
+
ExternalCaller.backtrace
|
|
49
72
|
end
|
|
50
73
|
end
|
|
51
74
|
end
|
|
@@ -4,7 +4,8 @@ module Stoplight
|
|
|
4
4
|
module Wiring
|
|
5
5
|
# Provides default settings for the Stoplight library.
|
|
6
6
|
DefaultConfig = Domain::Config.new(
|
|
7
|
-
|
|
7
|
+
id: Domain::Id.for("Default"),
|
|
8
|
+
name: "Default",
|
|
8
9
|
cool_off_time: Default::COOL_OFF_TIME,
|
|
9
10
|
threshold: Default::THRESHOLD,
|
|
10
11
|
recovery_threshold: Default::RECOVERY_THRESHOLD,
|
|
@@ -46,7 +46,7 @@ module Stoplight
|
|
|
46
46
|
#
|
|
47
47
|
class DefaultConfiguration
|
|
48
48
|
def initialize
|
|
49
|
-
@config = DefaultConfig
|
|
49
|
+
@config = DefaultConfig
|
|
50
50
|
@cool_off_time = T.undefined
|
|
51
51
|
@threshold = T.undefined
|
|
52
52
|
@recovery_threshold = T.undefined
|
|
@@ -76,7 +76,8 @@ module Stoplight
|
|
|
76
76
|
|
|
77
77
|
# Builds and validates configuration
|
|
78
78
|
def to_config!
|
|
79
|
-
|
|
79
|
+
SystemConfigurationDsl.new(
|
|
80
|
+
"Default",
|
|
80
81
|
cool_off_time: @cool_off_time,
|
|
81
82
|
threshold: @threshold,
|
|
82
83
|
recovery_threshold: @recovery_threshold,
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Stoplight
|
|
4
|
+
module Wiring
|
|
5
|
+
# Locates the application code that called into Stoplight, so configuration errors point at
|
|
6
|
+
# the light's call site instead of Stoplight's own internals.
|
|
7
|
+
#
|
|
8
|
+
# @api private
|
|
9
|
+
module ExternalCaller
|
|
10
|
+
LIB_ROOT = "#{File.expand_path("../../..", __FILE__)}/"
|
|
11
|
+
|
|
12
|
+
class << self
|
|
13
|
+
def backtrace
|
|
14
|
+
frames = caller_locations(1) || []
|
|
15
|
+
external = frames.reject { |frame| internal?(frame) }
|
|
16
|
+
external = frames if external.empty?
|
|
17
|
+
external.map(&:to_s)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
private def internal?(frame)
|
|
21
|
+
path = frame.path.to_s
|
|
22
|
+
path.start_with?(LIB_ROOT, "<internal:")
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Stoplight
|
|
4
|
+
module Wiring
|
|
5
|
+
# Empty notifiers prevent notification loops; inherited Memory data store prevents infinite recursion if Redis is down.
|
|
6
|
+
FailSafeConfig = DefaultConfig.with(
|
|
7
|
+
notifiers: [],
|
|
8
|
+
data_store: DataStore::Memory.new
|
|
9
|
+
)
|
|
10
|
+
end
|
|
11
|
+
end
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Stoplight
|
|
4
|
+
module Wiring
|
|
5
|
+
class GlobalState
|
|
6
|
+
attr_reader :default_system
|
|
7
|
+
attr_reader :default_config
|
|
8
|
+
|
|
9
|
+
def initialize(default_config:)
|
|
10
|
+
@default_config = default_config
|
|
11
|
+
@systems = Concurrent::Map.new
|
|
12
|
+
@default_system = register_system_with_config(@default_config)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def register_system(
|
|
16
|
+
name,
|
|
17
|
+
cool_off_time: T.undefined,
|
|
18
|
+
threshold: T.undefined,
|
|
19
|
+
recovery_threshold: T.undefined,
|
|
20
|
+
window_size: T.undefined,
|
|
21
|
+
tracked_errors: T.undefined,
|
|
22
|
+
skipped_errors: T.undefined,
|
|
23
|
+
data_store: T.undefined,
|
|
24
|
+
error_notifier: T.undefined,
|
|
25
|
+
notifiers: T.undefined,
|
|
26
|
+
traffic_control: T.undefined,
|
|
27
|
+
traffic_recovery: T.undefined
|
|
28
|
+
)
|
|
29
|
+
register_system_with_config(
|
|
30
|
+
Wiring::SystemConfigurationDsl.new(
|
|
31
|
+
name,
|
|
32
|
+
cool_off_time:,
|
|
33
|
+
threshold:,
|
|
34
|
+
recovery_threshold:,
|
|
35
|
+
window_size:,
|
|
36
|
+
tracked_errors:,
|
|
37
|
+
skipped_errors:,
|
|
38
|
+
traffic_control:,
|
|
39
|
+
traffic_recovery:,
|
|
40
|
+
data_store:,
|
|
41
|
+
error_notifier:,
|
|
42
|
+
notifiers:
|
|
43
|
+
).configure!(@default_config)
|
|
44
|
+
)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
private
|
|
48
|
+
|
|
49
|
+
def register_system_with_config(config)
|
|
50
|
+
@systems.compute(config.name) do |existing_system|
|
|
51
|
+
if existing_system
|
|
52
|
+
raise ArgumentError, "system `#{config.name}` is already in use"
|
|
53
|
+
else
|
|
54
|
+
failover_system = Wiring::System.new(
|
|
55
|
+
config: Wiring::FailSafeConfig.with(
|
|
56
|
+
name: "__stoplight__:failover:#{config.name}",
|
|
57
|
+
data_store: DataStore::Memory.new
|
|
58
|
+
),
|
|
59
|
+
failover_system: nil,
|
|
60
|
+
registry: Infrastructure::Memory::Storage::Registry.new
|
|
61
|
+
)
|
|
62
|
+
Wiring::System.new(
|
|
63
|
+
config: config,
|
|
64
|
+
failover_system: failover_system,
|
|
65
|
+
registry: create_registry(config, failover_system)
|
|
66
|
+
)
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def create_registry(config, failover_system)
|
|
72
|
+
case config.data_store
|
|
73
|
+
when DataStore::Redis
|
|
74
|
+
Infrastructure::FailSafe::Storage::Registry.new(
|
|
75
|
+
primary_registry: Infrastructure::Redis::Storage::Registry.new(
|
|
76
|
+
redis: config.data_store.redis,
|
|
77
|
+
key_space: config.data_store.key_space.join(config.id),
|
|
78
|
+
clock: Infrastructure::SystemClock.new,
|
|
79
|
+
config_serializer: Infrastructure::ConfigSerializer
|
|
80
|
+
),
|
|
81
|
+
error_notifier: config.error_notifier,
|
|
82
|
+
failover_registry: Infrastructure::Memory::Storage::Registry.new,
|
|
83
|
+
circuit_breaker: failover_system.register("registry")
|
|
84
|
+
)
|
|
85
|
+
when DataStore::Memory
|
|
86
|
+
Infrastructure::Memory::Storage::Registry.new
|
|
87
|
+
else
|
|
88
|
+
raise TypeError, "unsupported data store type"
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
end
|
|
@@ -2,17 +2,15 @@
|
|
|
2
2
|
|
|
3
3
|
module Stoplight
|
|
4
4
|
module Wiring
|
|
5
|
-
class
|
|
5
|
+
class LightConfigurationDsl
|
|
6
6
|
TrafficControlDsl = ->(value) {
|
|
7
7
|
case value
|
|
8
8
|
in _ if value.respond_to?(:stop_traffic?) # TODO: can be removed in 6.0
|
|
9
9
|
value
|
|
10
|
-
in
|
|
10
|
+
in Domain::TrafficControl::ConsecutiveErrors::NAME
|
|
11
11
|
Domain::TrafficControl::ConsecutiveErrors.new
|
|
12
|
-
in
|
|
12
|
+
in Domain::TrafficControl::ErrorRate::NAME
|
|
13
13
|
Domain::TrafficControl::ErrorRate.new
|
|
14
|
-
in {error_rate: error_rate_settings}
|
|
15
|
-
Domain::TrafficControl::ErrorRate.new(**error_rate_settings)
|
|
16
14
|
else
|
|
17
15
|
raise Stoplight::Error::ConfigurationError, <<~ERROR
|
|
18
16
|
unsupported traffic_control strategy provided (`#{value}`). Supported options:
|
|
@@ -2,12 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
module Stoplight
|
|
4
4
|
module Wiring
|
|
5
|
-
class
|
|
5
|
+
class LightConfigurationDsl
|
|
6
6
|
TrafficRecoveryDsl = ->(value) {
|
|
7
7
|
case value
|
|
8
8
|
in _ if value.respond_to?(:determine_color) # TODO: remove in 6.0
|
|
9
9
|
value
|
|
10
|
-
in
|
|
10
|
+
in Domain::TrafficRecovery::ConsecutiveSuccesses::NAME
|
|
11
11
|
Domain::TrafficRecovery::ConsecutiveSuccesses.new
|
|
12
12
|
else
|
|
13
13
|
raise Error::ConfigurationError, <<~ERROR
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Stoplight
|
|
4
|
+
module Wiring
|
|
5
|
+
class LightConfigurationDsl
|
|
6
|
+
def initialize(
|
|
7
|
+
name:,
|
|
8
|
+
cool_off_time: T.undefined,
|
|
9
|
+
threshold: T.undefined,
|
|
10
|
+
recovery_threshold: T.undefined,
|
|
11
|
+
window_size: T.undefined,
|
|
12
|
+
tracked_errors: T.undefined,
|
|
13
|
+
skipped_errors: T.undefined,
|
|
14
|
+
traffic_control: T.undefined,
|
|
15
|
+
traffic_recovery: T.undefined
|
|
16
|
+
)
|
|
17
|
+
@name = name.to_s
|
|
18
|
+
@id = Domain::Id.for(@name)
|
|
19
|
+
@cool_off_time = cool_off_time
|
|
20
|
+
@threshold = threshold
|
|
21
|
+
@recovery_threshold = recovery_threshold
|
|
22
|
+
@window_size = window_size
|
|
23
|
+
@tracked_errors = if tracked_errors.is_a?(Undefined)
|
|
24
|
+
tracked_errors
|
|
25
|
+
elsif tracked_errors.is_a?(Array)
|
|
26
|
+
tracked_errors
|
|
27
|
+
else
|
|
28
|
+
[tracked_errors]
|
|
29
|
+
end
|
|
30
|
+
@skipped_errors = if skipped_errors.is_a?(Undefined)
|
|
31
|
+
skipped_errors
|
|
32
|
+
elsif skipped_errors.is_a?(Array)
|
|
33
|
+
skipped_errors
|
|
34
|
+
else
|
|
35
|
+
[skipped_errors]
|
|
36
|
+
end
|
|
37
|
+
@traffic_control = traffic_control.is_a?(Undefined) ? traffic_control : TrafficControlDsl.call(traffic_control)
|
|
38
|
+
@traffic_recovery = traffic_recovery.is_a?(Undefined) ? traffic_recovery : TrafficRecoveryDsl.call(traffic_recovery)
|
|
39
|
+
|
|
40
|
+
@digest = calculate_digest
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def configure!(default_config)
|
|
44
|
+
ConfigCompatibilityValidator.call(
|
|
45
|
+
config: default_config.with(
|
|
46
|
+
id: @id,
|
|
47
|
+
name:,
|
|
48
|
+
cool_off_time:,
|
|
49
|
+
threshold:,
|
|
50
|
+
recovery_threshold:,
|
|
51
|
+
window_size:,
|
|
52
|
+
tracked_errors:,
|
|
53
|
+
skipped_errors:,
|
|
54
|
+
traffic_control:,
|
|
55
|
+
traffic_recovery:
|
|
56
|
+
)
|
|
57
|
+
)
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
attr_reader :digest
|
|
61
|
+
|
|
62
|
+
private
|
|
63
|
+
|
|
64
|
+
def calculate_digest
|
|
65
|
+
[
|
|
66
|
+
@id,
|
|
67
|
+
@cool_off_time,
|
|
68
|
+
@threshold,
|
|
69
|
+
@recovery_threshold,
|
|
70
|
+
@window_size,
|
|
71
|
+
errors_for_digest(@tracked_errors),
|
|
72
|
+
errors_for_digest(@skipped_errors),
|
|
73
|
+
@traffic_control,
|
|
74
|
+
@traffic_recovery
|
|
75
|
+
].hash
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
attr_reader :name
|
|
79
|
+
attr_reader :cool_off_time
|
|
80
|
+
attr_reader :threshold
|
|
81
|
+
attr_reader :recovery_threshold
|
|
82
|
+
attr_reader :window_size
|
|
83
|
+
attr_reader :tracked_errors
|
|
84
|
+
attr_reader :skipped_errors
|
|
85
|
+
attr_reader :traffic_control
|
|
86
|
+
attr_reader :traffic_recovery
|
|
87
|
+
|
|
88
|
+
def errors_for_digest(errors)
|
|
89
|
+
if errors.is_a?(Undefined)
|
|
90
|
+
errors
|
|
91
|
+
else
|
|
92
|
+
errors.map { |matcher| Domain::MatcherValidator.call(matcher) }
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
end
|