mirror of
https://github.com/lukaszraczylo/graphql-monitoring-proxy.git
synced 2026-10-12 05:32:36 +00:00
Performance / resource usage: - circuit_breaker_metrics: fix data race on failCounters map (RWMutex + double-checked locking) - server.go: drop user_id and op_name metric labels (Prometheus cardinality bound); de-duplicate extractUserInfo - graphql.go: gate runtime.ReadMemStats per-request behind ENABLE_ALLOCATION_TRACKING flag (default off) - graphql.go: collapse two-pass AST scan into single pass; lower-case once - sanitization.go: cache compiled redaction regexes per pattern via sync.Map; hoist inner constants to pkg vars - proxy.go: hoist connection/timeout substrings to pkg vars; sentinel errors for static error paths; drop dead Headers map alloc - metrics_aggregator.go: log-field allocation guarded by Logger.IsLevelEnabled - logging/logger.go: add IsLevelEnabled helper - lru_cache.go: 16-shard sharding, FNV-1a routing (concurrent throughput +22%) - cache/memory/lru_memory_cache.go: gzip compress/decompress moved outside mu.Lock - rps_tracker.go: RWMutex+uint64 -> atomic.Uint64 - retry_budget.go: drop unused mutex - api.go: bannedUsersIDs map+RWMutex -> sync.Map (+ snapshot/replace helpers) - tracing/tracing.go: pkg-level constSpanAttrs, copy-then-append in StartSpanWithAttributes - admin_dashboard.go: handleStatsWebSocket reuses bytes.Buffer + json.Encoder per connection Build / runtime: - Makefile: -ldflags="-s -w" -trimpath, CGO_ENABLED=0 for build (=1 for test recipes) - Dockerfile + Dockerfile.goreleaser: ENV GOMEMLIMIT=512MiB - main.go: blank import go.uber.org/automaxprocs (cgroup-aware GOMAXPROCS) - main.go: PPROF_PORT env var wires net/http/pprof on 127.0.0.1 only with full server timeouts - README.md: env-var docs + metric-label docs updated; cardinality note Test coverage push (per package): - main 51.2% -> 74.7% - cache 66.3% -> 93.7% - cache/redis 45.5% -> 98.2% - tracing 66.7% -> 72.9% - (cache/memory 91.6%, logging 91.9%, monitoring 77.6%, pkg/pools 100% unchanged) New test files: coverage_micro_test, coverage_extras_test, server_handlers_test, api_health_test, admin_dashboard_cluster_test, metrics_aggregator_test, concerns_test, cache/cache_coverage_test, cache/redis/redis_coverage_test, tracing/tracing_coverage_test. Bug fix: connection_resilience_test.go TestIntegratedHealthManagement.health_manager_startup was sync.Once-coupled to InitializeBackendHealth and panicked when another test (e.g. via parseConfig) had already triggered Once. Use NewBackendHealthManager directly.
82 lines
2.3 KiB
Go
82 lines
2.3 KiB
Go
package main
|
|
|
|
import (
|
|
"sync"
|
|
"sync/atomic"
|
|
|
|
"github.com/VictoriaMetrics/metrics"
|
|
libpack_monitoring "github.com/lukaszraczylo/graphql-monitoring-proxy/monitoring"
|
|
)
|
|
|
|
// CircuitBreakerMetrics manages circuit breaker metrics without recreating gauges
|
|
type CircuitBreakerMetrics struct {
|
|
stateValue atomic.Value // stores float64
|
|
stateGauge *metrics.Gauge
|
|
failCountersMu sync.RWMutex
|
|
failCounters map[string]*metrics.Counter
|
|
}
|
|
|
|
// NewCircuitBreakerMetrics creates a new circuit breaker metrics manager
|
|
func NewCircuitBreakerMetrics(monitoring *libpack_monitoring.MetricsSetup) *CircuitBreakerMetrics {
|
|
cbm := &CircuitBreakerMetrics{
|
|
failCounters: make(map[string]*metrics.Counter),
|
|
}
|
|
|
|
// Initialize state value
|
|
cbm.stateValue.Store(float64(0))
|
|
|
|
// Create gauge with callback that reads the atomic value on every scrape
|
|
// This ensures the metric always reflects the current circuit breaker state
|
|
cbm.stateGauge = monitoring.RegisterMetricsGaugeFunc(
|
|
libpack_monitoring.MetricsCircuitState,
|
|
nil,
|
|
func() float64 {
|
|
return cbm.GetState()
|
|
},
|
|
)
|
|
|
|
return cbm
|
|
}
|
|
|
|
// UpdateState updates the circuit breaker state value atomically
|
|
func (cbm *CircuitBreakerMetrics) UpdateState(state float64) {
|
|
cbm.stateValue.Store(state)
|
|
}
|
|
|
|
// GetState returns the current circuit breaker state value
|
|
func (cbm *CircuitBreakerMetrics) GetState() float64 {
|
|
if val := cbm.stateValue.Load(); val != nil {
|
|
return val.(float64)
|
|
}
|
|
return 0
|
|
}
|
|
|
|
// GetOrCreateFailCounter returns a counter for the given state key
|
|
func (cbm *CircuitBreakerMetrics) GetOrCreateFailCounter(monitoring *libpack_monitoring.MetricsSetup, stateKey string) *metrics.Counter {
|
|
cbm.failCountersMu.RLock()
|
|
counter, exists := cbm.failCounters[stateKey]
|
|
cbm.failCountersMu.RUnlock()
|
|
if exists {
|
|
return counter
|
|
}
|
|
|
|
cbm.failCountersMu.Lock()
|
|
defer cbm.failCountersMu.Unlock()
|
|
if counter, exists := cbm.failCounters[stateKey]; exists {
|
|
return counter
|
|
}
|
|
counter = monitoring.RegisterMetricsCounter(stateKey, nil)
|
|
cbm.failCounters[stateKey] = counter
|
|
return counter
|
|
}
|
|
|
|
// Global circuit breaker metrics instance
|
|
var cbMetrics *CircuitBreakerMetrics
|
|
|
|
// InitializeCircuitBreakerMetrics initializes the global circuit breaker metrics
|
|
func InitializeCircuitBreakerMetrics(monitoring *libpack_monitoring.MetricsSetup) {
|
|
if cbMetrics == nil {
|
|
cbMetrics = NewCircuitBreakerMetrics(monitoring)
|
|
}
|
|
}
|