package metrics import ( "fmt" "sync" "time" "git.frostfs.info/TrueCloudLab/frostfs-observability/metrics" "github.com/prometheus/client_golang/prometheus" ) const ( wcSubsystem = "writecache" wcShardID = "shard_id" wcSuccess = "success" wcStorage = "storage" wcMode = "mode" wcMethod = "method" wcOperation = "operation" ) type shardIDMode struct { shardID, mode string } type WriteCacheMetrics interface { AddMethodDuration(shardID string, method string, success bool, d time.Duration, storageType string) IncActualCount(shardID string, storageType string) DecActualCount(shardID string, storageType string) SetActualCount(shardID string, count uint64, storageType string) SetEstimateSize(shardID string, size uint64, storageType string) SetMode(shardID string, mode string) IncOperationCounter(shardID string, operation string, success NullBool, storageType string) Close(shardID string) } type writeCacheMetrics struct { methodDuration *prometheus.HistogramVec operationCounter *prometheus.CounterVec actualCount *prometheus.GaugeVec estimatedSize *prometheus.GaugeVec modeMetrics map[shardIDMode]prometheus.GaugeFunc modeValues map[string]string modeMtx sync.RWMutex } func newWriteCacheMetrics() *writeCacheMetrics { return &writeCacheMetrics{ methodDuration: metrics.NewHistogramVec(prometheus.HistogramOpts{ Namespace: namespace, Subsystem: wcSubsystem, Name: "request_duration_seconds", Help: "Writecache request process duration", }, []string{wcShardID, wcSuccess, wcStorage, wcMethod}), operationCounter: metrics.NewCounterVec(prometheus.CounterOpts{ Namespace: namespace, Subsystem: wcSubsystem, Name: "operations_total", Help: "The number of writecache operations processed", }, []string{wcShardID, wcStorage, wcSuccess, wcOperation}), actualCount: newWCGaugeVec("actual_objects_total", "Actual objects count in writecache", []string{wcShardID, wcStorage}), estimatedSize: newWCGaugeVec("estimated_size_bytes", "Estimated writecache size", []string{wcShardID, wcStorage}), modeMtx: sync.RWMutex{}, modeMetrics: make(map[shardIDMode]prometheus.GaugeFunc), modeValues: make(map[string]string), } } func (m *writeCacheMetrics) AddMethodDuration(shardID string, method string, success bool, d time.Duration, storageType string) { m.methodDuration.With( prometheus.Labels{ wcShardID: shardID, wcSuccess: fmt.Sprintf("%v", success), wcStorage: storageType, wcMethod: method, }, ).Observe(d.Seconds()) } func (m *writeCacheMetrics) IncActualCount(shardID string, storageType string) { m.actualCount.With(prometheus.Labels{ wcShardID: shardID, wcStorage: storageType, }).Inc() } func (m *writeCacheMetrics) DecActualCount(shardID string, storageType string) { m.actualCount.With(prometheus.Labels{ wcShardID: shardID, wcStorage: storageType, }).Dec() } func (m *writeCacheMetrics) SetActualCount(shardID string, count uint64, storageType string) { m.actualCount.With(prometheus.Labels{ wcShardID: shardID, wcStorage: storageType, }).Set(float64(count)) } func (m *writeCacheMetrics) SetEstimateSize(shardID string, size uint64, storageType string) { m.estimatedSize.With(prometheus.Labels{ wcShardID: shardID, wcStorage: storageType, }).Set(float64(size)) } func (m *writeCacheMetrics) SetMode(shardID string, mode string) { m.modeMtx.Lock() defer m.modeMtx.Unlock() m.modeValues[shardID] = mode key := shardIDMode{ shardID: shardID, mode: mode, } if _, found := m.modeMetrics[key]; found { return } metric := metrics.NewGaugeFunc( prometheus.GaugeOpts{ Namespace: namespace, Subsystem: wcSubsystem, Name: "writecache_mode", Help: "Writecache mode value", ConstLabels: prometheus.Labels{ wcShardID: shardID, wcMode: mode, }, }, func() float64 { m.modeMtx.RLock() defer m.modeMtx.RUnlock() value := m.modeValues[shardID] if value == mode { return 1 } return 0 }) m.modeMetrics[key] = metric } func (m *writeCacheMetrics) IncOperationCounter(shardID string, operation string, success NullBool, storageType string) { m.operationCounter.With(prometheus.Labels{ wcShardID: shardID, wcStorage: storageType, wcOperation: operation, wcSuccess: success.String(), }).Inc() } func (m *writeCacheMetrics) Close(shardID string) { m.SetMode(shardID, "CLOSED") m.methodDuration.DeletePartialMatch(prometheus.Labels{wcShardID: shardID}) m.operationCounter.DeletePartialMatch(prometheus.Labels{wcShardID: shardID}) m.actualCount.DeletePartialMatch(prometheus.Labels{wcShardID: shardID}) m.estimatedSize.DeletePartialMatch(prometheus.Labels{wcShardID: shardID}) } func newWCGaugeVec(name, help string, labels []string) *prometheus.GaugeVec { return metrics.NewGaugeVec(prometheus.GaugeOpts{ Namespace: namespace, Subsystem: wcSubsystem, Name: name, Help: help, }, labels) }