mirror of
https://github.com/VictoriaMetrics/VictoriaMetrics.git
synced 2025-03-11 15:34:56 +00:00
app/vminsert: split vm_rows_inserted_total into per-(accountID, projectID) metrics
Fixes https://github.com/VictoriaMetrics/VictoriaMetrics/issues/59
This commit is contained in:
parent
8b2a6c6182
commit
8cf0a0e59c
6 changed files with 172 additions and 8 deletions
|
@ -13,11 +13,12 @@ import (
|
|||
"github.com/VictoriaMetrics/VictoriaMetrics/app/vminsert/netstorage"
|
||||
"github.com/VictoriaMetrics/VictoriaMetrics/lib/auth"
|
||||
"github.com/VictoriaMetrics/VictoriaMetrics/lib/bytesutil"
|
||||
"github.com/VictoriaMetrics/VictoriaMetrics/lib/tenantmetrics"
|
||||
"github.com/VictoriaMetrics/metrics"
|
||||
"github.com/valyala/fastjson/fastfloat"
|
||||
)
|
||||
|
||||
var rowsInserted = metrics.NewCounter(`vm_rows_inserted_total{type="graphite"}`)
|
||||
var rowsInserted = tenantmetrics.NewCounterMap(`vm_rows_inserted_total{type="graphite"}`)
|
||||
|
||||
// insertHandler processes remote write for graphite plaintext protocol.
|
||||
//
|
||||
|
@ -66,7 +67,8 @@ func (ctx *pushCtx) InsertRows(at *auth.Token) error {
|
|||
return err
|
||||
}
|
||||
}
|
||||
rowsInserted.Add(len(rows))
|
||||
// Assume that all the rows for a single connection belong to the same (AccountID, ProjectID).
|
||||
rowsInserted.Get(&atCopy).Add(len(rows))
|
||||
return ic.FlushBufs()
|
||||
}
|
||||
|
||||
|
|
|
@ -15,10 +15,11 @@ import (
|
|||
"github.com/VictoriaMetrics/VictoriaMetrics/lib/auth"
|
||||
"github.com/VictoriaMetrics/VictoriaMetrics/lib/bytesutil"
|
||||
"github.com/VictoriaMetrics/VictoriaMetrics/lib/storage"
|
||||
"github.com/VictoriaMetrics/VictoriaMetrics/lib/tenantmetrics"
|
||||
"github.com/VictoriaMetrics/metrics"
|
||||
)
|
||||
|
||||
var rowsInserted = metrics.NewCounter(`vm_rows_inserted_total{type="influx"}`)
|
||||
var rowsInserted = tenantmetrics.NewCounterMap(`vm_rows_inserted_total{type="influx"}`)
|
||||
|
||||
// InsertHandler processes remote write for influx line protocol.
|
||||
//
|
||||
|
@ -76,6 +77,7 @@ func (ctx *pushCtx) InsertRows(at *auth.Token, db string) error {
|
|||
rows := ctx.Rows.Rows
|
||||
ic := &ctx.Common
|
||||
ic.Reset()
|
||||
rowsAdded := 0
|
||||
for i := range rows {
|
||||
r := &rows[i]
|
||||
ic.Labels = ic.Labels[:0]
|
||||
|
@ -103,8 +105,9 @@ func (ctx *pushCtx) InsertRows(at *auth.Token, db string) error {
|
|||
return err
|
||||
}
|
||||
}
|
||||
rowsInserted.Add(len(r.Fields))
|
||||
rowsAdded += len(r.Fields)
|
||||
}
|
||||
rowsInserted.Get(at).Add(rowsAdded)
|
||||
return ic.FlushBufs()
|
||||
}
|
||||
|
||||
|
|
|
@ -13,11 +13,12 @@ import (
|
|||
"github.com/VictoriaMetrics/VictoriaMetrics/app/vminsert/netstorage"
|
||||
"github.com/VictoriaMetrics/VictoriaMetrics/lib/auth"
|
||||
"github.com/VictoriaMetrics/VictoriaMetrics/lib/bytesutil"
|
||||
"github.com/VictoriaMetrics/VictoriaMetrics/lib/tenantmetrics"
|
||||
"github.com/VictoriaMetrics/metrics"
|
||||
"github.com/valyala/fastjson/fastfloat"
|
||||
)
|
||||
|
||||
var rowsInserted = metrics.NewCounter(`vm_rows_inserted_total{type="opentsdb"}`)
|
||||
var rowsInserted = tenantmetrics.NewCounterMap(`vm_rows_inserted_total{type="opentsdb"}`)
|
||||
|
||||
// insertHandler processes remote write for OpenTSDB put protocol.
|
||||
//
|
||||
|
@ -66,7 +67,8 @@ func (ctx *pushCtx) InsertRows(at *auth.Token) error {
|
|||
return err
|
||||
}
|
||||
}
|
||||
rowsInserted.Add(len(rows))
|
||||
// Assume that all the rows for a single connection belong to the same (AccountID, ProjectID).
|
||||
rowsInserted.Get(&atCopy).Add(len(rows))
|
||||
return ic.FlushBufs()
|
||||
}
|
||||
|
||||
|
|
|
@ -11,10 +11,11 @@ import (
|
|||
"github.com/VictoriaMetrics/VictoriaMetrics/lib/auth"
|
||||
"github.com/VictoriaMetrics/VictoriaMetrics/lib/prompb"
|
||||
"github.com/VictoriaMetrics/VictoriaMetrics/lib/storage"
|
||||
"github.com/VictoriaMetrics/VictoriaMetrics/lib/tenantmetrics"
|
||||
"github.com/VictoriaMetrics/metrics"
|
||||
)
|
||||
|
||||
var rowsInserted = metrics.NewCounter(`vm_rows_inserted_total{type="prometheus"}`)
|
||||
var rowsInserted = tenantmetrics.NewCounterMap(`vm_rows_inserted_total{type="prometheus"}`)
|
||||
|
||||
// InsertHandler processes remote write for prometheus.
|
||||
func InsertHandler(at *auth.Token, r *http.Request, maxSize int64) error {
|
||||
|
@ -46,7 +47,7 @@ func insertHandlerInternal(at *auth.Token, r *http.Request, maxSize int64) error
|
|||
return err
|
||||
}
|
||||
}
|
||||
rowsInserted.Add(len(ts.Samples))
|
||||
rowsInserted.Get(at).Add(len(ts.Samples))
|
||||
}
|
||||
return ic.FlushBufs()
|
||||
}
|
||||
|
|
69
lib/tenantmetrics/counter_map.go
Normal file
69
lib/tenantmetrics/counter_map.go
Normal file
|
@ -0,0 +1,69 @@
|
|||
package tenantmetrics
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync/atomic"
|
||||
|
||||
"github.com/VictoriaMetrics/VictoriaMetrics/lib/auth"
|
||||
"github.com/VictoriaMetrics/VictoriaMetrics/lib/logger"
|
||||
"github.com/VictoriaMetrics/metrics"
|
||||
)
|
||||
|
||||
type apKey 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 metricTemplate.
|
||||
//
|
||||
func NewCounterMap(metric string) *CounterMap {
|
||||
cm := &CounterMap{
|
||||
metric: metric,
|
||||
}
|
||||
cm.m.Store(make(map[apKey]*metrics.Counter))
|
||||
return cm
|
||||
}
|
||||
|
||||
// Get returns counter for the given at.
|
||||
//
|
||||
// It always returns non-nil counter.
|
||||
func (cm *CounterMap) Get(at *auth.Token) *metrics.Counter {
|
||||
key := apKey{
|
||||
accountID: at.AccountID,
|
||||
projectID: at.ProjectID,
|
||||
}
|
||||
m := cm.m.Load().(map[apKey]*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[apKey]*metrics.Counter, len(m)+1)
|
||||
for k, c := range m {
|
||||
newM[k] = c
|
||||
}
|
||||
metricName := createMetricName(cm.metric, at)
|
||||
c := metrics.GetOrCreateCounter(metricName)
|
||||
newM[key] = c
|
||||
cm.m.Store(newM)
|
||||
return c
|
||||
}
|
||||
|
||||
func createMetricName(metric string, at *auth.Token) 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, at.AccountID, at.ProjectID)
|
||||
}
|
||||
// Metric with labels.
|
||||
return fmt.Sprintf(`%s,accountID="%d",projectID="%d"}`, metric[:len(metric)-1], at.AccountID, at.ProjectID)
|
||||
}
|
87
lib/tenantmetrics/counter_map_test.go
Normal file
87
lib/tenantmetrics/counter_map_test.go
Normal file
|
@ -0,0 +1,87 @@
|
|||
package tenantmetrics
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/VictoriaMetrics/VictoriaMetrics/lib/auth"
|
||||
)
|
||||
|
||||
func TestCreateMetricNameError(t *testing.T) {
|
||||
defer func() {
|
||||
if r := recover(); r == nil {
|
||||
t.Fatal("expecting non-nil panic")
|
||||
}
|
||||
}()
|
||||
_ = createMetricName("", &auth.Token{})
|
||||
}
|
||||
|
||||
func TestCreateMetricNameSuccess(t *testing.T) {
|
||||
f := func(s string, at *auth.Token, metricExpected string) {
|
||||
t.Helper()
|
||||
metric := createMetricName(s, at)
|
||||
if metric != metricExpected {
|
||||
t.Fatalf("unexpected result for createMetricName(%q, %v); got %q; want %q", s, at, metric, metricExpected)
|
||||
}
|
||||
}
|
||||
f(`a`, &auth.Token{AccountID: 1, ProjectID: 2}, `a{accountID="1",projectID="2"}`)
|
||||
f(`foo{bar="baz"}`, &auth.Token{AccountID: 33, ProjectID: 41}, `foo{bar="baz",accountID="33",projectID="41"}`)
|
||||
f(`foo{bar="baz",a="aa"}`, &auth.Token{AccountID: 33, ProjectID: 41}, `foo{bar="baz",a="aa",accountID="33",projectID="41"}`)
|
||||
}
|
||||
|
||||
func TestCounterMap(t *testing.T) {
|
||||
cm := NewCounterMap("foobar")
|
||||
cm.Get(&auth.Token{AccountID: 1, ProjectID: 2}).Inc()
|
||||
cm.Get(&auth.Token{AccountID: 4, ProjectID: 0}).Add(12)
|
||||
|
||||
if n := cm.Get(&auth.Token{AccountID: 1, ProjectID: 2}).Get(); n != 1 {
|
||||
t.Fatalf("unexpected counter value; got %d; want %d", n, 1)
|
||||
}
|
||||
if n := cm.Get(&auth.Token{AccountID: 4, ProjectID: 0}).Get(); n != 12 {
|
||||
t.Fatalf("unexpected counter value; got %d; want %d", n, 12)
|
||||
}
|
||||
if n := cm.Get(&auth.Token{}).Get(); n != 0 {
|
||||
t.Fatalf("unexpected counter value; got %d; want %d", n, 0)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCounterMapConcurrent(t *testing.T) {
|
||||
cm := NewCounterMap(`aaa{bb="cc"}`)
|
||||
f := func() error {
|
||||
for i := 0; i < 10; i++ {
|
||||
cm.Get(&auth.Token{AccountID: 1, ProjectID: 2}).Inc()
|
||||
if n := cm.Get(&auth.Token{AccountID: 3, ProjectID: 4}).Get(); n != 0 {
|
||||
return fmt.Errorf("unexpected counter value; got %d; want %d", n, 0)
|
||||
}
|
||||
cm.Get(&auth.Token{AccountID: 1, ProjectID: 3}).Add(5)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
const concurrency = 5
|
||||
ch := make(chan error, concurrency)
|
||||
for i := 0; i < concurrency; i++ {
|
||||
go func() {
|
||||
ch <- f()
|
||||
}()
|
||||
}
|
||||
|
||||
for i := 0; i < concurrency; i++ {
|
||||
select {
|
||||
case err := <-ch:
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %s", err)
|
||||
}
|
||||
case <-time.After(time.Second):
|
||||
t.Fatalf("timeout")
|
||||
}
|
||||
}
|
||||
|
||||
if n := cm.Get(&auth.Token{AccountID: 1, ProjectID: 2}).Get(); n != concurrency*10 {
|
||||
t.Fatalf("unexpected counter value; got %d; want %d", n, concurrency*10)
|
||||
}
|
||||
if n := cm.Get(&auth.Token{AccountID: 1, ProjectID: 3}).Get(); n != concurrency*10*5 {
|
||||
t.Fatalf("unexpected counter value; got %d; want %d", n, concurrency*10*5)
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue