161 lines
5.6 KiB
Go
161 lines
5.6 KiB
Go
package metrics
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"sync"
|
|
|
|
"github.com/prometheus/client_golang/prometheus"
|
|
|
|
controllerPool "proxy-pool/internal/controller/pool"
|
|
)
|
|
|
|
// CapacityCollector aggregates Controller leader-term inventory samples. An
|
|
// upstream ID is retained only in process memory for replacement and removal;
|
|
// it is never emitted as a Prometheus label.
|
|
type CapacityCollector struct {
|
|
mu sync.Mutex
|
|
byUpstream map[string]capacityState
|
|
|
|
reads *prometheus.CounterVec
|
|
managed *prometheus.Desc
|
|
available *prometheus.Desc
|
|
effective *prometheus.Desc
|
|
pending *prometheus.Desc
|
|
activeUpstreams *prometheus.Desc
|
|
}
|
|
|
|
type capacityState struct {
|
|
sourceID string
|
|
managed int
|
|
available int64
|
|
effective int64
|
|
pending int
|
|
}
|
|
|
|
var (
|
|
_ controllerPool.CapacityObserver = (*CapacityCollector)(nil)
|
|
_ prometheus.Collector = (*CapacityCollector)(nil)
|
|
)
|
|
|
|
func NewCapacityCollector(registerer prometheus.Registerer) (*CapacityCollector, error) {
|
|
if registerer == nil {
|
|
return nil, ErrInvalidDependencies
|
|
}
|
|
reads, err := registerCounterVec(registerer, prometheus.NewCounterVec(prometheus.CounterOpts{
|
|
Namespace: "proxy_pool", Subsystem: "controller", Name: "capacity_inventory_reads_total",
|
|
Help: "Number of Provider inventory reads by fixed result.",
|
|
}, []string{"result"}))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
candidate := &CapacityCollector{
|
|
byUpstream: map[string]capacityState{}, reads: reads,
|
|
managed: prometheus.NewDesc(
|
|
"proxy_pool_controller_capacity_managed_proxies",
|
|
"Managed non-extracted proxies from the latest successful samples of active Controller Provider terms.", nil, nil,
|
|
),
|
|
available: prometheus.NewDesc(
|
|
"proxy_pool_controller_capacity_available_slots",
|
|
"Available proxy concurrency slots from the latest successful samples of active Controller Provider terms.", nil, nil,
|
|
),
|
|
effective: prometheus.NewDesc(
|
|
"proxy_pool_controller_capacity_effective_slots",
|
|
"Available slots plus expected pending fetch capacity from the latest successful active Controller samples.", nil, nil,
|
|
),
|
|
pending: prometheus.NewDesc(
|
|
"proxy_pool_controller_capacity_pending_expected_proxies",
|
|
"Expected proxy candidates reserved by in-flight fetches in the latest successful active Controller samples.", nil, nil,
|
|
),
|
|
activeUpstreams: prometheus.NewDesc(
|
|
"proxy_pool_controller_capacity_active_upstreams",
|
|
"Number of Upstreams with a successful inventory sample in this Controller process.", nil, nil,
|
|
),
|
|
}
|
|
return registerCapacityCollector(registerer, candidate)
|
|
}
|
|
|
|
func (collector *CapacityCollector) ObserveCapacity(observation controllerPool.CapacityObservation) {
|
|
if collector == nil || collector.reads == nil || observation.UpstreamID == "" || observation.SourceID == "" {
|
|
return
|
|
}
|
|
switch observation.Result {
|
|
case controllerPool.CapacityReadError:
|
|
collector.reads.WithLabelValues(string(observation.Result)).Inc()
|
|
return
|
|
case controllerPool.CapacityReadSuccess:
|
|
if observation.Managed < 0 || observation.AvailableSlots < 0 || observation.EffectiveSlots < 0 || observation.PendingExpected < 0 {
|
|
return
|
|
}
|
|
collector.reads.WithLabelValues(string(observation.Result)).Inc()
|
|
default:
|
|
return
|
|
}
|
|
collector.mu.Lock()
|
|
collector.byUpstream[observation.UpstreamID] = capacityState{
|
|
sourceID: observation.SourceID,
|
|
managed: observation.Managed, available: observation.AvailableSlots,
|
|
effective: observation.EffectiveSlots, pending: observation.PendingExpected,
|
|
}
|
|
collector.mu.Unlock()
|
|
}
|
|
|
|
func (collector *CapacityCollector) RemoveCapacityUpstream(upstreamID, sourceID string) {
|
|
if collector == nil || upstreamID == "" || sourceID == "" {
|
|
return
|
|
}
|
|
collector.mu.Lock()
|
|
if state, exists := collector.byUpstream[upstreamID]; exists && state.sourceID == sourceID {
|
|
delete(collector.byUpstream, upstreamID)
|
|
}
|
|
collector.mu.Unlock()
|
|
}
|
|
|
|
func (collector *CapacityCollector) Describe(descriptions chan<- *prometheus.Desc) {
|
|
if collector == nil {
|
|
return
|
|
}
|
|
descriptions <- collector.managed
|
|
descriptions <- collector.available
|
|
descriptions <- collector.effective
|
|
descriptions <- collector.pending
|
|
descriptions <- collector.activeUpstreams
|
|
}
|
|
|
|
func (collector *CapacityCollector) Collect(metrics chan<- prometheus.Metric) {
|
|
if collector == nil {
|
|
return
|
|
}
|
|
collector.mu.Lock()
|
|
var managed, available, effective, pending int64
|
|
for _, state := range collector.byUpstream {
|
|
managed += int64(state.managed)
|
|
available += state.available
|
|
effective += state.effective
|
|
pending += int64(state.pending)
|
|
}
|
|
activeUpstreams := len(collector.byUpstream)
|
|
collector.mu.Unlock()
|
|
metrics <- prometheus.MustNewConstMetric(collector.managed, prometheus.GaugeValue, float64(managed))
|
|
metrics <- prometheus.MustNewConstMetric(collector.available, prometheus.GaugeValue, float64(available))
|
|
metrics <- prometheus.MustNewConstMetric(collector.effective, prometheus.GaugeValue, float64(effective))
|
|
metrics <- prometheus.MustNewConstMetric(collector.pending, prometheus.GaugeValue, float64(pending))
|
|
metrics <- prometheus.MustNewConstMetric(collector.activeUpstreams, prometheus.GaugeValue, float64(activeUpstreams))
|
|
}
|
|
|
|
func registerCapacityCollector(registerer prometheus.Registerer, candidate *CapacityCollector) (*CapacityCollector, error) {
|
|
if err := registerer.Register(candidate); err == nil {
|
|
return candidate, nil
|
|
} else {
|
|
var registered prometheus.AlreadyRegisteredError
|
|
if !errors.As(err, ®istered) {
|
|
return nil, fmt.Errorf("register capacity collector: %w", err)
|
|
}
|
|
existing, ok := registered.ExistingCollector.(*CapacityCollector)
|
|
if !ok {
|
|
return nil, fmt.Errorf("register capacity collector: existing collector has unexpected type")
|
|
}
|
|
return existing, nil
|
|
}
|
|
}
|