VictoriaMetrics/lib/tenantmetrics/counter_map.go
Nikolay 33f40f4a5f
app/vminsert: allows parsing tenant id from labels (#3009)
* app/vminsert: allows parsing tenant id from labels
it should help mitigate issues with vmagent's multiTenant mode, which works incorrectly at heavy load
and it cannot handle more then 100 different tenants.
This functional hidden with flag and do not change vminsert default behaviour
https://github.com/VictoriaMetrics/VictoriaMetrics/issues/2970

* Update docs/Cluster-VictoriaMetrics.md

Co-authored-by: Roman Khavronenko <roman@victoriametrics.com>

* wip

* app/vminsert/netstorage: clean remaining labels in order to free up GC

* docs/Cluster-VictoriaMetrics.md: typo fix

* wip

* wip

Co-authored-by: Roman Khavronenko <roman@victoriametrics.com>
Co-authored-by: Aliaksandr Valialkin <valyala@victoriametrics.com>
2022-09-30 18:35:53 +03:00

79 lines
2 KiB
Go

package tenantmetrics
import (
"fmt"
"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
m atomic.Value
}
// NewCounterMap creates new CounterMap for the given metric.
func NewCounterMap(metric string) *CounterMap {
cm := &CounterMap{
metric: metric,
}
cm.m.Store(make(map[TenantID]*metrics.Counter))
return cm
}
// Get returns counter for the given at
func (cm *CounterMap) Get(at *auth.Token) *metrics.Counter {
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 {
m := cm.m.Load().(map[TenantID]*metrics.Counter)
if c := m[key]; c != nil {
// Fast path - the counter for k already exists.
return c
}
// Slow path - create missing counter for k and re-create m.
newM := make(map[TenantID]*metrics.Counter, len(m)+1)
for k, c := range m {
newM[k] = c
}
metricName := createMetricName(cm.metric, key)
c := metrics.GetOrCreateCounter(metricName)
newM[key] = c
cm.m.Store(newM)
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)
}