mirror of
https://github.com/lukaszraczylo/graphql-monitoring-proxy.git
synced 2026-10-26 06:58:39 +00:00
fix(circuitbreaker): guard nil monitoring to prevent boot panic
Starting with ENABLE_CIRCUIT_BREAKER=true panicked at boot: parseConfig -> initCircuitBreaker registers a metrics gauge, but cfg.Monitoring is assigned later by StartMonitoringServer, so the registration dereferenced a nil *MetricsSetup. Add nil-receiver guards to the MetricsSetup registration methods (RegisterMetricsGauge/GaugeFunc/Counter, RegisterFloatCounter, RegisterMetricsSummary/Histogram), matching their existing 'return a dummy to prevent panics' contract. The guard is a no-op for non-nil receivers, so existing callers are unaffected. The breaker and the admin dashboard (which reads gobreaker state directly) keep working. Add regression tests for the nil-receiver methods and for initCircuitBreaker with nil monitoring.
This commit is contained in:
@@ -0,0 +1,44 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"testing"
|
||||
|
||||
libpack_logger "github.com/lukaszraczylo/graphql-monitoring-proxy/logging"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
// TestInitCircuitBreaker_NilMonitoring_NoPanic is a regression test for a boot
|
||||
// panic: enabling the circuit breaker (ENABLE_CIRCUIT_BREAKER=true) crashed at
|
||||
// startup because parseConfig calls initCircuitBreaker before the monitoring
|
||||
// server assigns cfg.Monitoring (StartMonitoringServer runs later). The metrics
|
||||
// gauge registration then dereferenced a nil *MetricsSetup.
|
||||
//
|
||||
// The breaker (and the admin dashboard, which reads gobreaker state directly)
|
||||
// must initialise without panicking even when monitoring is not yet available.
|
||||
func TestInitCircuitBreaker_NilMonitoring_NoPanic(t *testing.T) {
|
||||
origCfg := cfg
|
||||
cbMutex.Lock()
|
||||
origCb, origMetrics := cb, cbMetrics
|
||||
cb, cbMetrics = nil, nil
|
||||
cbMutex.Unlock()
|
||||
t.Cleanup(func() {
|
||||
cbMutex.Lock()
|
||||
cb, cbMetrics = origCb, origMetrics
|
||||
cbMutex.Unlock()
|
||||
cfg = origCfg
|
||||
})
|
||||
|
||||
cfg = &config{}
|
||||
cfg.Logger = libpack_logger.New().SetOutput(&bytes.Buffer{})
|
||||
cfg.Monitoring = nil // the production state when parseConfig runs
|
||||
cfg.CircuitBreaker.Enable = true
|
||||
cfg.CircuitBreaker.MaxFailures = 3
|
||||
cfg.CircuitBreaker.Timeout = 5
|
||||
cfg.CircuitBreaker.MaxRequestsInHalfOpen = 2
|
||||
|
||||
require.NotPanics(t, func() { initCircuitBreaker(cfg) },
|
||||
"initCircuitBreaker must not panic when cfg.Monitoring is nil")
|
||||
require.NotNil(t, cb, "circuit breaker must initialise even without monitoring")
|
||||
require.NotNil(t, cbMetrics, "circuit breaker metrics manager must be created")
|
||||
}
|
||||
Reference in New Issue
Block a user