2021-08-05 06:46:19 +00:00
|
|
|
package tenantmetrics
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
2024-11-20 17:36:06 +00:00
|
|
|
"sync"
|
2021-08-05 06:46:19 +00:00
|
|
|
"sync/atomic"
|
|
|
|
|
|
|
|
"github.com/VictoriaMetrics/VictoriaMetrics/lib/auth"
|
|
|
|
"github.com/VictoriaMetrics/VictoriaMetrics/lib/logger"
|
|
|
|
"github.com/VictoriaMetrics/metrics"
|
|
|
|
)
|
|
|
|
|
|
|
|
// 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
|
2023-07-21 03:55:27 +00:00
|
|
|
|
2024-11-20 17:36:06 +00:00
|
|
|
m sync.Map
|
|
|
|
// mt holds value for multi-tenant metrics.
|
|
|
|
mt atomic.Value
|
2021-08-05 06:46:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// NewCounterMap creates new CounterMap for the given metric.
|
|
|
|
func NewCounterMap(metric string) *CounterMap {
|
2024-11-20 17:36:06 +00:00
|
|
|
return &CounterMap{
|
2021-08-05 06:46:19 +00:00
|
|
|
metric: metric,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Get returns counter for the given at
|
|
|
|
func (cm *CounterMap) Get(at *auth.Token) *metrics.Counter {
|
|
|
|
key := TenantID{
|
|
|
|
AccountID: at.AccountID,
|
|
|
|
ProjectID: at.ProjectID,
|
|
|
|
}
|
2024-11-20 17:36:06 +00:00
|
|
|
return cm.GetByTenant(&key)
|
2021-08-05 06:46:19 +00:00
|
|
|
}
|
|
|
|
|
2022-09-30 14:28:35 +00:00
|
|
|
// 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)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-08-05 06:46:19 +00:00
|
|
|
// GetByTenant returns counter for the given key.
|
2024-11-20 17:36:06 +00:00
|
|
|
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)
|
2021-08-05 06:46:19 +00:00
|
|
|
}
|
|
|
|
|
2024-11-20 17:36:06 +00:00
|
|
|
if counter, ok := cm.m.Load(*key); ok {
|
|
|
|
return counter.(*metrics.Counter)
|
2021-08-05 06:46:19 +00:00
|
|
|
}
|
2024-11-20 17:36:06 +00:00
|
|
|
|
|
|
|
// Slow path - create missing counter for k.
|
|
|
|
metricName := createMetricName(cm.metric, *key)
|
2021-08-05 06:46:19 +00:00
|
|
|
c := metrics.GetOrCreateCounter(metricName)
|
2024-11-20 17:36:06 +00:00
|
|
|
cm.m.Store(*key, c)
|
2021-08-05 06:46:19 +00:00
|
|
|
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)
|
|
|
|
}
|
2024-11-20 17:36:06 +00:00
|
|
|
|
|
|
|
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])
|
|
|
|
}
|