2019-05-22 23:16:55 +02:00
package influx
import (
2019-06-14 08:57:13 +02:00
"flag"
2020-02-25 18:09:46 +01:00
"io"
2019-05-22 23:16:55 +02:00
"net/http"
"runtime"
"sync"
2019-05-22 23:23:23 +02:00
"github.com/VictoriaMetrics/VictoriaMetrics/app/vminsert/netstorage"
2020-07-02 18:42:12 +02:00
"github.com/VictoriaMetrics/VictoriaMetrics/app/vminsert/relabel"
2019-05-22 23:23:23 +02:00
"github.com/VictoriaMetrics/VictoriaMetrics/lib/auth"
2019-05-22 23:16:55 +02:00
"github.com/VictoriaMetrics/VictoriaMetrics/lib/bytesutil"
2020-02-23 12:35:47 +01:00
parser "github.com/VictoriaMetrics/VictoriaMetrics/lib/protoparser/influx"
2019-05-22 23:16:55 +02:00
"github.com/VictoriaMetrics/VictoriaMetrics/lib/storage"
2019-06-07 20:16:05 +02:00
"github.com/VictoriaMetrics/VictoriaMetrics/lib/tenantmetrics"
2020-02-23 12:35:47 +01:00
"github.com/VictoriaMetrics/VictoriaMetrics/lib/writeconcurrencylimiter"
2019-05-22 23:16:55 +02:00
"github.com/VictoriaMetrics/metrics"
2020-05-12 12:13:00 +02:00
"github.com/valyala/fastjson/fastfloat"
2019-05-22 23:16:55 +02:00
)
2019-06-14 08:57:13 +02:00
var (
2020-02-04 14:46:13 +01:00
measurementFieldSeparator = flag . String ( "influxMeasurementFieldSeparator" , "_" , "Separator for '{measurement}{separator}{field_name}' metric name when inserted via Influx line protocol" )
skipSingleField = flag . Bool ( "influxSkipSingleField" , false , "Uses '{measurement}' instead of '{measurement}{separator}{field_name}' for metic name if Influx line contains only a single field" )
2020-07-14 13:17:22 +02:00
skipMeasurement = flag . Bool ( "influxSkipMeasurement" , false , "Uses '{field_name}' as a metric name while ignoring '{measurement}' and '-influxMeasurementFieldSeparator'" )
2019-06-14 08:57:13 +02:00
)
2019-07-27 12:20:47 +02:00
var (
rowsInserted = tenantmetrics . NewCounterMap ( ` vm_rows_inserted_total { type="influx"} ` )
2020-02-23 12:35:47 +01:00
rowsPerInsert = metrics . NewHistogram ( ` vm_rows_per_insert { type="influx"} ` )
2019-07-27 12:20:47 +02:00
)
2019-05-22 23:16:55 +02:00
2020-02-25 18:09:46 +01:00
// InsertHandlerForReader processes remote write for influx line protocol.
//
// See https://github.com/influxdata/telegraf/tree/master/plugins/inputs/socket_listener/
func InsertHandlerForReader ( at * auth . Token , r io . Reader ) error {
return writeconcurrencylimiter . Do ( func ( ) error {
return parser . ParseStream ( r , false , "" , "" , func ( db string , rows [ ] parser . Row ) error {
2020-05-12 12:13:00 +02:00
return insertRows ( at , db , rows , true )
2020-02-25 18:09:46 +01:00
} )
} )
}
// InsertHandlerForHTTP processes remote write for influx line protocol.
2019-05-22 23:16:55 +02:00
//
// See https://github.com/influxdata/influxdb/blob/4cbdc197b8117fee648d62e2e5be75c6575352f0/tsdb/README.md
2020-02-25 18:09:46 +01:00
func InsertHandlerForHTTP ( at * auth . Token , req * http . Request ) error {
2020-02-23 12:35:47 +01:00
return writeconcurrencylimiter . Do ( func ( ) error {
2020-02-25 18:09:46 +01:00
isGzipped := req . Header . Get ( "Content-Encoding" ) == "gzip"
q := req . URL . Query ( )
precision := q . Get ( "precision" )
// Read db tag from https://docs.influxdata.com/influxdb/v1.7/tools/api/#write-http-endpoint
db := q . Get ( "db" )
return parser . ParseStream ( req . Body , isGzipped , precision , db , func ( db string , rows [ ] parser . Row ) error {
2020-05-12 12:13:00 +02:00
return insertRows ( at , db , rows , false )
2020-02-23 12:35:47 +01:00
} )
2019-05-22 23:16:55 +02:00
} )
}
2020-05-12 12:13:00 +02:00
func insertRows ( at * auth . Token , db string , rows [ ] parser . Row , mayOverrideAccountProjectID bool ) error {
2019-05-22 23:16:55 +02:00
ctx := getPushCtx ( )
defer putPushCtx ( ctx )
ic := & ctx . Common
2020-02-26 20:17:35 +01:00
ic . Reset ( ) // This line is required for initializing ic internals.
2019-07-27 12:20:47 +02:00
rowsTotal := 0
2020-05-12 12:13:00 +02:00
atCopy := * at
2020-07-02 18:42:12 +02:00
hasRelabeling := relabel . HasRelabeling ( )
2019-05-22 23:16:55 +02:00
for i := range rows {
r := & rows [ i ]
ic . Labels = ic . Labels [ : 0 ]
2019-08-24 12:51:51 +02:00
hasDBLabel := false
2019-05-22 23:16:55 +02:00
for j := range r . Tags {
tag := & r . Tags [ j ]
2020-05-12 12:13:00 +02:00
if mayOverrideAccountProjectID {
// Multi-tenancy support via custom tags.
if tag . Key == "VictoriaMetrics_AccountID" {
atCopy . AccountID = uint32 ( fastfloat . ParseUint64BestEffort ( tag . Value ) )
}
if tag . Key == "VictoriaMetrics_ProjectID" {
atCopy . ProjectID = uint32 ( fastfloat . ParseUint64BestEffort ( tag . Value ) )
}
}
2019-08-24 12:51:51 +02:00
if tag . Key == "db" {
hasDBLabel = true
}
2019-05-22 23:16:55 +02:00
ic . AddLabel ( tag . Key , tag . Value )
}
2019-08-24 12:51:51 +02:00
if len ( db ) > 0 && ! hasDBLabel {
ic . AddLabel ( "db" , db )
}
2020-07-14 13:17:22 +02:00
ctx . metricGroupBuf = ctx . metricGroupBuf [ : 0 ]
if ! * skipMeasurement {
ctx . metricGroupBuf = append ( ctx . metricGroupBuf , r . Measurement ... )
}
2019-06-14 09:51:57 +02:00
skipFieldKey := len ( r . Fields ) == 1 && * skipSingleField
2019-11-30 20:54:34 +01:00
if len ( ctx . metricGroupBuf ) > 0 && ! skipFieldKey {
2019-06-14 09:51:57 +02:00
ctx . metricGroupBuf = append ( ctx . metricGroupBuf , * measurementFieldSeparator ... )
}
2019-05-22 23:16:55 +02:00
metricGroupPrefixLen := len ( ctx . metricGroupBuf )
2020-07-02 22:13:13 +02:00
labels := ic . Labels
if hasRelabeling {
labels = nil
2020-07-02 18:42:12 +02:00
}
ic . MetricNameBuf = storage . MarshalMetricNameRaw ( ic . MetricNameBuf [ : 0 ] , atCopy . AccountID , atCopy . ProjectID , labels )
metricNameBufLen := len ( ic . MetricNameBuf )
labelsLen := len ( ic . Labels )
2019-05-22 23:16:55 +02:00
for j := range r . Fields {
f := & r . Fields [ j ]
2019-06-14 09:51:57 +02:00
if ! skipFieldKey {
ctx . metricGroupBuf = append ( ctx . metricGroupBuf [ : metricGroupPrefixLen ] , f . Key ... )
}
2019-05-22 23:16:55 +02:00
metricGroup := bytesutil . ToUnsafeString ( ctx . metricGroupBuf )
2020-07-02 18:42:12 +02:00
ic . Labels = ic . Labels [ : labelsLen ]
2019-05-22 23:16:55 +02:00
ic . AddLabel ( "" , metricGroup )
2020-07-02 22:13:13 +02:00
ic . ApplyRelabeling ( ) // this must be called even if !hasRelabeling in order to remove labels with empty values
if len ( ic . Labels ) == 0 {
// Skip metric without labels.
continue
}
labels = ic . Labels
if ! hasRelabeling {
labels = labels [ labelsLen : labelsLen + 1 ]
2020-07-02 18:42:12 +02:00
}
ic . MetricNameBuf = ic . MetricNameBuf [ : metricNameBufLen ]
for i := range labels {
ic . MetricNameBuf = storage . MarshalMetricLabelRaw ( ic . MetricNameBuf , & labels [ i ] )
}
2020-05-12 12:13:00 +02:00
storageNodeIdx := ic . GetStorageNodeIdx ( & atCopy , ic . Labels )
if err := ic . WriteDataPointExt ( & atCopy , storageNodeIdx , ic . MetricNameBuf , r . Timestamp , f . Value ) ; err != nil {
2019-05-22 23:23:23 +02:00
return err
}
2019-05-22 23:16:55 +02:00
}
2019-07-27 12:20:47 +02:00
rowsTotal += len ( r . Fields )
2019-05-22 23:16:55 +02:00
}
2020-05-12 12:13:00 +02:00
rowsInserted . Get ( & atCopy ) . Add ( rowsTotal )
2019-07-27 12:20:47 +02:00
rowsPerInsert . Update ( float64 ( rowsTotal ) )
2019-05-22 23:16:55 +02:00
return ic . FlushBufs ( )
}
type pushCtx struct {
2020-02-23 12:35:47 +01:00
Common netstorage . InsertCtx
2019-05-22 23:16:55 +02:00
metricGroupBuf [ ] byte
}
func ( ctx * pushCtx ) reset ( ) {
2019-05-22 23:23:23 +02:00
ctx . Common . Reset ( )
2019-05-22 23:16:55 +02:00
ctx . metricGroupBuf = ctx . metricGroupBuf [ : 0 ]
}
func getPushCtx ( ) * pushCtx {
select {
case ctx := <- pushCtxPoolCh :
return ctx
default :
if v := pushCtxPool . Get ( ) ; v != nil {
return v . ( * pushCtx )
}
return & pushCtx { }
}
}
func putPushCtx ( ctx * pushCtx ) {
ctx . reset ( )
select {
case pushCtxPoolCh <- ctx :
default :
pushCtxPool . Put ( ctx )
}
}
var pushCtxPool sync . Pool
var pushCtxPoolCh = make ( chan * pushCtx , runtime . GOMAXPROCS ( - 1 ) )