package tenantmetrics import ( "fmt" "sync" "sync/atomic" "github.com/VictoriaMetrics/metrics" "github.com/VictoriaMetrics/VictoriaMetrics/lib/auth" "github.com/VictoriaMetrics/VictoriaMetrics/lib/logger" ) // TenantID defines metric tenant. type TenantID struct { AccountID uint32 ProjectID uint32 } // CounterMap is a map of counters keyed by tenant. type CounterMap struct { metric string m sync.Map // mt holds value for multi-tenant metrics. mt atomic.Value } // NewCounterMap creates new CounterMap for the given metric. func NewCounterMap(metric string) *CounterMap { return &CounterMap{ metric: metric, } } // Get returns counter for the given at func (cm *CounterMap) Get(at *auth.Token) *metrics.Counter { if at == nil { return cm.GetByTenant(nil) } key := TenantID{ AccountID: at.AccountID, ProjectID: at.ProjectID, } return cm.GetByTenant(&key) } // MultiAdd adds multiple values grouped by auth.Token func (cm *CounterMap) MultiAdd(perTenantValues map[auth.Token]int) { for token, value := range perTenantValues { cm.Get(&token).Add(value) } } // GetByTenant returns counter for the given key. func (cm *CounterMap) GetByTenant(key *TenantID) *metrics.Counter { if key == nil { mtm := cm.mt.Load() if mtm == nil { mtc := metrics.GetOrCreateCounter(createMetricNameMultitenant(cm.metric)) cm.mt.Store(mtc) return mtc } return mtm.(*metrics.Counter) } if counter, ok := cm.m.Load(*key); ok { return counter.(*metrics.Counter) } // Slow path - create missing counter for k. metricName := createMetricName(cm.metric, *key) c := metrics.GetOrCreateCounter(metricName) cm.m.Store(*key, c) return c } func createMetricName(metric string, key TenantID) string { if len(metric) == 0 { logger.Panicf("BUG: metric cannot be empty") } if metric[len(metric)-1] != '}' { // Metric without labels. return fmt.Sprintf(`%s{accountID="%d",projectID="%d"}`, metric, key.AccountID, key.ProjectID) } // Metric with labels. return fmt.Sprintf(`%s,accountID="%d",projectID="%d"}`, metric[:len(metric)-1], key.AccountID, key.ProjectID) } func createMetricNameMultitenant(metric string) string { if len(metric) == 0 { logger.Panicf("BUG: metric cannot be empty") } if metric[len(metric)-1] != '}' { // Metric without labels. return fmt.Sprintf(`%s{accountID="multitenant",projectID="multitenant"}`, metric) } // Metric with labels. return fmt.Sprintf(`%s,accountID="multitenant",projectID="multitenant"}`, metric[:len(metric)-1]) }