mirror of
https://github.com/VictoriaMetrics/VictoriaMetrics.git
synced 2026-07-14 04:41:15 +03:00
Compare commits
34 Commits
v1.147.0
...
docs-artic
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
37056ed0b2 | ||
|
|
8952c5b290 | ||
|
|
9c1ea5d5ad | ||
|
|
eed5299f84 | ||
|
|
c1e39b2cef | ||
|
|
0634599809 | ||
|
|
fccd724cd7 | ||
|
|
dedf4563d2 | ||
|
|
a0c60b4027 | ||
|
|
096bb8fdeb | ||
|
|
eaa39f8194 | ||
|
|
180a59119b | ||
|
|
414aa7b3ab | ||
|
|
6155e06ad4 | ||
|
|
fb26016beb | ||
|
|
66ac4114ed | ||
|
|
945dc5e42c | ||
|
|
2c9af81fbb | ||
|
|
431c315bff | ||
|
|
3b68e7fe1a | ||
|
|
404c86f6c5 | ||
|
|
e5943700a3 | ||
|
|
5aee4758da | ||
|
|
78015e5e4e | ||
|
|
06148ea0ec | ||
|
|
f8a99a51bf | ||
|
|
c110e70337 | ||
|
|
bf53f91dc1 | ||
|
|
92d10148d0 | ||
|
|
604dfff299 | ||
|
|
a183ec67c7 | ||
|
|
fc0559a8ab | ||
|
|
d17f79428e | ||
|
|
f3a3df0143 |
4
Makefile
4
Makefile
@@ -494,11 +494,13 @@ apptest-legacy: victoria-metrics-race vmbackup-race vmrestore-race
|
||||
apptest-mixed: victoria-metrics-race
|
||||
OS=$$(uname | tr '[:upper:]' '[:lower:]'); \
|
||||
ARCH=$$(uname -m | tr '[:upper:]' '[:lower:]' | sed 's/x86_64/amd64/'); \
|
||||
VERSION=v1.145.0; \
|
||||
VERSION=v1.147.0; \
|
||||
VMSINGLE=victoria-metrics-$${OS}-$${ARCH}-$${VERSION}.tar.gz; \
|
||||
VMCLUSTER=victoria-metrics-$${OS}-$${ARCH}-$${VERSION}-cluster.tar.gz; \
|
||||
URL=https://github.com/VictoriaMetrics/VictoriaMetrics/releases/download/$${VERSION}; \
|
||||
DIR=/tmp/$${VERSION}; \
|
||||
test -d $${DIR} || (mkdir $${DIR} && \
|
||||
curl --output-dir /tmp -LO $${URL}/$${VMSINGLE} && tar xzf /tmp/$${VMSINGLE} -C $${DIR} && \
|
||||
curl --output-dir /tmp -LO $${URL}/$${VMCLUSTER} && tar xzf /tmp/$${VMCLUSTER} -C $${DIR} \
|
||||
); \
|
||||
VMSELECT_PATH=$${DIR}/vmselect-prod \
|
||||
|
||||
@@ -63,6 +63,7 @@ func insertRows(at *auth.Token, tss []prompb.TimeSeries, mms []prompb.MetricMeta
|
||||
|
||||
rowsTotal := 0
|
||||
tssDst := ctx.WriteRequest.Timeseries[:0]
|
||||
mmsDst := ctx.WriteRequest.Metadata[:0]
|
||||
labels := ctx.Labels[:0]
|
||||
samples := ctx.Samples[:0]
|
||||
for i := range tss {
|
||||
@@ -82,7 +83,19 @@ func insertRows(at *auth.Token, tss []prompb.TimeSeries, mms []prompb.MetricMeta
|
||||
|
||||
var metadataTotal int
|
||||
if prommetadata.IsEnabled() {
|
||||
ctx.WriteRequest.Metadata = mms
|
||||
for i := range mms {
|
||||
mm := &mms[i]
|
||||
mmsDst = append(mmsDst, prompb.MetricMetadata{
|
||||
MetricFamilyName: mm.MetricFamilyName,
|
||||
Help: mm.Help,
|
||||
Type: mm.Type,
|
||||
Unit: mm.Unit,
|
||||
|
||||
AccountID: mm.AccountID,
|
||||
ProjectID: mm.ProjectID,
|
||||
})
|
||||
}
|
||||
ctx.WriteRequest.Metadata = mmsDst
|
||||
metadataTotal = len(mms)
|
||||
}
|
||||
|
||||
|
||||
@@ -6,7 +6,6 @@ import (
|
||||
"net/url"
|
||||
"reflect"
|
||||
"sort"
|
||||
"time"
|
||||
|
||||
"github.com/VictoriaMetrics/VictoriaMetrics/app/vmalert/datasource"
|
||||
"github.com/VictoriaMetrics/VictoriaMetrics/lib/prompb"
|
||||
@@ -94,10 +93,3 @@ Outer:
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func durationToTime(pd *promutil.Duration) time.Time {
|
||||
if pd == nil {
|
||||
return time.Time{}
|
||||
}
|
||||
return time.UnixMilli(pd.Duration().Milliseconds())
|
||||
}
|
||||
|
||||
@@ -44,12 +44,19 @@ import (
|
||||
var (
|
||||
storagePath string
|
||||
httpListenAddr string
|
||||
// insert series from 1970-01-01T00:00:00
|
||||
testStartTime = time.Unix(0, 0).UTC()
|
||||
// Insert series from 2000-01-01T00:00:00.
|
||||
testStartTime = time.Date(2000, 1, 1, 0, 0, 0, 0, time.UTC)
|
||||
testLogLevel = "ERROR"
|
||||
disableAlertgroupLabel bool
|
||||
)
|
||||
|
||||
func durationToTime(pd *promutil.Duration) time.Time {
|
||||
if pd == nil {
|
||||
return testStartTime
|
||||
}
|
||||
return testStartTime.Add(pd.Duration())
|
||||
}
|
||||
|
||||
const (
|
||||
testStoragePath = "vmalert-unittest"
|
||||
)
|
||||
|
||||
@@ -172,7 +172,13 @@ func newBinaryOpFunc(bf func(left, right float64, isBool bool) float64) binaryOp
|
||||
left = removeEmptySeries(left)
|
||||
right = removeEmptySeries(right)
|
||||
}
|
||||
if len(left) == 0 || len(right) == 0 {
|
||||
if len(left) == 0 && len(right) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
if len(left) == 0 && bfa.be.FillLeft == nil {
|
||||
return nil, nil
|
||||
}
|
||||
if len(right) == 0 && bfa.be.FillRight == nil {
|
||||
return nil, nil
|
||||
}
|
||||
left, right, dst, err := adjustBinaryOpTags(bfa.be, left, right)
|
||||
@@ -183,6 +189,8 @@ func newBinaryOpFunc(bf func(left, right float64, isBool bool) float64) binaryOp
|
||||
logger.Panicf("BUG: len(left) must match len(right) and len(dst); got %d vs %d vs %d", len(left), len(right), len(dst))
|
||||
}
|
||||
isBool := bfa.be.Bool
|
||||
fillLeft := bfa.be.FillLeft
|
||||
fillRight := bfa.be.FillRight
|
||||
for i, tsLeft := range left {
|
||||
leftValues := tsLeft.Values
|
||||
rightValues := right[i].Values
|
||||
@@ -193,6 +201,19 @@ func newBinaryOpFunc(bf func(left, right float64, isBool bool) float64) binaryOp
|
||||
}
|
||||
for j, a := range leftValues {
|
||||
b := rightValues[j]
|
||||
leftIsNaN := math.IsNaN(a)
|
||||
rightIsNaN := math.IsNaN(b)
|
||||
// apply the fill value when either the left or right side is NaN, but not both.
|
||||
if leftIsNaN && rightIsNaN {
|
||||
dstValues[j] = bf(a, b, isBool)
|
||||
continue
|
||||
}
|
||||
if leftIsNaN && fillLeft != nil {
|
||||
a = fillLeft.N
|
||||
}
|
||||
if rightIsNaN && fillRight != nil {
|
||||
b = fillRight.N
|
||||
}
|
||||
dstValues[j] = bf(a, b, isBool)
|
||||
}
|
||||
}
|
||||
@@ -226,7 +247,7 @@ func adjustBinaryOpTags(be *metricsql.BinaryOpExpr, left, right []*timeseries) (
|
||||
}
|
||||
}
|
||||
|
||||
// Slow path: `vector op vector` or `a op {on|ignoring} {group_left|group_right} b`
|
||||
// Slow path: `vector op vector` or `a op {on|ignoring} {group_left|group_right} {fill|fill_left|fill_right} b`
|
||||
var rvsLeft, rvsRight []*timeseries
|
||||
mLeft, mRight := createTimeseriesMapByTagSet(be, left, right)
|
||||
joinOp := strings.ToLower(be.JoinModifier.Op)
|
||||
@@ -239,10 +260,27 @@ func adjustBinaryOpTags(be *metricsql.BinaryOpExpr, left, right []*timeseries) (
|
||||
// Add __name__ to groupTags if metric name must be preserved.
|
||||
groupTags = append(groupTags[:len(groupTags):len(groupTags)], "__name__")
|
||||
}
|
||||
// Add missing keys from mRight to mLeft when fill_left()/fill() modifier is used
|
||||
if be.FillLeft != nil {
|
||||
for k := range mRight {
|
||||
if _, ok := mLeft[k]; !ok {
|
||||
mLeft[k] = nil
|
||||
}
|
||||
}
|
||||
}
|
||||
for k, tssLeft := range mLeft {
|
||||
tssRight := mRight[k]
|
||||
if len(tssLeft) == 0 {
|
||||
if be.FillLeft == nil {
|
||||
logger.Panicf("BUG: unexpected empty tssLeft for key %q when FillLeft is nil", k)
|
||||
}
|
||||
tssLeft = []*timeseries{newFillTimeseries(be, tssRight[0])}
|
||||
}
|
||||
if len(tssRight) == 0 {
|
||||
continue
|
||||
if be.FillRight == nil {
|
||||
continue
|
||||
}
|
||||
tssRight = []*timeseries{newFillTimeseries(be, tssLeft[0])}
|
||||
}
|
||||
switch joinOp {
|
||||
case "group_left":
|
||||
@@ -287,6 +325,28 @@ func adjustBinaryOpTags(be *metricsql.BinaryOpExpr, left, right []*timeseries) (
|
||||
return rvsLeft, rvsRight, dst, nil
|
||||
}
|
||||
|
||||
// newFillTimeseries returns a time series filled with NaN values for the fill_left()/fill_right()/fill() modifiers.
|
||||
// These NaN values will be replaced later with the fill value if needed.
|
||||
func newFillTimeseries(be *metricsql.BinaryOpExpr, src *timeseries) *timeseries {
|
||||
var ts timeseries
|
||||
ts.CopyFromShallowTimestamps(src)
|
||||
if !be.KeepMetricNames {
|
||||
ts.MetricName.ResetMetricGroup()
|
||||
}
|
||||
groupTags := be.GroupModifier.Args
|
||||
switch strings.ToLower(be.GroupModifier.Op) {
|
||||
case "on":
|
||||
ts.MetricName.RemoveTagsOn(groupTags)
|
||||
default:
|
||||
ts.MetricName.RemoveTagsIgnoring(groupTags)
|
||||
}
|
||||
values := ts.Values
|
||||
for i := range values {
|
||||
values[i] = math.NaN()
|
||||
}
|
||||
return &ts
|
||||
}
|
||||
|
||||
func ensureSingleTimeseries(side string, be *metricsql.BinaryOpExpr, tss []*timeseries) error {
|
||||
if len(tss) == 0 {
|
||||
logger.Panicf("BUG: tss must contain at least one value")
|
||||
|
||||
@@ -424,18 +424,7 @@ func evalBinaryOp(qt *querytracer.Tracer, ec *EvalConfig, be *metricsql.BinaryOp
|
||||
if bf == nil {
|
||||
return nil, fmt.Errorf(`unknown binary op %q`, be.Op)
|
||||
}
|
||||
var err error
|
||||
var tssLeft, tssRight []*timeseries
|
||||
switch strings.ToLower(be.Op) {
|
||||
case "and", "if":
|
||||
// Fetch right-side series at first, since it usually contains
|
||||
// lower number of time series for `and` and `if` operator.
|
||||
// This should produce more specific label filters for the left side of the query.
|
||||
// This, in turn, should reduce the time to select series for the left side of the query.
|
||||
tssRight, tssLeft, err = execBinaryOpArgs(qt, ec, be.Right, be.Left, be)
|
||||
default:
|
||||
tssLeft, tssRight, err = execBinaryOpArgs(qt, ec, be.Left, be.Right, be)
|
||||
}
|
||||
tssLeft, tssRight, err := execBinaryOpArgs(qt, ec, be)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot execute %q: %w", be.AppendString(nil), err)
|
||||
}
|
||||
@@ -451,6 +440,29 @@ func evalBinaryOp(qt *querytracer.Tracer, ec *EvalConfig, be *metricsql.BinaryOp
|
||||
return rv, nil
|
||||
}
|
||||
|
||||
// binaryOpEvalOrder might change the order of evaluation of the left and right sides of a binary operation,
|
||||
// when there is chance to push down common label filters from exprFirst to exprSecond in the following executions.
|
||||
func binaryOpEvalOrder(be *metricsql.BinaryOpExpr) (exprFirst, exprSecond metricsql.Expr) {
|
||||
exprFirst, exprSecond = be.Left, be.Right
|
||||
switch strings.ToLower(be.Op) {
|
||||
case "and", "if":
|
||||
// For `and` and `if`, fetch the right-side series first, since it usually contains
|
||||
// fewer time series and yields more specific filters for the left side.
|
||||
exprFirst, exprSecond = be.Right, be.Left
|
||||
}
|
||||
if be.FillLeft != nil && be.FillRight == nil {
|
||||
// For `fill_left(<value>)`, the unmatched series can only come from the right side, so evaluate it first.
|
||||
exprFirst, exprSecond = be.Right, be.Left
|
||||
}
|
||||
return exprFirst, exprSecond
|
||||
}
|
||||
|
||||
// canPushdownCommonFilters decides if common label filters can be pushed down from one side of a binary operation to the other.
|
||||
//
|
||||
// Common filters cannot be pushed down when:
|
||||
// - the operator is `or` or `default`;
|
||||
// - either side is an aggregation function without explicit grouping;
|
||||
// - fill(<value>) modifier is used.
|
||||
func canPushdownCommonFilters(be *metricsql.BinaryOpExpr) bool {
|
||||
switch strings.ToLower(be.Op) {
|
||||
case "or", "default":
|
||||
@@ -459,6 +471,10 @@ func canPushdownCommonFilters(be *metricsql.BinaryOpExpr) bool {
|
||||
if isAggrFuncWithoutGrouping(be.Left) || isAggrFuncWithoutGrouping(be.Right) {
|
||||
return false
|
||||
}
|
||||
// Filters cannot be propagated when fill(<value>) modifier is used.
|
||||
if be.FillLeft != nil && be.FillRight != nil {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -470,7 +486,15 @@ func isAggrFuncWithoutGrouping(e metricsql.Expr) bool {
|
||||
return len(afe.Modifier.Args) == 0
|
||||
}
|
||||
|
||||
func execBinaryOpArgs(qt *querytracer.Tracer, ec *EvalConfig, exprFirst, exprSecond metricsql.Expr, be *metricsql.BinaryOpExpr) ([]*timeseries, []*timeseries, error) {
|
||||
func execBinaryOpArgs(qt *querytracer.Tracer, ec *EvalConfig, be *metricsql.BinaryOpExpr) ([]*timeseries, []*timeseries, error) {
|
||||
exprFirst, exprSecond := binaryOpEvalOrder(be)
|
||||
firstIsLeft := exprFirst == be.Left
|
||||
sortResult := func(tssFirst, tssSecond []*timeseries) ([]*timeseries, []*timeseries, error) {
|
||||
if firstIsLeft {
|
||||
return tssFirst, tssSecond, nil
|
||||
}
|
||||
return tssSecond, tssFirst, nil
|
||||
}
|
||||
if canPushdownCommonFilters(be) {
|
||||
// Execute binary operation in the following way:
|
||||
//
|
||||
@@ -512,7 +536,7 @@ func execBinaryOpArgs(qt *querytracer.Tracer, ec *EvalConfig, exprFirst, exprSec
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
return tssFirst, tssSecond, nil
|
||||
return sortResult(tssFirst, tssSecond)
|
||||
}
|
||||
|
||||
// Execute exprFirst and exprSecond sequentially if there are cacheable repeated subexpressions
|
||||
@@ -568,7 +592,7 @@ func execBinaryOpArgs(qt *querytracer.Tracer, ec *EvalConfig, exprFirst, exprSec
|
||||
if errSecond != nil {
|
||||
return nil, nil, errSecond
|
||||
}
|
||||
return tssFirst, tssSecond, nil
|
||||
return sortResult(tssFirst, tssSecond)
|
||||
}
|
||||
|
||||
func shouldOptimizeRepeatedBinaryOpSubexprs(ec *EvalConfig, exprFirst, exprSecond metricsql.Expr) bool {
|
||||
|
||||
@@ -4006,6 +4006,275 @@ func TestExecSuccess(t *testing.T) {
|
||||
resultExpected := []netstorage.Result{r1, r2}
|
||||
f(q, resultExpected)
|
||||
})
|
||||
t.Run(`vector + vector fill()`, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
q := `sort_by_label((
|
||||
label_set(1, "foo", "common")
|
||||
or label_set(2, "foo", "left_only")
|
||||
) + fill(0) (
|
||||
label_set(3, "foo", "common")
|
||||
or label_set(4, "foo", "right_only")
|
||||
), "foo")`
|
||||
r1 := netstorage.Result{
|
||||
MetricName: metricNameExpected,
|
||||
Values: []float64{4, 4, 4, 4, 4, 4},
|
||||
Timestamps: timestampsExpected,
|
||||
}
|
||||
r1.MetricName.Tags = []storage.Tag{{
|
||||
Key: []byte("foo"),
|
||||
Value: []byte("common"),
|
||||
}}
|
||||
r2 := netstorage.Result{
|
||||
MetricName: metricNameExpected,
|
||||
Values: []float64{2, 2, 2, 2, 2, 2},
|
||||
Timestamps: timestampsExpected,
|
||||
}
|
||||
r2.MetricName.Tags = []storage.Tag{{
|
||||
Key: []byte("foo"),
|
||||
Value: []byte("left_only"),
|
||||
}}
|
||||
r3 := netstorage.Result{
|
||||
MetricName: metricNameExpected,
|
||||
Values: []float64{4, 4, 4, 4, 4, 4},
|
||||
Timestamps: timestampsExpected,
|
||||
}
|
||||
r3.MetricName.Tags = []storage.Tag{{
|
||||
Key: []byte("foo"),
|
||||
Value: []byte("right_only"),
|
||||
}}
|
||||
resultExpected := []netstorage.Result{r1, r2, r3}
|
||||
f(q, resultExpected)
|
||||
})
|
||||
t.Run(`vector + vector fill() both sides NaN case`, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
q := `(
|
||||
label_set(time() <= 1200, "foo", "common")
|
||||
) + fill(10) (
|
||||
label_set(time() >= 1600, "foo", "common")
|
||||
)`
|
||||
r := netstorage.Result{
|
||||
MetricName: metricNameExpected,
|
||||
Values: []float64{1010, 1210, nan, 1610, 1810, 2010},
|
||||
Timestamps: timestampsExpected,
|
||||
}
|
||||
r.MetricName.Tags = []storage.Tag{{
|
||||
Key: []byte("foo"),
|
||||
Value: []byte("common"),
|
||||
}}
|
||||
resultExpected := []netstorage.Result{r}
|
||||
f(q, resultExpected)
|
||||
})
|
||||
t.Run(`vector + vector fill_left() fill_right()`, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
q := `sort_by_label((
|
||||
label_set(1, "foo", "common")
|
||||
or label_set(2, "foo", "left_only")
|
||||
) + fill_left(10) fill_right(20) (
|
||||
label_set(3, "foo", "common")
|
||||
or label_set(4, "foo", "right_only")
|
||||
), "foo")`
|
||||
r1 := netstorage.Result{
|
||||
MetricName: metricNameExpected,
|
||||
Values: []float64{4, 4, 4, 4, 4, 4},
|
||||
Timestamps: timestampsExpected,
|
||||
}
|
||||
r1.MetricName.Tags = []storage.Tag{{
|
||||
Key: []byte("foo"),
|
||||
Value: []byte("common"),
|
||||
}}
|
||||
r2 := netstorage.Result{
|
||||
MetricName: metricNameExpected,
|
||||
Values: []float64{22, 22, 22, 22, 22, 22},
|
||||
Timestamps: timestampsExpected,
|
||||
}
|
||||
r2.MetricName.Tags = []storage.Tag{{
|
||||
Key: []byte("foo"),
|
||||
Value: []byte("left_only"),
|
||||
}}
|
||||
r3 := netstorage.Result{
|
||||
MetricName: metricNameExpected,
|
||||
Values: []float64{14, 14, 14, 14, 14, 14},
|
||||
Timestamps: timestampsExpected,
|
||||
}
|
||||
r3.MetricName.Tags = []storage.Tag{{
|
||||
Key: []byte("foo"),
|
||||
Value: []byte("right_only"),
|
||||
}}
|
||||
resultExpected := []netstorage.Result{r1, r2, r3}
|
||||
f(q, resultExpected)
|
||||
})
|
||||
t.Run(`vector + vector fill_right() only`, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
q := `sort_by_label((
|
||||
label_set(1, "foo", "common")
|
||||
or label_set(2, "foo", "left_only")
|
||||
) + fill_right(20) (
|
||||
label_set(3, "foo", "common")
|
||||
or label_set(4, "foo", "right_only")
|
||||
), "foo")`
|
||||
r1 := netstorage.Result{
|
||||
MetricName: metricNameExpected,
|
||||
Values: []float64{4, 4, 4, 4, 4, 4},
|
||||
Timestamps: timestampsExpected,
|
||||
}
|
||||
r1.MetricName.Tags = []storage.Tag{{
|
||||
Key: []byte("foo"),
|
||||
Value: []byte("common"),
|
||||
}}
|
||||
r2 := netstorage.Result{
|
||||
MetricName: metricNameExpected,
|
||||
Values: []float64{22, 22, 22, 22, 22, 22},
|
||||
Timestamps: timestampsExpected,
|
||||
}
|
||||
r2.MetricName.Tags = []storage.Tag{{
|
||||
Key: []byte("foo"),
|
||||
Value: []byte("left_only"),
|
||||
}}
|
||||
resultExpected := []netstorage.Result{r1, r2}
|
||||
f(q, resultExpected)
|
||||
})
|
||||
t.Run(`vector + vector on() fill()`, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
q := `sort_by_label((
|
||||
label_set(1, "foo", "common", "extra", "l")
|
||||
or label_set(2, "foo", "left_only", "extra", "l")
|
||||
) + on(foo) fill(0) (
|
||||
label_set(3, "foo", "common", "extra", "r")
|
||||
or label_set(4, "foo", "right_only", "extra", "r")
|
||||
), "foo")`
|
||||
r1 := netstorage.Result{
|
||||
MetricName: metricNameExpected,
|
||||
Values: []float64{4, 4, 4, 4, 4, 4},
|
||||
Timestamps: timestampsExpected,
|
||||
}
|
||||
r1.MetricName.Tags = []storage.Tag{{
|
||||
Key: []byte("foo"),
|
||||
Value: []byte("common"),
|
||||
}}
|
||||
r2 := netstorage.Result{
|
||||
MetricName: metricNameExpected,
|
||||
Values: []float64{2, 2, 2, 2, 2, 2},
|
||||
Timestamps: timestampsExpected,
|
||||
}
|
||||
r2.MetricName.Tags = []storage.Tag{{
|
||||
Key: []byte("foo"),
|
||||
Value: []byte("left_only"),
|
||||
}}
|
||||
r3 := netstorage.Result{
|
||||
MetricName: metricNameExpected,
|
||||
Values: []float64{4, 4, 4, 4, 4, 4},
|
||||
Timestamps: timestampsExpected,
|
||||
}
|
||||
r3.MetricName.Tags = []storage.Tag{{
|
||||
Key: []byte("foo"),
|
||||
Value: []byte("right_only"),
|
||||
}}
|
||||
resultExpected := []netstorage.Result{r1, r2, r3}
|
||||
f(q, resultExpected)
|
||||
})
|
||||
t.Run(`vector + vector on() group_left() fill_right()`, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
q := `sort_by_label((
|
||||
label_set(1, "method", "get", "code", "500")
|
||||
or label_set(2, "method", "get", "code", "404")
|
||||
or label_set(3, "method", "put", "code", "501")
|
||||
) + on(method) group_left() fill_right(0) (
|
||||
label_set(10, "method", "get")
|
||||
), "method", "code")`
|
||||
r1 := netstorage.Result{
|
||||
MetricName: metricNameExpected,
|
||||
Values: []float64{12, 12, 12, 12, 12, 12},
|
||||
Timestamps: timestampsExpected,
|
||||
}
|
||||
r1.MetricName.Tags = []storage.Tag{
|
||||
{
|
||||
Key: []byte("code"),
|
||||
Value: []byte("404"),
|
||||
},
|
||||
{
|
||||
Key: []byte("method"),
|
||||
Value: []byte("get"),
|
||||
},
|
||||
}
|
||||
r2 := netstorage.Result{
|
||||
MetricName: metricNameExpected,
|
||||
Values: []float64{11, 11, 11, 11, 11, 11},
|
||||
Timestamps: timestampsExpected,
|
||||
}
|
||||
r2.MetricName.Tags = []storage.Tag{
|
||||
{
|
||||
Key: []byte("code"),
|
||||
Value: []byte("500"),
|
||||
},
|
||||
{
|
||||
Key: []byte("method"),
|
||||
Value: []byte("get"),
|
||||
},
|
||||
}
|
||||
r3 := netstorage.Result{
|
||||
MetricName: metricNameExpected,
|
||||
Values: []float64{3, 3, 3, 3, 3, 3},
|
||||
Timestamps: timestampsExpected,
|
||||
}
|
||||
r3.MetricName.Tags = []storage.Tag{
|
||||
{
|
||||
Key: []byte("code"),
|
||||
Value: []byte("501"),
|
||||
},
|
||||
{
|
||||
Key: []byte("method"),
|
||||
Value: []byte("put"),
|
||||
},
|
||||
}
|
||||
resultExpected := []netstorage.Result{r1, r2, r3}
|
||||
f(q, resultExpected)
|
||||
})
|
||||
t.Run(`vector / vector ignoring() fill()`, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
q := `sort_by_label((
|
||||
label_set(6, "method", "get", "code", "500")
|
||||
or label_set(1, "method", "put", "code", "500")
|
||||
) / ignoring(code) fill(0) (
|
||||
label_set(12, "method", "get")
|
||||
or label_set(5, "method", "post")
|
||||
or label_set(10, "method", "put")
|
||||
), "method")`
|
||||
r1 := netstorage.Result{
|
||||
MetricName: metricNameExpected,
|
||||
Values: []float64{0.5, 0.5, 0.5, 0.5, 0.5, 0.5},
|
||||
Timestamps: timestampsExpected,
|
||||
}
|
||||
r1.MetricName.Tags = []storage.Tag{
|
||||
{
|
||||
Key: []byte("method"),
|
||||
Value: []byte("get"),
|
||||
},
|
||||
}
|
||||
r2 := netstorage.Result{
|
||||
MetricName: metricNameExpected,
|
||||
Values: []float64{0, 0, 0, 0, 0, 0},
|
||||
Timestamps: timestampsExpected,
|
||||
}
|
||||
r2.MetricName.Tags = []storage.Tag{
|
||||
{
|
||||
Key: []byte("method"),
|
||||
Value: []byte("post"),
|
||||
},
|
||||
}
|
||||
r3 := netstorage.Result{
|
||||
MetricName: metricNameExpected,
|
||||
Values: []float64{0.1, 0.1, 0.1, 0.1, 0.1, 0.1},
|
||||
Timestamps: timestampsExpected,
|
||||
}
|
||||
r3.MetricName.Tags = []storage.Tag{
|
||||
{
|
||||
Key: []byte("method"),
|
||||
Value: []byte("put"),
|
||||
},
|
||||
}
|
||||
resultExpected := []netstorage.Result{r1, r2, r3}
|
||||
f(q, resultExpected)
|
||||
})
|
||||
t.Run(`histogram_quantile(scalar)`, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
q := `histogram_quantile(0.6, time())`
|
||||
|
||||
@@ -30,6 +30,11 @@ var (
|
||||
"See https://docs.victoriametrics.com/victoriametrics/single-server-victoriametrics/#retention. See also -retentionFilter")
|
||||
futureRetention = flagutil.NewRetentionDuration("futureRetention", "2d", "Data with timestamps bigger than now+futureRetention is automatically deleted. "+
|
||||
"The minimum futureRetention is 2 days. See https://docs.victoriametrics.com/victoriametrics/single-server-victoriametrics/#retention")
|
||||
maxBackfillAge = flagutil.NewRetentionDuration("maxBackfillAge", "0", "The maximum allowed age for the ingested samples with historical timestamps. "+
|
||||
"Samples with timestamps older than now-maxBackfillAge are rejected during data ingestion. "+
|
||||
"By default, or when set to 0, -maxBackfillAge equals to -retentionPeriod, e.g. it is unlimited within the configured retention. "+
|
||||
"This can be useful for limiting ingestion of historical samples, for example, when older data has been moved to another storage tier. "+
|
||||
"See https://docs.victoriametrics.com/victoriametrics/single-server-victoriametrics/#retention")
|
||||
vmselectAddr = flag.String("vmselectAddr", "", "TCP address to accept connections from vmselect services")
|
||||
vmselectDisableRPCCompression = flag.Bool("rpc.disableCompression", false, "Whether to disable compression of the data sent from vmstorage to vmselect. "+
|
||||
"This reduces CPU usage at the cost of higher network bandwidth usage")
|
||||
@@ -146,6 +151,7 @@ func Init(vmselectMaxConcurrentRequests int, vmselectMaxQueueDuration time.Durat
|
||||
opts := storage.OpenOptions{
|
||||
Retention: retentionPeriod.Duration(),
|
||||
FutureRetention: futureRetention.Duration(),
|
||||
MaxBackfillAge: maxBackfillAge.Duration(),
|
||||
DenyQueriesOutsideRetention: *denyQueriesOutsideRetention,
|
||||
MaxHourlySeries: getMaxHourlySeries(),
|
||||
MaxDailySeries: getMaxDailySeries(),
|
||||
@@ -467,8 +473,10 @@ func (vms *VMStorage) writeStorageMetrics(w io.Writer) {
|
||||
metrics.WriteGaugeUint64(w, `vm_data_size_bytes{type="storage/inmemory"}`, tm.InmemorySizeBytes)
|
||||
metrics.WriteGaugeUint64(w, `vm_data_size_bytes{type="storage/small"}`, tm.SmallSizeBytes)
|
||||
metrics.WriteGaugeUint64(w, `vm_data_size_bytes{type="storage/big"}`, tm.BigSizeBytes)
|
||||
metrics.WriteGaugeUint64(w, `vm_data_size_bytes{type="storage/metaindex"}`, tm.MetaindexSizeBytes)
|
||||
metrics.WriteGaugeUint64(w, `vm_data_size_bytes{type="indexdb/inmemory"}`, idbm.InmemorySizeBytes)
|
||||
metrics.WriteGaugeUint64(w, `vm_data_size_bytes{type="indexdb/file"}`, idbm.FileSizeBytes)
|
||||
metrics.WriteGaugeUint64(w, `vm_data_size_bytes{type="indexdb/metaindex"}`, idbm.MetaindexSizeBytes)
|
||||
|
||||
metrics.WriteCounterUint64(w, `vm_rows_received_by_storage_total`, m.RowsReceivedTotal)
|
||||
metrics.WriteCounterUint64(w, `vm_rows_added_to_storage_total`, m.RowsAddedTotal)
|
||||
|
||||
@@ -17,10 +17,12 @@ interface HeaderNavProps {
|
||||
const HeaderNav: FC<HeaderNavProps> = ({ color, background, direction }) => {
|
||||
const { pathname } = useLocation();
|
||||
const [activeMenu, setActiveMenu] = useState(pathname);
|
||||
const [openMenu, setOpenMenu] = useState<string | null>(null);
|
||||
const menu = useNavigationMenu();
|
||||
|
||||
useEffect(() => {
|
||||
setActiveMenu(pathname);
|
||||
setOpenMenu(null);
|
||||
}, [pathname]);
|
||||
|
||||
return (
|
||||
@@ -41,6 +43,8 @@ const HeaderNav: FC<HeaderNavProps> = ({ color, background, direction }) => {
|
||||
color={color}
|
||||
background={background}
|
||||
direction={direction}
|
||||
openMenu={openMenu}
|
||||
setOpenMenu={setOpenMenu}
|
||||
/>
|
||||
)
|
||||
: (
|
||||
|
||||
@@ -1,11 +1,8 @@
|
||||
import { FC, useRef, useState } from "preact/compat";
|
||||
import { useLocation } from "react-router-dom";
|
||||
import { FC, useRef, useState, Dispatch, SetStateAction } from "preact/compat";
|
||||
import classNames from "classnames";
|
||||
import { ArrowDropDownIcon } from "../../../components/Main/Icons";
|
||||
import Popper from "../../../components/Main/Popper/Popper";
|
||||
import NavItem from "./NavItem";
|
||||
import { useEffect } from "react";
|
||||
import useBoolean from "../../../hooks/useBoolean";
|
||||
import { NavigationItem, NavigationItemType } from "../../../router/navigation";
|
||||
|
||||
interface NavItemProps {
|
||||
@@ -15,6 +12,8 @@ interface NavItemProps {
|
||||
color?: string
|
||||
background?: string
|
||||
direction?: "row" | "column"
|
||||
openMenu: string | null,
|
||||
setOpenMenu: Dispatch<SetStateAction<string | null>>,
|
||||
}
|
||||
|
||||
const NavSubItem: FC<NavItemProps> = ({
|
||||
@@ -23,21 +22,18 @@ const NavSubItem: FC<NavItemProps> = ({
|
||||
color,
|
||||
background,
|
||||
submenu,
|
||||
direction = "row"
|
||||
direction = "row",
|
||||
openMenu,
|
||||
setOpenMenu,
|
||||
}) => {
|
||||
const { pathname } = useLocation();
|
||||
|
||||
const [menuTimeout, setMenuTimeout] = useState<NodeJS.Timeout | null>(null);
|
||||
const buttonRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
const {
|
||||
value: openSubmenu,
|
||||
setFalse: handleCloseSubmenu,
|
||||
setTrue: setOpenSubmenu,
|
||||
} = useBoolean(false);
|
||||
const openSubmenu = openMenu === label;
|
||||
const handleCloseSubmenu = () => setOpenMenu(prev => (prev === label ? null : prev));
|
||||
|
||||
const handleOpenSubmenu = () => {
|
||||
if (direction === "row" || !openSubmenu) setOpenSubmenu();
|
||||
if (direction === "row" || !openSubmenu) setOpenMenu(label);
|
||||
if (direction === "column" && openSubmenu) handleCloseSubmenu();
|
||||
if (direction === "row" && menuTimeout) clearTimeout(menuTimeout);
|
||||
};
|
||||
@@ -52,10 +48,6 @@ const NavSubItem: FC<NavItemProps> = ({
|
||||
if (menuTimeout) clearTimeout(menuTimeout);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
handleCloseSubmenu();
|
||||
}, [pathname]);
|
||||
|
||||
return (
|
||||
<div
|
||||
className={classNames({
|
||||
|
||||
222
apptest/tests/max_backfill_age_test.go
Normal file
222
apptest/tests/max_backfill_age_test.go
Normal file
@@ -0,0 +1,222 @@
|
||||
package tests
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/VictoriaMetrics/VictoriaMetrics/apptest"
|
||||
)
|
||||
|
||||
func TestSingleMaxBackfillAge(t *testing.T) {
|
||||
tc := apptest.NewTestCase(t)
|
||||
defer tc.Stop()
|
||||
|
||||
opts := maxBackfillAgeOpts{
|
||||
start: func(retentionPeriod, maxBackfillAge string) apptest.PrometheusWriteQuerier {
|
||||
return tc.MustStartVmsingle("vmsingle", []string{
|
||||
"-storageDataPath=" + filepath.Join(tc.Dir(), "vmsingle"),
|
||||
"-retentionPeriod=" + retentionPeriod,
|
||||
"-maxBackfillAge=" + maxBackfillAge,
|
||||
})
|
||||
},
|
||||
stop: func() {
|
||||
tc.StopApp("vmsingle")
|
||||
},
|
||||
}
|
||||
|
||||
testMaxBackfillAge(tc, opts)
|
||||
}
|
||||
|
||||
func TestClusterMaxBackfillAge(t *testing.T) {
|
||||
tc := apptest.NewTestCase(t)
|
||||
defer tc.Stop()
|
||||
|
||||
opts := maxBackfillAgeOpts{
|
||||
start: func(retentionPeriod, maxBackfillAge string) apptest.PrometheusWriteQuerier {
|
||||
return tc.MustStartCluster(&apptest.ClusterOptions{
|
||||
Vmstorage1Instance: "vmstorage1",
|
||||
Vmstorage1Flags: []string{
|
||||
"-storageDataPath=" + filepath.Join(tc.Dir(), "vmstorage1"),
|
||||
"-retentionPeriod=" + retentionPeriod,
|
||||
"-maxBackfillAge=" + maxBackfillAge,
|
||||
},
|
||||
Vmstorage2Instance: "vmstorage2",
|
||||
Vmstorage2Flags: []string{
|
||||
"-storageDataPath=" + filepath.Join(tc.Dir(), "vmstorage2"),
|
||||
"-retentionPeriod=" + retentionPeriod,
|
||||
"-maxBackfillAge=" + maxBackfillAge,
|
||||
},
|
||||
VminsertInstance: "vminsert",
|
||||
VminsertFlags: []string{},
|
||||
VmselectInstance: "vmselect",
|
||||
VmselectFlags: []string{},
|
||||
})
|
||||
},
|
||||
stop: func() {
|
||||
tc.StopApp("vminsert")
|
||||
tc.StopApp("vmselect")
|
||||
tc.StopApp("vmstorage1")
|
||||
tc.StopApp("vmstorage2")
|
||||
},
|
||||
}
|
||||
|
||||
testMaxBackfillAge(tc, opts)
|
||||
}
|
||||
|
||||
type maxBackfillAgeOpts struct {
|
||||
start func(retentionPeriod, maxBackfillAge string) apptest.PrometheusWriteQuerier
|
||||
stop func()
|
||||
}
|
||||
|
||||
func testMaxBackfillAge(tc *apptest.TestCase, opts maxBackfillAgeOpts) {
|
||||
t := tc.T()
|
||||
|
||||
assertSeries := func(app apptest.PrometheusQuerier, prefix string, start, end int64, want []map[string]string) {
|
||||
t.Helper()
|
||||
|
||||
query := fmt.Sprintf(`{__name__=~"metric_%s.*"}`, prefix)
|
||||
tc.Assert(&apptest.AssertOptions{
|
||||
Msg: "unexpected /api/v1/series response",
|
||||
Got: func() any {
|
||||
return app.PrometheusAPIV1Series(t, query, apptest.QueryOpts{
|
||||
Start: fmt.Sprintf("%d", start),
|
||||
End: fmt.Sprintf("%d", end),
|
||||
}).Sort()
|
||||
},
|
||||
Want: &apptest.PrometheusAPIV1SeriesResponse{
|
||||
Status: "success",
|
||||
Data: want,
|
||||
},
|
||||
FailNow: true,
|
||||
})
|
||||
}
|
||||
|
||||
assertQueryResults := func(app apptest.PrometheusQuerier, prefix string, start, end, step int64, want []*apptest.QueryResult) {
|
||||
t.Helper()
|
||||
|
||||
query := fmt.Sprintf(`{__name__=~"metric_%s.*"}`, prefix)
|
||||
tc.Assert(&apptest.AssertOptions{
|
||||
Msg: "unexpected /api/v1/query_range response",
|
||||
Got: func() any {
|
||||
return app.PrometheusAPIV1QueryRange(t, query, apptest.QueryOpts{
|
||||
Start: fmt.Sprintf("%d", start),
|
||||
End: fmt.Sprintf("%d", end),
|
||||
Step: fmt.Sprintf("%dms", step),
|
||||
MaxLookback: fmt.Sprintf("%dms", step-1),
|
||||
NoCache: "1",
|
||||
})
|
||||
},
|
||||
Want: &apptest.PrometheusAPIV1QueryResponse{
|
||||
Status: "success",
|
||||
Data: &apptest.QueryData{
|
||||
ResultType: "matrix",
|
||||
Result: want,
|
||||
},
|
||||
},
|
||||
FailNow: true,
|
||||
})
|
||||
}
|
||||
|
||||
const numMetrics = 1000
|
||||
now := time.Now().UTC()
|
||||
var start, end, step int64
|
||||
emptySeries := []map[string]string{}
|
||||
emptyQueryResults := []*apptest.QueryResult{}
|
||||
|
||||
// Start sut with the same -retentionPeriod and -maxBackfillAge.
|
||||
sut := opts.start("1y", "1y")
|
||||
|
||||
// Verify that samples older than the retention period are rejected.
|
||||
start = now.Add(-365 * 24 * time.Hour).Add(-time.Hour).UnixMilli()
|
||||
end = now.Add(-365 * 24 * time.Hour).UnixMilli()
|
||||
step = (end - start) / numMetrics
|
||||
outsideRetention := genMaxBackfillAgeData("outside_retention", numMetrics, start, step)
|
||||
sut.PrometheusAPIV1ImportPrometheus(t, outsideRetention.samples, apptest.QueryOpts{})
|
||||
sut.ForceFlush(t)
|
||||
assertSeries(sut, "outside_retention", start, end, emptySeries)
|
||||
assertQueryResults(sut, "outside_retention", start, end, step, emptyQueryResults)
|
||||
|
||||
// Verify that samples within the retention period are accepted and
|
||||
// searcheable.
|
||||
start = now.Add(-365 * 24 * time.Hour).Add(time.Hour).UnixMilli()
|
||||
end = now.Add(-365 * 24 * time.Hour).Add(2 * time.Hour).UnixMilli()
|
||||
step = (end - start) / numMetrics
|
||||
insideRetention := genMaxBackfillAgeData("inside_retention", numMetrics, start, step)
|
||||
sut.PrometheusAPIV1ImportPrometheus(t, insideRetention.samples, apptest.QueryOpts{})
|
||||
sut.ForceFlush(t)
|
||||
assertSeries(sut, "inside_retention", start, end, insideRetention.wantSeries)
|
||||
assertQueryResults(sut, "inside_retention", start, end, step, insideRetention.wantQueryResults)
|
||||
|
||||
// Restart sut with -maxBackfillAge shorter than the -retentionPeriod.
|
||||
opts.stop()
|
||||
sut = opts.start("1y", "6M")
|
||||
|
||||
// Verify that new samples older than max backfill age but still within the
|
||||
// retention period are rejected but existing samples are still searcheable.
|
||||
start = now.Add(-365 * 24 * time.Hour).Add(time.Hour).UnixMilli()
|
||||
end = now.Add(-365 * 24 * time.Hour).Add(2 * time.Hour).UnixMilli()
|
||||
step = (end - start) / numMetrics
|
||||
insideRetention2 := genMaxBackfillAgeData("inside_retention2", numMetrics, start, step)
|
||||
sut.PrometheusAPIV1ImportPrometheus(t, insideRetention2.samples, apptest.QueryOpts{})
|
||||
sut.ForceFlush(t)
|
||||
assertSeries(sut, "inside_retention2", start, end, emptySeries)
|
||||
assertQueryResults(sut, "inside_retention2", start, end, step, emptyQueryResults)
|
||||
assertSeries(sut, "inside_retention", start, end, insideRetention.wantSeries)
|
||||
assertQueryResults(sut, "inside_retention", start, end, step, insideRetention.wantQueryResults)
|
||||
|
||||
// Verify that the metrics that are outside the backfill window can still
|
||||
// be deleted.
|
||||
sut.PrometheusAPIV1AdminTSDBDeleteSeries(t, `{__name__=~".*inside_retention.*"}`, apptest.QueryOpts{})
|
||||
sut.ForceFlush(t)
|
||||
assertSeries(sut, "inside_retention", start, end, emptySeries)
|
||||
assertQueryResults(sut, "inside_retention", start, end, step, emptyQueryResults)
|
||||
|
||||
// Verify that the samples that are within the backfill window are accepted
|
||||
// and searchable.
|
||||
start = now.Add(-180 * 24 * time.Hour).UnixMilli()
|
||||
end = now.Add(-180 * 24 * time.Hour).Add(1 * time.Hour).UnixMilli()
|
||||
step = (end - start) / numMetrics
|
||||
insideMaxBackfillAge := genMaxBackfillAgeData("inside_max_backfill_age", numMetrics, start, step)
|
||||
sut.PrometheusAPIV1ImportPrometheus(t, insideMaxBackfillAge.samples, apptest.QueryOpts{})
|
||||
sut.ForceFlush(t)
|
||||
assertSeries(sut, "inside_max_backfill_age", start, end, insideMaxBackfillAge.wantSeries)
|
||||
assertQueryResults(sut, "inside_max_backfill_age", start, end, step, insideMaxBackfillAge.wantQueryResults)
|
||||
|
||||
opts.stop()
|
||||
}
|
||||
|
||||
type maxBackfillAgeData struct {
|
||||
samples []string
|
||||
wantSeries []map[string]string
|
||||
wantQueryResults []*apptest.QueryResult
|
||||
}
|
||||
|
||||
func genMaxBackfillAgeData(prefix string, numMetrics, start, step int64) maxBackfillAgeData {
|
||||
samples := make([]string, numMetrics)
|
||||
wantSeries := make([]map[string]string, numMetrics)
|
||||
wantQueryResults := make([]*apptest.QueryResult, numMetrics)
|
||||
for i := range numMetrics {
|
||||
metricName := fmt.Sprintf("metric_%s_%04d", prefix, i)
|
||||
labelName := fmt.Sprintf("label_%s_%04d", prefix, i)
|
||||
labelValue := fmt.Sprintf("value_%s_%04d", prefix, i)
|
||||
value := i
|
||||
timestamp := start + i*step
|
||||
samples[i] = fmt.Sprintf(`%s{%s="value", label="%s"} %d %d`, metricName, labelName, labelValue, value, timestamp)
|
||||
wantSeries[i] = map[string]string{
|
||||
"__name__": metricName,
|
||||
labelName: "value",
|
||||
"label": labelValue,
|
||||
}
|
||||
wantQueryResults[i] = &apptest.QueryResult{
|
||||
Metric: map[string]string{
|
||||
"__name__": metricName,
|
||||
labelName: "value",
|
||||
"label": labelValue,
|
||||
},
|
||||
Samples: []*apptest.Sample{{Timestamp: timestamp, Value: float64(value)}},
|
||||
}
|
||||
}
|
||||
return maxBackfillAgeData{samples, wantSeries, wantQueryResults}
|
||||
}
|
||||
@@ -3,7 +3,7 @@ services:
|
||||
# It scrapes targets defined in --promscrape.config
|
||||
# And forward them to --remoteWrite.url
|
||||
vmagent:
|
||||
image: victoriametrics/vmagent:v1.146.0
|
||||
image: victoriametrics/vmagent:v1.147.0
|
||||
depends_on:
|
||||
- "vmauth"
|
||||
ports:
|
||||
@@ -42,14 +42,14 @@ services:
|
||||
# vmstorage shards. Each shard receives 1/N of all metrics sent to vminserts,
|
||||
# where N is number of vmstorages (2 in this case).
|
||||
vmstorage-1:
|
||||
image: victoriametrics/vmstorage:v1.146.0-cluster
|
||||
image: victoriametrics/vmstorage:v1.147.0-cluster
|
||||
volumes:
|
||||
- strgdata-1:/storage
|
||||
command:
|
||||
- "--storageDataPath=/storage"
|
||||
restart: always
|
||||
vmstorage-2:
|
||||
image: victoriametrics/vmstorage:v1.146.0-cluster
|
||||
image: victoriametrics/vmstorage:v1.147.0-cluster
|
||||
volumes:
|
||||
- strgdata-2:/storage
|
||||
command:
|
||||
@@ -59,7 +59,7 @@ services:
|
||||
# vminsert is ingestion frontend. It receives metrics pushed by vmagent,
|
||||
# pre-process them and distributes across configured vmstorage shards.
|
||||
vminsert-1:
|
||||
image: victoriametrics/vminsert:v1.146.0-cluster
|
||||
image: victoriametrics/vminsert:v1.147.0-cluster
|
||||
depends_on:
|
||||
- "vmstorage-1"
|
||||
- "vmstorage-2"
|
||||
@@ -68,7 +68,7 @@ services:
|
||||
- "--storageNode=vmstorage-2:8400"
|
||||
restart: always
|
||||
vminsert-2:
|
||||
image: victoriametrics/vminsert:v1.146.0-cluster
|
||||
image: victoriametrics/vminsert:v1.147.0-cluster
|
||||
depends_on:
|
||||
- "vmstorage-1"
|
||||
- "vmstorage-2"
|
||||
@@ -80,7 +80,7 @@ services:
|
||||
# vmselect is a query fronted. It serves read queries in MetricsQL or PromQL.
|
||||
# vmselect collects results from configured `--storageNode` shards.
|
||||
vmselect-1:
|
||||
image: victoriametrics/vmselect:v1.146.0-cluster
|
||||
image: victoriametrics/vmselect:v1.147.0-cluster
|
||||
depends_on:
|
||||
- "vmstorage-1"
|
||||
- "vmstorage-2"
|
||||
@@ -90,7 +90,7 @@ services:
|
||||
- "--vmalert.proxyURL=http://vmalert:8880"
|
||||
restart: always
|
||||
vmselect-2:
|
||||
image: victoriametrics/vmselect:v1.146.0-cluster
|
||||
image: victoriametrics/vmselect:v1.147.0-cluster
|
||||
depends_on:
|
||||
- "vmstorage-1"
|
||||
- "vmstorage-2"
|
||||
@@ -105,7 +105,7 @@ services:
|
||||
# read requests from Grafana, vmui, vmalert among vmselects.
|
||||
# It can be used as an authentication proxy.
|
||||
vmauth:
|
||||
image: victoriametrics/vmauth:v1.146.0
|
||||
image: victoriametrics/vmauth:v1.147.0
|
||||
depends_on:
|
||||
- "vmselect-1"
|
||||
- "vmselect-2"
|
||||
@@ -119,7 +119,7 @@ services:
|
||||
|
||||
# vmalert executes alerting and recording rules
|
||||
vmalert:
|
||||
image: victoriametrics/vmalert:v1.146.0
|
||||
image: victoriametrics/vmalert:v1.147.0
|
||||
depends_on:
|
||||
- "vmauth"
|
||||
ports:
|
||||
|
||||
@@ -3,7 +3,7 @@ services:
|
||||
# It scrapes targets defined in --promscrape.config
|
||||
# And forward them to --remoteWrite.url
|
||||
vmagent:
|
||||
image: victoriametrics/vmagent:v1.146.0
|
||||
image: victoriametrics/vmagent:v1.147.0
|
||||
depends_on:
|
||||
- "victoriametrics"
|
||||
ports:
|
||||
@@ -18,7 +18,7 @@ services:
|
||||
# VictoriaMetrics instance, a single process responsible for
|
||||
# storing metrics and serve read requests.
|
||||
victoriametrics:
|
||||
image: victoriametrics/victoria-metrics:v1.146.0
|
||||
image: victoriametrics/victoria-metrics:v1.147.0
|
||||
ports:
|
||||
- 8428:8428
|
||||
- 8089:8089
|
||||
@@ -59,7 +59,7 @@ services:
|
||||
|
||||
# vmalert executes alerting and recording rules
|
||||
vmalert:
|
||||
image: victoriametrics/vmalert:v1.146.0
|
||||
image: victoriametrics/vmalert:v1.147.0
|
||||
depends_on:
|
||||
- "victoriametrics"
|
||||
- "alertmanager"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
services:
|
||||
vmagent:
|
||||
image: victoriametrics/vmagent:v1.146.0
|
||||
image: victoriametrics/vmagent:v1.147.0
|
||||
depends_on:
|
||||
- "victoriametrics"
|
||||
ports:
|
||||
@@ -14,7 +14,7 @@ services:
|
||||
restart: always
|
||||
|
||||
victoriametrics:
|
||||
image: victoriametrics/victoria-metrics:v1.146.0
|
||||
image: victoriametrics/victoria-metrics:v1.147.0
|
||||
ports:
|
||||
- 8428:8428
|
||||
volumes:
|
||||
@@ -40,7 +40,7 @@ services:
|
||||
restart: always
|
||||
|
||||
vmalert:
|
||||
image: victoriametrics/vmalert:v1.146.0
|
||||
image: victoriametrics/vmalert:v1.147.0
|
||||
depends_on:
|
||||
- "victoriametrics"
|
||||
ports:
|
||||
|
||||
@@ -10,9 +10,9 @@ sitemap:
|
||||
|
||||
- To use *vmanomaly*, part of the enterprise package, a license key is required. Obtain your key [here](https://victoriametrics.com/products/enterprise/trial/) for this tutorial or for enterprise use.
|
||||
- In the tutorial, we'll be using the following VictoriaMetrics components:
|
||||
- [VictoriaMetrics Single-Node](https://docs.victoriametrics.com/victoriametrics/single-server-victoriametrics/) (v1.146.0)
|
||||
- [vmalert](https://docs.victoriametrics.com/victoriametrics/vmalert/) (v1.146.0)
|
||||
- [vmagent](https://docs.victoriametrics.com/victoriametrics/vmagent/) (v1.146.0)
|
||||
- [VictoriaMetrics Single-Node](https://docs.victoriametrics.com/victoriametrics/single-server-victoriametrics/) (v1.147.0)
|
||||
- [vmalert](https://docs.victoriametrics.com/victoriametrics/vmalert/) (v1.147.0)
|
||||
- [vmagent](https://docs.victoriametrics.com/victoriametrics/vmagent/) (v1.147.0)
|
||||
- [Grafana](https://grafana.com/) (v12.2.0)
|
||||
- [Docker](https://docs.docker.com/get-docker/) and [Docker Compose](https://docs.docker.com/compose/)
|
||||
- [Node exporter](https://github.com/prometheus/node_exporter#node-exporter) (v1.9.1) and [Alertmanager](https://prometheus.io/docs/alerting/latest/alertmanager/) (v0.28.1)
|
||||
@@ -323,7 +323,7 @@ Let's wrap it all up together into the `docker-compose.yml` file.
|
||||
services:
|
||||
vmagent:
|
||||
container_name: vmagent
|
||||
image: victoriametrics/vmagent:v1.146.0
|
||||
image: victoriametrics/vmagent:v1.147.0
|
||||
depends_on:
|
||||
- "victoriametrics"
|
||||
ports:
|
||||
@@ -340,7 +340,7 @@ services:
|
||||
|
||||
victoriametrics:
|
||||
container_name: victoriametrics
|
||||
image: victoriametrics/victoria-metrics:v1.146.0
|
||||
image: victoriametrics/victoria-metrics:v1.147.0
|
||||
ports:
|
||||
- 8428:8428
|
||||
volumes:
|
||||
@@ -373,7 +373,7 @@ services:
|
||||
|
||||
vmalert:
|
||||
container_name: vmalert
|
||||
image: victoriametrics/vmalert:v1.146.0
|
||||
image: victoriametrics/vmalert:v1.147.0
|
||||
depends_on:
|
||||
- "victoriametrics"
|
||||
ports:
|
||||
|
||||
@@ -240,23 +240,23 @@ vmagent will write data into VictoriaMetrics single-node and cluster (with tenan
|
||||
# compose.yaml
|
||||
services:
|
||||
vmsingle:
|
||||
image: victoriametrics/victoria-metrics:v1.146.0
|
||||
image: victoriametrics/victoria-metrics:v1.147.0
|
||||
|
||||
vmstorage:
|
||||
image: victoriametrics/vmstorage:v1.146.0-cluster
|
||||
image: victoriametrics/vmstorage:v1.147.0-cluster
|
||||
|
||||
vminsert:
|
||||
image: victoriametrics/vminsert:v1.146.0-cluster
|
||||
image: victoriametrics/vminsert:v1.147.0-cluster
|
||||
command:
|
||||
- -storageNode=vmstorage:8400
|
||||
|
||||
vmselect:
|
||||
image: victoriametrics/vmselect:v1.146.0-cluster
|
||||
image: victoriametrics/vmselect:v1.147.0-cluster
|
||||
command:
|
||||
- -storageNode=vmstorage:8401
|
||||
|
||||
vmagent:
|
||||
image: victoriametrics/vmagent:v1.146.0
|
||||
image: victoriametrics/vmagent:v1.147.0
|
||||
volumes:
|
||||
- ./scrape.yaml:/etc/vmagent/config.yaml
|
||||
command:
|
||||
@@ -308,7 +308,7 @@ Now add the vmauth service to `compose.yaml`:
|
||||
# compose.yaml
|
||||
services:
|
||||
vmauth:
|
||||
image: docker.io/victoriametrics/vmauth:v1.146.0
|
||||
image: docker.io/victoriametrics/vmauth:v1.147.0
|
||||
ports:
|
||||
- 8427:8427
|
||||
volumes:
|
||||
|
||||
@@ -155,15 +155,15 @@ These services will store and query the metrics scraped by vmagent.
|
||||
# compose.yaml
|
||||
services:
|
||||
vmstorage:
|
||||
image: victoriametrics/vmstorage:v1.146.0-cluster
|
||||
image: victoriametrics/vmstorage:v1.147.0-cluster
|
||||
|
||||
vminsert:
|
||||
image: victoriametrics/vminsert:v1.146.0-cluster
|
||||
image: victoriametrics/vminsert:v1.147.0-cluster
|
||||
command:
|
||||
- -storageNode=vmstorage:8400
|
||||
|
||||
vmselect:
|
||||
image: victoriametrics/vmselect:v1.146.0-cluster
|
||||
image: victoriametrics/vmselect:v1.147.0-cluster
|
||||
command:
|
||||
- -storageNode=vmstorage:8401
|
||||
ports:
|
||||
@@ -196,7 +196,7 @@ Add the vmauth service to `compose.yaml`:
|
||||
# compose.yaml
|
||||
services:
|
||||
vmauth:
|
||||
image: victoriametrics/vmauth:v1.146.0-enterprise
|
||||
image: victoriametrics/vmauth:v1.147.0-enterprise
|
||||
ports:
|
||||
- 8427:8427
|
||||
volumes:
|
||||
@@ -251,7 +251,7 @@ Add the vmagent service to `compose.yaml` with OAuth2 configuration:
|
||||
# compose.yaml
|
||||
services:
|
||||
vmagent:
|
||||
image: victoriametrics/vmagent:v1.146.0
|
||||
image: victoriametrics/vmagent:v1.147.0
|
||||
volumes:
|
||||
- ./scrape.yaml:/etc/vmagent/config.yaml
|
||||
command:
|
||||
|
||||
@@ -107,7 +107,7 @@ The final piece is the Docker Compose file. This ties all the services together
|
||||
# compose.yml
|
||||
services:
|
||||
victoriametrics:
|
||||
image: victoriametrics/victoria-metrics:v1.146.0
|
||||
image: victoriametrics/victoria-metrics:v1.147.0
|
||||
command:
|
||||
- "--storageDataPath=/victoria-metrics-data"
|
||||
- "--selfScrapeInterval=10s"
|
||||
@@ -128,7 +128,7 @@ services:
|
||||
- ./alertmanager.yml:/etc/alertmanager/alertmanager.yml:ro
|
||||
|
||||
vmalert:
|
||||
image: victoriametrics/vmalert:v1.146.0
|
||||
image: victoriametrics/vmalert:v1.147.0
|
||||
depends_on:
|
||||
- victoriametrics
|
||||
- alertmanager
|
||||
|
||||
@@ -154,6 +154,13 @@ See [our blog](https://victoriametrics.com/blog) for the latest articles written
|
||||
* [Why irate from Prometheus doesn't capture spikes](https://valyala.medium.com/why-irate-from-prometheus-doesnt-capture-spikes-45f9896d7832)
|
||||
* [VictoriaMetrics: PromQL compliance](https://medium.com/@romanhavronenko/victoriametrics-promql-compliance-d4318203f51e)
|
||||
* [How do open source solutions for logs work: Elasticsearch, Loki and VictoriaLogs](https://itnext.io/how-do-open-source-solutions-for-logs-work-elasticsearch-loki-and-victorialogs-9f7097ecbc2f)
|
||||
* [How vmagent Collects and Ships Metrics Fast with Aggregation, Deduplication, and More](https://victoriametrics.com/blog/vmagent-how-it-works/)
|
||||
* [When Metrics Meet vminsert: A Data-Delivery Story](https://victoriametrics.com/blog/vminsert-how-it-works/)
|
||||
* [How vmstorage Handles Data Ingestion From vminsert](https://victoriametrics.com/blog/vmstorage-how-it-handles-data-ingestion/)
|
||||
* [How vmstorage Processes Data: Retention, Merging, Deduplication,...](https://victoriametrics.com/blog/vmstorage-retention-merging-deduplication/)
|
||||
* [How vmstorage's IndexDB Works](https://victoriametrics.com/blog/vmstorage-how-indexdb-works/)
|
||||
* [How vmstorage Handles Query Requests From vmselect](https://victoriametrics.com/blog/vmstorage-how-it-handles-query-requests/)
|
||||
* [Inside vmselect: The Query Processing Engine of VictoriaMetrics](https://victoriametrics.com/blog/vmselect-how-it-works/)
|
||||
|
||||
### Tutorials, guides and how-to articles
|
||||
|
||||
@@ -171,6 +178,12 @@ See [our guides](https://docs.victoriametrics.com/guides/) for the up-to-date gu
|
||||
* [Prometheus storage: tech terms for humans](https://valyala.medium.com/prometheus-storage-technical-terms-for-humans-4ab4de6c3d48)
|
||||
* [Cardinality explorer](https://victoriametrics.com/blog/cardinality-explorer/)
|
||||
* [Rules backfilling via vmalert](https://victoriametrics.com/blog/rules-replay/)
|
||||
* [vmagent: Key Features Explained in Under 15 Minutes](https://victoriametrics.com/blog/vmagent-key-features-explained/)
|
||||
* [Prometheus Metrics Explained: Counters, Gauges, Histograms & Summaries](https://victoriametrics.com/blog/prometheus-monitoring-metrics-counters-gauges-histogram-summaries/)
|
||||
* [Prometheus Monitoring: Instant Queries and Range Queries Explained](https://victoriametrics.com/blog/prometheus-monitoring-instant-range-query/)
|
||||
* [Prometheus Monitoring: Functions, Subqueries, Operators, and Modifiers](https://victoriametrics.com/blog/prometheus-monitoring-function-operator-modifier/)
|
||||
* [Prometheus Alerting 101: Rules, Recording Rules, and Alertmanager](https://victoriametrics.com/blog/alerting-recording-rules-alertmanager/)
|
||||
* [Alerting Best Practices](https://victoriametrics.com/blog/alerting-best-practices/)
|
||||
|
||||
### Other articles
|
||||
|
||||
|
||||
@@ -57,6 +57,10 @@ Each service may scale independently and may run on the most suitable hardware.
|
||||
This is a [shared nothing architecture](https://en.wikipedia.org/wiki/Shared-nothing_architecture).
|
||||
It increases cluster availability, and simplifies cluster maintenance as well as cluster scaling.
|
||||
|
||||
> Further reading, deep dives into how each service works internally:
|
||||
> - `vmstorage`: [How vmstorage Handles Data Ingestion From vminsert](https://victoriametrics.com/blog/vmstorage-how-it-handles-data-ingestion/), [How vmstorage's IndexDB Works](https://victoriametrics.com/blog/vmstorage-how-indexdb-works/), [How vmstorage Handles Query Requests From vmselect](https://victoriametrics.com/blog/vmstorage-how-it-handles-query-requests/).
|
||||
> - `vmselect`: [Inside vmselect: The Query Processing Engine of VictoriaMetrics](https://victoriametrics.com/blog/vmselect-how-it-works/).
|
||||
|
||||

|
||||
|
||||
## vmui
|
||||
@@ -834,7 +838,7 @@ This ensures that incoming metrics are evenly distributed across all `vmstorage`
|
||||
The downside is that a single slow vmstorage node can throttle the entire cluster.
|
||||
|
||||
When `-disableRerouting=false` is enabled on `vminsert`,
|
||||
the cluster will automatically re-route writes away from the slowest vmstorage node to preserve maximum ingestion throughput.
|
||||
the cluster will automatically [re-route writes](https://victoriametrics.com/blog/vminsert-how-it-works/#31-rerouting) away from the slowest vmstorage node to preserve maximum ingestion throughput.
|
||||
|
||||
Re-routing occurs only when all of the following conditions hold:
|
||||
- the storage send buffer is full.
|
||||
@@ -877,7 +881,7 @@ See also [resource usage limits docs](#resource-usage-limits).
|
||||
|
||||
## Rebalancing
|
||||
|
||||
Every `vminsert` node evenly spreads (shards) incoming data among `vmstorage` nodes specified in the `-storageNode` command-line flag.
|
||||
Every `vminsert` node [evenly spreads (shards) incoming data](https://victoriametrics.com/blog/vminsert-how-it-works/#3-sharding-and-buffering) among `vmstorage` nodes specified in the `-storageNode` command-line flag.
|
||||
This guarantees even distribution of the ingested data among `vmstorage` nodes. When new `vmstorage` nodes are added to the `-storageNode`
|
||||
command-line flag at `vminsert`, then only newly ingested data is distributed evenly among old and new `vmstorage` nodes, while
|
||||
historical data remains on the old `vmstorage` nodes. This speeds up data ingestion and querying for the majority of production workloads,
|
||||
@@ -1025,7 +1029,7 @@ By default, VictoriaMetrics offloads replication to the underlying storage point
|
||||
which guarantees data durability. VictoriaMetrics supports application-level replication if replicated durable persistent disks cannot be used for some reason.
|
||||
|
||||
The replication can be enabled by passing `-replicationFactor=N` command-line flag to `vminsert`. This instructs `vminsert` to store `N` copies for every ingested sample
|
||||
on `N` distinct `vmstorage` nodes. This guarantees that all the stored data remains available for querying if up to `N-1` `vmstorage` nodes are unavailable.
|
||||
on `N` distinct `vmstorage` nodes. This guarantees that all the stored data remains available for querying if up to `N-1` `vmstorage` nodes are unavailable. See [how `vminsert` replicates each sample to `N` `vmstorage` nodes](https://victoriametrics.com/blog/vminsert-how-it-works/#4-replication-and-sending-data-to-vmstorage) for details.
|
||||
|
||||
Passing `-replicationFactor=N` command-line flag to `vmselect` instructs it to not mark responses as `partial` if less than `-replicationFactor` vmstorage nodes are unavailable during the query.
|
||||
See [cluster availability docs](#cluster-availability) for details.
|
||||
@@ -1060,7 +1064,7 @@ deduplication can't be guaranteed when samples and sample duplicates for the sam
|
||||
- when `vmstorage` node has no enough capacity for processing incoming data stream. Then `vminsert` re-routes new samples to other `vmstorage` nodes.
|
||||
|
||||
It is recommended to set **the same** `-dedup.minScrapeInterval` command-line flag value to both `vmselect` and `vmstorage` nodes
|
||||
to ensure query results consistency, even if storage layer didn't complete deduplication yet.
|
||||
to ensure query results consistency, even if [storage layer didn't complete deduplication](https://victoriametrics.com/blog/vmstorage-retention-merging-deduplication/#deduplication) yet.
|
||||
|
||||
## Metrics Metadata
|
||||
|
||||
|
||||
@@ -338,13 +338,15 @@ File bugs and feature requests in our [GitHub Issues](https://github.com/Victori
|
||||
## Where can I find information about multi-tenancy?
|
||||
|
||||
See [these docs](https://docs.victoriametrics.com/victoriametrics/cluster-victoriametrics/#multitenancy).
|
||||
Multitenancy is supported only by the [cluster version](https://docs.victoriametrics.com/victoriametrics/cluster-victoriametrics/) of VictoriaMetrics.
|
||||
Multitenancy is fully supported only by the [cluster version](https://docs.victoriametrics.com/victoriametrics/cluster-victoriametrics/) of VictoriaMetrics.
|
||||
Single-node provides limited [multitenancy](https://docs.victoriametrics.com/victoriametrics/single-server-victoriametrics/#multi-tenancy) support to facilitate
|
||||
the migration [from single-node to cluster](https://docs.victoriametrics.com/victoriametrics/single-server-victoriametrics/#from-single-node-to-cluster).
|
||||
|
||||
## How to set a memory limit for VictoriaMetrics components?
|
||||
|
||||
All VictoriaMetrics components provide command-line flags to control the size of internal buffers and caches:
|
||||
`-memory.allowedPercent` and `-memory.allowedBytes` (pass `-help` to any VictoriaMetrics component in order to see the description for these flags).
|
||||
These limits don't take into account additional memory, which may be needed for processing incoming queries.
|
||||
These limits don't account for additional memory that may be needed to process incoming queries.
|
||||
Hard limits may be enforced only by the OS via [cgroups](https://en.wikipedia.org/wiki/Cgroups),
|
||||
Docker (see [these docs](https://docs.docker.com/config/containers/resource_constraints)) or
|
||||
Kubernetes (see [these docs](https://kubernetes.io/docs/concepts/configuration/manage-resources-containers)).
|
||||
@@ -465,9 +467,13 @@ Cluster version of VictoriaMetrics may be preferred over single-node VictoriaMet
|
||||
|
||||
## How to migrate data from single-node VictoriaMetrics to cluster version?
|
||||
|
||||
The single-node version of VictoriaMetrics stores data on disk in slightly different format compared to the cluster version of VictoriaMetrics.
|
||||
This makes it impossible to just copy the on-disk data from `-storageDataPath` directory from single-node VictoriaMetrics to a `vmstorage` node in VictoriaMetrics cluster.
|
||||
If you need to migrate data from a single-node VictoriaMetrics to the cluster version, then [follow these instructions](https://docs.victoriametrics.com/victoriametrics/vmctl/victoriametrics/).
|
||||
The single-node version of VictoriaMetrics stores data on disk in a slightly different format compared to the cluster version of VictoriaMetrics.
|
||||
This makes it impossible to just copy the on-disk data from the `-storageDataPath` directory from single-node VictoriaMetrics to a `vmstorage` node in a VictoriaMetrics cluster.
|
||||
|
||||
There are two options, however:
|
||||
|
||||
1. Deploy a new cluster next to the existing single-node and let them co-exist until the cluster is filled with new data and the old data in vmsingle becomes outside of the retention period. This option requires no data migration or downtime. See instructions [here](https://docs.victoriametrics.com/victoriametrics/single-server-victoriametrics/#from-single-node-to-cluster).
|
||||
2. If you need to actually migrate data from a single-node VictoriaMetrics to the cluster version (and/or possibly modify it), then follow [these vmctl instructions](https://docs.victoriametrics.com/victoriametrics/vmctl/victoriametrics/).
|
||||
|
||||
## Why isn't MetricsQL 100% compatible with PromQL?
|
||||
|
||||
|
||||
@@ -59,6 +59,7 @@ Other PromQL functionality should work the same in MetricsQL.
|
||||
|
||||
MetricsQL implements [PromQL](https://medium.com/@valyala/promql-tutorial-for-beginners-9ab455142085)
|
||||
and provides additional functionality mentioned below, which is aimed towards solving practical cases.
|
||||
See [operators and modifiers in MetricsQL](https://victoriametrics.com/blog/prometheus-monitoring-function-operator-modifier/#operators--modifiers) for details.
|
||||
Feel free [filing a feature request](https://github.com/VictoriaMetrics/VictoriaMetrics/issues) if you think MetricsQL misses certain useful functionality.
|
||||
|
||||
This functionality can be evaluated at [VictoriaMetrics demo playground](https://play.victoriametrics.com/select/accounting/1/6a716b0f-38bc-4856-90ce-448fd713e3fe/prometheus/graph/)
|
||||
@@ -156,7 +157,7 @@ MetricsQL provides the following functions:
|
||||
|
||||
### Rollup functions
|
||||
|
||||
**Rollup functions** (aka range functions or window functions) calculate rollups over [raw samples](https://docs.victoriametrics.com/victoriametrics/keyconcepts/#raw-samples)
|
||||
**[Rollup functions](https://victoriametrics.com/blog/prometheus-monitoring-function-operator-modifier/#rollup-functions)** (aka range functions or window functions) calculate rollups over [raw samples](https://docs.victoriametrics.com/victoriametrics/keyconcepts/#raw-samples)
|
||||
on the given lookbehind window for the [selected time series](https://docs.victoriametrics.com/victoriametrics/keyconcepts/#filtering).
|
||||
For example, `avg_over_time(temperature[24h])` calculates the average temperature over [raw samples](https://docs.victoriametrics.com/victoriametrics/keyconcepts/#raw-samples) for the last 24 hours.
|
||||
|
||||
@@ -348,6 +349,7 @@ If the lookbehind window is skipped in square brackets, then it is automatically
|
||||
passed to [/api/v1/query_range](https://docs.victoriametrics.com/victoriametrics/keyconcepts/#range-query) or [/api/v1/query](https://docs.victoriametrics.com/victoriametrics/keyconcepts/#instant-query),
|
||||
while `scrape_interval` is the interval between [raw samples](https://docs.victoriametrics.com/victoriametrics/keyconcepts/#raw-samples) for the selected time series.
|
||||
This allows avoiding unexpected gaps on the graph when `step` is smaller than the `scrape_interval`.
|
||||
See [range vector selectors](https://victoriametrics.com/blog/prometheus-monitoring-instant-range-query/#range-vector-selector) for details.
|
||||
|
||||
#### delta
|
||||
|
||||
@@ -1110,7 +1112,7 @@ See also [zscore](#zscore), [range_trim_zscore](#range_trim_zscore) and [outlier
|
||||
|
||||
### Transform functions
|
||||
|
||||
**Transform functions** calculate transformations over [rollup results](#rollup-functions).
|
||||
**[Transform functions](https://victoriametrics.com/blog/prometheus-monitoring-function-operator-modifier/#transformation-functions)** calculate transformations over [rollup results](#rollup-functions).
|
||||
For example, `abs(delta(temperature[24h]))` calculates the absolute value for every point of every time series
|
||||
returned from the rollup `delta(temperature[24h])`.
|
||||
|
||||
@@ -1846,7 +1848,7 @@ This function is supported by PromQL.
|
||||
|
||||
### Label manipulation functions
|
||||
|
||||
**Label manipulation functions** perform manipulations with labels on the selected [rollup results](#rollup-functions).
|
||||
**[Label manipulation functions](https://victoriametrics.com/blog/prometheus-monitoring-function-operator-modifier/#label-manipulation-functions)** perform manipulations with labels on the selected [rollup results](#rollup-functions).
|
||||
|
||||
Additional details:
|
||||
|
||||
@@ -2016,7 +2018,7 @@ See also [sort_by_label_numeric](#sort_by_label_numeric) and [sort_by_label_desc
|
||||
|
||||
### Aggregate functions
|
||||
|
||||
**Aggregate functions** calculate aggregates over groups of [rollup results](#rollup-functions).
|
||||
**[Aggregate functions](https://victoriametrics.com/blog/prometheus-monitoring-function-operator-modifier/#aggregation-functions)** calculate aggregates over groups of [rollup results](#rollup-functions).
|
||||
|
||||
Additional details:
|
||||
|
||||
@@ -2339,7 +2341,7 @@ See also [zscore_over_time](#zscore_over_time), [range_trim_zscore](#range_trim_
|
||||
|
||||
## Subqueries
|
||||
|
||||
MetricsQL supports and extends PromQL subqueries. See [this article](https://valyala.medium.com/prometheus-subqueries-in-victoriametrics-9b1492b720b3) for details.
|
||||
MetricsQL supports and extends PromQL [subqueries](https://victoriametrics.com/blog/prometheus-monitoring-function-operator-modifier/#subqueries). See [this article](https://valyala.medium.com/prometheus-subqueries-in-victoriametrics-9b1492b720b3) for details.
|
||||
Any [rollup function](#rollup-functions) for something other than [series selector](https://docs.victoriametrics.com/victoriametrics/keyconcepts/#filtering) form a subquery.
|
||||
Nested rollup functions can be implicit thanks to the [implicit query conversions](#implicit-query-conversions).
|
||||
For example, `delta(sum(m))` is implicitly converted to `delta(sum(default_rollup(m))[1i:1i])`, so it becomes a subquery,
|
||||
|
||||
@@ -60,9 +60,9 @@ Download the newest available [VictoriaMetrics release](https://docs.victoriamet
|
||||
from [DockerHub](https://hub.docker.com/r/victoriametrics/victoria-metrics) or [Quay](https://quay.io/repository/victoriametrics/victoria-metrics?tab=tags):
|
||||
|
||||
```sh
|
||||
docker pull victoriametrics/victoria-metrics:v1.146.0
|
||||
docker pull victoriametrics/victoria-metrics:v1.147.0
|
||||
docker run -it --rm -v `pwd`/victoria-metrics-data:/victoria-metrics-data -p 8428:8428 \
|
||||
victoriametrics/victoria-metrics:v1.146.0 --selfScrapeInterval=5s -storageDataPath=victoria-metrics-data
|
||||
victoriametrics/victoria-metrics:v1.147.0 --selfScrapeInterval=5s -storageDataPath=victoria-metrics-data
|
||||
```
|
||||
|
||||
_For Enterprise images, see [this link](https://docs.victoriametrics.com/victoriametrics/enterprise/#docker-images)._
|
||||
|
||||
@@ -403,6 +403,9 @@ Resources:
|
||||
* [Cardinality explorer blog post](https://victoriametrics.com/blog/cardinality-explorer/).
|
||||
* [skills/victoriametrics-cardinality-analysis](https://github.com/VictoriaMetrics/skills/blob/main/plugins/diagnostics/skills/victoriametrics-cardinality-analysis/SKILL.md) for [agent-assisted](https://docs.victoriametrics.com/ai-tools/#agent-skills) analysis.
|
||||
|
||||
For monitoring or alerting on cardinality, use [vmestimator](https://docs.victoriametrics.com/victoriametrics/vmestimator/).
|
||||
vmestimator measures metrics cardinality across [arbitrary label dimensions](https://docs.victoriametrics.com/victoriametrics/vmestimator/#basic) in real time and exposes the [results as metrics](https://docs.victoriametrics.com/victoriametrics/vmestimator/#cardinality-metrics).
|
||||
|
||||
### Cardinality explorer statistic inaccuracy
|
||||
|
||||
In [cluster version of VictoriaMetrics](https://docs.victoriametrics.com/victoriametrics/cluster-victoriametrics/) each vmstorage tracks the stored time series individually.
|
||||
@@ -1311,6 +1314,7 @@ per each `-dedup.minScrapeInterval` discrete interval if `-dedup.minScrapeInterv
|
||||
For example, `-dedup.minScrapeInterval=60s` would leave a single raw sample with the biggest timestamp per each discrete
|
||||
`60s` interval.
|
||||
This aligns with the [staleness rules in Prometheus](https://prometheus.io/docs/prometheus/latest/querying/basics/#staleness).
|
||||
See [how deduplication works](https://victoriametrics.com/blog/vmstorage-retention-merging-deduplication/#deduplication) for details.
|
||||
|
||||
If multiple raw samples have **the same timestamp** on the given `-dedup.minScrapeInterval` discrete interval,
|
||||
then the sample with **the biggest value** is kept.
|
||||
@@ -1394,7 +1398,7 @@ in separate files under `part` directory - `timestamps.bin` and `values.bin`.
|
||||
The `part` directory also contains `index.bin` and `metaindex.bin` files - these files contain index
|
||||
for fast block lookups, which belong to the given `TSID` and cover the given time range.
|
||||
|
||||
`Parts` are periodically merged into bigger parts in background. The background merge provides the following benefits:
|
||||
`Parts` are periodically merged into bigger parts in background. The [background merge](https://victoriametrics.com/blog/vmstorage-retention-merging-deduplication/#merge-process) provides the following benefits:
|
||||
|
||||
* keeping the number of data files under control, so they don't exceed limits on open files
|
||||
* improved data compression, since bigger parts are usually compressed better than smaller parts
|
||||
@@ -1529,6 +1533,7 @@ are **eventually deleted** during [background merge](https://medium.com/@valyala
|
||||
The time range covered by data part is **not limited by retention period unit**. One data part can cover hours or days of
|
||||
data. Hence, a data part can be deleted only **when fully outside the configured retention**.
|
||||
See more about partitions and parts in the [Storage section](#storage).
|
||||
See [how retention frees disk space](https://victoriametrics.com/blog/vmstorage-retention-merging-deduplication/#retention-free-disk-space-guard-and-downsampling) for details.
|
||||
|
||||
The maximum disk space usage for a given `-retentionPeriod` is going to be (`-retentionPeriod` + 1) months.
|
||||
For example, if `-retentionPeriod` is set to 1, data for January is deleted on March 1st.
|
||||
@@ -1548,6 +1553,18 @@ For example, the following command starts VictoriaMetrics, which accepts samples
|
||||
/path/to/victoria-metrics -futureRetention=1y
|
||||
```
|
||||
|
||||
By default, VictoriaMetrics accepts samples with timestamps as old as the configured `-retentionPeriod` allows, e.g. it accepts backfilled
|
||||
historical data as long as it fits into the retention. If you need rejecting samples with historical timestamps older than the specified
|
||||
duration, then specify the desired duration via the `-maxBackfillAge` command-line flag. This can be useful for limiting ingestion of
|
||||
historical samples, for example, when older data has been moved to another storage tier (nvme/hdd, hot/cold).
|
||||
`-maxBackfillAge` cannot exceed the configured `-retentionPeriod` - bigger values are automatically clamped to `-retentionPeriod`.
|
||||
|
||||
For example, the following command starts VictoriaMetrics, which rejects ingested samples with timestamps older than 2 days:
|
||||
|
||||
```sh
|
||||
/path/to/victoria-metrics -maxBackfillAge=2d
|
||||
```
|
||||
|
||||
### Multiple retentions
|
||||
|
||||
Distinct retentions for distinct time series can be configured via [retention filters](#retention-filters)
|
||||
@@ -1619,6 +1636,7 @@ See how to request a [free trial license](https://victoriametrics.com/products/e
|
||||
This command-line flag instructs leaving the last sample per each `interval` for [time series](https://docs.victoriametrics.com/victoriametrics/keyconcepts/#time-series)
|
||||
[samples](https://docs.victoriametrics.com/victoriametrics/keyconcepts/#raw-samples) older than the `offset`. The `offset` must be a multiple of `interval`. For example, `-downsampling.period=30d:5m` instructs leaving the last sample
|
||||
per each 5-minute interval for samples older than 30 days, while the rest of samples are dropped.
|
||||
See [Enterprise downsampling internals](https://victoriametrics.com/blog/vmstorage-retention-merging-deduplication/#retention-filters-and-downsampling-enterprise-plan) for details.
|
||||
|
||||
The `-downsampling.period` command-line flag can be specified multiple times in order to apply different downsampling levels for different time ranges (aka multi-level downsampling).
|
||||
For example, `-downsampling.period=30d:5m,180d:1h` instructs leaving the last sample per each 5-minute interval for samples older than 30 days,
|
||||
@@ -1682,9 +1700,46 @@ See also [retention filters](#retention-filters).
|
||||
The downsampling can be evaluated for free by downloading and using enterprise binaries from [the releases page](https://github.com/VictoriaMetrics/VictoriaMetrics/releases/latest).
|
||||
See how to request a [free trial license](https://victoriametrics.com/products/enterprise/trial/).
|
||||
|
||||
## Multi-tenancy
|
||||
## Multitenancy {#multi-tenancy}
|
||||
|
||||
Single-node VictoriaMetrics doesn't support multi-tenancy. Use the [cluster version](https://docs.victoriametrics.com/victoriametrics/cluster-victoriametrics/#multitenancy) instead.
|
||||
Single-node VictoriaMetrics has limited
|
||||
[multitenancy](https://docs.victoriametrics.com/victoriametrics/cluster-victoriametrics/#multitenancy)
|
||||
support {{% available_from "v1.147.0" %}}. Specifically, a single-node can serve
|
||||
multitenant queries as if it were a `vmstorage`. The write path is not supported.
|
||||
|
||||
The functionality is disabled by default and can be enabled by setting the
|
||||
`-vmselectAddr` flag. This will start the `vmselect RPC server` that accepts
|
||||
requests and serves responses in cluster format.
|
||||
|
||||
Cluster data format assumes the presence of a `tenantID`. Single-node data
|
||||
format still does not support multitenancy, but it is possible to configure the
|
||||
single-node to specify which `tenantID` the single-node's data corresponds to with the
|
||||
`-accountID` and `-projectID` flags. Both are `0` by default, which means that
|
||||
`"0:0"` `tenantID` is used by default.
|
||||
|
||||
For example, the following command will start a single-node that listens for
|
||||
vmselect RPC requests on the `8401` port. The requests must be either `multitenant`
|
||||
(i.e., want data for all tenants) or for `"12:34"` tenant. Otherwise, the
|
||||
single-node will return an empty result:
|
||||
|
||||
```shell
|
||||
./victoria-metrics -storageDataPath=/data -vmselectAddr=:8401 -accountID=12 -projectID=34
|
||||
```
|
||||
|
||||
The `tenantID` configuration is not persisted in any way and is enforced only at
|
||||
runtime. Thus, it is safe to change the `-accountID` and `-projectID` flag
|
||||
values at any time.
|
||||
|
||||
Note that the single-node's HTTP handlers still do not support multitenancy.
|
||||
|
||||
The purpose of this limited multitenancy support is enabling the single-node to
|
||||
operate in VictoriaMetrics cluster setups. I.e., one or more single-nodes that
|
||||
contain data for different tenants can be a part of a cluster, and the entire
|
||||
non-homogeneous deployment can be queried with a higher-level `vmselect`.
|
||||
|
||||
This, in turn, enables easy [migrations from single-node to cluster](https://docs.victoriametrics.com/victoriametrics/single-server-victoriametrics/#from-single-node-to-cluster).
|
||||
Previously, the only option was the use of
|
||||
[vmctl](https://docs.victoriametrics.com/victoriametrics/vmctl/victoriametrics/).
|
||||
|
||||
## Scalability and cluster version
|
||||
|
||||
@@ -2294,6 +2349,30 @@ Things to consider when copying data:
|
||||
For scenarios like single-to-cluster, cluster-to-single, re-sharding or migrating only a fraction of data:
|
||||
[see how to migrate data from VictoriaMetrics via vmctl](https://docs.victoriametrics.com/victoriametrics/vmctl/victoriametrics/).
|
||||
|
||||
### From Single-node to Cluster
|
||||
|
||||
When, for some reason, the deployment needs to be switched from single-node to
|
||||
cluster (such as the single-node can't be scaled vertically anymore, or
|
||||
multitenancy becomes a requirement, etc.) the migration can be as simple as:
|
||||
|
||||
1. Restart the existing single-node with multitenancy support enabled as
|
||||
described in [Multitenancy](https://docs.victoriametrics.com/victoriametrics/single-server-victoriametrics/#multi-tenancy) section.
|
||||
1. Deploy an empty cluster next to the existing single-node.
|
||||
1. Deploy higher-level `vmselect` and configure it to query both the existing
|
||||
single-node and the new cluster.
|
||||
1. Start writing data to the cluster.
|
||||
1. Stop writing data to the single-node.
|
||||
|
||||
This approach requires no data migration nor downtime (apart from restarting the
|
||||
single-node at step 1). And once the single-node data becomes outside the
|
||||
retention period, the single-node can be removed from the deployment.
|
||||
|
||||
Note that if you need to actually migrate data to cluster and/or modify it, you
|
||||
will need to use
|
||||
[vmctl](https://docs.victoriametrics.com/victoriametrics/vmctl/victoriametrics/)
|
||||
instead.
|
||||
|
||||
|
||||
### From other systems
|
||||
|
||||
Use [vmctl](https://docs.victoriametrics.com/victoriametrics/vmctl/) to migrate data from other systems to VictoriaMetrics.
|
||||
|
||||
@@ -26,9 +26,21 @@ See also [LTS releases](https://docs.victoriametrics.com/victoriametrics/lts-rel
|
||||
|
||||
## tip
|
||||
|
||||
* FEATURE: [MetricsQL](https://docs.victoriametrics.com/victoriametrics/metricsql/): support `fill` modifiers to allow missing series on either side of a binary operation to be filled with a provided default value. See [#10598](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/10598).
|
||||
* FEATURE: [vmagent](https://docs.victoriametrics.com/victoriametrics/vmagent/) and [vmsingle](https://docs.victoriametrics.com/victoriametrics/single-server-victoriametrics/): Improve background discovery performance for [http_sd](https://docs.victoriametrics.com/victoriametrics/sd_configs/#http_sd_configs) discovery. See [#8838](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/8838).
|
||||
* FEATURE: [vmagent](https://docs.victoriametrics.com/victoriametrics/vmagent/) and [vmsingle](https://docs.victoriametrics.com/victoriametrics/single-server-victoriametrics/): allow overriding `max_scrape_size` on a per-target basis via the `__max_scrape_size__` label during target relabeling. See [#11188](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/11188).
|
||||
* FEATURE: [vmstorage](https://docs.victoriametrics.com/victoriametrics/cluster-victoriametrics/) and [vmsingle](https://docs.victoriametrics.com/victoriametrics/single-server-victoriametrics/): add `-maxBackfillAge` command-line flag for limiting ingestion of samples with historical timestamps, for example, when older data has been moved between storage tiers (nvme/hdd, hot/cold). See [#11199](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/11199). Thanks to @AshwinRamaniPsg for contribution.
|
||||
|
||||
* BUGFIX: `vminsert` in [VictoriaMetrics cluster](https://docs.victoriametrics.com/victoriametrics/cluster-victoriametrics/): Now drops metadata blocks when communicating with vmstorage nodes over the legacy RPC protocol. To avoid this limitation, upgrade `vmstorage` to a version that supports the new RPC protocol (>= [v1.137.0](https://github.com/VictoriaMetrics/VictoriaMetrics/blob/master/docs/victoriametrics/changelog/CHANGELOG.md#v11370)). See [#11146](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/11146).
|
||||
* BUGFIX: [vmbackup](https://docs.victoriametrics.com/victoriametrics/vmbackup/) and [vmbackupmanager](https://docs.victoriametrics.com/victoriametrics/vmbackupmanager/): retry S3 requests failing with `HTTP 429` status code or `TooManyRequests` error code. Previously such requests were not retried, so a short burst of rate limiting would fail the whole backup. See [#11218](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/11218). Thanks to @gautamrizwani for contribution.
|
||||
* BUGFIX: `vmselect` in [VictoriaMetrics cluster](https://docs.victoriametrics.com/victoriametrics/cluster-victoriametrics/): properly apply limit to metrics metadata response. See [#11139](https://github.com/VictoriaMetrics/VictoriaMetrics/pull/11139).
|
||||
* BUGFIX: [vmui](https://docs.victoriametrics.com/victoriametrics/single-server-victoriametrics/#vmui): keep only one header navigation dropdown (`Explore`, `Tools`) open at a time. Previously, hovering across two dropdowns could briefly leave both open due to the close delay. See [#11224](https://github.com/VictoriaMetrics/VictoriaMetrics/pull/11224). Thanks to @antedotee for contribution.
|
||||
* BUGFIX: [vmagent](https://docs.victoriametrics.com/victoriametrics/vmagent/): fix a possible data race when processing OpenTelemetry metadata. See [#11238](https://github.com/VictoriaMetrics/VictoriaMetrics/pull/11238). Thanks to @nevgeny for contribution.
|
||||
* BUGFIX: [vmagent](https://docs.victoriametrics.com/victoriametrics/vmagent/): flush pending persistent queue data to chunk file before updating the metadata. This prevents the metadata writer offset from getting ahead of the chunk file size and avoids losing the persistent queue after an unclean shutdown. See [#11192](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/11192).
|
||||
|
||||
## [v1.147.0](https://github.com/VictoriaMetrics/VictoriaMetrics/releases/tag/v1.147.0)
|
||||
|
||||
Release candidate
|
||||
Released at 2026-07-06
|
||||
|
||||
* SECURITY: upgrade base docker image (Alpine) from 3.23.4 to 3.24.1. See [Alpine 3.24.1 release notes](https://www.alpinelinux.org/posts/Alpine-3.24.1-released.html).
|
||||
|
||||
@@ -36,10 +48,11 @@ Release candidate
|
||||
* FEATURE: [vmagent](https://docs.victoriametrics.com/victoriametrics/vmagent/): reduces CPU usage by 10% at [sharding among remote storages](https://docs.victoriametrics.com/victoriametrics/vmagent/#sharding-among-remote-storages). See [#11113](https://github.com/VictoriaMetrics/VictoriaMetrics/pull/11113). Thanks to @bennf for contribution.
|
||||
* FEATURE: [vmagent](https://docs.victoriametrics.com/victoriametrics/vmagent/), `vminsert` in [VictoriaMetrics cluster](https://docs.victoriametrics.com/victoriametrics/cluster-victoriametrics/) and [vmsingle](https://docs.victoriametrics.com/victoriametrics/single-server-victoriametrics/): introduce `64KiB` size limit for `metric metadata` fields - `Unit`, `Help` and `MetricFamilyName`. See [#11128](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/11128).
|
||||
* FEATURE: [vmagent](https://docs.victoriametrics.com/victoriametrics/vmagent/) and [vmsingle](https://docs.victoriametrics.com/victoriametrics/single-server-victoriametrics/): reduce CPU usage for storing scrape target labels. See [#10919](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/10919).
|
||||
* FEATURE: [vmsingle](https://docs.victoriametrics.com/victoriametrics/single-server-victoriametrics/) and `vmstorage` in [VictoriaMetrics cluster](https://docs.victoriametrics.com/victoriametrics/cluster-victoriametrics/): expose `vm_data_size_bytes{type="storage/metaindex"}` and `vm_data_size_bytes{type="indexdb/metaindex"}` metrics for tracking memory occupied by metaindex data. See [#11204](https://github.com/VictoriaMetrics/VictoriaMetrics/pull/11204). Thanks to @SamarthBagga for contribution.
|
||||
* FEATURE: [vmsingle](https://docs.victoriametrics.com/victoriametrics/single-server-victoriametrics/) and `vmselect` in [VictoriaMetrics cluster](https://docs.victoriametrics.com/victoriametrics/cluster-victoriametrics/): add `optimize_repeated_binary_op_subexprs=1` query arg to [/api/v1/query_range](https://docs.victoriametrics.com/victoriametrics/keyconcepts/#range-query) for executing binary operator sides sequentially when they share the same optimized aggregate rollup result expression. This allows the second side to reuse rollup result cache populated by the first side. See [#10575](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/10575). Thanks to @xhebox for the contribution.
|
||||
* FEATURE: [vmauth](https://docs.victoriametrics.com/victoriametrics/vmauth/): prevent possible password brute-force attacks with an artificial 2-3 second delay as recommended by [OWASP](https://owasp.org/Top10/2025/A07_2025-Authentication_Failures). See [#11180](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/11180).
|
||||
* FEATURE: [alerts](https://github.com/VictoriaMetrics/VictoriaMetrics/blob/master/deployment/docker/rules): add `InvalidAuthTokenRequestErrors` alerting rule to [vmauth alerts](https://github.com/VictoriaMetrics/VictoriaMetrics/blob/master/deployment/docker/rules/alerts-vmauth.yml). The new rule notifies when vmauth receives requests with invalid or missing auth tokens, which may indicate a client misconfiguration, expired token use, or brute-force attack. See [#11180](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/11180).
|
||||
* FEATURE: [vmsingle](https://docs.victoriametrics.com/victoriametrics/single-server-victoriametrics/): Add the support of vmselect RPC to vmsingle so that single node can be queried by a vmselect from a vmcluster deployment. See [4328](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/4328) and [10926](https://github.com/VictoriaMetrics/VictoriaMetrics/pull/10926).
|
||||
* FEATURE: [vmsingle](https://docs.victoriametrics.com/victoriametrics/single-server-victoriametrics/): Add the support of vmselect RPC to vmsingle so that single node can be queried by a vmselect from a vmcluster deployment. See [4328](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/4328), [10926](https://github.com/VictoriaMetrics/VictoriaMetrics/pull/10926), and the [documentation](https://docs.victoriametrics.com/victoriametrics/single-server-victoriametrics/#multi-tenancy).
|
||||
* FEATURE: [vmauth](https://docs.victoriametrics.com/victoriametrics/vmauth/): allow log requests with missing or invalid auth tokens to [access log](https://docs.victoriametrics.com/victoriametrics/vmauth/#access-log). This is useful for identifying `remote_addr` IPs performing brute-force attacks. See [#11180](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/11180).
|
||||
* FEATURE: [vmauth](https://docs.victoriametrics.com/victoriametrics/vmauth/): fall through to `unauthorized_user` when a [JWT token](https://docs.victoriametrics.com/victoriametrics/vmauth/#jwt-token-auth-proxy) has no `vm_access` claim and no `default_vm_access_claim` is configured. Previously, vmauth returned `401 Unauthorized` immediately in this case, which prevented `unauthorized_user` from handling such requests. See [#5740](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/5740).
|
||||
* FEATURE: [MetricsQL](https://docs.victoriametrics.com/victoriametrics/metricsql/): improve the selection algorithm of [buckets_limit](https://docs.victoriametrics.com/victoriametrics/metricsql/#buckets_limit) to remove consecutive empty buckets at the beginning and end to obtain more accurate min and max values. See [#10417](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/10417).
|
||||
@@ -320,6 +333,24 @@ It enables back `Discovered targets` debug UI by default.
|
||||
* BUGFIX: `vmstorage` in [VictoriaMetrics cluster](https://docs.victoriametrics.com/victoriametrics/cluster-victoriametrics/): properly apply `extra_filters[]` filter when querying `vm_account_id` or `vm_project_id` labels via [multitenant](https://docs.victoriametrics.com/victoriametrics/cluster-victoriametrics/#multitenancy) request for `/api/v1/label/…/values` API. Before, `extra_filters` was ignored. See [#10503](https://github.com/VictoriaMetrics/VictoriaMetrics/pull/10503).
|
||||
* BUGFIX: [vmsingle](https://docs.victoriametrics.com/victoriametrics/single-server-victoriametrics/) and `vmselect` in [VictoriaMetrics cluster](https://docs.victoriametrics.com/victoriametrics/cluster-victoriametrics/): revert the use of rollup result cache for [instant queries](https://docs.victoriametrics.com/keyConcepts.html#instant-query) that contain [`rate`](https://docs.victoriametrics.com/MetricsQL.html#rate) function with a lookbehind window larger than `-search.minWindowForInstantRollupOptimization`. The cache usage was removed since [v1.132.0](https://github.com/VictoriaMetrics/VictoriaMetrics/releases/tag/v1.132.0). See [#10098](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/10098#issuecomment-3895011084) for more details.
|
||||
|
||||
## [v1.136.13](https://github.com/VictoriaMetrics/VictoriaMetrics/releases/tag/v1.136.13)
|
||||
|
||||
Released at 2026-07-03
|
||||
|
||||
**v1.136.x is a line of [LTS releases](https://docs.victoriametrics.com/victoriametrics/lts-releases/). It contains important up-to-date bugfixes for [VictoriaMetrics enterprise](https://docs.victoriametrics.com/victoriametrics/enterprise/).
|
||||
All these fixes are also included in [the latest community release](https://github.com/VictoriaMetrics/VictoriaMetrics/releases/latest).
|
||||
The v1.136.x line will be supported for at least 12 months since [v1.136.0](https://docs.victoriametrics.com/victoriametrics/changelog/#v11360) release**
|
||||
|
||||
* SECURITY: upgrade base docker image (Alpine) from 3.23.4 to 3.24.1. See [Alpine 3.24.1 release notes](https://www.alpinelinux.org/posts/Alpine-3.24.1-released.html).
|
||||
|
||||
* BUGFIX: `vminsert` in [VictoriaMetrics cluster](https://docs.victoriametrics.com/victoriametrics/cluster-victoriametrics/) and [vmsingle](https://docs.victoriametrics.com/victoriametrics/single-server-victoriametrics/): properly check values range for the limits configured with flags `-maxLabelsPerTimeseries`, `-maxLabelNameLen` and `-maxLabelValueLen`. It must be in range `1..65535`. See [#11128](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/11128).
|
||||
* BUGFIX: `vminsert` in [VictoriaMetrics cluster](https://docs.victoriametrics.com/victoriametrics/cluster-victoriametrics/): fixes unexpected rare rerouting. See [#11162](https://github.com/VictoriaMetrics/VictoriaMetrics/pull/11162).
|
||||
* BUGFIX: `vmselect` in [VictoriaMetrics cluster](https://docs.victoriametrics.com/victoriametrics/cluster-victoriametrics/): propagate cache reset operation to `selectNode` when `/internal/resetRollupResultCache` is called. Previously, the propagation only happened when the `delete_series` API was called. See [#11112](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/11112).
|
||||
* BUGFIX: [stream aggregation](https://docs.victoriametrics.com/victoriametrics/stream-aggregation/): fix possible unexpected increases in `rate_avg` and `rate_sum` if an out-of-order sample is ingested after the previous flush. See [#11140](https://github.com/VictoriaMetrics/VictoriaMetrics/pull/11140).
|
||||
* BUGFIX: [vmctl](https://docs.victoriametrics.com/victoriametrics/vmctl/): properly URL-encode `-vm-extra-label` values when building import requests, so special characters such as `&` don't get split into broken query parameters. See [#11144](https://github.com/VictoriaMetrics/VictoriaMetrics/pull/11144). Thanks to @immanuwell for contribution.
|
||||
* BUGFIX: [enterprise](https://docs.victoriametrics.com/enterprise/) [vmagent](https://docs.victoriametrics.com/vmagent/): ignore `enable.auto.offset.store` option in `kafka.consumer.topic.options`, since `vmagent` manages offset storage internally. Previously, setting this option could cause `vmagent` to stop committing Kafka messages. See [#11208](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/11208).
|
||||
* BUGFIX: all VictoriaMetrics components: cancel in-flight HTTP requests shortly before `-http.maxGracefulShutdownDuration` elapses during graceful shutdown, so they can drain and the shutdown completes cleanly within that window instead of timing out and exiting via `logger.Fatalf` -> `os.Exit`. This prevents skipping the storage flush and losing in-memory data when long-lived requests are in flight (such as VictoriaLogs live tailing). See [#1502](https://github.com/VictoriaMetrics/VictoriaLogs/issues/1502).
|
||||
|
||||
## [v1.136.12](https://github.com/VictoriaMetrics/VictoriaMetrics/releases/tag/v1.136.12)
|
||||
|
||||
Released at 2026-06-19
|
||||
@@ -700,6 +731,22 @@ See changes [here](https://docs.victoriametrics.com/victoriametrics/changelog/ch
|
||||
|
||||
See changes [here](https://docs.victoriametrics.com/victoriametrics/changelog/changelog_2025/#v11230)
|
||||
|
||||
## [v1.122.26](https://github.com/VictoriaMetrics/VictoriaMetrics/releases/tag/v1.122.26)
|
||||
|
||||
Released at 2026-07-03
|
||||
|
||||
**v1.122.x is a line of [LTS releases](https://docs.victoriametrics.com/victoriametrics/lts-releases/). It contains important up-to-date bugfixes for [VictoriaMetrics enterprise](https://docs.victoriametrics.com/victoriametrics/enterprise/).
|
||||
All these fixes are also included in [the latest community release](https://github.com/VictoriaMetrics/VictoriaMetrics/releases/latest).
|
||||
The v1.122.x line will be supported for at least 12 months since [v1.122.0](https://docs.victoriametrics.com/victoriametrics/changelog/#v11220) release**
|
||||
|
||||
* SECURITY: upgrade base docker image (Alpine) from 3.23.4 to 3.24.1. See [Alpine 3.24.1 release notes](https://www.alpinelinux.org/posts/Alpine-3.24.1-released.html).
|
||||
|
||||
* BUGFIX: `vminsert` in [VictoriaMetrics cluster](https://docs.victoriametrics.com/victoriametrics/cluster-victoriametrics/) and [vmsingle](https://docs.victoriametrics.com/victoriametrics/single-server-victoriametrics/): properly check values range for the limits configured with flags `-maxLabelsPerTimeseries`, `-maxLabelNameLen` and `-maxLabelValueLen`. It must be in range `1..65535`. See [#11128](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/11128).
|
||||
* BUGFIX: [stream aggregation](https://docs.victoriametrics.com/victoriametrics/stream-aggregation/): fix possible unexpected increases in `rate_avg` and `rate_sum` if an out-of-order sample is ingested after the previous flush. See [#11140](https://github.com/VictoriaMetrics/VictoriaMetrics/pull/11140).
|
||||
* BUGFIX: `vmselect` in [VictoriaMetrics cluster](https://docs.victoriametrics.com/victoriametrics/cluster-victoriametrics/): propagate cache reset operation to `selectNode` when `/internal/resetRollupResultCache` is called. Previously, the propagation only happened when the `delete_series` API was called. See [#11112](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/11112).
|
||||
* BUGFIX: [vmctl](https://docs.victoriametrics.com/victoriametrics/vmctl/): properly URL-encode `-vm-extra-label` values when building import requests, so special characters such as `&` don't get split into broken query parameters. See [#11144](https://github.com/VictoriaMetrics/VictoriaMetrics/pull/11144). Thanks to @immanuwell for contribution.
|
||||
* BUGFIX: all VictoriaMetrics components: cancel in-flight HTTP requests shortly before `-http.maxGracefulShutdownDuration` elapses during graceful shutdown, so they can drain and the shutdown completes cleanly within that window instead of timing out and exiting via `logger.Fatalf` -> `os.Exit`. This prevents skipping the storage flush and losing in-memory data when long-lived requests are in flight (such as VictoriaLogs live tailing). See [#1502](https://github.com/VictoriaMetrics/VictoriaLogs/issues/1502).
|
||||
|
||||
## [v1.122.25](https://github.com/VictoriaMetrics/VictoriaMetrics/releases/tag/v1.122.25)
|
||||
|
||||
Released at 2026-06-19
|
||||
|
||||
@@ -121,7 +121,7 @@ It is allowed to run Enterprise components in [cases listed here](https://docs.v
|
||||
Binary releases of Enterprise components are available at [the releases page for VictoriaMetrics](https://github.com/VictoriaMetrics/VictoriaMetrics/releases/latest),
|
||||
[the releases page for VictoriaLogs](https://github.com/VictoriaMetrics/VictoriaLogs/releases/latest)
|
||||
and [the releases page for VictoriaTraces](https://github.com/VictoriaMetrics/VictoriaTraces/releases/latest).
|
||||
Enterprise binaries and packages have `enterprise` suffix in their names. For example, `victoria-metrics-linux-amd64-v1.146.0-enterprise.tar.gz`.
|
||||
Enterprise binaries and packages have `enterprise` suffix in their names. For example, `victoria-metrics-linux-amd64-v1.147.0-enterprise.tar.gz`.
|
||||
|
||||
In order to run binary release of Enterprise component, please download the `*-enterprise.tar.gz` archive for your OS and architecture
|
||||
from the corresponding releases page and unpack it. Then run the unpacked binary.
|
||||
@@ -139,8 +139,8 @@ For example, the following command runs VictoriaMetrics Enterprise binary with t
|
||||
obtained at [this page](https://victoriametrics.com/products/enterprise/trial/):
|
||||
|
||||
```sh
|
||||
wget https://github.com/VictoriaMetrics/VictoriaMetrics/releases/download/v1.146.0/victoria-metrics-linux-amd64-v1.146.0-enterprise.tar.gz
|
||||
tar -xzf victoria-metrics-linux-amd64-v1.146.0-enterprise.tar.gz
|
||||
wget https://github.com/VictoriaMetrics/VictoriaMetrics/releases/download/v1.147.0/victoria-metrics-linux-amd64-v1.147.0-enterprise.tar.gz
|
||||
tar -xzf victoria-metrics-linux-amd64-v1.147.0-enterprise.tar.gz
|
||||
./victoria-metrics-prod -license=BASE64_ENCODED_LICENSE_KEY
|
||||
```
|
||||
|
||||
@@ -155,7 +155,7 @@ Alternatively, VictoriaMetrics Enterprise license can be stored in the file and
|
||||
It is allowed to run Enterprise components in [cases listed here](https://docs.victoriametrics.com/victoriametrics/enterprise/#valid-cases-for-victoriametrics-enterprise).
|
||||
|
||||
Docker images for Enterprise components are available at [VictoriaMetrics Docker Hub](https://hub.docker.com/u/victoriametrics) and [VictoriaMetrics Quay](https://quay.io/organization/victoriametrics).
|
||||
Enterprise docker images have `enterprise` suffix in their names. For example, `victoriametrics/victoria-metrics:v1.146.0-enterprise`.
|
||||
Enterprise docker images have `enterprise` suffix in their names. For example, `victoriametrics/victoria-metrics:v1.147.0-enterprise`.
|
||||
|
||||
In order to run Docker image of VictoriaMetrics Enterprise component, it is required to provide the license key via the command-line
|
||||
flag as described in the [binary-releases](https://docs.victoriametrics.com/victoriametrics/enterprise/#binary-releases) section.
|
||||
@@ -165,13 +165,13 @@ Enterprise license key can be obtained at [this page](https://victoriametrics.co
|
||||
For example, the following command runs VictoriaMetrics Enterprise Docker image with the specified license key:
|
||||
|
||||
```sh
|
||||
docker run --name=victoria-metrics victoriametrics/victoria-metrics:v1.146.0-enterprise -license=BASE64_ENCODED_LICENSE_KEY
|
||||
docker run --name=victoria-metrics victoriametrics/victoria-metrics:v1.147.0-enterprise -license=BASE64_ENCODED_LICENSE_KEY
|
||||
```
|
||||
|
||||
Alternatively, the license code can be stored in the file and then referred via `-licenseFile` command-line flag:
|
||||
|
||||
```sh
|
||||
docker run --name=victoria-metrics -v /vm-license:/vm-license victoriametrics/victoria-metrics:v1.146.0-enterprise -licenseFile=/path/to/vm-license
|
||||
docker run --name=victoria-metrics -v /vm-license:/vm-license victoriametrics/victoria-metrics:v1.147.0-enterprise -licenseFile=/path/to/vm-license
|
||||
```
|
||||
|
||||
Example docker-compose configuration:
|
||||
@@ -181,7 +181,7 @@ version: "3.5"
|
||||
services:
|
||||
victoriametrics:
|
||||
container_name: victoriametrics
|
||||
image: victoriametrics/victoria-metrics:v1.146.0
|
||||
image: victoriametrics/victoria-metrics:v1.147.0
|
||||
ports:
|
||||
- 8428:8428
|
||||
volumes:
|
||||
@@ -213,7 +213,7 @@ is used to provide the license key in plain-text:
|
||||
```yaml
|
||||
server:
|
||||
image:
|
||||
tag: v1.146.0-enterprise
|
||||
tag: v1.147.0-enterprise
|
||||
|
||||
license:
|
||||
key: {BASE64_ENCODED_LICENSE_KEY}
|
||||
@@ -224,7 +224,7 @@ In order to provide the license key via existing secret, the following values fi
|
||||
```yaml
|
||||
server:
|
||||
image:
|
||||
tag: v1.146.0-enterprise
|
||||
tag: v1.147.0-enterprise
|
||||
|
||||
license:
|
||||
secret:
|
||||
@@ -274,7 +274,7 @@ spec:
|
||||
license:
|
||||
key: {BASE64_ENCODED_LICENSE_KEY}
|
||||
image:
|
||||
tag: v1.146.0-enterprise
|
||||
tag: v1.147.0-enterprise
|
||||
```
|
||||
|
||||
In order to provide the license key via an existing secret, the following custom resource is used:
|
||||
@@ -291,7 +291,7 @@ spec:
|
||||
name: vm-license
|
||||
key: license
|
||||
image:
|
||||
tag: v1.146.0-enterprise
|
||||
tag: v1.147.0-enterprise
|
||||
```
|
||||
|
||||
Example secret with license key:
|
||||
@@ -342,7 +342,7 @@ Builds are available for amd64 and arm64 architectures.
|
||||
|
||||
Example archive:
|
||||
|
||||
`victoria-metrics-linux-amd64-v1.146.0-enterprise.tar.gz`
|
||||
`victoria-metrics-linux-amd64-v1.147.0-enterprise.tar.gz`
|
||||
|
||||
Includes:
|
||||
|
||||
@@ -351,7 +351,7 @@ Includes:
|
||||
|
||||
Example Docker image:
|
||||
|
||||
`victoriametrics/victoria-metrics:v1.146.0-enterprise-fips` – uses the FIPS-compatible binary and based on `scratch` image.
|
||||
`victoriametrics/victoria-metrics:v1.147.0-enterprise-fips` – uses the FIPS-compatible binary and based on `scratch` image.
|
||||
|
||||
## What Happens to Licensed Components When a License Expires
|
||||
|
||||
|
||||
@@ -40,7 +40,7 @@ requests_total{path="/", code="403"}
|
||||
The meta-information - a set of `labels` in curly braces - gives us a context for which `path` and with what `code`
|
||||
the `request` was served. Label-value pairs are always of a `string` type. VictoriaMetrics data model is schemaless,
|
||||
which means there is no need to define metric names or their labels in advance. The user is free to add or change ingested
|
||||
metrics anytime.
|
||||
metrics anytime. See [metric names and labels explained](https://victoriametrics.com/blog/prometheus-monitoring-metrics-counters-gauges-histogram-summaries/#metric-name-and-labels) for details.
|
||||
|
||||
Actually, the metric name is also a label with a special name `__name__`.
|
||||
The `__name__` key could be omitted {{% available_from "v1.111.0" %}} for simplicity. So the following series are identical:
|
||||
@@ -75,7 +75,7 @@ See [these docs](https://docs.victoriametrics.com/victoriametrics/faq/#what-is-h
|
||||
|
||||
#### Raw samples
|
||||
|
||||
Every unique time series may consist of an arbitrary number of `(value, timestamp)` data points (aka `raw samples`) sorted by `timestamp`.
|
||||
Every unique time series may consist of an arbitrary number of `(value, timestamp)` data points (aka [`raw samples`](https://victoriametrics.com/blog/prometheus-monitoring-metrics-counters-gauges-histogram-summaries/#sample)) sorted by `timestamp`.
|
||||
VictoriaMetrics stores all the `values` as [float64](https://en.wikipedia.org/wiki/Double-precision_floating-point_format)
|
||||
with [extra compression](https://faun.pub/victoriametrics-achieving-better-compression-for-time-series-data-than-gorilla-317bc1f95932) applied.
|
||||
This allows storing precise integer values with up to 12 decimal digits and any floating-point values with up to 12 significant decimal digits.
|
||||
@@ -126,7 +126,7 @@ type exists specifically to help users to understand how the metric was measured
|
||||
|
||||
#### Counter
|
||||
|
||||
Counter is a metric, which counts some events. Its value increases or stays the same over time.
|
||||
[Counter](https://victoriametrics.com/blog/prometheus-monitoring-metrics-counters-gauges-histogram-summaries/#counter) is a metric, which counts some events. Its value increases or stays the same over time.
|
||||
It cannot decrease in general case. The only exception is e.g. `counter reset`,
|
||||
when the metric resets to zero. The `counter reset` can occur when the service, which exposes the counter, restarts.
|
||||
So, the `counter` metric shows the number of observed events since the service start.
|
||||
@@ -157,7 +157,7 @@ by humans from other metric types.
|
||||
|
||||
#### Gauge
|
||||
|
||||
Gauge is used for measuring a value that can go up and down:
|
||||
[Gauge](https://victoriametrics.com/blog/prometheus-monitoring-metrics-counters-gauges-histogram-summaries/#gauge) is used for measuring a value that can go up and down:
|
||||
|
||||

|
||||
|
||||
@@ -178,7 +178,7 @@ and [rollup functions](https://docs.victoriametrics.com/victoriametrics/metricsq
|
||||
|
||||
#### Histogram
|
||||
|
||||
Histogram is a set of [counter](#counter) metrics with different `vmrange` or `le` labels.
|
||||
[Histogram](https://victoriametrics.com/blog/prometheus-monitoring-metrics-counters-gauges-histogram-summaries/#histogram) is a set of [counter](#counter) metrics with different `vmrange` or `le` labels.
|
||||
The `vmrange` or `le` labels define measurement boundaries of a particular bucket.
|
||||
When the observed measurement hits a particular bucket, then the corresponding counter is incremented.
|
||||
|
||||
@@ -282,7 +282,7 @@ We recommend reading the following articles before you start using histograms:
|
||||
|
||||
#### Summary
|
||||
|
||||
Summary metric type is quite similar to [histogram](#histogram) and is used for
|
||||
[Summary](https://victoriametrics.com/blog/prometheus-monitoring-metrics-counters-gauges-histogram-summaries/#summary) metric type is quite similar to [histogram](#histogram) and is used for
|
||||
[quantiles](https://prometheus.io/docs/practices/histograms/#quantiles) calculations. The main difference
|
||||
is that calculations are made on the client-side, so metrics exposition format already contains pre-defined
|
||||
quantiles:
|
||||
@@ -504,6 +504,7 @@ Params:
|
||||
The result of Instant query is a list of [time series](https://docs.victoriametrics.com/victoriametrics/keyconcepts/#time-series)
|
||||
matching the filter in `query` expression. Each returned series contains exactly one `(timestamp, value)` entry,
|
||||
where `timestamp` equals to the `time` query arg, while the `value` contains `query` result at the requested `time`.
|
||||
See [instant vectors explained](https://victoriametrics.com/blog/prometheus-monitoring-instant-range-query/#instant-vector) for details.
|
||||
|
||||
To understand how instant queries work, let's begin with a data sample:
|
||||
|
||||
@@ -606,6 +607,7 @@ at `start`, `start+step`, `start+2*step`, ..., `start+N*step` timestamps. In oth
|
||||
executed independently at `start`, `start+step`, ..., `start+N*step` timestamps with the only difference that an instant query
|
||||
does not return `ephemeral` samples (see below). Instead, if the database does not contain any samples for the requested time and step,
|
||||
it simply returns an empty result.
|
||||
See [range vectors explained](https://victoriametrics.com/blog/prometheus-monitoring-instant-range-query/#range-vector) for details.
|
||||
|
||||
|
||||
For example, to get the values of `foo_bar` during the time range from `2022-05-10T07:59:00Z` to `2022-05-10T08:17:00Z`,
|
||||
|
||||
@@ -35,8 +35,8 @@ scrape_configs:
|
||||
After you created the `scrape.yaml` file, download and unpack [single-node VictoriaMetrics](https://docs.victoriametrics.com/victoriametrics/single-server-victoriametrics/) to the same directory:
|
||||
|
||||
```sh
|
||||
wget https://github.com/VictoriaMetrics/VictoriaMetrics/releases/download/v1.146.0/victoria-metrics-linux-amd64-v1.146.0.tar.gz
|
||||
tar xzf victoria-metrics-linux-amd64-v1.146.0.tar.gz
|
||||
wget https://github.com/VictoriaMetrics/VictoriaMetrics/releases/download/v1.147.0/victoria-metrics-linux-amd64-v1.147.0.tar.gz
|
||||
tar xzf victoria-metrics-linux-amd64-v1.147.0.tar.gz
|
||||
```
|
||||
|
||||
Then start VictoriaMetrics and instruct it to scrape targets defined in `scrape.yaml` and save scraped metrics
|
||||
@@ -150,8 +150,8 @@ Then start [single-node VictoriaMetrics](https://docs.victoriametrics.com/victor
|
||||
|
||||
```yaml
|
||||
# Download and unpack single-node VictoriaMetrics
|
||||
wget https://github.com/VictoriaMetrics/VictoriaMetrics/releases/download/v1.146.0/victoria-metrics-linux-amd64-v1.146.0.tar.gz
|
||||
tar xzf victoria-metrics-linux-amd64-v1.146.0.tar.gz
|
||||
wget https://github.com/VictoriaMetrics/VictoriaMetrics/releases/download/v1.147.0/victoria-metrics-linux-amd64-v1.147.0.tar.gz
|
||||
tar xzf victoria-metrics-linux-amd64-v1.147.0.tar.gz
|
||||
|
||||
# Run single-node VictoriaMetrics with the given scrape.yaml
|
||||
./victoria-metrics-prod -promscrape.config=scrape.yaml
|
||||
|
||||
@@ -1923,6 +1923,9 @@ scrape_configs:
|
||||
# Example values:
|
||||
# - "10MiB" - 10 * 1024 * 1024 bytes
|
||||
# - "100MB" - 100 * 1000 * 1000 bytes
|
||||
# The max_scrape_size can be set on a per-target basis by specifying `__max_scrape_size__`
|
||||
# label during target relabeling phase.
|
||||
# See https://docs.victoriametrics.com/victoriametrics/relabeling/
|
||||
#
|
||||
# max_scrape_size: <size>
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -13,6 +13,8 @@ victoria-metrics is a time series database and monitoring solution.
|
||||
|
||||
See the docs at https://docs.victoriametrics.com/victoriametrics/
|
||||
|
||||
-accountID uint
|
||||
The accountID of the stored data
|
||||
-bigMergeConcurrency int
|
||||
Deprecated: this flag does nothing
|
||||
-blockcache.missesBeforeCaching int
|
||||
@@ -104,7 +106,7 @@ See the docs at https://docs.victoriametrics.com/victoriametrics/
|
||||
-http.idleConnTimeout duration
|
||||
Timeout for incoming idle http connections (default 1m0s)
|
||||
-http.maxGracefulShutdownDuration duration
|
||||
The maximum duration for a graceful shutdown of the HTTP server. A highly loaded server may require increased value for a graceful shutdown (default 7s)
|
||||
The maximum duration for a graceful shutdown of the HTTP server. During this period the server stops accepting new connections, but it will continue serving existing connections. The remaining in-flight requests are canceled before the deadline, so the shutdown can finish within this duration. A highly loaded server may require increased value for a graceful shutdown (default 7s)
|
||||
-http.pathPrefix string
|
||||
An optional prefix to add to all the paths handled by http server. For example, if '-http.pathPrefix=/foo/bar' is set, then all the http requests will be handled on '/foo/bar/*' paths. This may be useful for proxied requests. See https://www.robustperception.io/using-external-urls-and-proxies-with-prometheus
|
||||
-http.shutdownDelay duration
|
||||
@@ -194,11 +196,11 @@ See the docs at https://docs.victoriametrics.com/victoriametrics/
|
||||
The maximum size in bytes of a single Prometheus remote_write API request
|
||||
Supports the following optional suffixes for size values: KB, MB, GB, TB, KiB, MiB, GiB, TiB (default 33554432)
|
||||
-maxLabelNameLen int
|
||||
The maximum length of label name in the accepted time series. Series with longer label name are ignored. In this case the vm_rows_ignored_total{reason="too_long_label_name"} metric at /metrics page is incremented (default 256)
|
||||
The maximum length of label name in the accepted time series. Series with longer label name are ignored. In this case the vm_rows_ignored_total{reason="too_long_label_name"} metric at /metrics page is incremented. Value must be in range 1..65535. (default 256)
|
||||
-maxLabelValueLen int
|
||||
The maximum length of label values in the accepted time series. Series with longer label value are ignored. In this case the vm_rows_ignored_total{reason="too_long_label_value"} metric at /metrics page is incremented (default 4096)
|
||||
The maximum length of label values in the accepted time series. Series with longer label value are ignored. In this case the vm_rows_ignored_total{reason="too_long_label_value"} metric at /metrics page is incremented. Value must be in range 1..65535. (default 4096)
|
||||
-maxLabelsPerTimeseries int
|
||||
The maximum number of labels per time series to be accepted. Series with superfluous labels are ignored. In this case the vm_rows_ignored_total{reason="too_many_labels"} metric at /metrics page is incremented (default 40)
|
||||
The maximum number of labels per time series to be accepted. Series with superfluous labels are ignored. In this case the vm_rows_ignored_total{reason="too_many_labels"} metric at /metrics page is incremented. (default 40)
|
||||
-memory.allowedBytes size
|
||||
Allowed size of system memory VictoriaMetrics caches may occupy. This option overrides -memory.allowedPercent if set to a non-zero value. Too low a value may increase the cache miss rate usually resulting in higher CPU and disk IO usage. Too high a value may evict too much data from the OS page cache resulting in higher disk IO usage. The process may behave unexpectedly if this flag is set too small (e.g., 1 byte).
|
||||
Supports the following optional suffixes for size values: KB, MB, GB, TB, KiB, MiB, GiB, TiB (default 0)
|
||||
@@ -261,6 +263,8 @@ See the docs at https://docs.victoriametrics.com/victoriametrics/
|
||||
The number of precision bits to store per each value. Lower precision bits improves data compression at the cost of precision loss (default 64)
|
||||
-prevCacheRemovalPercent float
|
||||
Items in the previous caches are removed when the percent of requests it serves becomes lower than this value. Higher values reduce memory usage at the cost of higher CPU usage. See also -cacheExpireDuration (default 0.1)
|
||||
-projectID uint
|
||||
The projectID of the stored data
|
||||
-promscrape.azureSDCheckInterval duration
|
||||
Interval for checking for changes in Azure. This works only if azure_sd_configs is configured in '-promscrape.config' file. See https://docs.victoriametrics.com/victoriametrics/sd_configs/#azure_sd_configs for details (default 1m0s)
|
||||
-promscrape.cluster.memberLabel string
|
||||
@@ -401,6 +405,10 @@ See the docs at https://docs.victoriametrics.com/victoriametrics/
|
||||
The following optional suffixes are supported: s (second), h (hour), d (day), w (week), M (month), y (year). If suffix isn't set, then the duration is counted in months (default 1M)
|
||||
-retentionTimezoneOffset duration
|
||||
The offset for performing indexdb rotation. If set to 0, then the indexdb rotation is performed at 4am UTC time per each -retentionPeriod. If set to 2h, then the indexdb rotation is performed at 4am EET time (the timezone with +2h offset)
|
||||
-rpc.disableCompression
|
||||
Whether to disable compression of the data sent from vmstorage to vmselect. This reduces CPU usage at the cost of higher network bandwidth usage
|
||||
-rpc.handshakeTimeout duration
|
||||
Timeout for RPC handshake between vminsert/vmselect and vmstorage. Increase this value if transient handshake failures occur. See https://docs.victoriametrics.com/victoriametrics/troubleshooting/#cluster-instability section for more details. (default 5s)
|
||||
-search.cacheTimestampOffset duration
|
||||
The maximum duration since the current time for response data, which is always queried from the original raw data, without using the response cache. Increase this value if you see gaps in responses due to time synchronization issues between VictoriaMetrics and data sources. See also -search.disableAutoCacheReset (default 5m0s)
|
||||
-search.disableAutoCacheReset
|
||||
@@ -628,6 +636,8 @@ See the docs at https://docs.victoriametrics.com/victoriametrics/
|
||||
Show VictoriaMetrics version
|
||||
-vmalert.proxyURL string
|
||||
Optional URL for proxying requests to vmalert. For example, if -vmalert.proxyURL=http://vmalert:8880 , then alerting API requests such as /api/v1/rules from Grafana will be proxied to http://vmalert:8880/api/v1/rules . See https://docs.victoriametrics.com/victoriametrics/single-server-victoriametrics/#vmalert
|
||||
-vmselectAddr string
|
||||
TCP address to accept connections from vmselect services
|
||||
-vmui.customDashboardsPath string
|
||||
Optional path to vmui dashboards. See https://github.com/VictoriaMetrics/VictoriaMetrics/tree/master/app/vmui/packages/vmui/public/dashboards
|
||||
-vmui.defaultTimezone string
|
||||
|
||||
@@ -21,7 +21,7 @@ sitemap:
|
||||
-licenseFile string
|
||||
Path to file with license key for VictoriaMetrics Enterprise. See https://victoriametrics.com/products/enterprise/ . Trial Enterprise license can be obtained from https://victoriametrics.com/products/enterprise/trial/ . This flag is available only in Enterprise binaries. The license key can be also passed inline via -license command-line flag
|
||||
-licenseFile.reloadInterval duration
|
||||
Interval for reloading the license file specified via -licenseFile. See https://victoriametrics.com/products/enterprise/ . This flag is available only in Enterprise binaries (default 1h0m0s)
|
||||
Interval for reloading the license file specified via -licenseFile. A non-positive value disables periodic and SIGHUP-triggered reloads of -licenseFile. See https://victoriametrics.com/products/enterprise/ . This flag is available only in Enterprise binaries (default 1h0m0s)
|
||||
-mtls array
|
||||
Whether to require valid client certificate for https requests to the corresponding -httpListenAddr . This flag works only if -tls flag is set. See also -mtlsCAFile . This flag is available only in Enterprise binaries. See https://docs.victoriametrics.com/victoriametrics/enterprise/
|
||||
Supports array of values separated by comma or specified via multiple flags.
|
||||
@@ -45,7 +45,7 @@ sitemap:
|
||||
-tlsAutocertEmail string
|
||||
Contact email for the issued Let's Encrypt TLS certificates. See also -tlsAutocertHosts and -tlsAutocertCacheDir . This flag is available only in Enterprise binaries. See https://docs.victoriametrics.com/victoriametrics/enterprise/
|
||||
-tlsAutocertHosts array
|
||||
Optional hostnames for automatic issuing of Let's Encrypt TLS certificates. These hostnames must be reachable at -httpListenAddr . The -httpListenAddr must listen tcp port 443 . The -tlsAutocertHosts overrides -tlsCertFile and -tlsKeyFile . See also -tlsAutocertEmail and -tlsAutocertCacheDir . This flag is available only in Enterprise binaries. See https://docs.victoriametrics.com/victoriametrics/enterprise/
|
||||
Optional hostnames for automatic issuing of Let's Encrypt TLS certificates. These hostnames must be reachable at -httpListenAddr . The -httpListenAddr must listen tcp port 443 . The -tlsCertFile and -tlsKeyFile must be unset when -tlsAutocertHosts is used . See also -tlsAutocertEmail and -tlsAutocertCacheDir . This flag is available only in Enterprise binaries. See https://docs.victoriametrics.com/victoriametrics/enterprise/
|
||||
Supports an array of values separated by comma or specified via multiple flags.
|
||||
Each array item can contain comma inside single-quoted or double-quoted string, {}, [] and () braces.
|
||||
```
|
||||
|
||||
@@ -127,7 +127,8 @@ See [these docs](#how-to-collect-metrics-in-prometheus-format) for details.
|
||||
|
||||
`vmagent` can be used as an alternative to [StatsD](https://github.com/statsd/statsd)
|
||||
when [stream aggregation](https://docs.victoriametrics.com/victoriametrics/stream-aggregation/) is enabled.
|
||||
See [these docs](https://docs.victoriametrics.com/victoriametrics/stream-aggregation/#statsd-alternative) for details.
|
||||
See [these docs](https://docs.victoriametrics.com/victoriametrics/stream-aggregation/#statsd-alternative) for details,
|
||||
or the blog post on [how vmagent's stream aggregation works](https://victoriametrics.com/blog/vmagent-key-features-explained/#stream-aggregation).
|
||||
|
||||
### Flexible metrics relay
|
||||
|
||||
@@ -137,7 +138,7 @@ to other remote storage systems that support Prometheus `remote_write` protocol
|
||||
|
||||
### Replication and high availability
|
||||
|
||||
`vmagent` replicates the collected metrics among multiple remote storage instances configured via `-remoteWrite.url` args.
|
||||
`vmagent` [replicates the collected metrics](https://victoriametrics.com/blog/vmagent-how-it-works/#step-4-sharding--replication) among multiple remote storage instances configured via `-remoteWrite.url` args.
|
||||
If a single remote storage instance is temporarily unavailable, the collected data remains available on the other remote storage instances.
|
||||
`vmagent` buffers the collected data in files at `-remoteWrite.tmpDataPath` until the remote storage becomes available again.
|
||||
Then it sends the buffered data to the remote storage in order to prevent data gaps.
|
||||
@@ -150,7 +151,8 @@ See [these docs](https://docs.victoriametrics.com/victoriametrics/cluster-victor
|
||||
|
||||
`vmagent` can add, remove, or update labels on the collected data before sending it to the remote storage.
|
||||
It can filter scrape targets or remove unwanted samples via Prometheus-like relabeling.
|
||||
Please see the [Relabeling cookbook](https://docs.victoriametrics.com/victoriametrics/relabeling/) for details.
|
||||
Please see the [Relabeling cookbook](https://docs.victoriametrics.com/victoriametrics/relabeling/) for details,
|
||||
or the blog post on [how vmagent applies global relabeling to reduce cardinality](https://victoriametrics.com/blog/vmagent-how-it-works/#step-2-global-relabeling-cardinality-reduction).
|
||||
|
||||
### Sharding among remote storages
|
||||
|
||||
@@ -158,6 +160,7 @@ By default, `vmagent` replicates data to remote storage systems via the `-remote
|
||||
If the `-remoteWrite.shardByURL` command-line flag is set, then `vmagent` spreads
|
||||
the outgoing [time series](https://docs.victoriametrics.com/victoriametrics/keyconcepts/#time-series) evenly among all the remote storage
|
||||
systems listed in `-remoteWrite.url`.
|
||||
See the blog post on [how vmagent shards data across remote storage systems](https://victoriametrics.com/blog/vmagent-key-features-explained/#sharding) for the details.
|
||||
|
||||
It is possible to replicate samples among remote storage systems by passing `-remoteWrite.shardByURLReplicas=N`
|
||||
to `vmagent` in addition to the `-remoteWrite.shardByURL` command-line flag.
|
||||
@@ -268,7 +271,9 @@ for the collected samples. Examples:
|
||||
```sh
|
||||
./vmagent -remoteWrite.url=http://remote-storage/api/v1/write -streamAggr.dropInputLabels=replica -streamAggr.dedupInterval=60s
|
||||
```
|
||||
|
||||
|
||||
See the blog post on [how vmagent performs global deduplication and stream aggregation](https://victoriametrics.com/blog/vmagent-how-it-works/#step-3-global-deduplication--stream-aggregation) for the details.
|
||||
|
||||
### Monitoring Data eXchange
|
||||
|
||||
The Monitoring Data eXchange (MDX){{% available_from "v1.147.0" %}} feature allows `vmagent` to forward only VictoriaMetrics metrics to selected `-remoteWrite.url` destinations while dropping metrics from non-VictoriaMetrics services.
|
||||
@@ -357,6 +362,8 @@ in addition to the pull-based Prometheus-compatible targets' scraping:
|
||||
* Prometheus exposition format via `http://<vmagent>:8429/api/v1/import/prometheus`. See [these docs](https://docs.victoriametrics.com/victoriametrics/single-server-victoriametrics/#how-to-import-data-in-prometheus-exposition-format) for details.
|
||||
* Arbitrary CSV data via `http://<vmagent>:8429/api/v1/import/csv`. See [these docs](https://docs.victoriametrics.com/victoriametrics/single-server-victoriametrics/#how-to-import-csv-data).
|
||||
|
||||
See the blog post on [how vmagent receives data via push APIs or scraping](https://victoriametrics.com/blog/vmagent-how-it-works/#step-1-receiving-data-via-api-or-scrape) for the details.
|
||||
|
||||
## How to collect metrics in Prometheus format
|
||||
|
||||
Specify the path to the `prometheus.yml` file via the `-promscrape.config` command-line flag. `vmagent` takes into account the following
|
||||
@@ -490,15 +497,13 @@ SRV URLs are useful when HTTP services run on different TCP ports or when their
|
||||
|
||||
When comparing the remote protocols between VictoriaMetrics and Prometheus, VictoriaMetrics provides the following benefits:
|
||||
|
||||
* Reduced network bandwidth usage by 2x-5x. This allows saving network bandwidth usage costs when `vmagent` and
|
||||
* Reduced network bandwidth usage by 2x-5x. This allows [saving network bandwidth usage costs](https://victoriametrics.com/blog/victoriametrics-remote-write/) when `vmagent` and
|
||||
the configured remote storage systems are located in different datacenters, availability zones, or regions.
|
||||
|
||||
* Reduced disk read/write IO and disk space usage at `vmagent` when the remote storage is temporarily unavailable.
|
||||
In this case, `vmagent` buffers incoming data to disk using the VictoriaMetrics remote write format.
|
||||
This reduces disk read/write IO and disk space usage by 2x-5x compared to the Prometheus remote write format.
|
||||
|
||||
> See blogpost [Save network costs with VictoriaMetrics remote write protocol](https://victoriametrics.com/blog/victoriametrics-remote-write/).
|
||||
|
||||
`vmagent` uses VictoriaMetrics remote write protocol by default {{% available_from "v1.116.0" %}} when it sends data to VictoriaMetrics components such as other `vmagent` instances,
|
||||
[single-node VictoriaMetrics](https://docs.victoriametrics.com/victoriametrics/single-server-victoriametrics/)
|
||||
or `vminsert` at [cluster version](https://docs.victoriametrics.com/victoriametrics/cluster-victoriametrics/). If needed, it can automatically downgrade to a Prometheus protocol at runtime.
|
||||
@@ -835,7 +840,7 @@ as soon as it is parsed in stream parsing mode.
|
||||
## Scraping big number of targets
|
||||
|
||||
A single `vmagent` instance can scrape tens of thousands of scrape targets. Sometimes this isn't enough due to limitations on CPU, network, RAM, etc.
|
||||
In this case, scrape targets can be split among multiple `vmagent` instances (aka `vmagent` horizontal scaling, sharding, and clustering).
|
||||
In this case, scrape targets can be split among multiple `vmagent` instances (aka [`vmagent` horizontal scaling](https://victoriametrics.com/blog/vmagent-key-features-explained/#scaling-vmagent), sharding, and clustering).
|
||||
The number of `vmagent` instances in the cluster must be passed to the `-promscrape.cluster.membersCount` command-line flag.
|
||||
Each `vmagent` instance in the cluster must use identical `-promscrape.config` files with distinct `-promscrape.cluster.memberNum` values
|
||||
in the range `0 ... N-1`, where `N` is the number of `vmagent` instances in the cluster specified via `-promscrape.cluster.membersCount`.
|
||||
@@ -948,7 +953,7 @@ scrape_configs:
|
||||
|
||||
## On-disk persistence
|
||||
|
||||
`vmagent` stores pending data that cannot be sent to the configured remote storage systems in a timely manner.
|
||||
`vmagent` [stores pending data that cannot be sent to the configured remote storage systems](https://victoriametrics.com/blog/vmagent-key-features-explained/#persistent-disk-for-remote-write) in a timely manner.
|
||||
By default, `vmagent` writes all the pending data to the folder configured via `-remoteWrite.tmpDataPath` cmd-line flag
|
||||
until this data is sent to the configured `-remoteWrite.url` systems or until the folder becomes full.
|
||||
The maximum data size that can be saved to `-remoteWrite.tmpDataPath` per every configured `-remoteWrite.url` can be
|
||||
@@ -996,7 +1001,7 @@ moment it becomes visible at the remote storage.
|
||||
|
||||
This behavior can be changed with the `-remoteWrite.inmemoryQueues` {{% available_from "v1.146.0" %}} command-line flag.
|
||||
When set to a non-zero value, vmagent starts the given number of additional workers,
|
||||
which send only recently ingested data from the in-memory queue, while the workers configured via `-remoteWrite.queues` drain the file-based backlog concurrently.
|
||||
which send only recently ingested data from the [in-memory queue](https://victoriametrics.com/blog/vmagent-how-it-works/#in-memory-queue), while the workers configured via `-remoteWrite.queues` drain the file-based backlog concurrently.
|
||||
This reduces the delivery lag for fresh samples after remote storage outages or slowdowns. The flag can be set individually per each `-remoteWrite.url`.
|
||||
|
||||
Note that these workers are started in addition to the workers configured via `-remoteWrite.queues`, so the total number of concurrent connections to
|
||||
|
||||
@@ -83,7 +83,7 @@ See the docs at https://docs.victoriametrics.com/victoriametrics/vmagent/ .
|
||||
-http.idleConnTimeout duration
|
||||
Timeout for incoming idle http connections (default 1m0s)
|
||||
-http.maxGracefulShutdownDuration duration
|
||||
The maximum duration for a graceful shutdown of the HTTP server. A highly loaded server may require increased value for a graceful shutdown (default 7s)
|
||||
The maximum duration for a graceful shutdown of the HTTP server. During this period the server stops accepting new connections, but it will continue serving existing connections. The remaining in-flight requests are canceled before the deadline, so the shutdown can finish within this duration. A highly loaded server may require increased value for a graceful shutdown (default 7s)
|
||||
-http.pathPrefix string
|
||||
An optional prefix to add to all the paths handled by http server. For example, if '-http.pathPrefix=/foo/bar' is set, then all the http requests will be handled on '/foo/bar/*' paths. This may be useful for proxied requests. See https://www.robustperception.io/using-external-urls-and-proxies-with-prometheus
|
||||
-http.shutdownDelay duration
|
||||
@@ -170,6 +170,8 @@ See the docs at https://docs.victoriametrics.com/victoriametrics/vmagent/ .
|
||||
The maximum length of label values in the accepted time series. Series with longer label value are ignored. In this case the vm_rows_ignored_total{reason="too_long_label_value"} metric at /metrics page is incremented
|
||||
-maxLabelsPerTimeseries int
|
||||
The maximum number of labels per time series to be accepted. Series with superfluous labels are ignored. In this case the vm_rows_ignored_total{reason="too_many_labels"} metric at /metrics page is incremented
|
||||
-mdx.label -remoteWrite.url
|
||||
Optional label value in the form 'name=value' used to identify VictoriaMetrics metrics for MDX. Metrics containing the specified label are forwarded to -remoteWrite.url endpoints configured with `-remoteWrite.mdx.enable=true`.
|
||||
-memory.allowedBytes size
|
||||
Allowed size of system memory VictoriaMetrics caches may occupy. This option overrides -memory.allowedPercent if set to a non-zero value. Too low a value may increase the cache miss rate usually resulting in higher CPU and disk IO usage. Too high a value may evict too much data from the OS page cache resulting in higher disk IO usage. The process may behave unexpectedly if this flag is set too small (e.g., 1 byte).
|
||||
Supports the following optional suffixes for size values: KB, MB, GB, TB, KiB, MiB, GiB, TiB (default 0)
|
||||
@@ -465,6 +467,10 @@ See the docs at https://docs.victoriametrics.com/victoriametrics/vmagent/ .
|
||||
The maximum number of metadata to send in each block to remote storage. Higher number may improve performance at the cost of the increased memory usage. See also -remoteWrite.maxBlockSize (default 5000)
|
||||
-remoteWrite.maxRowsPerBlock int
|
||||
The maximum number of samples to send in each block to remote storage. Higher number may improve performance at the cost of the increased memory usage. See also -remoteWrite.maxBlockSize (default 10000)
|
||||
-remoteWrite.mdx.enable array
|
||||
Whether to only retain metrics from VictoriaMetrics services before sending them to the corresponding -remoteWrite.url. Please see https://docs.victoriametrics.com/victoriametrics/vmagent/#monitoring-data-exchange
|
||||
Supports array of values separated by comma or specified via multiple flags.
|
||||
Empty values are set to false.
|
||||
-remoteWrite.oauth2.clientID array
|
||||
Optional OAuth2 clientID to use for the corresponding -remoteWrite.url
|
||||
Supports an array of values separated by comma or specified via multiple flags.
|
||||
|
||||
@@ -89,7 +89,7 @@ sitemap:
|
||||
-licenseFile string
|
||||
Path to file with license key for VictoriaMetrics Enterprise. See https://victoriametrics.com/products/enterprise/ . Trial Enterprise license can be obtained from https://victoriametrics.com/products/enterprise/trial/ . This flag is available only in Enterprise binaries. The license key can be also passed inline via -license command-line flag
|
||||
-licenseFile.reloadInterval duration
|
||||
Interval for reloading the license file specified via -licenseFile. See https://victoriametrics.com/products/enterprise/ . This flag is available only in Enterprise binaries (default 1h0m0s)
|
||||
Interval for reloading the license file specified via -licenseFile. A non-positive value disables periodic and SIGHUP-triggered reloads of -licenseFile. See https://victoriametrics.com/products/enterprise/ . This flag is available only in Enterprise binaries (default 1h0m0s)
|
||||
-mtls array
|
||||
Whether to require valid client certificate for https requests to the corresponding -httpListenAddr . This flag works only if -tls flag is set. See also -mtlsCAFile . This flag is available only in Enterprise binaries. See https://docs.victoriametrics.com/victoriametrics/enterprise/
|
||||
Supports array of values separated by comma or specified via multiple flags.
|
||||
@@ -103,7 +103,7 @@ sitemap:
|
||||
-tlsAutocertEmail string
|
||||
Contact email for the issued Let's Encrypt TLS certificates. See also -tlsAutocertHosts and -tlsAutocertCacheDir . This flag is available only in Enterprise binaries. See https://docs.victoriametrics.com/victoriametrics/enterprise/
|
||||
-tlsAutocertHosts array
|
||||
Optional hostnames for automatic issuing of Let's Encrypt TLS certificates. These hostnames must be reachable at -httpListenAddr . The -httpListenAddr must listen tcp port 443 . The -tlsAutocertHosts overrides -tlsCertFile and -tlsKeyFile . See also -tlsAutocertEmail and -tlsAutocertCacheDir . This flag is available only in Enterprise binaries. See https://docs.victoriametrics.com/victoriametrics/enterprise/
|
||||
Optional hostnames for automatic issuing of Let's Encrypt TLS certificates. These hostnames must be reachable at -httpListenAddr . The -httpListenAddr must listen tcp port 443 . The -tlsCertFile and -tlsKeyFile must be unset when -tlsAutocertHosts is used . See also -tlsAutocertEmail and -tlsAutocertCacheDir . This flag is available only in Enterprise binaries. See https://docs.victoriametrics.com/victoriametrics/enterprise/
|
||||
Supports an array of values separated by comma or specified via multiple flags.
|
||||
Each array item can contain comma inside single-quoted or double-quoted string, {}, [] and () braces.
|
||||
```
|
||||
|
||||
@@ -209,6 +209,8 @@ rules:
|
||||
[ debug: <bool> | default = false ]
|
||||
```
|
||||
|
||||
See a blogpost about [how rule groups work](https://victoriametrics.com/blog/alerting-recording-rules-alertmanager/#groups).
|
||||
|
||||
### Rules
|
||||
|
||||
Every rule contains an `expr` field for the expression to evaluate against the configured datasource.
|
||||
@@ -285,6 +287,8 @@ annotations:
|
||||
[ <labelname>: <tmpl_string> ]
|
||||
```
|
||||
|
||||
See a blogpost about [how alerting rules work](https://victoriametrics.com/blog/alerting-recording-rules-alertmanager/#alerting-rules).
|
||||
|
||||
#### Recording rules
|
||||
|
||||
The syntax for recording rules is the following:
|
||||
@@ -322,10 +326,13 @@ labels:
|
||||
|
||||
For recording rules to work `-remoteWrite.url` must be specified.
|
||||
|
||||
See a blogpost about [how recording rules work](https://victoriametrics.com/blog/alerting-recording-rules-alertmanager/#recording-rules).
|
||||
|
||||
## Templating
|
||||
|
||||
It is allowed to use [Go templating](https://golang.org/pkg/text/template/) in annotations and labels(with limited support) to format data, iterate over
|
||||
or execute expressions.
|
||||
See a blogpost about [templating alerts](https://victoriametrics.com/blog/alerting-recording-rules-alertmanager/#templates).
|
||||
The following variables are available in templating:
|
||||
|
||||
| Variable | Description | Example |
|
||||
@@ -928,7 +935,7 @@ See full description for these flags in `./vmalert -help`.
|
||||
|
||||
## Unit Testing for Rules
|
||||
|
||||
You can use `vmalert-tool` to test your alerting and recording rules like [promtool does](https://prometheus.io/docs/prometheus/latest/configuration/unit_testing_rules/).
|
||||
You can use `vmalert-tool` to [test your alerting and recording rules](https://victoriametrics.com/blog/alerting-best-practices/#testing-alerts) like [promtool does](https://prometheus.io/docs/prometheus/latest/configuration/unit_testing_rules/).
|
||||
See more details in [vmalert-tool](https://docs.victoriametrics.com/victoriametrics/vmalert-tool/#unit-testing-for-rules).
|
||||
|
||||
## Monitoring
|
||||
@@ -982,6 +989,8 @@ Try the following tips to avoid common issues:
|
||||
In that case, the default step will be used (`-datasource.queryStep`) and may cause unexpected results compared to
|
||||
executing this query in vmui/Grafana, where step is adjusted differently.
|
||||
|
||||
See a blogpost about [reducing alert noise](https://victoriametrics.com/blog/alerting-best-practices/#reducing-noise).
|
||||
|
||||
### Rule state
|
||||
|
||||
vmalert keeps the last `-rule.updateEntriesLimit` updates (or `update_entries_limit` [per-rule config](https://docs.victoriametrics.com/victoriametrics/vmalert/#alerting-rules))
|
||||
@@ -1037,6 +1046,8 @@ Sometimes, it's hard to understand why a specific alert fired or not. Keep in mi
|
||||
If evaluation returns error (i.e. datasource is unavailable), alert state doesn't change.
|
||||
If at least one evaluation returns no data, then alert's `for` state resets.
|
||||
|
||||
See a blogpost about [tuning the `for` param](https://victoriametrics.com/blog/alerting-best-practices/#the-for-param).
|
||||
|
||||
> Note: The alert state is tracked separately for each time series returned during evaluation.
|
||||
> For example, if the 1st evaluation returns series A and B, and the 2nd evaluation returns only B – the alert will remain active **only for B**.
|
||||
|
||||
@@ -1110,6 +1121,8 @@ How to reduce the chance for a rule to flap:
|
||||
|
||||
See [common mistakes](#common-mistakes) for rules config.
|
||||
|
||||
See a blogpost about [tuning `keep_firing_for`](https://victoriametrics.com/blog/alerting-best-practices/#the-keep_firing_for-param).
|
||||
|
||||
### Never-firing alerts
|
||||
|
||||
vmalert can detect {{% available_from "v1.91.0" %}} if alert's expression doesn't match any time series in runtime.
|
||||
@@ -1360,6 +1373,8 @@ The list of configured or discovered Notifiers can be explored via [UI](#web).
|
||||
If Alertmanager runs in cluster mode then all its URLs needs to be available during discovery
|
||||
to ensure [high availability](https://github.com/prometheus/alertmanager#high-availability).
|
||||
|
||||
See a blogpost about [how Alertmanager processes alerts](https://victoriametrics.com/blog/alerting-recording-rules-alertmanager/#alertmanager).
|
||||
|
||||
The configuration file [specification](https://github.com/VictoriaMetrics/VictoriaMetrics/blob/master/app/vmalert/notifier/config.go)
|
||||
is the following:
|
||||
|
||||
|
||||
@@ -124,7 +124,7 @@ See the docs at https://docs.victoriametrics.com/victoriametrics/vmalert/ .
|
||||
-http.idleConnTimeout duration
|
||||
Timeout for incoming idle http connections (default 1m0s)
|
||||
-http.maxGracefulShutdownDuration duration
|
||||
The maximum duration for a graceful shutdown of the HTTP server. A highly loaded server may require increased value for a graceful shutdown (default 7s)
|
||||
The maximum duration for a graceful shutdown of the HTTP server. During this period the server stops accepting new connections, but it will continue serving existing connections. The remaining in-flight requests are canceled before the deadline, so the shutdown can finish within this duration. A highly loaded server may require increased value for a graceful shutdown (default 7s)
|
||||
-http.pathPrefix string
|
||||
An optional prefix to add to all the paths handled by http server. For example, if '-http.pathPrefix=/foo/bar' is set, then all the http requests will be handled on '/foo/bar/*' paths. This may be useful for proxied requests. See https://www.robustperception.io/using-external-urls-and-proxies-with-prometheus
|
||||
-http.shutdownDelay duration
|
||||
|
||||
@@ -23,7 +23,7 @@ sitemap:
|
||||
-licenseFile string
|
||||
Path to file with license key for VictoriaMetrics Enterprise. See https://victoriametrics.com/products/enterprise/ . Trial Enterprise license can be obtained from https://victoriametrics.com/products/enterprise/trial/ . This flag is available only in Enterprise binaries. The license key can be also passed inline via -license command-line flag
|
||||
-licenseFile.reloadInterval duration
|
||||
Interval for reloading the license file specified via -licenseFile. See https://victoriametrics.com/products/enterprise/ . This flag is available only in Enterprise binaries (default 1h0m0s)
|
||||
Interval for reloading the license file specified via -licenseFile. A non-positive value disables periodic and SIGHUP-triggered reloads of -licenseFile. See https://victoriametrics.com/products/enterprise/ . This flag is available only in Enterprise binaries (default 1h0m0s)
|
||||
-mtls array
|
||||
Whether to require valid client certificate for https requests to the corresponding -httpListenAddr . This flag works only if -tls flag is set. See also -mtlsCAFile . This flag is available only in Enterprise binaries. See https://docs.victoriametrics.com/victoriametrics/enterprise/
|
||||
Supports array of values separated by comma or specified via multiple flags.
|
||||
@@ -49,7 +49,7 @@ sitemap:
|
||||
-tlsAutocertEmail string
|
||||
Contact email for the issued Let's Encrypt TLS certificates. See also -tlsAutocertHosts and -tlsAutocertCacheDir . This flag is available only in Enterprise binaries. See https://docs.victoriametrics.com/victoriametrics/enterprise/
|
||||
-tlsAutocertHosts array
|
||||
Optional hostnames for automatic issuing of Let's Encrypt TLS certificates. These hostnames must be reachable at -httpListenAddr . The -httpListenAddr must listen tcp port 443 . The -tlsAutocertHosts overrides -tlsCertFile and -tlsKeyFile . See also -tlsAutocertEmail and -tlsAutocertCacheDir . This flag is available only in Enterprise binaries. See https://docs.victoriametrics.com/victoriametrics/enterprise/
|
||||
Optional hostnames for automatic issuing of Let's Encrypt TLS certificates. These hostnames must be reachable at -httpListenAddr . The -httpListenAddr must listen tcp port 443 . The -tlsCertFile and -tlsKeyFile must be unset when -tlsAutocertHosts is used . See also -tlsAutocertEmail and -tlsAutocertCacheDir . This flag is available only in Enterprise binaries. See https://docs.victoriametrics.com/victoriametrics/enterprise/
|
||||
Supports an array of values separated by comma or specified via multiple flags.
|
||||
Each array item can contain comma inside single-quoted or double-quoted string, {}, [] and () braces.
|
||||
```
|
||||
@@ -68,7 +68,7 @@ See the docs at https://docs.victoriametrics.com/victoriametrics/vmauth/ .
|
||||
-http.idleConnTimeout duration
|
||||
Timeout for incoming idle http connections (default 1m0s)
|
||||
-http.maxGracefulShutdownDuration duration
|
||||
The maximum duration for a graceful shutdown of the HTTP server. A highly loaded server may require increased value for a graceful shutdown (default 7s)
|
||||
The maximum duration for a graceful shutdown of the HTTP server. During this period the server stops accepting new connections, but it will continue serving existing connections. The remaining in-flight requests are canceled before the deadline, so the shutdown can finish within this duration. A highly loaded server may require increased value for a graceful shutdown (default 7s)
|
||||
-http.pathPrefix string
|
||||
An optional prefix to add to all the paths handled by http server. For example, if '-http.pathPrefix=/foo/bar' is set, then all the http requests will be handled on '/foo/bar/*' paths. This may be useful for proxied requests. See https://www.robustperception.io/using-external-urls-and-proxies-with-prometheus
|
||||
-http.shutdownDelay duration
|
||||
|
||||
@@ -19,7 +19,7 @@ sitemap:
|
||||
-licenseFile string
|
||||
Path to file with license key for VictoriaMetrics Enterprise. See https://victoriametrics.com/products/enterprise/ . Trial Enterprise license can be obtained from https://victoriametrics.com/products/enterprise/trial/ . This flag is available only in Enterprise binaries. The license key can be also passed inline via -license command-line flag
|
||||
-licenseFile.reloadInterval duration
|
||||
Interval for reloading the license file specified via -licenseFile. See https://victoriametrics.com/products/enterprise/ . This flag is available only in Enterprise binaries (default 1h0m0s)
|
||||
Interval for reloading the license file specified via -licenseFile. A non-positive value disables periodic and SIGHUP-triggered reloads of -licenseFile. See https://victoriametrics.com/products/enterprise/ . This flag is available only in Enterprise binaries (default 1h0m0s)
|
||||
-mtls array
|
||||
Whether to require valid client certificate for https requests to the corresponding -httpListenAddr . This flag works only if -tls flag is set. See also -mtlsCAFile . This flag is available only in Enterprise binaries. See https://docs.victoriametrics.com/victoriametrics/enterprise/
|
||||
Supports array of values separated by comma or specified via multiple flags.
|
||||
@@ -33,7 +33,7 @@ sitemap:
|
||||
-tlsAutocertEmail string
|
||||
Contact email for the issued Let's Encrypt TLS certificates. See also -tlsAutocertHosts and -tlsAutocertCacheDir . This flag is available only in Enterprise binaries. See https://docs.victoriametrics.com/victoriametrics/enterprise/
|
||||
-tlsAutocertHosts array
|
||||
Optional hostnames for automatic issuing of Let's Encrypt TLS certificates. These hostnames must be reachable at -httpListenAddr . The -httpListenAddr must listen tcp port 443 . The -tlsAutocertHosts overrides -tlsCertFile and -tlsKeyFile . See also -tlsAutocertEmail and -tlsAutocertCacheDir . This flag is available only in Enterprise binaries. See https://docs.victoriametrics.com/victoriametrics/enterprise/
|
||||
Optional hostnames for automatic issuing of Let's Encrypt TLS certificates. These hostnames must be reachable at -httpListenAddr . The -httpListenAddr must listen tcp port 443 . The -tlsCertFile and -tlsKeyFile must be unset when -tlsAutocertHosts is used . See also -tlsAutocertEmail and -tlsAutocertCacheDir . This flag is available only in Enterprise binaries. See https://docs.victoriametrics.com/victoriametrics/enterprise/
|
||||
Supports an array of values separated by comma or specified via multiple flags.
|
||||
Each array item can contain comma inside single-quoted or double-quoted string, {}, [] and () braces.
|
||||
```
|
||||
@@ -8,9 +8,9 @@ menu:
|
||||
weight: 9
|
||||
---
|
||||
|
||||
The simplest way to migrate data between VictoriaMetrics installations is [to copy data between instances](https://docs.victoriametrics.com/victoriametrics/single-server-victoriametrics/#data-migration).
|
||||
But when simple copy isn't possible (migration between single-node and cluster, or re-sharding) or when data need to be
|
||||
modified - use `vmctl vm-native` to migrate data.
|
||||
The simplest way to migrate data between VictoriaMetrics installations is [to copy data between instances](https://docs.victoriametrics.com/victoriametrics/single-server-victoriametrics/#data-migration) or [to run single-node and cluster together](https://docs.victoriametrics.com/victoriametrics/single-server-victoriametrics/#from-single-node-to-cluster).
|
||||
But when simple copy isn't possible (migration between single-node and cluster, or re-sharding) or when data needs to be
|
||||
modified, use `vmctl vm-native` to migrate data.
|
||||
|
||||
See `./vmctl vm-native --help` for details and full list of flags.
|
||||
|
||||
@@ -190,4 +190,4 @@ See general [vmctl migration tips](https://docs.victoriametrics.com/victoriametr
|
||||
|
||||
See `./vmctl vm-native --help` for details and full list of flags:
|
||||
|
||||
{{% content "vmctl_vm-native_flags.md" %}}
|
||||
{{% content "vmctl_vm-native_flags.md" %}}
|
||||
|
||||
@@ -34,9 +34,9 @@ vmctl command-line tool is available as:
|
||||
|
||||
Download and unpack vmctl:
|
||||
```sh
|
||||
wget https://github.com/VictoriaMetrics/VictoriaMetrics/releases/download/v1.146.0/vmutils-darwin-arm64-v1.146.0.tar.gz
|
||||
wget https://github.com/VictoriaMetrics/VictoriaMetrics/releases/download/v1.147.0/vmutils-darwin-arm64-v1.147.0.tar.gz
|
||||
|
||||
tar xzf vmutils-darwin-arm64-v1.146.0.tar.gz
|
||||
tar xzf vmutils-darwin-arm64-v1.147.0.tar.gz
|
||||
```
|
||||
|
||||
Once binary is unpacked, see the full list of supported modes by running the following command:
|
||||
|
||||
@@ -1,584 +0,0 @@
|
||||
---
|
||||
weight: 12
|
||||
menu:
|
||||
docs:
|
||||
parent: victoriametrics
|
||||
weight: 12
|
||||
title: vmestimator
|
||||
tags:
|
||||
- metrics
|
||||
- cardinality
|
||||
aliases:
|
||||
- /vmestimator.html
|
||||
- /vmestimator/index.html
|
||||
- /vmestimator/
|
||||
---
|
||||
|
||||
`vmestimator` measures metrics cardinality across arbitrary label dimensions and exposes the results as metrics.
|
||||
|
||||
## Why measure?
|
||||
|
||||
Consider a setup where metrics are scraped from dozens of Prometheus targets.
|
||||
One day, a team deploys a new version of their service with a `trace_id` or `user_id` label.
|
||||
Overnight, that job's cardinality explodes from 500 to 500,000 time series.
|
||||
Suddenly, VictoriaMetrics consumes 100x more memory and disk.
|
||||
Ingestion slows down, storage struggles to keep up, and in the worst case becomes unavailable.
|
||||
|
||||
By the time someone gets paged, the damage is already done: indexes are bloated, caches are oversized, and observability across the entire system is affected.
|
||||
|
||||
`vmestimator` continuously tracks cardinality and exposes the estimation results as [metrics](https://github.com/VictoriaMetrics/vmestimator/blob/main/README.md#cardinality-metrics).
|
||||
This allows alerting on cardinality spikes within minutes and identifying the offending job directly from the alert.
|
||||
Instead of discovering the problem after it impacts the infrastructure, it becomes possible to react before it turns into an outage.
|
||||
|
||||
Per-job cardinality tracking is the most actionable use case, but it’s not the only one (see [use cases](https://github.com/VictoriaMetrics/vmestimator/#use-cases)).
|
||||
`vmestimator` can measure cardinality across arbitrary label dimensions,
|
||||
enabling use cases such as per-tenant usage analysis, long-term trend tracking, and capacity planning.
|
||||
|
||||
## Design
|
||||
|
||||
We recommend deploying `vmestimator` close to the metrics source, ideally alongside `vmagent` instances that scrape targets.
|
||||
Each `vmagent` mirrors all ingested metrics into the estimator.
|
||||
|
||||
To reduce overhead, persistent queueing and metadata ingestion can be disabled for the estimator remote write path.
|
||||
It is safe to send metrics from multiple independent `vmagent` instances into a single `vmestimator`.
|
||||
|
||||
Run vmestimator (see [configuration](https://github.com/VictoriaMetrics/vmestimator#configuration)):
|
||||
```bash
|
||||
/path/to/vmestimator -config=streams.yaml # -httpListenAddr=:8490
|
||||
```
|
||||
|
||||
Run vmagent:
|
||||
```bash
|
||||
/path/to/vmagent \
|
||||
-remoteWrite.url=http://127.0.0.1:8428/api/v1/write \
|
||||
-remoteWrite.url=http://127.0.0.1:8490/cardinality/api/v1/write \
|
||||
-remoteWrite.disableOnDiskQueue=false,true \
|
||||
-remoteWrite.disableMetadata=false,true
|
||||
```
|
||||
|
||||
The next step is to expose cardinality estimates as metrics.
|
||||
For this, `vmagent` should scrape the estimator `/metrics` endpoint and forward those metrics to a `vmsingle` instance (or another VictoriaMetrics storage).
|
||||
|
||||
<img style="min-width:0;width: 100%" src="https://github.com/user-attachments/assets/e52d9210-b6f9-457b-8d8f-1d6ff6ba1416" />
|
||||
|
||||
This setup is straightforward and introduces minimal overhead.
|
||||
The main drawback is that cardinality data shares the same storage with production metrics.
|
||||
If that storage becomes unavailable, the visibility into cardinality is lost precisely when it may be most needed.
|
||||
|
||||
To mitigate this, we recommend running a separate `vmsingle` instance dedicated to scraping and storing VictoriaMetrics-related monitoring signals only.
|
||||
This pattern is commonly referred to as a monitoring-of-monitoring (MoM) setup.
|
||||
In this architecture, `vmestimator` metrics are isolated from production observability storage,
|
||||
ensuring cardinality visibility remains available even during incidents affecting the primary monitoring system.
|
||||
|
||||
The resulting topology looks like this:
|
||||
<img style="min-width:0;width: 100%" src="https://github.com/user-attachments/assets/e2ca4a69-e931-47a1-9d91-99749382d4a9" />
|
||||
|
||||
## Install
|
||||
|
||||
Create a `streams.yaml` from [example config](https://github.com/VictoriaMetrics/vmestimator/blob/main/streams.yaml).
|
||||
Run the Docker image from [Docker Hub](https://hub.docker.com/r/victoriametrics/vmestimator) or [Quay](https://quay.io/repository/victoriametrics/vmestimator), mounting your config file:
|
||||
```bash
|
||||
docker run --rm \
|
||||
-p 8490:8490 \
|
||||
-v /path/to/streams.yaml:/streams.yaml \
|
||||
docker.io/victoriametrics/vmestimator:latest \
|
||||
-config=/streams.yaml
|
||||
```
|
||||
|
||||
See [Use Cases](https://github.com/VictoriaMetrics/vmestimator#use-cases) for more configuration examples and
|
||||
[Command-line flags](https://github.com/VictoriaMetrics/vmestimator#command-line-flags) for all available options.
|
||||
|
||||
To build from sources, see [How to build from sources](https://github.com/VictoriaMetrics/vmestimator#how-to-build-from-sources).
|
||||
|
||||
## Configuration
|
||||
|
||||
To run vmestimator a `streams.yaml` config has to be provided (see [example config](https://github.com/VictoriaMetrics/vmestimator/blob/main/streams.yaml)):
|
||||
|
||||
```bash
|
||||
/path/to/vmestimator -config=streams.yaml # -httpListenAddr=:8490
|
||||
```
|
||||
|
||||
Config reference:
|
||||
```yaml
|
||||
streams:
|
||||
-
|
||||
# The measurement window: how long unique series are retained before the HLL sketch resets.
|
||||
# Increases are always reflected immediately. Interval only controls how fast the estimate
|
||||
# drops after previously seen series disappear.
|
||||
#
|
||||
# Running two streams with different intervals (e.g. 5m and 1h) lets you derive churn rate
|
||||
# by comparing their estimates. See Use Cases -> Churn Rate
|
||||
#
|
||||
# default: 5m
|
||||
interval: 'golang duration'
|
||||
|
||||
# Label names used to split the cardinality estimate into per-combination groups.
|
||||
# Each distinct combination of values for these labels gets its own estimate metric.
|
||||
# Omit entirely for a single global estimate across all series.
|
||||
# Examples:
|
||||
# - ["job"]
|
||||
# - ["__name__"]
|
||||
# - ["vm_account_id","vm_project_id"]
|
||||
#
|
||||
# default: none (single global estimate)
|
||||
group_by: 'string array'
|
||||
|
||||
# Maximum number of distinct groups (HLL sketches) to track.
|
||||
# Once the limit is reached, excess groups are counted in a single shared "rejected" sketch
|
||||
# rather than getting their own entry. Acts as a memory cap and a safeguard against OOM
|
||||
# when the group_by label values grow unboundedly.
|
||||
# Memory upper bound per stream:
|
||||
# group_limit * 2^hll_precision bytes.
|
||||
#
|
||||
# default: 10000
|
||||
group_limit: 'integer'
|
||||
|
||||
# Number of shards used to reduce lock contention during parallel ingestion.
|
||||
# Slightly increases memory for global streams (no group_by); negligible otherwise.
|
||||
# Leave at the default unless you have profiled lock contention or have a specific reason to change it.
|
||||
#
|
||||
# default: min(64, 2*availableCPUs)
|
||||
buckets: 'integer'
|
||||
|
||||
# HyperLogLog precision p, in range [4..18].
|
||||
# Determines the number of registers m = 2^p and the relative error 1.04 / sqrt(m):
|
||||
# p=14 → m=16 384, error ~0.81%, memory ~16 KB per sketch (default, suits most cases)
|
||||
# p=18 → m=262 144, error ~0.20%, memory ~256 KB per sketch (billing-grade accuracy)
|
||||
# p=10 → m=1 024, error ~3.25%, memory ~1 KB per sketch (thousands of groups, memory-tight)
|
||||
# See more in https://research.google.com/pubs/archive/40671.pdf
|
||||
#
|
||||
# default: 14
|
||||
hll_precision: 'integer'
|
||||
|
||||
# Whether to use the sparse HyperLogLog representation for low-cardinality groups.
|
||||
# Sparse mode uses far less memory until a group's cardinality reaches ~2^(p-1),
|
||||
# at which point it automatically promotes to the dense representation.
|
||||
# See more in https://research.google.com/pubs/archive/40671.pdf
|
||||
#
|
||||
# default: true
|
||||
hll_sparse: 'boolean'
|
||||
|
||||
# Static labels attached to every output metric produced by this stream entry.
|
||||
# Useful when multiple vmestimator instances feed the same storage and you need
|
||||
# to distinguish their estimates in dashboards and alerts.
|
||||
labels: 'map key string: value string'
|
||||
```
|
||||
|
||||
## Cardinality Metrics
|
||||
|
||||
Cardinality estimates are exposed as the `cardinality_estimate` metric.
|
||||
All metrics include `interval`, `group_by_keys`, `group_by_values`, and any static labels defined in the stream config.
|
||||
|
||||
For global estimates (no `group_by` configured), `group_by_keys` is `__global__` and `group_by_values` is omitted:
|
||||
```
|
||||
cardinality_estimate{interval="1h0m0s",group_by_keys="__global__"} 142300
|
||||
```
|
||||
|
||||
For grouped estimates, one summary line shows the total number of distinct groups `group_by_keys="__group__"`, followed by one line per distinct label value combination.
|
||||
Each per-group line also includes individual `by_{key}="{val}"` labels:
|
||||
```
|
||||
cardinality_estimate{interval="5m0s",group_by_keys="__group__",group_by_values="instance,job"} 2
|
||||
cardinality_estimate{interval="5m0s",group_by_keys="instance,job",group_by_values="host1:9090,prometheus",by_instance="host1:9090",by_job="prometheus"} 312
|
||||
cardinality_estimate{interval="5m0s",group_by_keys="instance,job",group_by_values="host2:9100,node",by_instance="host2:9100",by_job="node"} 87
|
||||
```
|
||||
|
||||
Note: the total distinct group count in the summary line may exceed the number of per-group lines when `group_limit` is reached
|
||||
and excess groups are counted in a single shared "rejected" sketch rather than getting their own entry.
|
||||
|
||||
By default, cardinality estimates are merged with the estimator's operational metrics and exposed at `/metrics`.
|
||||
This is controlled by the `-cardinalityMetrics.exposeAt` flag:
|
||||
- `-cardinalityMetrics.exposeAt=/metrics` (default): cardinality metrics merged with operational metrics at `/metrics`
|
||||
- `-cardinalityMetrics.exposeAt=/cardinality/metrics`: cardinality metrics exposed at separate path
|
||||
- `-cardinalityMetrics.exposeAt=`: cardinality metrics not exposed via HTTP
|
||||
|
||||
Computing cardinality estimates is expensive, so results are cached.
|
||||
Cache duration is controlled by `-cardinalityMetrics.cacheTTL` (default: `30s`).
|
||||
Set to `0` to disable caching entirely.
|
||||
|
||||
## Use Cases
|
||||
|
||||
### Basic
|
||||
|
||||
Global cardinality:
|
||||
```yaml
|
||||
# streams.yaml
|
||||
|
||||
- interval: '5m'
|
||||
```
|
||||
|
||||
Per metric name cardinality:
|
||||
```yaml
|
||||
# streams.yaml
|
||||
|
||||
- interval: '5m'
|
||||
group_by: ['__name__']
|
||||
```
|
||||
|
||||
Per job label cardinality:
|
||||
```yaml
|
||||
# streams.yaml
|
||||
|
||||
- interval: '5m'
|
||||
group_by: ['job']
|
||||
```
|
||||
|
||||
Per tenant cardinality:
|
||||
```yaml
|
||||
# streams.yaml
|
||||
|
||||
- interval: '5m'
|
||||
group_by: ['vm_account_id', 'vm_project_id']
|
||||
```
|
||||
|
||||
### Churn calculation
|
||||
|
||||
[Churn rate](https://valyala.medium.com/prometheus-storage-technical-terms-for-humans-4ab4de6c3d48#churn-rate) measures how quickly time series are created and disappear.
|
||||
[High churn](https://docs.victoriametrics.com/victoriametrics/faq/#what-is-high-churn-rate) means many series appear briefly and are replaced by new ones.
|
||||
This puts pressure on storage, because each new series must be indexed regardless of how short its lifetime is.
|
||||
|
||||
To measure churn, configure two streams with the same `group_by` but different intervals. A short one (`15m`) and a long one (`30m`):
|
||||
```yaml
|
||||
# streams.yaml
|
||||
|
||||
- interval: '15m'
|
||||
group_by: ['job']
|
||||
|
||||
- interval: '30m'
|
||||
group_by: ['job']
|
||||
```
|
||||
|
||||
When churn is low, both estimates are roughly equal.
|
||||
When churn is high, the `30m` estimate grows significantly larger than the `15m` estimate, because the long window accumulates series that have already disappeared.
|
||||
|
||||
The following query computes the churn ratio per job:
|
||||
```
|
||||
(
|
||||
sum(
|
||||
max(cardinality_estimate{group_by_keys="job",interval="30m0s"}) without (instance)
|
||||
) by (group_by_keys,group_by_values)
|
||||
-
|
||||
sum(
|
||||
max(cardinality_estimate{group_by_keys="job",interval="15m0s"}) without (instance)
|
||||
) by (group_by_keys,group_by_values)
|
||||
)
|
||||
/
|
||||
sum(
|
||||
max(cardinality_estimate{group_by_keys="job",interval="30m0s"}) without (instance)
|
||||
) by (group_by_keys,group_by_values) * 100
|
||||
```
|
||||
|
||||
A result near `0` means the series set is stable. The same series were active throughout the entire hour.
|
||||
A result near `1` means complete churn. Entirely different series appeared each 5-minute window.
|
||||
Values in between indicate the fraction of maximum possible churn that is occurring.
|
||||
|
||||
This helps identify jobs that create the most indexing pressure on storage, even when their current active cardinality appears moderate.
|
||||
|
||||
### Alerting
|
||||
|
||||
Pre-built alert rules for cardinality monitoring are available in
|
||||
[deployment/docker/rules/alerts-cardinality.yml](https://github.com/VictoriaMetrics/vmestimator/blob/main/deployment/docker/rules/alerts-cardinality.yml).
|
||||
|
||||
They require two streams with the same `group_by` but different intervals to also support churn detection:
|
||||
```yaml
|
||||
# streams.yaml
|
||||
# or use example config:
|
||||
# https://github.com/VictoriaMetrics/vmestimator/blob/main/streams.yaml
|
||||
|
||||
- interval: '15m'
|
||||
group_by: ['job']
|
||||
|
||||
- interval: '30m'
|
||||
group_by: ['job']
|
||||
```
|
||||
|
||||
The included alerts are:
|
||||
|
||||
- **JobTooHighCardinality** — fires when any job exceeds 20,000 estimated active series over the last 30 minutes.
|
||||
The threshold is a starting point and should be calibrated to reflect the expected cardinality of your largest jobs.
|
||||
|
||||
- **JobTooHighChurnRate** — fires when more than 10% of a job's series churned between the 15m and 30m windows.
|
||||
Catches jobs that generate continuous indexing pressure even when their active series count looks moderate.
|
||||
|
||||
- **CardinalityGroupLimitNearlyReached** — fires when the number of tracked groups exceeds 80% of the configured `group_limit`.
|
||||
Acts as an early warning that some label value combinations may soon be dropped from individual tracking.
|
||||
|
||||
- **CardinalityGroupLimitReached** — fires when groups are actively rejected because `group_limit` is full.
|
||||
At this point, some label combinations are being counted in a shared "rejected" sketch rather than tracked individually.
|
||||
|
||||
All alerts link to the [Cardinality Explorer dashboard](https://play-grafana.victoriametrics.com/d/mktd5h8/).
|
||||
|
||||
## Alternative solutions
|
||||
|
||||
### PromQL
|
||||
|
||||
Cardinality can be estimated with PromQL.
|
||||
|
||||
Global cardinality:
|
||||
```
|
||||
count({__name__=~".*"})
|
||||
```
|
||||
|
||||
Top ten metric names by cardinality:
|
||||
```
|
||||
topk(10, count({__name__=~".*"}) by (__name__))
|
||||
```
|
||||
|
||||
Top ten jobs by cardinality:
|
||||
```
|
||||
topk(10, count({__name__=~".*"}) by (job))
|
||||
```
|
||||
|
||||
This approach works for small setups but does not scale well, because these queries scan the entire time series set.
|
||||
Most critically, if the storage is overloaded or unavailable, these queries could not be executed.
|
||||
|
||||
### Cardinality Explorer
|
||||
|
||||
VictoriaMetrics includes a built-in [cardinality explorer](https://docs.victoriametrics.com/victoriametrics/single-server-victoriametrics/#cardinality-explorer).
|
||||
It provides per-metric detail beyond raw series counts: query frequency, last access time, day-over-day change, and share of total cardinality.
|
||||
It is well suited for in-depth, ad-hoc investigation.
|
||||
For example, finding metrics that are high-cardinality but rarely queried,
|
||||
so they can be [dropped via relabeling](https://docs.victoriametrics.com/victoriametrics/relabeling/#how-to-drop-metrics-during-scrape) or reduce cardinality with [stream aggregation](https://docs.victoriametrics.com/victoriametrics/stream-aggregation/).
|
||||
|
||||
Both tools serve different purposes and work well together.
|
||||
Use `vmestimator` for continuous monitoring, alerting, and cross-cluster cardinality tracking.
|
||||
Use the cardinality explorer when you need to drill into a specific metric or label and understand what is driving its cardinality.
|
||||
|
||||
## Cluster
|
||||
|
||||
`vmestimator` supports a clustered deployment for high availability or when CPU on a single instance becomes a limiting factor.
|
||||
|
||||
Instances are split into two roles: **storage nodes** accept Prometheus remote write and maintain local HyperLogLog sketches; **selector nodes** query all storage nodes, merge their sketches, and expose a unified cardinality estimate. Cardinality estimate results should be scraped from selector nodes.
|
||||
|
||||
<img style="min-width:0;width: 100%" src="https://github.com/user-attachments/assets/846e5f77-378a-44dc-a4c8-2a1c64eca9d8" />
|
||||
|
||||
**Storage nodes:**
|
||||
```
|
||||
vmestimator -config=streams.yaml -httpListenAddr=:8491 -cardinalityMetrics.exposeAt=/cardinality/metrics
|
||||
vmestimator -config=streams.yaml -httpListenAddr=:8492 -cardinalityMetrics.exposeAt=/cardinality/metrics
|
||||
vmestimator -config=streams.yaml -httpListenAddr=:8493 -cardinalityMetrics.exposeAt=/cardinality/metrics
|
||||
```
|
||||
|
||||
**Selector nodes:**
|
||||
```
|
||||
vmestimator -storageNode=http://vmestimator-storage-1:8491 \
|
||||
-storageNode=http://vmestimator-storage-2:8492 \
|
||||
-storageNode=http://vmestimator-storage-3:8493 \
|
||||
-httpListenAddr=:8490
|
||||
```
|
||||
|
||||
Setting `-cardinalityMetrics.exposeAt=/cardinality/metrics` on storage nodes keeps per-node estimates off the default `/metrics` path. The `/metrics` endpoint then returns only operational metrics, while `/cardinality/metrics` exposes the node's local estimate — useful for inspecting or debugging a specific node.
|
||||
|
||||
A selector with `-storageNode` flags and no `-config` runs without local estimators and only merges remote data.
|
||||
|
||||
When multiple selector nodes are scraped, each returns a fully merged estimate.
|
||||
Deduplicate at query time to avoid overcounting:
|
||||
```
|
||||
max(cardinality_estimate) without (instance)
|
||||
```
|
||||
|
||||
## Operational metrics
|
||||
|
||||
When grouping is enabled, vmestimator exposes per-bucket operational metrics at `/metrics`:
|
||||
|
||||
- `vmestimator_estimator_group_size{group_by_keys, bucket}` — number of active groups in this bucket after the last rotation
|
||||
- `vmestimator_estimator_group_rejected_size{group_by_keys}` — estimated number of distinct group values rejected since the last rotation because `group_limit` was reached
|
||||
- `vmestimator_estimator_group_limit{group_by_keys, bucket}` — configured `group_limit` for this bucket
|
||||
|
||||
|
||||
## Dashboards
|
||||
|
||||
Two Grafana dashboards are available in the [dashboards](https://github.com/VictoriaMetrics/vmestimator/tree/main/dashboards) directory:
|
||||
|
||||
- [VictoriaMetrics - vmestimator](https://play-grafana.victoriametrics.com/d/mkv22l4/victoriametrics-vmestimator) — application health: CPU, memory, ingestion rates, concurrent inserts, and group key saturation.
|
||||
<img width="1507" height="801" alt="Screenshot 2026-06-29 at 19 06 46" src="https://github.com/user-attachments/assets/cbfd979d-f403-4270-b098-2d2f0b392172" />
|
||||
|
||||
- [VictoriaMetrics - Cardinality Explorer](https://play-grafana.victoriametrics.com/d/mktd5h8/victoriametrics-cardinality-explorer) — cardinality analysis: global estimates, per-group-key series counts, and top-10 highest-cardinality label value combinations.
|
||||
<img width="1510" height="796" alt="Screenshot 2026-06-29 at 19 05 47" src="https://github.com/user-attachments/assets/a1aea6e1-8714-4d5a-a629-8bdee978f1c6" />
|
||||
|
||||
## How to build from sources
|
||||
|
||||
It is recommended to use the [docker images](https://hub.docker.com/r/victoriametrics/vmestimator).
|
||||
|
||||
Development build:
|
||||
1. [Install Go](https://golang.org/doc/install).
|
||||
1. Run `make vmestimator` from the root folder of [the repository](https://github.com/VictoriaMetrics/vmestimator).
|
||||
It builds `vmestimator` binary and places it into the `bin` folder.
|
||||
|
||||
Production build:
|
||||
1. [Install docker](https://docs.docker.com/install/).
|
||||
1. Run `make vmestimator-prod` from the root folder of [the repository](https://github.com/VictoriaMetrics/vmestimator).
|
||||
It builds `vmestimator-prod` binary and puts it into the `bin` folder.
|
||||
|
||||
Building docker images:
|
||||
|
||||
Run `make package-vmestimator`. It builds `victoriametrics/vmestimator:<PKG_TAG>` docker image locally.
|
||||
`<PKG_TAG>` is auto-generated image tag, which depends on source code in the repository.
|
||||
The `<PKG_TAG>` may be manually set via `PKG_TAG=foobar make package-vmestimator`.
|
||||
|
||||
The base docker image is [alpine](https://hub.docker.com/_/alpine) but it is possible to use any other base image by setting it via `<ROOT_IMAGE>` environment variable.
|
||||
For example, the following command builds the image on top of [scratch](https://hub.docker.com/_/scratch) image:
|
||||
|
||||
```sh
|
||||
ROOT_IMAGE=scratch make package-vmestimator
|
||||
```
|
||||
|
||||
You can build and publish to your own registry and namespace:
|
||||
```
|
||||
DOCKER_REGISTRIES=ghcr.io DOCKER_NAMESPACE=foo make publish-vmestimator
|
||||
```
|
||||
|
||||
## Command-line flags
|
||||
|
||||
Run `vmestimator -help` in order to see all the available options:
|
||||
|
||||
```
|
||||
Usage of ./bin/vmestimator:
|
||||
-cardinalityMetrics.cacheTTL duration
|
||||
Duration for caching cardinality metrics response (default 30s)
|
||||
-cardinalityMetrics.exposeAt string
|
||||
HTTP path for exposing cardinality metrics. If set to the default /metrics, cardinality metrics are merged with regular metrics and exposed together. If set to a different path, only cardinality metrics are exposed at that endpoint. If set to an empty value, cardinality metrics are not exposed via HTTP at all. (default "/metrics")
|
||||
-config string
|
||||
Path to YAML configuration file. Must be set unless -storageNode is specified. See https://github.com/VictoriaMetrics/vmestimator/blob/main/streams.yaml for config example
|
||||
-enableTCP6
|
||||
Whether to enable IPv6 for listening and dialing. By default, only IPv4 TCP and UDP are used
|
||||
-envflag.enable
|
||||
Whether to enable reading flags from environment variables in addition to the command line. Command line flag values have priority over values from environment vars. Flags are read only from the command line if this flag isn't set. See https://docs.victoriametrics.com/victoriametrics/single-server-victoriametrics/#environment-variables for more details
|
||||
-envflag.prefix string
|
||||
Prefix for environment variables if -envflag.enable is set
|
||||
-filestream.disableFadvise
|
||||
Whether to disable fadvise() syscall when reading large data files. The fadvise() syscall prevents from eviction of recently accessed data from OS page cache during background merges and backups. In some rare cases it is better to disable the syscall if it uses too much CPU
|
||||
-flagsAuthKey value
|
||||
Auth key for /flags endpoint. It must be passed via authKey query arg. It overrides -httpAuth.*
|
||||
Flag value can be read from the given file when using -flagsAuthKey=file:///abs/path/to/file or -flagsAuthKey=file://./relative/path/to/file.
|
||||
Flag value can be read from the given http/https url when using -flagsAuthKey=http://host/path or -flagsAuthKey=https://host/path
|
||||
-fs.maxConcurrency int
|
||||
The maximum number of concurrent goroutines to work with files; smaller values may help reducing Go scheduling latency on systems with small number of CPU cores; higher values may help reducing data ingestion latency on systems with high-latency storage such as NFS or Ceph (default 160)
|
||||
-http.connTimeout duration
|
||||
Incoming connections to -httpListenAddr are closed after the configured timeout. This may help evenly spreading load among a cluster of services behind TCP-level load balancer. Zero value disables closing of incoming connections (default 2m0s)
|
||||
-http.disableCORS
|
||||
Disable CORS for all origins (*)
|
||||
-http.disableKeepAlive
|
||||
Whether to disable HTTP keep-alive for incoming connections at -httpListenAddr
|
||||
-http.disableResponseCompression
|
||||
Disable compression of HTTP responses to save CPU resources. By default, compression is enabled to save network bandwidth
|
||||
-http.header.csp string
|
||||
Value for 'Content-Security-Policy' header, recommended: "default-src 'self'"
|
||||
-http.header.disableServerHostname
|
||||
Whether to disable 'X-Server-Hostname' header in HTTP responses
|
||||
-http.header.frameOptions string
|
||||
Value for 'X-Frame-Options' header
|
||||
-http.header.hsts string
|
||||
Value for 'Strict-Transport-Security' header, recommended: 'max-age=31536000; includeSubDomains'
|
||||
-http.idleConnTimeout duration
|
||||
Timeout for incoming idle http connections (default 1m0s)
|
||||
-http.maxGracefulShutdownDuration duration
|
||||
The maximum duration for a graceful shutdown of the HTTP server. A highly loaded server may require increased value for a graceful shutdown (default 7s)
|
||||
-http.pathPrefix string
|
||||
An optional prefix to add to all the paths handled by http server. For example, if '-http.pathPrefix=/foo/bar' is set, then all the http requests will be handled on '/foo/bar/*' paths. This may be useful for proxied requests. See https://www.robustperception.io/using-external-urls-and-proxies-with-prometheus
|
||||
-http.shutdownDelay duration
|
||||
Optional delay before http server shutdown. During this delay, the server returns non-OK responses from /health page, so load balancers can route new requests to other servers
|
||||
-httpAuth.password value
|
||||
Password for HTTP server's Basic Auth. The authentication is disabled if -httpAuth.username is empty
|
||||
Flag value can be read from the given file when using -httpAuth.password=file:///abs/path/to/file or -httpAuth.password=file://./relative/path/to/file.
|
||||
Flag value can be read from the given http/https url when using -httpAuth.password=http://host/path or -httpAuth.password=https://host/path
|
||||
-httpAuth.username string
|
||||
Username for HTTP server's Basic Auth. The authentication is disabled if empty. See also -httpAuth.password
|
||||
-httpListenAddr array
|
||||
TCP address to listen for incoming HTTP requests
|
||||
Supports an array of values separated by comma or specified via multiple flags.
|
||||
Each array item can contain comma inside single-quoted or double-quoted string, {}, [] and () braces.
|
||||
-insert.maxQueueDuration duration
|
||||
The maximum duration to wait in the queue when -maxConcurrentInserts concurrent insert requests are executed (default 1m0s)
|
||||
-internStringCacheExpireDuration duration
|
||||
The expiry duration for caches for interned strings. See https://en.wikipedia.org/wiki/String_interning . See also -internStringMaxLen and -internStringDisableCache (default 6m0s)
|
||||
-internStringDisableCache
|
||||
Whether to disable caches for interned strings. This may reduce memory usage at the cost of higher CPU usage. See https://en.wikipedia.org/wiki/String_interning . See also -internStringCacheExpireDuration and -internStringMaxLen
|
||||
-internStringMaxLen int
|
||||
The maximum length for strings to intern. A lower limit may save memory at the cost of higher CPU usage. See https://en.wikipedia.org/wiki/String_interning . See also -internStringDisableCache and -internStringCacheExpireDuration (default 500)
|
||||
-loggerDisableTimestamps
|
||||
Whether to disable writing timestamps in logs
|
||||
-loggerErrorsPerSecondLimit int
|
||||
Per-second limit on the number of ERROR messages. If more than the given number of errors are emitted per second, the remaining errors are suppressed. Zero values disable the rate limit
|
||||
-loggerFormat string
|
||||
Format for logs. Possible values: default, json (default "default")
|
||||
-loggerJSONFields string
|
||||
Allows renaming fields in JSON formatted logs. Example: "ts:timestamp,msg:message" renames "ts" to "timestamp" and "msg" to "message". Supported fields: ts, level, caller, msg
|
||||
-loggerLevel string
|
||||
Minimum level of errors to log. Possible values: INFO, WARN, ERROR, FATAL, PANIC (default "INFO")
|
||||
-loggerMaxArgLen int
|
||||
The maximum length of a single logged argument. Longer arguments are replaced with 'arg_start..arg_end', where 'arg_start' and 'arg_end' is prefix and suffix of the arg with the length not exceeding -loggerMaxArgLen / 2 (default 5000)
|
||||
-loggerOutput string
|
||||
Output for the logs. Supported values: stderr, stdout (default "stderr")
|
||||
-loggerTimezone string
|
||||
Timezone to use for timestamps in logs. Timezone must be a valid IANA Time Zone. For example: America/New_York, Europe/Berlin, Etc/GMT+3 or Local (default "UTC")
|
||||
-loggerWarnsPerSecondLimit int
|
||||
Per-second limit on the number of WARN messages. If more than the given number of warns are emitted per second, then the remaining warns are suppressed. Zero values disable the rate limit
|
||||
-maxConcurrentInserts int
|
||||
The maximum number of concurrent insert requests. Set higher value when clients send data over slow networks. Default value depends on the number of available CPU cores. It should work fine in most cases since it minimizes resource usage. See also -insert.maxQueueDuration (default 20)
|
||||
-maxInsertRequestSize size
|
||||
The maximum size in bytes of a single Prometheus remote_write API request
|
||||
Supports the following optional suffixes for size values: KB, MB, GB, TB, KiB, MiB, GiB, TiB (default 33554432)
|
||||
-memory.allowedBytes size
|
||||
Allowed size of system memory VictoriaMetrics caches may occupy. This option overrides -memory.allowedPercent if set to a non-zero value. Too low a value may increase the cache miss rate usually resulting in higher CPU and disk IO usage. Too high a value may evict too much data from the OS page cache resulting in higher disk IO usage. The process may behave unexpectedly if this flag is set too small (e.g., 1 byte).
|
||||
Supports the following optional suffixes for size values: KB, MB, GB, TB, KiB, MiB, GiB, TiB (default 0)
|
||||
-memory.allowedPercent float
|
||||
Allowed percent of system memory VictoriaMetrics caches may occupy. See also -memory.allowedBytes. Too low a value may increase cache miss rate usually resulting in higher CPU and disk IO usage. Too high a value may evict too much data from the OS page cache which will result in higher disk IO usage (default 60)
|
||||
-metrics.exposeMetadata
|
||||
Whether to expose TYPE and HELP metadata at the /metrics page, which is exposed at -httpListenAddr . The metadata may be needed when the /metrics page is consumed by systems, which require this information. For example, Managed Prometheus in Google Cloud - https://cloud.google.com/stackdriver/docs/managed-prometheus/troubleshooting#missing-metric-type
|
||||
-metricsAuthKey value
|
||||
Auth key for /metrics endpoint. It must be passed via authKey query arg. It overrides -httpAuth.*
|
||||
Flag value can be read from the given file when using -metricsAuthKey=file:///abs/path/to/file or -metricsAuthKey=file://./relative/path/to/file.
|
||||
Flag value can be read from the given http/https url when using -metricsAuthKey=http://host/path or -metricsAuthKey=https://host/path
|
||||
-pprofAuthKey value
|
||||
Auth key for /debug/pprof/* endpoints. It must be passed via authKey query arg. It overrides -httpAuth.*
|
||||
Flag value can be read from the given file when using -pprofAuthKey=file:///abs/path/to/file or -pprofAuthKey=file://./relative/path/to/file.
|
||||
Flag value can be read from the given http/https url when using -pprofAuthKey=http://host/path or -pprofAuthKey=https://host/path
|
||||
-pushmetrics.disableCompression
|
||||
Whether to disable request body compression when pushing metrics to every -pushmetrics.url
|
||||
-pushmetrics.extraLabel array
|
||||
Optional labels to add to metrics pushed to every -pushmetrics.url . For example, -pushmetrics.extraLabel='instance="foo"' adds instance="foo" label to all the metrics pushed to every -pushmetrics.url
|
||||
Supports an array of values separated by comma or specified via multiple flags.
|
||||
Each array item can contain comma inside single-quoted or double-quoted string, {}, [] and () braces.
|
||||
-pushmetrics.header array
|
||||
Optional HTTP request header to send to every -pushmetrics.url . For example, -pushmetrics.header='Authorization: Basic foobar' adds 'Authorization: Basic foobar' header to every request to every -pushmetrics.url
|
||||
Supports an array of values separated by comma or specified via multiple flags.
|
||||
Each array item can contain comma inside single-quoted or double-quoted string, {}, [] and () braces.
|
||||
-pushmetrics.interval duration
|
||||
Interval for pushing metrics to every -pushmetrics.url (default 10s)
|
||||
-pushmetrics.url array
|
||||
Optional URL to push metrics exposed at /metrics page. See https://docs.victoriametrics.com/victoriametrics/single-server-victoriametrics/#push-metrics . By default, metrics exposed at /metrics page aren't pushed to any remote storage
|
||||
Supports an array of values separated by comma or specified via multiple flags.
|
||||
Each array item can contain comma inside single-quoted or double-quoted string, {}, [] and () braces.
|
||||
-secret.flags array
|
||||
Comma-separated list of flag names with secret values. Values for these flags are hidden in logs and on /metrics page
|
||||
Supports an array of values separated by comma or specified via multiple flags.
|
||||
Each array item can contain comma inside single-quoted or double-quoted string, {}, [] and () braces.
|
||||
-storageNode array
|
||||
HTTP URLs of remote vmestimator nodes to query for cardinality snapshots, e.g. http://vmestimator-2:8490
|
||||
Supports an array of values separated by comma or specified via multiple flags.
|
||||
Each array item can contain comma inside single-quoted or double-quoted string, {}, [] and () braces.
|
||||
-tls array
|
||||
Whether to enable TLS for incoming HTTP requests at the given -httpListenAddr (aka https). -tlsCertFile and -tlsKeyFile must be set if -tls is set. See also -mtls
|
||||
Supports array of values separated by comma or specified via multiple flags.
|
||||
Empty values are set to false.
|
||||
-tlsCertFile array
|
||||
Path to file with TLS certificate for the corresponding -httpListenAddr if -tls is set. Prefer ECDSA certs instead of RSA certs as RSA certs are slower. The provided certificate file is automatically re-read every second, so it can be dynamically updated. See also -tlsAutocertHosts
|
||||
Supports an array of values separated by comma or specified via multiple flags.
|
||||
Each array item can contain comma inside single-quoted or double-quoted string, {}, [] and () braces.
|
||||
-tlsCipherSuites array
|
||||
Optional list of TLS cipher suites for incoming requests over HTTPS if -tls is set. See the list of supported cipher suites at https://pkg.go.dev/crypto/tls#pkg-constants
|
||||
Supports an array of values separated by comma or specified via multiple flags.
|
||||
Each array item can contain comma inside single-quoted or double-quoted string, {}, [] and () braces.
|
||||
-tlsKeyFile array
|
||||
Path to file with TLS key for the corresponding -httpListenAddr if -tls is set. The provided key file is automatically re-read every second, so it can be dynamically updated. See also -tlsAutocertHosts
|
||||
Supports an array of values separated by comma or specified via multiple flags.
|
||||
Each array item can contain comma inside single-quoted or double-quoted string, {}, [] and () braces.
|
||||
-tlsMinVersion array
|
||||
Optional minimum TLS version to use for the corresponding -httpListenAddr if -tls is set. Supported values: TLS10, TLS11, TLS12, TLS13
|
||||
Supports an array of values separated by comma or specified via multiple flags.
|
||||
Each array item can contain comma inside single-quoted or double-quoted string, {}, [] and () braces.
|
||||
-version
|
||||
Show VictoriaMetrics version
|
||||
```
|
||||
|
||||
|
||||
@@ -85,7 +85,7 @@ See the docs at https://docs.victoriametrics.com/victoriametrics/cluster-victori
|
||||
-http.idleConnTimeout duration
|
||||
Timeout for incoming idle http connections (default 1m0s)
|
||||
-http.maxGracefulShutdownDuration duration
|
||||
The maximum duration for a graceful shutdown of the HTTP server. A highly loaded server may require increased value for a graceful shutdown (default 7s)
|
||||
The maximum duration for a graceful shutdown of the HTTP server. During this period the server stops accepting new connections, but it will continue serving existing connections. The remaining in-flight requests are canceled before the deadline, so the shutdown can finish within this duration. A highly loaded server may require increased value for a graceful shutdown (default 7s)
|
||||
-http.pathPrefix string
|
||||
An optional prefix to add to all the paths handled by http server. For example, if '-http.pathPrefix=/foo/bar' is set, then all the http requests will be handled on '/foo/bar/*' paths. This may be useful for proxied requests. See https://www.robustperception.io/using-external-urls-and-proxies-with-prometheus
|
||||
-http.shutdownDelay duration
|
||||
@@ -165,9 +165,9 @@ See the docs at https://docs.victoriametrics.com/victoriametrics/cluster-victori
|
||||
The maximum size in bytes of a single Prometheus remote_write API request
|
||||
Supports the following optional suffixes for size values: KB, MB, GB, TB, KiB, MiB, GiB, TiB (default 33554432)
|
||||
-maxLabelNameLen int
|
||||
The maximum length of label name in the accepted time series. Series with longer label name are ignored. In this case the vm_rows_ignored_total{reason="too_long_label_name"} metric at /metrics page is incremented (default 256)
|
||||
The maximum length of label name in the accepted time series. Series with longer label name are ignored. In this case the vm_rows_ignored_total{reason="too_long_label_name"} metric at /metrics page is incremented. Value must be in range 1..65535. (default 256)
|
||||
-maxLabelValueLen int
|
||||
The maximum length of label values in the accepted time series. Series with longer label value are ignored. In this case the vm_rows_ignored_total{reason="too_long_label_value"} metric at /metrics page is incremented (default 4096)
|
||||
The maximum length of label values in the accepted time series. Series with longer label value are ignored. In this case the vm_rows_ignored_total{reason="too_long_label_value"} metric at /metrics page is incremented. Value must be in range 1..65535. (default 4096)
|
||||
-maxLabelsPerTimeseries int
|
||||
The maximum number of labels per time series to be accepted. Series with superfluous labels are ignored. In this case the vm_rows_ignored_total{reason="too_many_labels"} metric at /metrics page is incremented (default 40)
|
||||
-memory.allowedBytes size
|
||||
|
||||
@@ -41,7 +41,7 @@ sitemap:
|
||||
-licenseFile string
|
||||
Path to file with license key for VictoriaMetrics Enterprise. See https://victoriametrics.com/products/enterprise/ . Trial Enterprise license can be obtained from https://victoriametrics.com/products/enterprise/trial/ . This flag is available only in Enterprise binaries. The license key can be also passed inline via -license command-line flag
|
||||
-licenseFile.reloadInterval duration
|
||||
Interval for reloading the license file specified via -licenseFile. See https://victoriametrics.com/products/enterprise/ . This flag is available only in Enterprise binaries (default 1h0m0s)
|
||||
Interval for reloading the license file specified via -licenseFile. A non-positive value disables periodic and SIGHUP-triggered reloads of -licenseFile. See https://victoriametrics.com/products/enterprise/ . This flag is available only in Enterprise binaries (default 1h0m0s)
|
||||
-mtls array
|
||||
Whether to require valid client certificate for https requests to the corresponding -httpListenAddr . This flag works only if -tls flag is set. See also -mtlsCAFile . This flag is available only in Enterprise binaries. See https://docs.victoriametrics.com/victoriametrics/enterprise/
|
||||
Supports array of values separated by comma or specified via multiple flags.
|
||||
@@ -61,7 +61,7 @@ sitemap:
|
||||
-tlsAutocertEmail string
|
||||
Contact email for the issued Let's Encrypt TLS certificates. See also -tlsAutocertHosts and -tlsAutocertCacheDir . This flag is available only in Enterprise binaries. See https://docs.victoriametrics.com/victoriametrics/enterprise/
|
||||
-tlsAutocertHosts array
|
||||
Optional hostnames for automatic issuing of Let's Encrypt TLS certificates. These hostnames must be reachable at -httpListenAddr . The -httpListenAddr must listen tcp port 443 . The -tlsAutocertHosts overrides -tlsCertFile and -tlsKeyFile . See also -tlsAutocertEmail and -tlsAutocertCacheDir . This flag is available only in Enterprise binaries. See https://docs.victoriametrics.com/victoriametrics/enterprise/
|
||||
Optional hostnames for automatic issuing of Let's Encrypt TLS certificates. These hostnames must be reachable at -httpListenAddr . The -httpListenAddr must listen tcp port 443 . The -tlsCertFile and -tlsKeyFile must be unset when -tlsAutocertHosts is used . See also -tlsAutocertEmail and -tlsAutocertCacheDir . This flag is available only in Enterprise binaries. See https://docs.victoriametrics.com/victoriametrics/enterprise/
|
||||
Supports an array of values separated by comma or specified via multiple flags.
|
||||
Each array item can contain comma inside single-quoted or double-quoted string, {}, [] and () braces.
|
||||
```
|
||||
@@ -84,7 +84,7 @@ See the docs at https://docs.victoriametrics.com/victoriametrics/cluster-victori
|
||||
-http.idleConnTimeout duration
|
||||
Timeout for incoming idle http connections (default 1m0s)
|
||||
-http.maxGracefulShutdownDuration duration
|
||||
The maximum duration for a graceful shutdown of the HTTP server. A highly loaded server may require increased value for a graceful shutdown (default 7s)
|
||||
The maximum duration for a graceful shutdown of the HTTP server. During this period the server stops accepting new connections, but it will continue serving existing connections. The remaining in-flight requests are canceled before the deadline, so the shutdown can finish within this duration. A highly loaded server may require increased value for a graceful shutdown (default 7s)
|
||||
-http.pathPrefix string
|
||||
An optional prefix to add to all the paths handled by http server. For example, if '-http.pathPrefix=/foo/bar' is set, then all the http requests will be handled on '/foo/bar/*' paths. This may be useful for proxied requests. See https://www.robustperception.io/using-external-urls-and-proxies-with-prometheus
|
||||
-http.shutdownDelay duration
|
||||
@@ -277,7 +277,7 @@ See the docs at https://docs.victoriametrics.com/victoriametrics/cluster-victori
|
||||
The minimum memory bytes consumption for queries to track in query stats at /api/v1/status/top_queries. Queries with lower memory bytes consumption are ignored in query stats
|
||||
Supports the following optional suffixes for size values: KB, MB, GB, TB, KiB, MiB, GiB, TiB (default 1024)
|
||||
-search.resetCacheAuthKey value
|
||||
Optional authKey for resetting rollup cache via /internal/resetRollupResultCache call. It could be passed via authKey query arg. It overrides -httpAuth.*
|
||||
Optional authKey for resetting rollup cache via /internal/resetRollupResultCache call. It could be passed via authKey query arg. It overrides -httpAuth.*. It'll be used when reset request is propagate to other -selectNode.
|
||||
Flag value can be read from the given file when using -search.resetCacheAuthKey=file:///abs/path/to/file or -search.resetCacheAuthKey=file://./relative/path/to/file.
|
||||
Flag value can be read from the given http/https url when using -search.resetCacheAuthKey=http://host/path or -search.resetCacheAuthKey=https://host/path
|
||||
-search.resetRollupResultCacheOnStartup
|
||||
@@ -295,7 +295,7 @@ See the docs at https://docs.victoriametrics.com/victoriametrics/cluster-victori
|
||||
Supports an array of values separated by comma or specified via multiple flags.
|
||||
Each array item can contain comma inside single-quoted or double-quoted string, {}, [] and () braces.
|
||||
-selectNode array
|
||||
A list of vmselect node addresses to propagate the '/internal/resetRollupResultCache' call. If this flag isn't set, then cache need to be purged from each vmselect individually. Comma-separated addresses of vmselect nodes; usage: -selectNode=vmselect-host1,...,vmselect-hostN
|
||||
A list of vmselect node addresses to propagate the '/internal/resetRollupResultCache?propagate=1' call. If either this flag or the 'propagate=1' query parameter is missing, the cache must be purged on each vmselect instance individually.Comma-separated addresses of vmselect nodes; usage: -selectNode=vmselect-host1,...,vmselect-hostN
|
||||
Supports an array of values separated by comma or specified via multiple flags.
|
||||
Each array item can contain comma inside single-quoted or double-quoted string, {}, [] and () braces.
|
||||
-storageNode array
|
||||
|
||||
@@ -45,7 +45,7 @@ sitemap:
|
||||
-licenseFile string
|
||||
Path to file with license key for VictoriaMetrics Enterprise. See https://victoriametrics.com/products/enterprise/ . Trial Enterprise license can be obtained from https://victoriametrics.com/products/enterprise/trial/ . This flag is available only in Enterprise binaries. The license key can be also passed inline via -license command-line flag
|
||||
-licenseFile.reloadInterval duration
|
||||
Interval for reloading the license file specified via -licenseFile. See https://victoriametrics.com/products/enterprise/ . This flag is available only in Enterprise binaries (default 1h0m0s)
|
||||
Interval for reloading the license file specified via -licenseFile. A non-positive value disables periodic and SIGHUP-triggered reloads of -licenseFile. See https://victoriametrics.com/products/enterprise/ . This flag is available only in Enterprise binaries (default 1h0m0s)
|
||||
-mtls array
|
||||
Whether to require valid client certificate for https requests to the corresponding -httpListenAddr . This flag works only if -tls flag is set. See also -mtlsCAFile . This flag is available only in Enterprise binaries. See https://docs.victoriametrics.com/victoriametrics/enterprise/
|
||||
Supports array of values separated by comma or specified via multiple flags.
|
||||
@@ -69,7 +69,7 @@ sitemap:
|
||||
-tlsAutocertEmail string
|
||||
Contact email for the issued Let's Encrypt TLS certificates. See also -tlsAutocertHosts and -tlsAutocertCacheDir . This flag is available only in Enterprise binaries. See https://docs.victoriametrics.com/victoriametrics/enterprise/
|
||||
-tlsAutocertHosts array
|
||||
Optional hostnames for automatic issuing of Let's Encrypt TLS certificates. These hostnames must be reachable at -httpListenAddr . The -httpListenAddr must listen tcp port 443 . The -tlsAutocertHosts overrides -tlsCertFile and -tlsKeyFile . See also -tlsAutocertEmail and -tlsAutocertCacheDir . This flag is available only in Enterprise binaries. See https://docs.victoriametrics.com/victoriametrics/enterprise/
|
||||
Optional hostnames for automatic issuing of Let's Encrypt TLS certificates. These hostnames must be reachable at -httpListenAddr . The -httpListenAddr must listen tcp port 443 . The -tlsCertFile and -tlsKeyFile must be unset when -tlsAutocertHosts is used . See also -tlsAutocertEmail and -tlsAutocertCacheDir . This flag is available only in Enterprise binaries. See https://docs.victoriametrics.com/victoriametrics/enterprise/
|
||||
Supports an array of values separated by comma or specified via multiple flags.
|
||||
Each array item can contain comma inside single-quoted or double-quoted string, {}, [] and () braces.
|
||||
```
|
||||
@@ -77,7 +77,7 @@ See the docs at https://docs.victoriametrics.com/victoriametrics/cluster-victori
|
||||
-http.idleConnTimeout duration
|
||||
Timeout for incoming idle http connections (default 1m0s)
|
||||
-http.maxGracefulShutdownDuration duration
|
||||
The maximum duration for a graceful shutdown of the HTTP server. A highly loaded server may require increased value for a graceful shutdown (default 7s)
|
||||
The maximum duration for a graceful shutdown of the HTTP server. During this period the server stops accepting new connections, but it will continue serving existing connections. The remaining in-flight requests are canceled before the deadline, so the shutdown can finish within this duration. A highly loaded server may require increased value for a graceful shutdown (default 7s)
|
||||
-http.pathPrefix string
|
||||
An optional prefix to add to all the paths handled by http server. For example, if '-http.pathPrefix=/foo/bar' is set, then all the http requests will be handled on '/foo/bar/*' paths. This may be useful for proxied requests. See https://www.robustperception.io/using-external-urls-and-proxies-with-prometheus
|
||||
-http.shutdownDelay duration
|
||||
|
||||
@@ -35,7 +35,7 @@ sitemap:
|
||||
-licenseFile string
|
||||
Path to file with license key for VictoriaMetrics Enterprise. See https://victoriametrics.com/products/enterprise/ . Trial Enterprise license can be obtained from https://victoriametrics.com/products/enterprise/trial/ . This flag is available only in Enterprise binaries. The license key can be also passed inline via -license command-line flag
|
||||
-licenseFile.reloadInterval duration
|
||||
Interval for reloading the license file specified via -licenseFile. See https://victoriametrics.com/products/enterprise/ . This flag is available only in Enterprise binaries (default 1h0m0s)
|
||||
Interval for reloading the license file specified via -licenseFile. A non-positive value disables periodic and SIGHUP-triggered reloads of -licenseFile. See https://victoriametrics.com/products/enterprise/ . This flag is available only in Enterprise binaries (default 1h0m0s)
|
||||
-mtls array
|
||||
Whether to require valid client certificate for https requests to the corresponding -httpListenAddr . This flag works only if -tls flag is set. See also -mtlsCAFile . This flag is available only in Enterprise binaries. See https://docs.victoriametrics.com/victoriametrics/enterprise/
|
||||
Supports array of values separated by comma or specified via multiple flags.
|
||||
@@ -53,7 +53,7 @@ sitemap:
|
||||
-tlsAutocertEmail string
|
||||
Contact email for the issued Let's Encrypt TLS certificates. See also -tlsAutocertHosts and -tlsAutocertCacheDir . This flag is available only in Enterprise binaries. See https://docs.victoriametrics.com/victoriametrics/enterprise/
|
||||
-tlsAutocertHosts array
|
||||
Optional hostnames for automatic issuing of Let's Encrypt TLS certificates. These hostnames must be reachable at -httpListenAddr . The -httpListenAddr must listen tcp port 443 . The -tlsAutocertHosts overrides -tlsCertFile and -tlsKeyFile . See also -tlsAutocertEmail and -tlsAutocertCacheDir . This flag is available only in Enterprise binaries. See https://docs.victoriametrics.com/victoriametrics/enterprise/
|
||||
Optional hostnames for automatic issuing of Let's Encrypt TLS certificates. These hostnames must be reachable at -httpListenAddr . The -httpListenAddr must listen tcp port 443 . The -tlsCertFile and -tlsKeyFile must be unset when -tlsAutocertHosts is used . See also -tlsAutocertEmail and -tlsAutocertCacheDir . This flag is available only in Enterprise binaries. See https://docs.victoriametrics.com/victoriametrics/enterprise/
|
||||
Supports an array of values separated by comma or specified via multiple flags.
|
||||
Each array item can contain comma inside single-quoted or double-quoted string, {}, [] and () braces.
|
||||
```
|
||||
4
go.mod
4
go.mod
@@ -11,7 +11,7 @@ require (
|
||||
github.com/VictoriaMetrics/easyproto v1.2.0
|
||||
github.com/VictoriaMetrics/fastcache v1.13.3
|
||||
github.com/VictoriaMetrics/metrics v1.44.0
|
||||
github.com/VictoriaMetrics/metricsql v0.87.2
|
||||
github.com/VictoriaMetrics/metricsql v0.87.3
|
||||
github.com/aws/aws-sdk-go-v2 v1.42.0
|
||||
github.com/aws/aws-sdk-go-v2/config v1.32.25
|
||||
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.22.27
|
||||
@@ -36,7 +36,7 @@ require (
|
||||
github.com/valyala/quicktemplate v1.8.0
|
||||
golang.org/x/net v0.56.0
|
||||
golang.org/x/oauth2 v0.36.0
|
||||
golang.org/x/sys v0.46.0
|
||||
golang.org/x/sys v0.47.0
|
||||
google.golang.org/api v0.284.0
|
||||
gopkg.in/yaml.v2 v2.4.0
|
||||
)
|
||||
|
||||
4
go.sum
4
go.sum
@@ -62,6 +62,8 @@ github.com/VictoriaMetrics/metrics v1.44.0 h1:Fr8yqQSV+ZfYaDD/anqk1E8e9YPgfleSle
|
||||
github.com/VictoriaMetrics/metrics v1.44.0/go.mod h1:xDM82ULLYCYdFRgQ2JBxi8Uf1+8En1So9YUwlGTOqTc=
|
||||
github.com/VictoriaMetrics/metricsql v0.87.2 h1:7OsrcDBWREWKqqpnFyIUEOM4FNv2qHvCoww2GYz3Tc0=
|
||||
github.com/VictoriaMetrics/metricsql v0.87.2/go.mod h1:d4EisFO6ONP/HIGDYTAtwrejJBBeKGQYiRl095bS4QQ=
|
||||
github.com/VictoriaMetrics/metricsql v0.87.3 h1:JU4JnVKSC5Vp3b4AvogXyOAjkz1iFF9n1KBMphS4WO8=
|
||||
github.com/VictoriaMetrics/metricsql v0.87.3/go.mod h1:d4EisFO6ONP/HIGDYTAtwrejJBBeKGQYiRl095bS4QQ=
|
||||
github.com/VividCortex/ewma v1.2.0 h1:f58SaIzcDXrSy3kWaHNvuJgJ3Nmz59Zji6XoJR/q1ow=
|
||||
github.com/VividCortex/ewma v1.2.0/go.mod h1:nz4BbCtbLyFDeC9SUHbtcT5644juEuWfUAUnGx7j5l4=
|
||||
github.com/alecthomas/units v0.0.0-20240927000941-0f3dac36c52b h1:mimo19zliBX/vSQ6PWWSL9lK8qwHozUj03+zLoEB8O0=
|
||||
@@ -544,6 +546,8 @@ golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.46.0 h1:noSf2Fq6F8DBgS+LysIkx7rIExoNHJsxOAtPp4rthXw=
|
||||
golang.org/x/sys v0.46.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
|
||||
golang.org/x/sys v0.47.0 h1:o7XGOvZQCADBQQ4Y7VNq2dRWQR7JmOUW8Kxx4ZsNgWs=
|
||||
golang.org/x/sys v0.47.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
|
||||
golang.org/x/term v0.44.0 h1:0rLvDRCtNj0gZkyIXhCyOb2OAzEhLVqc4B+hrsBhrmc=
|
||||
golang.org/x/term v0.44.0/go.mod h1:7ze4MdzUzLXpSAoFP1H0bOI9aXDqveSvatT5vKcFh2Y=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
|
||||
@@ -164,6 +164,14 @@ func (fs *FS) Init(ctx context.Context) error {
|
||||
// when using EKS Pod Identity or similar.
|
||||
// See: https://github.com/VictoriaMetrics/VictoriaMetrics/issues/9280
|
||||
"ExpiredToken": {},
|
||||
// S3-compatible stores may return TooManyRequests instead of TooManyRequestsException.
|
||||
// See: https://github.com/VictoriaMetrics/VictoriaMetrics/issues/11218
|
||||
"TooManyRequests": {},
|
||||
},
|
||||
}, retry.RetryableHTTPStatusCode{
|
||||
Codes: map[int]struct{}{
|
||||
// S3-compatible stores may return HTTP 429 for rate limiting instead of HTTP 503.
|
||||
429: {},
|
||||
},
|
||||
}, retry.IsErrorRetryableFunc(func(err error) aws.Ternary {
|
||||
if errors.Is(err, io.ErrUnexpectedEOF) {
|
||||
|
||||
@@ -78,7 +78,8 @@ type part struct {
|
||||
|
||||
size uint64
|
||||
|
||||
mrs []metaindexRow
|
||||
mrs []metaindexRow
|
||||
metaindexSizeBytes uint64
|
||||
|
||||
indexFile fs.MustReadAtCloser
|
||||
itemsFile fs.MustReadAtCloser
|
||||
@@ -131,6 +132,7 @@ func newPart(ph *partHeader, path string, size uint64, metaindexReader filestrea
|
||||
p.path = path
|
||||
p.size = size
|
||||
p.mrs = mrs
|
||||
p.metaindexSizeBytes = metaindexSizeBytes(mrs)
|
||||
|
||||
p.indexFile = indexFile
|
||||
p.itemsFile = itemsFile
|
||||
@@ -155,6 +157,14 @@ func (p *part) MustClose() {
|
||||
ibSparseCache.RemoveBlocksForPart(p)
|
||||
}
|
||||
|
||||
func metaindexSizeBytes(mrs []metaindexRow) uint64 {
|
||||
n := uint64(cap(mrs)) * uint64(unsafe.Sizeof(metaindexRow{}))
|
||||
for i := range mrs {
|
||||
n += uint64(cap(mrs[i].firstItem))
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
type indexBlock struct {
|
||||
bhs []blockHeader
|
||||
|
||||
|
||||
@@ -584,6 +584,8 @@ type TableMetrics struct {
|
||||
PartsRefCount uint64
|
||||
|
||||
TooLongItemsDroppedTotal uint64
|
||||
|
||||
MetaindexSizeBytes uint64
|
||||
}
|
||||
|
||||
// TotalItemsCount returns the total number of items in the table.
|
||||
@@ -617,6 +619,7 @@ func (tb *Table) UpdateMetrics(m *TableMetrics) {
|
||||
m.InmemoryBlocksCount += p.ph.blocksCount
|
||||
m.InmemoryItemsCount += p.ph.itemsCount
|
||||
m.InmemorySizeBytes += p.size
|
||||
m.MetaindexSizeBytes += p.metaindexSizeBytes
|
||||
m.PartsRefCount += uint64(pw.refCount.Load())
|
||||
}
|
||||
|
||||
@@ -626,6 +629,7 @@ func (tb *Table) UpdateMetrics(m *TableMetrics) {
|
||||
m.FileBlocksCount += p.ph.blocksCount
|
||||
m.FileItemsCount += p.ph.itemsCount
|
||||
m.FileSizeBytes += p.size
|
||||
m.MetaindexSizeBytes += p.metaindexSizeBytes
|
||||
m.PartsRefCount += uint64(pw.refCount.Load())
|
||||
}
|
||||
tb.partsLock.Unlock()
|
||||
|
||||
@@ -52,6 +52,7 @@ type queue struct {
|
||||
writerFlushedOffset uint64
|
||||
|
||||
lastMetainfoFlushTime uint64
|
||||
hasDataToFlush bool
|
||||
|
||||
blocksDropped *metrics.Counter
|
||||
bytesDropped *metrics.Counter
|
||||
@@ -84,6 +85,7 @@ func (q *queue) mustResetFiles() {
|
||||
}
|
||||
q.reader.MustClose()
|
||||
q.writer.MustClose()
|
||||
q.hasDataToFlush = false
|
||||
fs.MustRemovePath(q.readerPath)
|
||||
|
||||
q.writerOffset = 0
|
||||
@@ -318,6 +320,7 @@ func tryOpeningQueue(path, name string, chunkFileSize, maxBlockSize, maxPendingB
|
||||
func (q *queue) MustClose() {
|
||||
// Close writer.
|
||||
q.writer.MustClose()
|
||||
q.hasDataToFlush = false
|
||||
q.writer = nil
|
||||
|
||||
// Close reader.
|
||||
@@ -414,7 +417,7 @@ func (q *queue) writeBlock(block []byte) error {
|
||||
}
|
||||
q.blocksWritten.Inc()
|
||||
q.bytesWritten.Add(len(block))
|
||||
return q.flushWriterMetainfoIfNeeded()
|
||||
return q.flushBufAndMetainfoIfNeeded()
|
||||
}
|
||||
|
||||
var writeDurationSeconds = metrics.NewFloatCounter(`vm_persistentqueue_write_duration_seconds_total`)
|
||||
@@ -422,6 +425,7 @@ var writeDurationSeconds = metrics.NewFloatCounter(`vm_persistentqueue_write_dur
|
||||
func (q *queue) nextChunkFileForWrite() error {
|
||||
// Finalize the current chunk and start new one.
|
||||
q.writer.MustClose()
|
||||
q.hasDataToFlush = false
|
||||
// There is no need to do fs.MustSyncPath(q.writerPath) here,
|
||||
// since MustClose already does this.
|
||||
if n := q.writerOffset % q.chunkFileSize; n > 0 {
|
||||
@@ -513,7 +517,7 @@ again:
|
||||
}
|
||||
q.blocksRead.Inc()
|
||||
q.bytesRead.Add(int(blockLen))
|
||||
if err := q.flushReaderMetainfoIfNeeded(); err != nil {
|
||||
if err := q.flushBufAndMetainfoIfNeeded(); err != nil {
|
||||
return dst, err
|
||||
}
|
||||
return dst, nil
|
||||
@@ -566,6 +570,7 @@ func (q *queue) write(buf []byte) error {
|
||||
}
|
||||
q.writerLocalOffset += bufLen
|
||||
q.writerOffset += bufLen
|
||||
q.hasDataToFlush = true
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -595,24 +600,16 @@ func (q *queue) checkReaderWriterOffsets() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (q *queue) flushReaderMetainfoIfNeeded() error {
|
||||
func (q *queue) flushBufAndMetainfoIfNeeded() error {
|
||||
t := fasttime.UnixTimestamp()
|
||||
if t == q.lastMetainfoFlushTime {
|
||||
return nil
|
||||
}
|
||||
if err := q.flushMetainfo(); err != nil {
|
||||
return fmt.Errorf("cannot flush metainfo: %w", err)
|
||||
if q.hasDataToFlush {
|
||||
q.writer.MustFlush(true)
|
||||
q.writerFlushedOffset = q.writerOffset
|
||||
q.hasDataToFlush = false
|
||||
}
|
||||
q.lastMetainfoFlushTime = t
|
||||
return nil
|
||||
}
|
||||
|
||||
func (q *queue) flushWriterMetainfoIfNeeded() error {
|
||||
t := fasttime.UnixTimestamp()
|
||||
if t == q.lastMetainfoFlushTime {
|
||||
return nil
|
||||
}
|
||||
q.writer.MustFlush(true)
|
||||
if err := q.flushMetainfo(); err != nil {
|
||||
return fmt.Errorf("cannot flush metainfo: %w", err)
|
||||
}
|
||||
|
||||
60
lib/persistentqueue/persistentqueue_synctest_test.go
Normal file
60
lib/persistentqueue/persistentqueue_synctest_test.go
Normal file
@@ -0,0 +1,60 @@
|
||||
//go:build synctest
|
||||
|
||||
package persistentqueue
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"testing/synctest"
|
||||
"time"
|
||||
|
||||
"github.com/VictoriaMetrics/VictoriaMetrics/lib/encoding"
|
||||
"github.com/VictoriaMetrics/VictoriaMetrics/lib/fs"
|
||||
)
|
||||
|
||||
func TestFlushReaderMetainfoFlushesPendingWriterData(t *testing.T) {
|
||||
synctest.Test(t, func(t *testing.T) {
|
||||
path := "queue-flush-reader-metainfo"
|
||||
fs.MustRemoveDir(path)
|
||||
q := mustOpen(path, "foobar", 0)
|
||||
defer func() {
|
||||
q.MustClose()
|
||||
fs.MustRemoveDir(path)
|
||||
}()
|
||||
|
||||
block := []byte("foobar")
|
||||
data := encoding.MarshalUint64(nil, uint64(len(block)))
|
||||
data = append(data, block...)
|
||||
// it will call `flushBufAndMetainfoIfNeeded` internally to flush the data and metadata.
|
||||
err := q.writeBlock(data)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error when writing data to queue: %s", err)
|
||||
}
|
||||
// the second call will update the writeOffset in memory without flushing the data and metadata,
|
||||
// because the last flush was performed less than 1 second ago.
|
||||
err = q.writeBlock(data)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error when writing data to queue: %s", err)
|
||||
}
|
||||
time.Sleep(2 * time.Second)
|
||||
|
||||
// it will call `flushBufAndMetainfoIfNeeded` internally to flush the data and metadata.
|
||||
if _, err = q.readBlock(nil); err != nil {
|
||||
t.Fatalf("unexpected error when flushing reader metainfo: %s", err)
|
||||
}
|
||||
|
||||
if fileSize := fs.MustFileSize(q.writerPath); fileSize != q.writerOffset {
|
||||
t.Fatalf("unexpected writer file size after flushing reader metainfo; got %d bytes; want %d bytes", fileSize, q.writerOffset)
|
||||
}
|
||||
var mi metainfo
|
||||
if err := mi.ReadFromFile(q.metainfoPath()); err != nil {
|
||||
t.Fatalf("cannot read metainfo: %s", err)
|
||||
}
|
||||
if mi.ReaderOffset != q.readerOffset {
|
||||
t.Fatalf("unexpected ReaderOffset in metainfo; got %d; want %d", mi.ReaderOffset, q.readerOffset)
|
||||
}
|
||||
if mi.WriterOffset != q.writerOffset {
|
||||
t.Fatalf("unexpected WriterOffset in metainfo; got %d; want %d", mi.WriterOffset, q.writerOffset)
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
@@ -367,6 +367,9 @@ func (sc *ScrapeConfig) mustStart(baseDir string) {
|
||||
for i := range sc.KubernetesSDConfigs {
|
||||
sc.KubernetesSDConfigs[i].MustStart(baseDir, swosFunc)
|
||||
}
|
||||
for i := range sc.HTTPSDConfigs {
|
||||
sc.HTTPSDConfigs[i].MustStart(baseDir)
|
||||
}
|
||||
}
|
||||
|
||||
func (sc *ScrapeConfig) mustStop() {
|
||||
@@ -1271,6 +1274,17 @@ func (swc *scrapeWorkConfig) getScrapeWork(target string, extraLabels, metaLabel
|
||||
}
|
||||
scrapeTimeout = d
|
||||
}
|
||||
// Read max_scrape_size option from __max_scrape_size__ label.
|
||||
targetMaxScrapeSize := swc.maxScrapeSize
|
||||
if s := labels.Get("__max_scrape_size__"); len(s) > 0 {
|
||||
n, err := flagutil.ParseBytes(s)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot parse __max_scrape_size__=%q: %w", s, err)
|
||||
}
|
||||
if n > 0 {
|
||||
targetMaxScrapeSize = n
|
||||
}
|
||||
}
|
||||
// Read series_limit option from __series_limit__ label.
|
||||
// See https://docs.victoriametrics.com/victoriametrics/vmagent/#cardinality-limiter
|
||||
seriesLimit := swc.seriesLimit
|
||||
@@ -1333,7 +1347,7 @@ func (swc *scrapeWorkConfig) getScrapeWork(target string, extraLabels, metaLabel
|
||||
ScrapeURL: scrapeURL,
|
||||
ScrapeInterval: scrapeInterval,
|
||||
ScrapeTimeout: scrapeTimeout,
|
||||
MaxScrapeSize: swc.maxScrapeSize,
|
||||
MaxScrapeSize: targetMaxScrapeSize,
|
||||
HonorLabels: swc.honorLabels,
|
||||
HonorTimestamps: swc.honorTimestamps,
|
||||
DenyRedirects: swc.denyRedirects,
|
||||
|
||||
@@ -1153,6 +1153,57 @@ scrape_configs:
|
||||
})
|
||||
f(`
|
||||
scrape_configs:
|
||||
- job_name: foo
|
||||
max_scrape_size: 8MiB
|
||||
relabel_configs:
|
||||
- source_labels: [__address__]
|
||||
regex: foo1:.*
|
||||
target_label: __max_scrape_size__
|
||||
replacement: 2.5MiB
|
||||
- source_labels: [__address__]
|
||||
regex: foo2:.*
|
||||
target_label: __max_scrape_size__
|
||||
replacement: -1
|
||||
static_configs:
|
||||
- targets: ["foo1:1234", "foo2:1234", "foo3:1234"]
|
||||
`, []*ScrapeWork{
|
||||
{
|
||||
ScrapeURL: "http://foo1:1234/metrics",
|
||||
ScrapeInterval: defaultScrapeInterval,
|
||||
ScrapeTimeout: defaultScrapeTimeout,
|
||||
MaxScrapeSize: 2.5 * 1024 * 1024,
|
||||
Labels: promutil.NewLabelsFromMap(map[string]string{
|
||||
"instance": "foo1:1234",
|
||||
"job": "foo",
|
||||
}),
|
||||
jobNameOriginal: "foo",
|
||||
},
|
||||
// invalid __max_scrape_size__ will be ignored
|
||||
{
|
||||
ScrapeURL: "http://foo2:1234/metrics",
|
||||
ScrapeInterval: defaultScrapeInterval,
|
||||
ScrapeTimeout: defaultScrapeTimeout,
|
||||
MaxScrapeSize: 8 * 1024 * 1024,
|
||||
Labels: promutil.NewLabelsFromMap(map[string]string{
|
||||
"instance": "foo2:1234",
|
||||
"job": "foo",
|
||||
}),
|
||||
jobNameOriginal: "foo",
|
||||
},
|
||||
{
|
||||
ScrapeURL: "http://foo3:1234/metrics",
|
||||
ScrapeInterval: defaultScrapeInterval,
|
||||
ScrapeTimeout: defaultScrapeTimeout,
|
||||
MaxScrapeSize: 8 * 1024 * 1024,
|
||||
Labels: promutil.NewLabelsFromMap(map[string]string{
|
||||
"instance": "foo3:1234",
|
||||
"job": "foo",
|
||||
}),
|
||||
jobNameOriginal: "foo",
|
||||
},
|
||||
})
|
||||
f(`
|
||||
scrape_configs:
|
||||
- job_name: foo
|
||||
static_configs:
|
||||
- targets: ["foo.bar:1234"]
|
||||
|
||||
@@ -1,25 +1,41 @@
|
||||
package http
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/VictoriaMetrics/metrics"
|
||||
|
||||
"github.com/VictoriaMetrics/VictoriaMetrics/lib/promscrape/discoveryutil"
|
||||
"github.com/VictoriaMetrics/VictoriaMetrics/lib/promutil"
|
||||
"github.com/VictoriaMetrics/metrics"
|
||||
)
|
||||
|
||||
var configMap = discoveryutil.NewConfigMap()
|
||||
|
||||
type apiConfig struct {
|
||||
client *discoveryutil.Client
|
||||
path string
|
||||
client *discoveryutil.Client
|
||||
path string
|
||||
sourceURL string
|
||||
checkInterval time.Duration
|
||||
|
||||
fetchErrors *metrics.Counter
|
||||
parseErrors *metrics.Counter
|
||||
|
||||
initOnce sync.Once
|
||||
prevAPIResponse atomic.Pointer[[]byte]
|
||||
targetLabels atomic.Pointer[targetLabelsResult]
|
||||
|
||||
wg sync.WaitGroup
|
||||
}
|
||||
|
||||
type targetLabelsResult struct {
|
||||
labels []*promutil.Labels
|
||||
err error
|
||||
}
|
||||
|
||||
// httpGroupTarget represent prometheus GroupTarget
|
||||
@@ -49,37 +65,89 @@ func newAPIConfig(sdc *SDConfig, baseDir string) (*apiConfig, error) {
|
||||
return nil, fmt.Errorf("cannot create HTTP client for %q: %w", apiServer, err)
|
||||
}
|
||||
cfg := &apiConfig{
|
||||
client: client,
|
||||
path: parsedURL.RequestURI(),
|
||||
fetchErrors: metrics.GetOrCreateCounter(fmt.Sprintf(`promscrape_discovery_http_errors_total{type="fetch",url=%q}`, sdc.URL)),
|
||||
parseErrors: metrics.GetOrCreateCounter(fmt.Sprintf(`promscrape_discovery_http_errors_total{type="parse",url=%q}`, sdc.URL)),
|
||||
client: client,
|
||||
path: parsedURL.RequestURI(),
|
||||
sourceURL: sdc.URL,
|
||||
checkInterval: max(*SDCheckInterval/2, time.Second),
|
||||
fetchErrors: metrics.GetOrCreateCounter(fmt.Sprintf(`promscrape_discovery_http_errors_total{type="fetch",url=%q}`, sdc.URL)),
|
||||
parseErrors: metrics.GetOrCreateCounter(fmt.Sprintf(`promscrape_discovery_http_errors_total{type="parse",url=%q}`, sdc.URL)),
|
||||
}
|
||||
cfg.wg.Go(func() {
|
||||
cfg.run()
|
||||
})
|
||||
return cfg, nil
|
||||
}
|
||||
|
||||
func getAPIConfig(sdc *SDConfig, baseDir string) (*apiConfig, error) {
|
||||
v, err := configMap.Get(sdc, func() (any, error) { return newAPIConfig(sdc, baseDir) })
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return v.(*apiConfig), nil
|
||||
func (cfg *apiConfig) init() {
|
||||
cfg.initOnce.Do(func() {
|
||||
cfg.refreshTargetsIfNeeded()
|
||||
})
|
||||
}
|
||||
|
||||
func getHTTPTargets(cfg *apiConfig) ([]httpGroupTarget, error) {
|
||||
func (cfg *apiConfig) run() {
|
||||
cfg.init()
|
||||
|
||||
ticker := time.NewTicker(cfg.checkInterval)
|
||||
defer ticker.Stop()
|
||||
stopCh := cfg.client.Context().Done()
|
||||
for {
|
||||
select {
|
||||
case <-ticker.C:
|
||||
cfg.refreshTargetsIfNeeded()
|
||||
case <-stopCh:
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (cfg *apiConfig) refreshTargetsIfNeeded() {
|
||||
apiResponse, err := cfg.getAPIResponseData()
|
||||
if err != nil {
|
||||
cfg.targetLabels.Store(&targetLabelsResult{err: err})
|
||||
cfg.prevAPIResponse.Store(nil)
|
||||
return
|
||||
}
|
||||
prevAPIResponse := cfg.prevAPIResponse.Load()
|
||||
if prevAPIResponse != nil && bytes.Equal(apiResponse, *prevAPIResponse) {
|
||||
return
|
||||
}
|
||||
hts, err := parseAPIResponse(apiResponse, cfg.path)
|
||||
if err != nil {
|
||||
cfg.prevAPIResponse.Store(nil)
|
||||
cfg.parseErrors.Inc()
|
||||
cfg.targetLabels.Store(&targetLabelsResult{err: err})
|
||||
return
|
||||
}
|
||||
newTargets := addHTTPTargetLabels(hts, cfg.sourceURL)
|
||||
cfg.targetLabels.Store(&targetLabelsResult{labels: newTargets})
|
||||
cfg.prevAPIResponse.Store(&apiResponse)
|
||||
}
|
||||
|
||||
func (cfg *apiConfig) getAPIResponseData() ([]byte, error) {
|
||||
data, err := cfg.client.GetAPIResponseWithReqParams(cfg.path, func(request *http.Request) {
|
||||
request.Header.Set("X-Prometheus-Refresh-Interval-Seconds", strconv.FormatFloat(SDCheckInterval.Seconds(), 'f', 0, 64))
|
||||
request.Header.Set("X-Prometheus-Refresh-Interval-Seconds", strconv.FormatFloat(cfg.checkInterval.Seconds(), 'f', 0, 64))
|
||||
request.Header.Set("Accept", "application/json")
|
||||
})
|
||||
if err != nil {
|
||||
cfg.fetchErrors.Inc()
|
||||
return nil, fmt.Errorf("cannot read http_sd api response: %w", err)
|
||||
}
|
||||
tg, err := parseAPIResponse(data, cfg.path)
|
||||
if err != nil {
|
||||
cfg.parseErrors.Inc()
|
||||
return nil, err
|
||||
return data, nil
|
||||
}
|
||||
|
||||
func (cfg *apiConfig) getLabels() ([]*promutil.Labels, error) {
|
||||
cfg.init()
|
||||
|
||||
tlr := cfg.targetLabels.Load()
|
||||
if tlr.err != nil {
|
||||
return nil, tlr.err
|
||||
}
|
||||
return tg, nil
|
||||
return tlr.labels, nil
|
||||
}
|
||||
|
||||
func (cfg *apiConfig) mustStop() {
|
||||
cfg.client.Stop()
|
||||
cfg.wg.Wait()
|
||||
}
|
||||
|
||||
func parseAPIResponse(data []byte, path string) ([]httpGroupTarget, error) {
|
||||
|
||||
@@ -23,19 +23,35 @@ type SDConfig struct {
|
||||
HTTPClientConfig promauth.HTTPClientConfig `yaml:",inline"`
|
||||
ProxyURL *proxy.URL `yaml:"proxy_url,omitempty"`
|
||||
ProxyClientConfig promauth.ProxyClientConfig `yaml:",inline"`
|
||||
|
||||
cfg *apiConfig
|
||||
startErr error
|
||||
}
|
||||
|
||||
// MustStart initializes sdc before its usage.
|
||||
func (sdc *SDConfig) MustStart(baseDir string) {
|
||||
cfg, err := newAPIConfig(sdc, baseDir)
|
||||
if err != nil {
|
||||
sdc.startErr = fmt.Errorf("cannot create API config for http_sd: %w", err)
|
||||
return
|
||||
}
|
||||
sdc.cfg = cfg
|
||||
}
|
||||
|
||||
// GetLabels returns http service discovery labels according to sdc.
|
||||
func (sdc *SDConfig) GetLabels(baseDir string) ([]*promutil.Labels, error) {
|
||||
cfg, err := getAPIConfig(sdc, baseDir)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot get API config: %w", err)
|
||||
if sdc.cfg == nil {
|
||||
return nil, sdc.startErr
|
||||
}
|
||||
hts, err := getHTTPTargets(cfg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return sdc.cfg.getLabels()
|
||||
}
|
||||
|
||||
// MustStop stops further usage for sdc.
|
||||
func (sdc *SDConfig) MustStop() {
|
||||
if sdc.cfg == nil {
|
||||
return
|
||||
}
|
||||
return addHTTPTargetLabels(hts, sdc.URL), nil
|
||||
sdc.cfg.mustStop()
|
||||
}
|
||||
|
||||
func addHTTPTargetLabels(src []httpGroupTarget, sourceURL string) []*promutil.Labels {
|
||||
@@ -54,12 +70,3 @@ func addHTTPTargetLabels(src []httpGroupTarget, sourceURL string) []*promutil.La
|
||||
}
|
||||
return ms
|
||||
}
|
||||
|
||||
// MustStop stops further usage for sdc.
|
||||
func (sdc *SDConfig) MustStop() {
|
||||
v := configMap.Delete(sdc)
|
||||
if v != nil {
|
||||
cfg := v.(*apiConfig)
|
||||
cfg.client.Stop()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
package http
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"sync/atomic"
|
||||
"testing"
|
||||
|
||||
"github.com/VictoriaMetrics/VictoriaMetrics/lib/promscrape/discoveryutil"
|
||||
@@ -38,3 +41,121 @@ func TestAddHTTPTargetLabels(t *testing.T) {
|
||||
}
|
||||
f(src, labelssExpected)
|
||||
}
|
||||
|
||||
func TestSDConfigGetLabels(t *testing.T) {
|
||||
|
||||
type apiResponse struct {
|
||||
statusCode int
|
||||
body string
|
||||
}
|
||||
var currentResponse atomic.Pointer[apiResponse]
|
||||
|
||||
// add initial non-empty response
|
||||
currentResponse.Store(&apiResponse{
|
||||
body: `[{"targets":["10.0.0.2:9100"],"labels":{"job":"node"}}]`,
|
||||
statusCode: http.StatusOK,
|
||||
})
|
||||
|
||||
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
|
||||
resp := currentResponse.Load()
|
||||
w.WriteHeader(resp.statusCode)
|
||||
_, _ = w.Write([]byte(resp.body))
|
||||
}))
|
||||
defer srv.Close()
|
||||
|
||||
sdc := &SDConfig{
|
||||
URL: srv.URL,
|
||||
}
|
||||
sdc.MustStart(".")
|
||||
defer sdc.MustStop()
|
||||
|
||||
assertLabelss := func(expectedLabelss []*promutil.Labels) {
|
||||
t.Helper()
|
||||
got, err := sdc.GetLabels(".")
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected GetLabels error: %s", err)
|
||||
}
|
||||
if len(got) == 0 && len(expectedLabelss) == 0 {
|
||||
return
|
||||
}
|
||||
discoveryutil.TestEqualLabelss(t, got, expectedLabelss)
|
||||
}
|
||||
|
||||
// check initial state, it must be non-empty
|
||||
// it also inits apiConfig below
|
||||
assertLabelss([]*promutil.Labels{
|
||||
promutil.NewLabelsFromMap(map[string]string{
|
||||
"__address__": "10.0.0.2:9100",
|
||||
"job": "node",
|
||||
"__meta_url": srv.URL,
|
||||
}),
|
||||
})
|
||||
|
||||
updateAPIResponse := func(response apiResponse) {
|
||||
currentResponse.Store(&response)
|
||||
sdc.cfg.refreshTargetsIfNeeded()
|
||||
|
||||
}
|
||||
|
||||
// change response to empty
|
||||
updateAPIResponse(apiResponse{
|
||||
statusCode: http.StatusOK,
|
||||
body: `[]`,
|
||||
})
|
||||
assertLabelss([]*promutil.Labels{})
|
||||
|
||||
// change response to non-empty
|
||||
updateAPIResponse(apiResponse{
|
||||
statusCode: http.StatusOK,
|
||||
body: `[{"targets":["10.0.0.1:9100"],"labels":{"job":"node"}},{"targets":["10.0.0.5:8429"],"labels":{"job":"vmagent"}}]`,
|
||||
})
|
||||
assertLabelss([]*promutil.Labels{
|
||||
promutil.NewLabelsFromMap(map[string]string{
|
||||
"__address__": "10.0.0.1:9100",
|
||||
"job": "node",
|
||||
"__meta_url": srv.URL,
|
||||
}),
|
||||
promutil.NewLabelsFromMap(map[string]string{
|
||||
"__address__": "10.0.0.5:8429",
|
||||
"job": "vmagent",
|
||||
"__meta_url": srv.URL,
|
||||
}),
|
||||
})
|
||||
|
||||
// change response to error
|
||||
updateAPIResponse(apiResponse{
|
||||
statusCode: http.StatusServiceUnavailable,
|
||||
body: `Internal Server Error`,
|
||||
})
|
||||
_, err := sdc.GetLabels(".")
|
||||
if err == nil {
|
||||
t.Fatalf("unexpected empty error")
|
||||
}
|
||||
|
||||
// transit back to correct api response
|
||||
updateAPIResponse(apiResponse{
|
||||
statusCode: http.StatusOK,
|
||||
body: `[{"targets":["10.0.0.1:9100"],"labels":{"job":"node"}},{"targets":["10.0.0.5:8429"],"labels":{"job":"vmagent"}}]`,
|
||||
})
|
||||
assertLabelss([]*promutil.Labels{
|
||||
promutil.NewLabelsFromMap(map[string]string{
|
||||
"__address__": "10.0.0.1:9100",
|
||||
"job": "node",
|
||||
"__meta_url": srv.URL,
|
||||
}),
|
||||
promutil.NewLabelsFromMap(map[string]string{
|
||||
"__address__": "10.0.0.5:8429",
|
||||
"job": "vmagent",
|
||||
"__meta_url": srv.URL,
|
||||
}),
|
||||
})
|
||||
|
||||
// make sure that api response is properly cached
|
||||
before := sdc.cfg.targetLabels.Load()
|
||||
updateAPIResponse(apiResponse{statusCode: http.StatusOK,
|
||||
body: `[{"targets":["10.0.0.1:9100"],"labels":{"job":"node"}},{"targets":["10.0.0.5:8429"],"labels":{"job":"vmagent"}}]`})
|
||||
|
||||
if sdc.cfg.targetLabels.Load() != before {
|
||||
t.Fatalf("expected identical response to be deduplicated")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1242,12 +1242,9 @@ func (db *indexDB) GetSeriesCount(deadline uint64) (uint64, error) {
|
||||
func (is *indexSearch) getSeriesCount() (uint64, error) {
|
||||
ts := &is.ts
|
||||
kb := &is.kb
|
||||
mp := &is.mp
|
||||
loopsPaceLimiter := 0
|
||||
var metricIDsLen uint64
|
||||
// Extract the number of series from ((__name__=value): metricIDs) rows
|
||||
kb.B = is.marshalCommonPrefix(kb.B[:0], nsPrefixTagToMetricIDs)
|
||||
kb.B = marshalTagValue(kb.B, nil)
|
||||
kb.B = is.marshalCommonPrefix(kb.B[:0], nsPrefixMetricIDToTSID)
|
||||
ts.Seek(kb.B)
|
||||
for ts.NextItem() {
|
||||
if loopsPaceLimiter&paceLimiterFastIterationsMask == 0 {
|
||||
@@ -1260,19 +1257,10 @@ func (is *indexSearch) getSeriesCount() (uint64, error) {
|
||||
if !bytes.HasPrefix(item, kb.B) {
|
||||
break
|
||||
}
|
||||
tail := item[len(kb.B):]
|
||||
n := bytes.IndexByte(tail, tagSeparatorChar)
|
||||
if n < 0 {
|
||||
return 0, fmt.Errorf("invalid tag->metricIDs line %q: cannot find tagSeparatorChar %d", item, tagSeparatorChar)
|
||||
}
|
||||
tail = tail[n+1:]
|
||||
if err := mp.InitOnlyTail(item, tail); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
// Take into account deleted timeseries too.
|
||||
// It is OK if series can be counted multiple times in rare cases -
|
||||
// the returned number is an estimation.
|
||||
metricIDsLen += uint64(mp.MetricIDsLen())
|
||||
metricIDsLen++
|
||||
}
|
||||
if err := ts.Error(); err != nil {
|
||||
return 0, fmt.Errorf("error when counting unique timeseries: %w", err)
|
||||
|
||||
@@ -20,6 +20,7 @@ import (
|
||||
"github.com/VictoriaMetrics/VictoriaMetrics/lib/mergeset"
|
||||
"github.com/VictoriaMetrics/VictoriaMetrics/lib/uint64set"
|
||||
"github.com/VictoriaMetrics/VictoriaMetrics/lib/workingsetcache"
|
||||
"github.com/google/go-cmp/cmp"
|
||||
)
|
||||
|
||||
func TestTagFiltersToMetricIDsCache(t *testing.T) {
|
||||
@@ -1948,10 +1949,11 @@ func newTestStorage() *Storage {
|
||||
s := &Storage{
|
||||
cachePath: "test-storage-cache",
|
||||
|
||||
metricIDCache: workingsetcache.New(1234),
|
||||
metricNameCache: workingsetcache.New(1234),
|
||||
tsidCache: workingsetcache.New(1234),
|
||||
retentionMsecs: retentionMax.Milliseconds(),
|
||||
metricIDCache: workingsetcache.New(1234),
|
||||
metricNameCache: workingsetcache.New(1234),
|
||||
tsidCache: workingsetcache.New(1234),
|
||||
retentionMsecs: retentionMax.Milliseconds(),
|
||||
maxBackfillAgeMsecs: retentionMax.Milliseconds(),
|
||||
}
|
||||
return s
|
||||
}
|
||||
@@ -2141,3 +2143,46 @@ func TestSearchLabelValues(t *testing.T) {
|
||||
s.MustClose()
|
||||
fs.MustRemoveDir(path)
|
||||
}
|
||||
|
||||
func TestFilterLabelValues(t *testing.T) {
|
||||
const n = 1000
|
||||
key := "key"
|
||||
var all []string
|
||||
for i := range n {
|
||||
all = append(all, fmt.Sprintf("value_%03d", i))
|
||||
}
|
||||
|
||||
var got, want map[string]struct{}
|
||||
|
||||
tfsAll := NewTagFilters()
|
||||
if err := tfsAll.Add([]byte("key"), []byte("value_.*"), false, true); err != nil {
|
||||
t.Fatalf("unexpected error in TagFilters.Add: %v", err)
|
||||
}
|
||||
got = make(map[string]struct{})
|
||||
want = make(map[string]struct{})
|
||||
for _, v := range all {
|
||||
got[v] = struct{}{}
|
||||
want[v] = struct{}{}
|
||||
}
|
||||
filterLabelValues(got, &tfsAll.tfs[0], key)
|
||||
if diff := cmp.Diff(want, got); diff != "" {
|
||||
t.Fatalf("unexpected label values (-want, +got):\n%s", diff)
|
||||
}
|
||||
|
||||
tfsEvery10th := NewTagFilters()
|
||||
if err := tfsEvery10th.Add([]byte("key"), []byte("value_[0-9]{2}0"), false, true); err != nil {
|
||||
t.Fatalf("unexpected error in TagFilters.Add: %v", err)
|
||||
}
|
||||
got = make(map[string]struct{})
|
||||
want = make(map[string]struct{})
|
||||
for i, v := range all {
|
||||
got[v] = struct{}{}
|
||||
if i%10 == 0 {
|
||||
want[v] = struct{}{}
|
||||
}
|
||||
}
|
||||
filterLabelValues(got, &tfsEvery10th.tfs[0], key)
|
||||
if diff := cmp.Diff(want, got); diff != "" {
|
||||
t.Fatalf("unexpected label values (-want, +got):\n%s", diff)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,7 +42,8 @@ type part struct {
|
||||
valuesFile fs.MustReadAtCloser
|
||||
indexFile fs.MustReadAtCloser
|
||||
|
||||
metaindex []metaindexRow
|
||||
metaindex []metaindexRow
|
||||
metaindexSizeBytes uint64
|
||||
}
|
||||
|
||||
// mustOpenFilePart opens file-based part from the given path.
|
||||
@@ -102,6 +103,7 @@ func newPart(ph *partHeader, path string, size uint64, metaindexReader filestrea
|
||||
p.valuesFile = valuesFile
|
||||
p.indexFile = indexFile
|
||||
p.metaindex = metaindex
|
||||
p.metaindexSizeBytes = metaindexSizeBytes(metaindex)
|
||||
|
||||
return &p
|
||||
}
|
||||
@@ -128,6 +130,10 @@ func (p *part) MustClose() {
|
||||
ibCache.RemoveBlocksForPart(p)
|
||||
}
|
||||
|
||||
func metaindexSizeBytes(metaindex []metaindexRow) uint64 {
|
||||
return uint64(cap(metaindex)) * uint64(unsafe.Sizeof(metaindexRow{}))
|
||||
}
|
||||
|
||||
type indexBlock struct {
|
||||
bhs []blockHeader
|
||||
}
|
||||
|
||||
@@ -11,9 +11,7 @@ import (
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
"unsafe"
|
||||
|
||||
"github.com/VictoriaMetrics/VictoriaMetrics/lib/atomicutil"
|
||||
"github.com/VictoriaMetrics/VictoriaMetrics/lib/cgroup"
|
||||
"github.com/VictoriaMetrics/VictoriaMetrics/lib/encoding"
|
||||
"github.com/VictoriaMetrics/VictoriaMetrics/lib/fs"
|
||||
@@ -40,14 +38,6 @@ const maxInmemoryParts = 60
|
||||
// See appendPartsToMerge tests for details.
|
||||
const defaultPartsToMerge = 15
|
||||
|
||||
// The number of shards for rawRow entries per partition.
|
||||
//
|
||||
// Higher number of shards reduces CPU contention and increases the max bandwidth on multi-core systems.
|
||||
var rawRowsShardsPerPartition = cgroup.AvailableCPUs()
|
||||
|
||||
// The interval for flushing buffered rows into parts, so they become visible to search.
|
||||
const pendingRowsFlushInterval = 2 * time.Second
|
||||
|
||||
// The interval for guaranteed flush of recently ingested data from memory to on-disk parts, so they survive process crash.
|
||||
var dataFlushInterval = 5 * time.Second
|
||||
|
||||
@@ -66,11 +56,6 @@ func SetDataFlushInterval(d time.Duration) {
|
||||
dataFlushInterval = d
|
||||
}
|
||||
|
||||
// The maximum number of rawRow items in rawRowsShard.
|
||||
//
|
||||
// Limit the maximum shard size to 8Mb, since this gives the lowest CPU usage under high ingestion rate.
|
||||
const maxRawRowsPerShard = (8 << 20) / int(unsafe.Sizeof(rawRow{}))
|
||||
|
||||
// partition represents a partition.
|
||||
type partition struct {
|
||||
activeInmemoryMerges atomic.Int64
|
||||
@@ -334,9 +319,10 @@ type partitionMetrics struct {
|
||||
IndexBlocksCacheRequests uint64
|
||||
IndexBlocksCacheMisses uint64
|
||||
|
||||
InmemorySizeBytes uint64
|
||||
SmallSizeBytes uint64
|
||||
BigSizeBytes uint64
|
||||
InmemorySizeBytes uint64
|
||||
SmallSizeBytes uint64
|
||||
BigSizeBytes uint64
|
||||
MetaindexSizeBytes uint64
|
||||
|
||||
InmemoryRowsCount uint64
|
||||
SmallRowsCount uint64
|
||||
@@ -397,6 +383,7 @@ func (pt *partition) UpdateMetrics(m *partitionMetrics) {
|
||||
m.InmemoryRowsCount += p.ph.RowsCount
|
||||
m.InmemoryBlocksCount += p.ph.BlocksCount
|
||||
m.InmemorySizeBytes += p.size
|
||||
m.MetaindexSizeBytes += p.metaindexSizeBytes
|
||||
m.InmemoryPartsRefCount += uint64(pw.refCount.Load())
|
||||
if isDedupScheduled {
|
||||
m.ScheduledDownsamplingPartitionsSize += p.size
|
||||
@@ -407,6 +394,7 @@ func (pt *partition) UpdateMetrics(m *partitionMetrics) {
|
||||
m.SmallRowsCount += p.ph.RowsCount
|
||||
m.SmallBlocksCount += p.ph.BlocksCount
|
||||
m.SmallSizeBytes += p.size
|
||||
m.MetaindexSizeBytes += p.metaindexSizeBytes
|
||||
m.SmallPartsRefCount += uint64(pw.refCount.Load())
|
||||
if isDedupScheduled {
|
||||
m.ScheduledDownsamplingPartitionsSize += p.size
|
||||
@@ -417,6 +405,7 @@ func (pt *partition) UpdateMetrics(m *partitionMetrics) {
|
||||
m.BigRowsCount += p.ph.RowsCount
|
||||
m.BigBlocksCount += p.ph.BlocksCount
|
||||
m.BigSizeBytes += p.size
|
||||
m.MetaindexSizeBytes += p.metaindexSizeBytes
|
||||
m.BigPartsRefCount += uint64(pw.refCount.Load())
|
||||
if isDedupScheduled {
|
||||
m.ScheduledDownsamplingPartitionsSize += p.size
|
||||
@@ -476,150 +465,29 @@ func (pt *partition) AddRows(rows []rawRow) {
|
||||
}
|
||||
}
|
||||
|
||||
pt.rawRows.addRows(pt, rows)
|
||||
pt.rawRows.addRows(pt.flushRowssToInmemoryParts, rows)
|
||||
}
|
||||
|
||||
var isDebug = false
|
||||
|
||||
type rawRowsShards struct {
|
||||
flushDeadlineMs atomic.Int64
|
||||
|
||||
shardIdx atomic.Uint32
|
||||
|
||||
// Shards reduce lock contention when adding rows on multi-CPU systems.
|
||||
shards []rawRowsShard
|
||||
|
||||
rowssToFlushLock sync.Mutex
|
||||
rowssToFlush [][]rawRow
|
||||
}
|
||||
|
||||
func (rrss *rawRowsShards) init() {
|
||||
rrss.shards = make([]rawRowsShard, rawRowsShardsPerPartition)
|
||||
}
|
||||
|
||||
func (rrss *rawRowsShards) addRows(pt *partition, rows []rawRow) {
|
||||
shards := rrss.shards
|
||||
shardsLen := uint32(len(shards))
|
||||
for len(rows) > 0 {
|
||||
n := rrss.shardIdx.Add(1)
|
||||
idx := n % shardsLen
|
||||
tailRows, rowsToFlush := shards[idx].addRows(rows)
|
||||
rrss.addRowsToFlush(pt, rowsToFlush)
|
||||
rows = tailRows
|
||||
}
|
||||
}
|
||||
|
||||
func (rrss *rawRowsShards) addRowsToFlush(pt *partition, rowsToFlush []rawRow) {
|
||||
if len(rowsToFlush) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
var rowssToMerge [][]rawRow
|
||||
|
||||
rrss.rowssToFlushLock.Lock()
|
||||
if len(rrss.rowssToFlush) == 0 {
|
||||
rrss.updateFlushDeadline()
|
||||
}
|
||||
rrss.rowssToFlush = append(rrss.rowssToFlush, rowsToFlush)
|
||||
if len(rrss.rowssToFlush) >= defaultPartsToMerge {
|
||||
rowssToMerge = rrss.rowssToFlush
|
||||
rrss.rowssToFlush = nil
|
||||
}
|
||||
rrss.rowssToFlushLock.Unlock()
|
||||
|
||||
pt.flushRowssToInmemoryParts(rowssToMerge)
|
||||
}
|
||||
|
||||
func (rrss *rawRowsShards) Len() int {
|
||||
n := 0
|
||||
for i := range rrss.shards[:] {
|
||||
n += rrss.shards[i].Len()
|
||||
}
|
||||
|
||||
rrss.rowssToFlushLock.Lock()
|
||||
for _, rows := range rrss.rowssToFlush {
|
||||
n += len(rows)
|
||||
}
|
||||
rrss.rowssToFlushLock.Unlock()
|
||||
|
||||
return n
|
||||
}
|
||||
|
||||
func (rrss *rawRowsShards) updateFlushDeadline() {
|
||||
rrss.flushDeadlineMs.Store(time.Now().Add(pendingRowsFlushInterval).UnixMilli())
|
||||
}
|
||||
|
||||
type rawRowsShardNopad struct {
|
||||
flushDeadlineMs atomic.Int64
|
||||
|
||||
mu sync.Mutex
|
||||
rows []rawRow
|
||||
}
|
||||
|
||||
type rawRowsShard struct {
|
||||
rawRowsShardNopad
|
||||
|
||||
// The padding prevents false sharing
|
||||
_ [atomicutil.CacheLineSize - unsafe.Sizeof(rawRowsShardNopad{})%atomicutil.CacheLineSize]byte
|
||||
}
|
||||
|
||||
func (rrs *rawRowsShard) Len() int {
|
||||
rrs.mu.Lock()
|
||||
n := len(rrs.rows)
|
||||
rrs.mu.Unlock()
|
||||
return n
|
||||
}
|
||||
|
||||
func (rrs *rawRowsShard) addRows(rows []rawRow) ([]rawRow, []rawRow) {
|
||||
var rowsToFlush []rawRow
|
||||
|
||||
rrs.mu.Lock()
|
||||
if cap(rrs.rows) == 0 {
|
||||
rrs.rows = newRawRows()
|
||||
}
|
||||
if len(rrs.rows) == 0 {
|
||||
rrs.updateFlushDeadline()
|
||||
}
|
||||
n := copy(rrs.rows[len(rrs.rows):cap(rrs.rows)], rows)
|
||||
rrs.rows = rrs.rows[:len(rrs.rows)+n]
|
||||
rows = rows[n:]
|
||||
if len(rows) > 0 {
|
||||
rowsToFlush = rrs.rows
|
||||
rrs.rows = newRawRows()
|
||||
rrs.updateFlushDeadline()
|
||||
n = copy(rrs.rows[:cap(rrs.rows)], rows)
|
||||
rrs.rows = rrs.rows[:n]
|
||||
rows = rows[n:]
|
||||
}
|
||||
rrs.mu.Unlock()
|
||||
|
||||
return rows, rowsToFlush
|
||||
}
|
||||
|
||||
func newRawRows() []rawRow {
|
||||
return make([]rawRow, 0, maxRawRowsPerShard)
|
||||
}
|
||||
|
||||
func (pt *partition) flushRowssToInmemoryParts(rowss [][]rawRow) {
|
||||
if len(rowss) == 0 {
|
||||
var nonEmptyRowss [][]rawRow
|
||||
for _, rows := range rowss {
|
||||
if len(rows) > 0 {
|
||||
nonEmptyRowss = append(nonEmptyRowss, rows)
|
||||
}
|
||||
}
|
||||
if len(nonEmptyRowss) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
// Convert rowss into in-memory parts.
|
||||
var pwsLock sync.Mutex
|
||||
pws := make([]*partWrapper, 0, len(rowss))
|
||||
pws := make([]*partWrapper, len(nonEmptyRowss))
|
||||
wg := getWaitGroup()
|
||||
for _, rows := range rowss {
|
||||
for i, rows := range nonEmptyRowss {
|
||||
inmemoryPartsConcurrencyCh <- struct{}{}
|
||||
|
||||
wg.Go(func() {
|
||||
pw := pt.createInmemoryPart(rows)
|
||||
if pw != nil {
|
||||
pwsLock.Lock()
|
||||
pws = append(pws, pw)
|
||||
pwsLock.Unlock()
|
||||
}
|
||||
|
||||
pws[i] = pt.mustCreateInmemoryPart(rows)
|
||||
<-inmemoryPartsConcurrencyCh
|
||||
})
|
||||
}
|
||||
@@ -874,9 +742,14 @@ func (pt *partition) mustMergeInmemoryPartsFinal(pws []*partWrapper) *partWrappe
|
||||
return newPartWrapperFromInmemoryPart(mpDst, flushToDiskDeadline)
|
||||
}
|
||||
|
||||
func (pt *partition) createInmemoryPart(rows []rawRow) *partWrapper {
|
||||
// mustCreateInmemoryPart creates a new in-memory part from rawRows.
|
||||
//
|
||||
// The number of rawRows cannot be zero. Otherwise the method will panic.
|
||||
//
|
||||
// The returned value is always a non-nil partWrapper.
|
||||
func (pt *partition) mustCreateInmemoryPart(rows []rawRow) *partWrapper {
|
||||
if len(rows) == 0 {
|
||||
return nil
|
||||
logger.Panicf("BUG: a part cannot be created from 0 rawRows")
|
||||
}
|
||||
mp := getInmemoryPart()
|
||||
mp.InitFromRows(rows)
|
||||
@@ -1138,7 +1011,7 @@ func (pt *partition) pendingRowsFlusher() {
|
||||
}
|
||||
|
||||
func (pt *partition) flushPendingRows(isFinal bool) {
|
||||
pt.rawRows.flush(pt, isFinal)
|
||||
pt.rawRows.flush(pt.flushRowssToInmemoryParts, isFinal)
|
||||
}
|
||||
|
||||
func (pt *partition) flushInmemoryRowsToFiles() {
|
||||
@@ -1164,66 +1037,6 @@ func (pt *partition) flushInmemoryPartsToFiles(isFinal bool) {
|
||||
}
|
||||
}
|
||||
|
||||
func (rrss *rawRowsShards) flush(pt *partition, isFinal bool) {
|
||||
var dst [][]rawRow
|
||||
|
||||
currentTimeMs := time.Now().UnixMilli()
|
||||
flushDeadlineMs := rrss.flushDeadlineMs.Load()
|
||||
if isFinal || currentTimeMs >= flushDeadlineMs {
|
||||
rrss.rowssToFlushLock.Lock()
|
||||
dst = rrss.rowssToFlush
|
||||
rrss.rowssToFlush = nil
|
||||
rrss.rowssToFlushLock.Unlock()
|
||||
}
|
||||
|
||||
for i := range rrss.shards {
|
||||
dst = rrss.shards[i].appendRawRowsToFlush(dst, currentTimeMs, isFinal)
|
||||
}
|
||||
|
||||
pt.flushRowssToInmemoryParts(dst)
|
||||
}
|
||||
|
||||
func (rrs *rawRowsShard) appendRawRowsToFlush(dst [][]rawRow, currentTimeMs int64, isFinal bool) [][]rawRow {
|
||||
flushDeadlineMs := rrs.flushDeadlineMs.Load()
|
||||
if !isFinal && currentTimeMs < flushDeadlineMs {
|
||||
// Fast path - nothing to flush
|
||||
return dst
|
||||
}
|
||||
|
||||
// Slow path - move rrs.rows to dst.
|
||||
rrs.mu.Lock()
|
||||
dst = appendRawRowss(dst, rrs.rows)
|
||||
rrs.rows = rrs.rows[:0]
|
||||
rrs.mu.Unlock()
|
||||
|
||||
return dst
|
||||
}
|
||||
|
||||
func (rrs *rawRowsShard) updateFlushDeadline() {
|
||||
rrs.flushDeadlineMs.Store(time.Now().Add(pendingRowsFlushInterval).UnixMilli())
|
||||
}
|
||||
|
||||
func appendRawRowss(dst [][]rawRow, src []rawRow) [][]rawRow {
|
||||
if len(src) == 0 {
|
||||
return dst
|
||||
}
|
||||
if len(dst) == 0 {
|
||||
dst = append(dst, newRawRows())
|
||||
}
|
||||
prows := &dst[len(dst)-1]
|
||||
n := copy((*prows)[len(*prows):cap(*prows)], src)
|
||||
*prows = (*prows)[:len(*prows)+n]
|
||||
src = src[n:]
|
||||
for len(src) > 0 {
|
||||
rows := newRawRows()
|
||||
n := copy(rows[:cap(rows)], src)
|
||||
rows = rows[:len(rows)+n]
|
||||
src = src[n:]
|
||||
dst = append(dst, rows)
|
||||
}
|
||||
return dst
|
||||
}
|
||||
|
||||
func (pt *partition) mergePartsToFiles(pws []*partWrapper, stopCh <-chan struct{}, concurrencyCh chan struct{}, useSparseCache bool) error {
|
||||
pwsLen := len(pws)
|
||||
|
||||
|
||||
@@ -3,11 +3,29 @@ package storage
|
||||
import (
|
||||
"sort"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
"unsafe"
|
||||
|
||||
"github.com/VictoriaMetrics/VictoriaMetrics/lib/atomicutil"
|
||||
"github.com/VictoriaMetrics/VictoriaMetrics/lib/cgroup"
|
||||
"github.com/VictoriaMetrics/VictoriaMetrics/lib/decimal"
|
||||
"github.com/VictoriaMetrics/VictoriaMetrics/lib/logger"
|
||||
)
|
||||
|
||||
// The number of shards for rawRow entries.
|
||||
//
|
||||
// Higher number of shards reduces CPU contention and increases the max bandwidth on multi-core systems.
|
||||
var numRawRowsShards = cgroup.AvailableCPUs()
|
||||
|
||||
// The interval for flushing buffered rows into parts, so they become visible to search.
|
||||
const pendingRowsFlushInterval = 2 * time.Second
|
||||
|
||||
// The maximum number of rawRow items in rawRowsShard.
|
||||
//
|
||||
// Limit the maximum shard size to 8Mb, since this gives the lowest CPU usage under high ingestion rate.
|
||||
const maxRawRowsPerShard = (8 << 20) / int(unsafe.Sizeof(rawRow{}))
|
||||
|
||||
// rawRow represents raw timeseries row.
|
||||
type rawRow struct {
|
||||
// TSID is time series id.
|
||||
@@ -149,3 +167,182 @@ func putRawRowsMarshaler(rrm *rawRowsMarshaler) {
|
||||
}
|
||||
|
||||
var rrmPool sync.Pool
|
||||
|
||||
type rawRowsShards struct {
|
||||
flushDeadlineMs atomic.Int64
|
||||
|
||||
shardIdx atomic.Uint32
|
||||
|
||||
// Shards reduce lock contention when adding rows on multi-CPU systems.
|
||||
shards []rawRowsShard
|
||||
|
||||
rowssToFlushLock sync.Mutex
|
||||
rowssToFlush [][]rawRow
|
||||
}
|
||||
|
||||
func (rrss *rawRowsShards) init() {
|
||||
rrss.shards = make([]rawRowsShard, numRawRowsShards)
|
||||
}
|
||||
|
||||
func (rrss *rawRowsShards) Len() int {
|
||||
n := 0
|
||||
for i := range rrss.shards[:] {
|
||||
n += rrss.shards[i].Len()
|
||||
}
|
||||
|
||||
rrss.rowssToFlushLock.Lock()
|
||||
for _, rows := range rrss.rowssToFlush {
|
||||
n += len(rows)
|
||||
}
|
||||
rrss.rowssToFlushLock.Unlock()
|
||||
|
||||
return n
|
||||
}
|
||||
|
||||
func (rrss *rawRowsShards) addRows(flush func([][]rawRow), rows []rawRow) {
|
||||
shards := rrss.shards
|
||||
shardsLen := uint32(len(shards))
|
||||
for len(rows) > 0 {
|
||||
n := rrss.shardIdx.Add(1)
|
||||
idx := n % shardsLen
|
||||
tailRows, rowsToFlush := shards[idx].addRows(rows)
|
||||
rrss.addRowsToFlush(flush, rowsToFlush)
|
||||
rows = tailRows
|
||||
}
|
||||
}
|
||||
|
||||
func (rrss *rawRowsShards) addRowsToFlush(flush func([][]rawRow), rowsToFlush []rawRow) {
|
||||
if len(rowsToFlush) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
var rowssToMerge [][]rawRow
|
||||
|
||||
rrss.rowssToFlushLock.Lock()
|
||||
if len(rrss.rowssToFlush) == 0 {
|
||||
rrss.updateFlushDeadline()
|
||||
}
|
||||
rrss.rowssToFlush = append(rrss.rowssToFlush, rowsToFlush)
|
||||
if len(rrss.rowssToFlush) >= defaultPartsToMerge {
|
||||
rowssToMerge = rrss.rowssToFlush
|
||||
rrss.rowssToFlush = nil
|
||||
}
|
||||
rrss.rowssToFlushLock.Unlock()
|
||||
|
||||
flush(rowssToMerge)
|
||||
}
|
||||
|
||||
func (rrss *rawRowsShards) updateFlushDeadline() {
|
||||
rrss.flushDeadlineMs.Store(time.Now().Add(pendingRowsFlushInterval).UnixMilli())
|
||||
}
|
||||
|
||||
func (rrss *rawRowsShards) flush(flush func(rrs [][]rawRow), isFinal bool) {
|
||||
var dst [][]rawRow
|
||||
|
||||
currentTimeMs := time.Now().UnixMilli()
|
||||
flushDeadlineMs := rrss.flushDeadlineMs.Load()
|
||||
if isFinal || currentTimeMs >= flushDeadlineMs {
|
||||
rrss.rowssToFlushLock.Lock()
|
||||
dst = rrss.rowssToFlush
|
||||
rrss.rowssToFlush = nil
|
||||
rrss.rowssToFlushLock.Unlock()
|
||||
}
|
||||
|
||||
for i := range rrss.shards {
|
||||
dst = rrss.shards[i].appendRawRowsToFlush(dst, currentTimeMs, isFinal)
|
||||
}
|
||||
|
||||
flush(dst)
|
||||
}
|
||||
|
||||
type rawRowsShardNopad struct {
|
||||
flushDeadlineMs atomic.Int64
|
||||
|
||||
mu sync.Mutex
|
||||
rows []rawRow
|
||||
}
|
||||
|
||||
type rawRowsShard struct {
|
||||
rawRowsShardNopad
|
||||
|
||||
// The padding prevents false sharing
|
||||
_ [atomicutil.CacheLineSize - unsafe.Sizeof(rawRowsShardNopad{})%atomicutil.CacheLineSize]byte
|
||||
}
|
||||
|
||||
func (rrs *rawRowsShard) Len() int {
|
||||
rrs.mu.Lock()
|
||||
n := len(rrs.rows)
|
||||
rrs.mu.Unlock()
|
||||
return n
|
||||
}
|
||||
|
||||
func (rrs *rawRowsShard) addRows(rows []rawRow) ([]rawRow, []rawRow) {
|
||||
var rowsToFlush []rawRow
|
||||
|
||||
rrs.mu.Lock()
|
||||
if cap(rrs.rows) == 0 {
|
||||
rrs.rows = newRawRows()
|
||||
}
|
||||
if len(rrs.rows) == 0 {
|
||||
rrs.updateFlushDeadline()
|
||||
}
|
||||
n := copy(rrs.rows[len(rrs.rows):cap(rrs.rows)], rows)
|
||||
rrs.rows = rrs.rows[:len(rrs.rows)+n]
|
||||
rows = rows[n:]
|
||||
if len(rows) > 0 {
|
||||
rowsToFlush = rrs.rows
|
||||
rrs.rows = newRawRows()
|
||||
rrs.updateFlushDeadline()
|
||||
n = copy(rrs.rows[:cap(rrs.rows)], rows)
|
||||
rrs.rows = rrs.rows[:n]
|
||||
rows = rows[n:]
|
||||
}
|
||||
rrs.mu.Unlock()
|
||||
|
||||
return rows, rowsToFlush
|
||||
}
|
||||
|
||||
func newRawRows() []rawRow {
|
||||
return make([]rawRow, 0, maxRawRowsPerShard)
|
||||
}
|
||||
|
||||
func (rrs *rawRowsShard) updateFlushDeadline() {
|
||||
rrs.flushDeadlineMs.Store(time.Now().Add(pendingRowsFlushInterval).UnixMilli())
|
||||
}
|
||||
|
||||
func (rrs *rawRowsShard) appendRawRowsToFlush(dst [][]rawRow, currentTimeMs int64, isFinal bool) [][]rawRow {
|
||||
flushDeadlineMs := rrs.flushDeadlineMs.Load()
|
||||
if !isFinal && currentTimeMs < flushDeadlineMs {
|
||||
// Fast path - nothing to flush
|
||||
return dst
|
||||
}
|
||||
|
||||
// Slow path - move rrs.rows to dst.
|
||||
rrs.mu.Lock()
|
||||
dst = appendRawRowss(dst, rrs.rows)
|
||||
rrs.rows = rrs.rows[:0]
|
||||
rrs.mu.Unlock()
|
||||
|
||||
return dst
|
||||
}
|
||||
|
||||
func appendRawRowss(dst [][]rawRow, src []rawRow) [][]rawRow {
|
||||
if len(src) == 0 {
|
||||
return dst
|
||||
}
|
||||
if len(dst) == 0 {
|
||||
dst = append(dst, newRawRows())
|
||||
}
|
||||
prows := &dst[len(dst)-1]
|
||||
n := copy((*prows)[len(*prows):cap(*prows)], src)
|
||||
*prows = (*prows)[:len(*prows)+n]
|
||||
src = src[n:]
|
||||
for len(src) > 0 {
|
||||
rows := newRawRows()
|
||||
n := copy(rows[:cap(rows)], src)
|
||||
rows = rows[:len(rows)+n]
|
||||
src = src[n:]
|
||||
dst = append(dst, rows)
|
||||
}
|
||||
return dst
|
||||
}
|
||||
|
||||
@@ -65,6 +65,7 @@ type Storage struct {
|
||||
cachePath string
|
||||
retentionMsecs int64
|
||||
futureRetentionMsecs int64
|
||||
maxBackfillAgeMsecs int64
|
||||
denyQueriesOutsideRetention bool
|
||||
|
||||
// lock file for exclusive access to the storage on the given path.
|
||||
@@ -164,6 +165,7 @@ type Storage struct {
|
||||
type OpenOptions struct {
|
||||
Retention time.Duration
|
||||
FutureRetention time.Duration
|
||||
MaxBackfillAge time.Duration
|
||||
DenyQueriesOutsideRetention bool
|
||||
MaxHourlySeries int
|
||||
MaxDailySeries int
|
||||
@@ -187,6 +189,10 @@ func MustOpenStorage(path string, opts OpenOptions) *Storage {
|
||||
retention = retentionMax
|
||||
}
|
||||
futureRetention := max(opts.FutureRetention, retention2Days)
|
||||
maxBackfillAge := opts.MaxBackfillAge
|
||||
if maxBackfillAge <= 0 || maxBackfillAge > retention {
|
||||
maxBackfillAge = retention
|
||||
}
|
||||
idbPrefillStart := opts.IDBPrefillStart
|
||||
if idbPrefillStart <= 0 {
|
||||
idbPrefillStart = time.Hour
|
||||
@@ -196,6 +202,7 @@ func MustOpenStorage(path string, opts OpenOptions) *Storage {
|
||||
cachePath: filepath.Join(path, cacheDirname),
|
||||
retentionMsecs: retention.Milliseconds(),
|
||||
futureRetentionMsecs: futureRetention.Milliseconds(),
|
||||
maxBackfillAgeMsecs: maxBackfillAge.Milliseconds(),
|
||||
denyQueriesOutsideRetention: opts.DenyQueriesOutsideRetention,
|
||||
stopCh: make(chan struct{}),
|
||||
idbPrefillStartSeconds: idbPrefillStart.Milliseconds() / 1000,
|
||||
@@ -1236,7 +1243,7 @@ func (s *Storage) checkTimeRange(tr TimeRange) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
minTimestamp, maxTimestamp := s.tb.getMinMaxTimestamps()
|
||||
minTimestamp, maxTimestamp := s.tb.getMinMaxRetentionTimestamps()
|
||||
if minTimestamp <= tr.MinTimestamp && tr.MaxTimestamp <= maxTimestamp {
|
||||
return nil
|
||||
}
|
||||
@@ -1896,7 +1903,7 @@ func (s *Storage) add(rows []rawRow, dstMrs []*MetricRow, mrs []MetricRow, preci
|
||||
var newSeriesCount uint64
|
||||
var seriesRepopulated uint64
|
||||
|
||||
minTimestamp, maxTimestamp := s.tb.getMinMaxTimestamps()
|
||||
minTimestamp, maxTimestamp := s.tb.getMinMaxIngestionTimestamps()
|
||||
|
||||
var lTSID legacyTSID
|
||||
var ptw *partitionWrapper
|
||||
@@ -1918,11 +1925,11 @@ func (s *Storage) add(rows []rawRow, dstMrs []*MetricRow, mrs []MetricRow, preci
|
||||
}
|
||||
}
|
||||
if mr.Timestamp < minTimestamp {
|
||||
// Skip rows with too small timestamps outside the retention.
|
||||
// Skip rows with too small timestamps outside the retention or -maxBackfillAge.
|
||||
if firstWarn == nil {
|
||||
metricName := getUserReadableMetricName(mr.MetricNameRaw)
|
||||
firstWarn = fmt.Errorf("cannot insert row with too small timestamp %d outside the retention; minimum allowed timestamp is %d; "+
|
||||
"probably you need updating -retentionPeriod command-line flag; metricName: %s",
|
||||
firstWarn = fmt.Errorf("cannot insert row with too small timestamp %d; minimum allowed timestamp is %d; "+
|
||||
"probably you need updating -retentionPeriod or -maxBackfillAge command-line flags; metricName: %s",
|
||||
mr.Timestamp, minTimestamp, metricName)
|
||||
}
|
||||
s.tooSmallTimestampRows.Add(1)
|
||||
|
||||
@@ -1410,3 +1410,80 @@ func TestStorage_denyQueriesOutsideRetention(t *testing.T) {
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
func TestStorageAddRows_MaxBackfillAge(t *testing.T) {
|
||||
defer testRemoveAll(t)
|
||||
|
||||
mn := MetricName{
|
||||
MetricGroup: []byte("metric"),
|
||||
}
|
||||
mr := MetricRow{
|
||||
MetricNameRaw: mn.marshalRaw(nil),
|
||||
Value: 123,
|
||||
}
|
||||
|
||||
f := func(s *Storage, age time.Duration, want uint64) {
|
||||
t.Helper()
|
||||
|
||||
mr.Timestamp = time.Now().UTC().Add(-age).UnixMilli()
|
||||
s.AddRows([]MetricRow{mr}, defaultPrecisionBits)
|
||||
s.DebugFlush()
|
||||
if got := s.tooSmallTimestampRows.Load(); got != want {
|
||||
t.Fatalf("unexpected number of tooSmallTimestampRows: got %d, want %d", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
synctest.Test(t, func(t *testing.T) {
|
||||
// synctest time begins at 2000-01-01T00:00:00Z.
|
||||
|
||||
retention1y := 365 * 24 * time.Hour
|
||||
var s *Storage
|
||||
|
||||
s = MustOpenStorage(t.Name(), OpenOptions{
|
||||
Retention: retention1y,
|
||||
// By default MaxBackfillAge must be the same as Retention
|
||||
})
|
||||
// Verify that the sample with timestamp 1ms older than retention is
|
||||
// rejected.
|
||||
f(s, retention1y+time.Millisecond, 1)
|
||||
// Verify that the sample with timestamp which is exactly at retention
|
||||
// boundary is accepted.
|
||||
f(s, retention1y, 1)
|
||||
|
||||
// Restart storage with negative MaxBackfillAge. In this case,
|
||||
// MaxBackfillAge must be the same as Retention.
|
||||
// Also advance time a bit so that the storage will not use the same
|
||||
// nanosecond for creating a new part for storing the samples.
|
||||
s.MustClose()
|
||||
time.Sleep(time.Nanosecond)
|
||||
s = MustOpenStorage(t.Name(), OpenOptions{
|
||||
Retention: retention1y,
|
||||
MaxBackfillAge: -1,
|
||||
})
|
||||
f(s, retention1y+time.Millisecond, 1)
|
||||
f(s, retention1y, 1)
|
||||
|
||||
// Restart storage with MaxBackfillAge bigger than Retention. In this
|
||||
// case, MaxBackfillAge must be the same as Retention.
|
||||
s.MustClose()
|
||||
time.Sleep(time.Nanosecond)
|
||||
s = MustOpenStorage(t.Name(), OpenOptions{
|
||||
Retention: retention1y,
|
||||
MaxBackfillAge: retention1y + time.Millisecond,
|
||||
})
|
||||
f(s, retention1y+time.Millisecond, 1)
|
||||
f(s, retention1y, 1)
|
||||
|
||||
// Restart storage with MaxBackfillAge smaller than Retention.
|
||||
s.MustClose()
|
||||
time.Sleep(time.Nanosecond)
|
||||
s = MustOpenStorage(t.Name(), OpenOptions{
|
||||
Retention: retention1y,
|
||||
MaxBackfillAge: retention1y - time.Millisecond,
|
||||
})
|
||||
f(s, retention1y, 1)
|
||||
f(s, retention1y-time.Millisecond, 1)
|
||||
|
||||
s.MustClose()
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1807,18 +1807,19 @@ func TestStorageRowsNotAdded(t *testing.T) {
|
||||
defer testRemoveAll(t)
|
||||
|
||||
type options struct {
|
||||
name string
|
||||
retention time.Duration
|
||||
mrs []MetricRow
|
||||
tr TimeRange
|
||||
wantMetrics *Metrics
|
||||
name string
|
||||
retention time.Duration
|
||||
maxBackfillAge time.Duration
|
||||
mrs []MetricRow
|
||||
tr TimeRange
|
||||
wantMetrics *Metrics
|
||||
}
|
||||
f := func(opts *options) {
|
||||
t.Helper()
|
||||
|
||||
var gotMetrics Metrics
|
||||
path := fmt.Sprintf("%s/%s", t.Name(), opts.name)
|
||||
s := MustOpenStorage(path, OpenOptions{Retention: opts.retention})
|
||||
s := MustOpenStorage(path, OpenOptions{Retention: opts.retention, MaxBackfillAge: opts.maxBackfillAge})
|
||||
defer s.MustClose()
|
||||
s.AddRows(opts.mrs, defaultPrecisionBits)
|
||||
s.DebugFlush()
|
||||
@@ -1890,6 +1891,22 @@ func TestStorageRowsNotAdded(t *testing.T) {
|
||||
},
|
||||
})
|
||||
|
||||
retention = retentionMax
|
||||
maxBackfillAge := 48 * time.Hour
|
||||
minTimestamp = time.Now().Add(-maxBackfillAge - time.Hour).UnixMilli()
|
||||
maxTimestamp = minTimestamp + 1000
|
||||
f(&options{
|
||||
name: "TooSmallTimestampsForMaxBackfillAge",
|
||||
retention: retention,
|
||||
maxBackfillAge: maxBackfillAge,
|
||||
mrs: testGenerateMetricRows(rng, numRows, minTimestamp, maxTimestamp),
|
||||
tr: TimeRange{minTimestamp, maxTimestamp},
|
||||
wantMetrics: &Metrics{
|
||||
RowsReceivedTotal: numRows,
|
||||
TooSmallTimestampRows: numRows,
|
||||
},
|
||||
})
|
||||
|
||||
minTimestamp = time.Now().UnixMilli()
|
||||
maxTimestamp = minTimestamp + 1000
|
||||
mrs = testGenerateMetricRows(rng, numRows, minTimestamp, maxTimestamp)
|
||||
@@ -2506,6 +2523,80 @@ func testStorageOpOnVariousTimeRanges(t *testing.T, op func(t *testing.T, tr Tim
|
||||
})
|
||||
}
|
||||
|
||||
func TestStorageSearchLabelValues_SingleFilterOnLabelName(t *testing.T) {
|
||||
defer testRemoveAll(t)
|
||||
|
||||
const N = 1000
|
||||
|
||||
tr := TimeRange{
|
||||
MinTimestamp: time.Date(2024, 1, 15, 0, 0, 0, 0, time.UTC).UnixMilli(),
|
||||
MaxTimestamp: time.Date(2024, 12, 15, 23, 59, 59, 999_999_999, time.UTC).UnixMilli(),
|
||||
}
|
||||
step := (tr.MaxTimestamp - tr.MinTimestamp) / N
|
||||
mrs := make([]MetricRow, N)
|
||||
allMetricNames := make([]string, N)
|
||||
allLabelValues := make([]string, N)
|
||||
|
||||
for i := range int64(N) {
|
||||
metricName := fmt.Sprintf("metric_%03d", i)
|
||||
labelValue := fmt.Sprintf("value_%03d", i)
|
||||
mn := MetricName{
|
||||
MetricGroup: []byte(metricName),
|
||||
Tags: []Tag{
|
||||
{
|
||||
Key: []byte("label"),
|
||||
Value: []byte(labelValue),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
mrs[i].MetricNameRaw = mn.marshalRaw(nil)
|
||||
mrs[i].Timestamp = tr.MinTimestamp + i*step
|
||||
mrs[i].Value = float64(i)
|
||||
allMetricNames[i] = metricName
|
||||
allLabelValues[i] = labelValue
|
||||
}
|
||||
|
||||
s := MustOpenStorage(t.Name(), OpenOptions{})
|
||||
defer s.MustClose()
|
||||
s.AddRows(mrs, defaultPrecisionBits)
|
||||
s.DebugFlush()
|
||||
|
||||
f := func(label, k, v string, isNegative, isRegex bool, tr TimeRange, maxLabelValues, maxMetrics int, want []string) {
|
||||
t.Helper()
|
||||
tfs := NewTagFilters()
|
||||
if err := tfs.Add([]byte(k), []byte(v), isNegative, isRegex); err != nil {
|
||||
t.Fatalf("unexpected error in TagFilters.Add: %v", err)
|
||||
}
|
||||
got, err := s.SearchLabelValues(nil, label, []*TagFilters{tfs}, tr, maxLabelValues, maxMetrics, noDeadline)
|
||||
if err != nil {
|
||||
t.Fatalf("SearchLabelValues() failed unexpectedly: %v", err)
|
||||
}
|
||||
slices.Sort(got)
|
||||
if diff := cmp.Diff(want, got); diff != "" {
|
||||
t.Fatalf("unexpected label values (-want, +got):\n%s", diff)
|
||||
}
|
||||
}
|
||||
|
||||
wantAllMetricNames := allMetricNames
|
||||
wantAllLabelValues := allLabelValues
|
||||
f("", "", "metric_.*", false, true, tr, N+1, N+1, wantAllMetricNames)
|
||||
f("__name__", "", "metric_.*", false, true, tr, N+1, N+1, wantAllMetricNames)
|
||||
f("label", "label", "value_.*", false, true, tr, N+1, N+1, wantAllLabelValues)
|
||||
|
||||
var wantEvery10thMetricName []string
|
||||
var wantEvery10thLabelValue []string
|
||||
for i := range N {
|
||||
if i%10 == 0 {
|
||||
wantEvery10thMetricName = append(wantEvery10thMetricName, allMetricNames[i])
|
||||
wantEvery10thLabelValue = append(wantEvery10thLabelValue, allLabelValues[i])
|
||||
}
|
||||
}
|
||||
f("", "", "metric_[0-9]{2}0", false, true, tr, N+1, N+1, wantEvery10thMetricName)
|
||||
f("__name__", "", "metric_[0-9]{2}0", false, true, tr, N+1, N+1, wantEvery10thMetricName)
|
||||
f("label", "label", "value_[0-9]{2}0", false, true, tr, N+1, N+1, wantEvery10thLabelValue)
|
||||
}
|
||||
|
||||
func TestStorageSearchLabelValues_EmptyValuesAreNotReturned(t *testing.T) {
|
||||
defer testRemoveAll(t)
|
||||
|
||||
|
||||
@@ -368,7 +368,7 @@ func (tb *table) MustAddRows(rows []rawRow) {
|
||||
// The slowest path - there are rows that don't fit any existing partition.
|
||||
// Create new partitions for these rows.
|
||||
// Do this under tb.ptwsLock.
|
||||
minTimestamp, maxTimestamp := tb.getMinMaxTimestamps()
|
||||
minTimestamp, maxTimestamp := tb.getMinMaxIngestionTimestamps()
|
||||
tb.ptwsLock.Lock()
|
||||
for i := range missingRows {
|
||||
r := &missingRows[i]
|
||||
@@ -407,9 +407,28 @@ func (tb *table) MustGetIndexDBIDByHour(hour uint64) uint64 {
|
||||
return ptw.pt.idb.id
|
||||
}
|
||||
|
||||
func (tb *table) getMinMaxTimestamps() (int64, int64) {
|
||||
// getMinMaxRetentionTimestamps returns the minimum and maximum timestamps
|
||||
// allowed by the configured -retentionPeriod and -futureRetention.
|
||||
//
|
||||
// It is used for checking whether the given time range is fully covered
|
||||
// by the retention, e.g. for -denyQueriesOutsideRetention.
|
||||
func (tb *table) getMinMaxRetentionTimestamps() (int64, int64) {
|
||||
return tb.getMinMaxTimestampsForAge(tb.s.retentionMsecs)
|
||||
}
|
||||
|
||||
// getMinMaxIngestionTimestamps returns the minimum and maximum timestamps
|
||||
// allowed for newly ingested rows.
|
||||
//
|
||||
// The minimum timestamp is bound by -maxBackfillAge instead of -retentionPeriod,
|
||||
// since -maxBackfillAge can be configured to reject backfilled rows with historical
|
||||
// timestamps stricter than the full -retentionPeriod window.
|
||||
func (tb *table) getMinMaxIngestionTimestamps() (int64, int64) {
|
||||
return tb.getMinMaxTimestampsForAge(tb.s.maxBackfillAgeMsecs)
|
||||
}
|
||||
|
||||
func (tb *table) getMinMaxTimestampsForAge(minAgeMsecs int64) (int64, int64) {
|
||||
now := int64(fasttime.UnixTimestamp() * 1000)
|
||||
minTimestamp := now - tb.s.retentionMsecs
|
||||
minTimestamp := now - minAgeMsecs
|
||||
if minTimestamp < 0 {
|
||||
// Negative timestamps aren't supported by the storage.
|
||||
minTimestamp = 0
|
||||
|
||||
@@ -1406,3 +1406,38 @@ func TestTagFilterLess(t *testing.T) {
|
||||
f(prefixA, prefixB, true)
|
||||
f(prefixB, prefixA, false)
|
||||
}
|
||||
|
||||
func TestTagFilters_Add(t *testing.T) {
|
||||
tfs := NewTagFilters()
|
||||
f := func(k, v string, isNegative, isRegexp bool, want int) {
|
||||
t.Helper()
|
||||
if err := tfs.Add([]byte(k), []byte(v), isNegative, isRegexp); err != nil {
|
||||
t.Fatalf("unexpected error in TagFilters.Add: %v", err)
|
||||
}
|
||||
if got := len(tfs.tfs); got != want {
|
||||
t.Fatalf("unexpected tfs count: got %d, want %d", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
// .* filter matches anything and therefore is not added to tfs.
|
||||
f("key0", ".*", false, true, 0)
|
||||
|
||||
// empty filter matches only empty label values and is added to tfs.
|
||||
f("key1", "", false, false, 1)
|
||||
|
||||
f("key2", "value", false, false, 2)
|
||||
f("key3", "value.*", false, true, 3)
|
||||
f("key4", "value", true, false, 4)
|
||||
f("key5", "value.*", true, true, 5)
|
||||
|
||||
// .* filter matches anything and therefore is not added to tfs.
|
||||
f("", ".*", false, true, 5)
|
||||
|
||||
// empty filter matches only empty label values and is added to tfs.
|
||||
f("", "", false, false, 6)
|
||||
|
||||
f("", "value", false, false, 7)
|
||||
f("", "value.*", false, true, 8)
|
||||
f("", "value", true, false, 9)
|
||||
f("", "value.*", true, true, 10)
|
||||
}
|
||||
|
||||
20
vendor/github.com/VictoriaMetrics/metricsql/binary_op.go
generated
vendored
20
vendor/github.com/VictoriaMetrics/metricsql/binary_op.go
generated
vendored
@@ -73,6 +73,16 @@ func isBinaryOp(op string) bool {
|
||||
return binaryOps[op]
|
||||
}
|
||||
|
||||
func isSetOperator(op string) bool {
|
||||
op = strings.ToLower(op)
|
||||
switch op {
|
||||
case "and", "or", "unless", "if", "ifnot", "default":
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func binaryOpPriority(op string) int {
|
||||
op = strings.ToLower(op)
|
||||
return binaryOpPriorities[op]
|
||||
@@ -123,6 +133,16 @@ func isBinaryOpBoolModifier(s string) bool {
|
||||
return s == "bool"
|
||||
}
|
||||
|
||||
func isBinaryOpFillModifier(s string) bool {
|
||||
s = strings.ToLower(s)
|
||||
switch s {
|
||||
case "fill", "fill_left", "fill_right":
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// IsBinaryOpCmp returns true if op is comparison operator such as '==', '!=', etc.
|
||||
func IsBinaryOpCmp(op string) bool {
|
||||
switch op {
|
||||
|
||||
22
vendor/github.com/VictoriaMetrics/metricsql/optimizer.go
generated
vendored
22
vendor/github.com/VictoriaMetrics/metricsql/optimizer.go
generated
vendored
@@ -39,6 +39,9 @@ func canOptimize(e Expr) bool {
|
||||
}
|
||||
}
|
||||
case *BinaryOpExpr:
|
||||
if t.FillLeft != nil && t.FillRight != nil {
|
||||
return canOptimize(t.Left) || canOptimize(t.Right)
|
||||
}
|
||||
return true
|
||||
}
|
||||
return false
|
||||
@@ -66,8 +69,23 @@ func optimizeInplace(e Expr) {
|
||||
case *BinaryOpExpr:
|
||||
optimizeInplace(t.Left)
|
||||
optimizeInplace(t.Right)
|
||||
lfs := getCommonLabelFilters(t)
|
||||
pushdownBinaryOpFiltersInplace(lfs, t)
|
||||
switch {
|
||||
case t.FillLeft != nil && t.FillRight != nil:
|
||||
// for two-sided fill, no cross-side pushdown.
|
||||
case t.FillLeft != nil:
|
||||
// for fill_left(), only propagate filters from right to left.
|
||||
lfs := getCommonLabelFilters(t.Right)
|
||||
lfs = TrimFiltersByGroupModifier(lfs, t)
|
||||
pushdownBinaryOpFiltersInplace(lfs, t.Left)
|
||||
case t.FillRight != nil:
|
||||
// for fill_right(), only propagate filters from left to right.
|
||||
lfs := getCommonLabelFilters(t.Left)
|
||||
lfs = TrimFiltersByGroupModifier(lfs, t)
|
||||
pushdownBinaryOpFiltersInplace(lfs, t.Right)
|
||||
default:
|
||||
lfs := getCommonLabelFilters(t)
|
||||
pushdownBinaryOpFiltersInplace(lfs, t)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
92
vendor/github.com/VictoriaMetrics/metricsql/parser.go
generated
vendored
92
vendor/github.com/VictoriaMetrics/metricsql/parser.go
generated
vendored
@@ -402,6 +402,15 @@ func (p *parser) parseExpr() (Expr, error) {
|
||||
}
|
||||
}
|
||||
}
|
||||
for {
|
||||
ok, err := p.parseFillModifier(&be)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !ok {
|
||||
break
|
||||
}
|
||||
}
|
||||
e2, err := p.parseSingleExpr()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -506,6 +515,65 @@ func (p *parser) parseSingleExprWithoutRollupSuffix() (Expr, error) {
|
||||
}
|
||||
}
|
||||
|
||||
func (p *parser) parseFillModifier(be *BinaryOpExpr) (bool, error) {
|
||||
if !isBinaryOpFillModifier(p.lex.Token) {
|
||||
return false, nil
|
||||
}
|
||||
if isSetOperator(be.Op) {
|
||||
return false, fmt.Errorf(`fill modifier cannot be applied to %q`, be.Op)
|
||||
}
|
||||
op := strings.ToLower(p.lex.Token)
|
||||
if err := p.lex.Next(); err != nil {
|
||||
return false, err
|
||||
}
|
||||
if p.lex.Token != "(" {
|
||||
p.lex.Prev()
|
||||
return false, nil
|
||||
}
|
||||
if err := p.lex.Next(); err != nil {
|
||||
return false, err
|
||||
}
|
||||
ne, err := p.parseFillValue()
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("cannot parse %s fill value: %w", op, err)
|
||||
}
|
||||
if p.lex.Token != ")" {
|
||||
return false, fmt.Errorf(`%s: unexpected token %q; want ")"`, op, p.lex.Token)
|
||||
}
|
||||
if err := p.lex.Next(); err != nil {
|
||||
return false, err
|
||||
}
|
||||
switch op {
|
||||
case "fill":
|
||||
be.FillLeft = ne
|
||||
be.FillRight = ne
|
||||
case "fill_left":
|
||||
be.FillLeft = ne
|
||||
case "fill_right":
|
||||
be.FillRight = ne
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func (p *parser) parseFillValue() (*NumberExpr, error) {
|
||||
neg := false
|
||||
if p.lex.Token == "-" {
|
||||
neg = true
|
||||
if err := p.lex.Next(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
ne, err := p.parsePositiveNumberExpr()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if neg {
|
||||
ne.N = -ne.N
|
||||
ne.s = "-" + ne.s
|
||||
}
|
||||
return ne, nil
|
||||
}
|
||||
|
||||
func (p *parser) parsePositiveNumberExpr() (*NumberExpr, error) {
|
||||
if !isPositiveNumberPrefix(p.lex.Token) && !isInfOrNaN(p.lex.Token) {
|
||||
return nil, fmt.Errorf(`positiveNumberExpr: unexpected token %q; want "number"`, p.lex.Token)
|
||||
@@ -1896,6 +1964,12 @@ type BinaryOpExpr struct {
|
||||
// If KeepMetricNames is set to true, then the operation should keep metric names.
|
||||
KeepMetricNames bool
|
||||
|
||||
// FillLeft contains the fill value for fill_left() or fill() modifier.
|
||||
FillLeft *NumberExpr
|
||||
|
||||
// FillRight contains the fill value for fill_right() or fill() modifier.
|
||||
FillRight *NumberExpr
|
||||
|
||||
// Left contains left arg for the `left op right` expression.
|
||||
Left Expr
|
||||
|
||||
@@ -1971,6 +2045,22 @@ func (be *BinaryOpExpr) appendModifiers(dst []byte) []byte {
|
||||
dst = prefix.AppendString(dst)
|
||||
}
|
||||
}
|
||||
if be.FillLeft != nil && be.FillLeft == be.FillRight {
|
||||
dst = append(dst, " fill("...)
|
||||
dst = be.FillLeft.AppendString(dst)
|
||||
dst = append(dst, ')')
|
||||
} else {
|
||||
if be.FillLeft != nil {
|
||||
dst = append(dst, " fill_left("...)
|
||||
dst = be.FillLeft.AppendString(dst)
|
||||
dst = append(dst, ')')
|
||||
}
|
||||
if be.FillRight != nil {
|
||||
dst = append(dst, " fill_right("...)
|
||||
dst = be.FillRight.AppendString(dst)
|
||||
dst = append(dst, ')')
|
||||
}
|
||||
}
|
||||
return dst
|
||||
}
|
||||
|
||||
@@ -1989,7 +2079,7 @@ func needBinaryOpArgParens(arg Expr) bool {
|
||||
}
|
||||
|
||||
func isReservedBinaryOpIdent(s string) bool {
|
||||
return isBinaryOpGroupModifier(s) || isBinaryOpJoinModifier(s) || isBinaryOpBoolModifier(s) || isPrefixModifier(s)
|
||||
return isBinaryOpGroupModifier(s) || isBinaryOpJoinModifier(s) || isBinaryOpBoolModifier(s) || isPrefixModifier(s) || isBinaryOpFillModifier(s)
|
||||
}
|
||||
|
||||
func isPrefixModifier(s string) bool {
|
||||
|
||||
62
vendor/golang.org/x/sys/cpu/parse.go
generated
vendored
62
vendor/golang.org/x/sys/cpu/parse.go
generated
vendored
@@ -6,38 +6,50 @@ package cpu
|
||||
|
||||
import "strconv"
|
||||
|
||||
// parseRelease parses a dot-separated version number. It follows the semver
|
||||
// syntax, but allows the minor and patch versions to be elided.
|
||||
// parseRelease parses a dot-separated version number from the prefix
|
||||
// of rel. It returns ok=true only if at least the major and minor
|
||||
// components were successfully parsed; the patch component is
|
||||
// best-effort. Trailing vendor or build suffixes such as
|
||||
// "-generic", "+", "_hi3535", or "-rc1" are ignored.
|
||||
//
|
||||
// This is a copy of the Go runtime's parseRelease from
|
||||
// https://golang.org/cl/209597.
|
||||
// https://golang.org/cl/209597, updated in https://golang.org/cl/781800.
|
||||
func parseRelease(rel string) (major, minor, patch int, ok bool) {
|
||||
// Strip anything after a dash or plus.
|
||||
for i := range len(rel) {
|
||||
if rel[i] == '-' || rel[i] == '+' {
|
||||
rel = rel[:i]
|
||||
break
|
||||
// next consumes a run of decimal digits from the front of rel,
|
||||
// returning the parsed value. If the digits are followed by a
|
||||
// '.', it is consumed and more is set so the caller knows to
|
||||
// parse another component; otherwise scanning terminates and
|
||||
// the rest of rel is discarded.
|
||||
next := func() (n int, more, ok bool) {
|
||||
i := 0
|
||||
for i < len(rel) && rel[i] >= '0' && rel[i] <= '9' {
|
||||
i++
|
||||
}
|
||||
if i == 0 {
|
||||
return 0, false, false
|
||||
}
|
||||
n, err := strconv.Atoi(rel[:i])
|
||||
if err != nil {
|
||||
return 0, false, false
|
||||
}
|
||||
if i < len(rel) && rel[i] == '.' {
|
||||
rel = rel[i+1:]
|
||||
return n, true, true
|
||||
}
|
||||
rel = ""
|
||||
return n, false, true
|
||||
}
|
||||
|
||||
next := func() (int, bool) {
|
||||
for i := range len(rel) {
|
||||
if rel[i] == '.' {
|
||||
ver, err := strconv.Atoi(rel[:i])
|
||||
rel = rel[i+1:]
|
||||
return ver, err == nil
|
||||
}
|
||||
}
|
||||
ver, err := strconv.Atoi(rel)
|
||||
rel = ""
|
||||
return ver, err == nil
|
||||
var more bool
|
||||
if major, more, ok = next(); !ok || !more {
|
||||
return 0, 0, 0, false
|
||||
}
|
||||
if major, ok = next(); !ok || rel == "" {
|
||||
return
|
||||
if minor, more, ok = next(); !ok {
|
||||
return 0, 0, 0, false
|
||||
}
|
||||
if minor, ok = next(); !ok || rel == "" {
|
||||
return
|
||||
if !more {
|
||||
return major, minor, 0, true
|
||||
}
|
||||
patch, ok = next()
|
||||
return
|
||||
patch, _, _ = next()
|
||||
return major, minor, patch, true
|
||||
}
|
||||
|
||||
1
vendor/golang.org/x/sys/unix/syscall_linux.go
generated
vendored
1
vendor/golang.org/x/sys/unix/syscall_linux.go
generated
vendored
@@ -1874,6 +1874,7 @@ func Dup2(oldfd, newfd int) error {
|
||||
//sys Dup3(oldfd int, newfd int, flags int) (err error)
|
||||
//sysnb EpollCreate1(flag int) (fd int, err error)
|
||||
//sysnb EpollCtl(epfd int, op int, fd int, event *EpollEvent) (err error)
|
||||
//sys EpollWait(epfd int, events []EpollEvent, msec int) (n int, err error) = SYS_EPOLL_PWAIT
|
||||
//sys Eventfd(initval uint, flags int) (fd int, err error) = SYS_EVENTFD2
|
||||
//sys Exit(code int) = SYS_EXIT_GROUP
|
||||
//sys Fallocate(fd int, mode uint32, off int64, len int64) (err error)
|
||||
|
||||
1
vendor/golang.org/x/sys/unix/syscall_linux_386.go
generated
vendored
1
vendor/golang.org/x/sys/unix/syscall_linux_386.go
generated
vendored
@@ -20,7 +20,6 @@ func setTimeval(sec, usec int64) Timeval {
|
||||
|
||||
// 64-bit file system and 32-bit uid calls
|
||||
// (386 default is 32-bit file system and 16-bit uid).
|
||||
//sys EpollWait(epfd int, events []EpollEvent, msec int) (n int, err error)
|
||||
//sys Fadvise(fd int, offset int64, length int64, advice int) (err error) = SYS_FADVISE64_64
|
||||
//sys Fchown(fd int, uid int, gid int) (err error) = SYS_FCHOWN32
|
||||
//sys Fstat(fd int, stat *Stat_t) (err error) = SYS_FSTAT64
|
||||
|
||||
1
vendor/golang.org/x/sys/unix/syscall_linux_amd64.go
generated
vendored
1
vendor/golang.org/x/sys/unix/syscall_linux_amd64.go
generated
vendored
@@ -6,7 +6,6 @@
|
||||
|
||||
package unix
|
||||
|
||||
//sys EpollWait(epfd int, events []EpollEvent, msec int) (n int, err error)
|
||||
//sys Fadvise(fd int, offset int64, length int64, advice int) (err error) = SYS_FADVISE64
|
||||
//sys Fchown(fd int, uid int, gid int) (err error)
|
||||
//sys Fstat(fd int, stat *Stat_t) (err error)
|
||||
|
||||
1
vendor/golang.org/x/sys/unix/syscall_linux_arm.go
generated
vendored
1
vendor/golang.org/x/sys/unix/syscall_linux_arm.go
generated
vendored
@@ -44,7 +44,6 @@ func Seek(fd int, offset int64, whence int) (newoffset int64, err error) {
|
||||
|
||||
// 64-bit file system and 32-bit uid calls
|
||||
// (16-bit uid calls are not always supported in newer kernels)
|
||||
//sys EpollWait(epfd int, events []EpollEvent, msec int) (n int, err error)
|
||||
//sys Fchown(fd int, uid int, gid int) (err error) = SYS_FCHOWN32
|
||||
//sys Fstat(fd int, stat *Stat_t) (err error) = SYS_FSTAT64
|
||||
//sys Fstatat(dirfd int, path string, stat *Stat_t, flags int) (err error) = SYS_FSTATAT64
|
||||
|
||||
1
vendor/golang.org/x/sys/unix/syscall_linux_arm64.go
generated
vendored
1
vendor/golang.org/x/sys/unix/syscall_linux_arm64.go
generated
vendored
@@ -8,7 +8,6 @@ package unix
|
||||
|
||||
import "unsafe"
|
||||
|
||||
//sys EpollWait(epfd int, events []EpollEvent, msec int) (n int, err error) = SYS_EPOLL_PWAIT
|
||||
//sys Fadvise(fd int, offset int64, length int64, advice int) (err error) = SYS_FADVISE64
|
||||
//sys Fchown(fd int, uid int, gid int) (err error)
|
||||
//sys Fstat(fd int, stat *Stat_t) (err error)
|
||||
|
||||
1
vendor/golang.org/x/sys/unix/syscall_linux_loong64.go
generated
vendored
1
vendor/golang.org/x/sys/unix/syscall_linux_loong64.go
generated
vendored
@@ -8,7 +8,6 @@ package unix
|
||||
|
||||
import "unsafe"
|
||||
|
||||
//sys EpollWait(epfd int, events []EpollEvent, msec int) (n int, err error) = SYS_EPOLL_PWAIT
|
||||
//sys Fadvise(fd int, offset int64, length int64, advice int) (err error) = SYS_FADVISE64
|
||||
//sys Fchown(fd int, uid int, gid int) (err error)
|
||||
//sys Fstatfs(fd int, buf *Statfs_t) (err error)
|
||||
|
||||
1
vendor/golang.org/x/sys/unix/syscall_linux_mips64x.go
generated
vendored
1
vendor/golang.org/x/sys/unix/syscall_linux_mips64x.go
generated
vendored
@@ -6,7 +6,6 @@
|
||||
|
||||
package unix
|
||||
|
||||
//sys EpollWait(epfd int, events []EpollEvent, msec int) (n int, err error)
|
||||
//sys Fadvise(fd int, offset int64, length int64, advice int) (err error) = SYS_FADVISE64
|
||||
//sys Fchown(fd int, uid int, gid int) (err error)
|
||||
//sys Fstatfs(fd int, buf *Statfs_t) (err error)
|
||||
|
||||
1
vendor/golang.org/x/sys/unix/syscall_linux_mipsx.go
generated
vendored
1
vendor/golang.org/x/sys/unix/syscall_linux_mipsx.go
generated
vendored
@@ -13,7 +13,6 @@ import (
|
||||
|
||||
func Syscall9(trap, a1, a2, a3, a4, a5, a6, a7, a8, a9 uintptr) (r1, r2 uintptr, err syscall.Errno)
|
||||
|
||||
//sys EpollWait(epfd int, events []EpollEvent, msec int) (n int, err error)
|
||||
//sys Fadvise(fd int, offset int64, length int64, advice int) (err error) = SYS_FADVISE64
|
||||
//sys Fchown(fd int, uid int, gid int) (err error)
|
||||
//sys Ftruncate(fd int, length int64) (err error) = SYS_FTRUNCATE64
|
||||
|
||||
1
vendor/golang.org/x/sys/unix/syscall_linux_ppc.go
generated
vendored
1
vendor/golang.org/x/sys/unix/syscall_linux_ppc.go
generated
vendored
@@ -11,7 +11,6 @@ import (
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
//sys EpollWait(epfd int, events []EpollEvent, msec int) (n int, err error)
|
||||
//sys Fchown(fd int, uid int, gid int) (err error)
|
||||
//sys Fstat(fd int, stat *Stat_t) (err error) = SYS_FSTAT64
|
||||
//sys Fstatat(dirfd int, path string, stat *Stat_t, flags int) (err error) = SYS_FSTATAT64
|
||||
|
||||
1
vendor/golang.org/x/sys/unix/syscall_linux_ppc64x.go
generated
vendored
1
vendor/golang.org/x/sys/unix/syscall_linux_ppc64x.go
generated
vendored
@@ -6,7 +6,6 @@
|
||||
|
||||
package unix
|
||||
|
||||
//sys EpollWait(epfd int, events []EpollEvent, msec int) (n int, err error)
|
||||
//sys Fadvise(fd int, offset int64, length int64, advice int) (err error) = SYS_FADVISE64
|
||||
//sys Fchown(fd int, uid int, gid int) (err error)
|
||||
//sys Fstat(fd int, stat *Stat_t) (err error)
|
||||
|
||||
1
vendor/golang.org/x/sys/unix/syscall_linux_riscv64.go
generated
vendored
1
vendor/golang.org/x/sys/unix/syscall_linux_riscv64.go
generated
vendored
@@ -8,7 +8,6 @@ package unix
|
||||
|
||||
import "unsafe"
|
||||
|
||||
//sys EpollWait(epfd int, events []EpollEvent, msec int) (n int, err error) = SYS_EPOLL_PWAIT
|
||||
//sys Fadvise(fd int, offset int64, length int64, advice int) (err error) = SYS_FADVISE64
|
||||
//sys Fchown(fd int, uid int, gid int) (err error)
|
||||
//sys Fstat(fd int, stat *Stat_t) (err error)
|
||||
|
||||
1
vendor/golang.org/x/sys/unix/syscall_linux_s390x.go
generated
vendored
1
vendor/golang.org/x/sys/unix/syscall_linux_s390x.go
generated
vendored
@@ -10,7 +10,6 @@ import (
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
//sys EpollWait(epfd int, events []EpollEvent, msec int) (n int, err error)
|
||||
//sys Fadvise(fd int, offset int64, length int64, advice int) (err error) = SYS_FADVISE64
|
||||
//sys Fchown(fd int, uid int, gid int) (err error)
|
||||
//sys Fstat(fd int, stat *Stat_t) (err error)
|
||||
|
||||
1
vendor/golang.org/x/sys/unix/syscall_linux_sparc64.go
generated
vendored
1
vendor/golang.org/x/sys/unix/syscall_linux_sparc64.go
generated
vendored
@@ -6,7 +6,6 @@
|
||||
|
||||
package unix
|
||||
|
||||
//sys EpollWait(epfd int, events []EpollEvent, msec int) (n int, err error)
|
||||
//sys Fadvise(fd int, offset int64, length int64, advice int) (err error) = SYS_FADVISE64
|
||||
//sys Fchown(fd int, uid int, gid int) (err error)
|
||||
//sys Fstat(fd int, stat *Stat_t) (err error)
|
||||
|
||||
8
vendor/golang.org/x/sys/unix/zerrors_linux.go
generated
vendored
8
vendor/golang.org/x/sys/unix/zerrors_linux.go
generated
vendored
@@ -1359,6 +1359,7 @@ const (
|
||||
FAN_UNLIMITED_MARKS = 0x20
|
||||
FAN_UNLIMITED_QUEUE = 0x10
|
||||
FD_CLOEXEC = 0x1
|
||||
FD_PIDFS_ROOT = -0x2712
|
||||
FD_SETSIZE = 0x400
|
||||
FF0 = 0x0
|
||||
FIB_RULE_DEV_DETACHED = 0x8
|
||||
@@ -1970,6 +1971,8 @@ const (
|
||||
MADV_DONTNEED = 0x4
|
||||
MADV_DONTNEED_LOCKED = 0x18
|
||||
MADV_FREE = 0x8
|
||||
MADV_GUARD_INSTALL = 0x66
|
||||
MADV_GUARD_REMOVE = 0x67
|
||||
MADV_HUGEPAGE = 0xe
|
||||
MADV_HWPOISON = 0x64
|
||||
MADV_KEEPONFORK = 0x13
|
||||
@@ -2114,7 +2117,7 @@ const (
|
||||
MS_NOSEC = 0x10000000
|
||||
MS_NOSUID = 0x2
|
||||
MS_NOSYMFOLLOW = 0x100
|
||||
MS_NOUSER = -0x80000000
|
||||
MS_NOUSER = 0x80000000
|
||||
MS_POSIXACL = 0x10000
|
||||
MS_PRIVATE = 0x40000
|
||||
MS_RDONLY = 0x1
|
||||
@@ -3786,6 +3789,9 @@ const (
|
||||
TCPOPT_TIMESTAMP = 0x8
|
||||
TCPOPT_TSTAMP_HDR = 0x101080a
|
||||
TCPOPT_WINDOW = 0x3
|
||||
TCP_AO_KEYF_EXCLUDE_OPT = 0x2
|
||||
TCP_AO_KEYF_IFINDEX = 0x1
|
||||
TCP_AO_MAXKEYLEN = 0x50
|
||||
TCP_CC_INFO = 0x1a
|
||||
TCP_CM_INQ = 0x24
|
||||
TCP_CONGESTION = 0xd
|
||||
|
||||
17
vendor/golang.org/x/sys/unix/zsyscall_linux.go
generated
vendored
17
vendor/golang.org/x/sys/unix/zsyscall_linux.go
generated
vendored
@@ -700,6 +700,23 @@ func EpollCtl(epfd int, op int, fd int, event *EpollEvent) (err error) {
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func EpollWait(epfd int, events []EpollEvent, msec int) (n int, err error) {
|
||||
var _p0 unsafe.Pointer
|
||||
if len(events) > 0 {
|
||||
_p0 = unsafe.Pointer(&events[0])
|
||||
} else {
|
||||
_p0 = unsafe.Pointer(&_zero)
|
||||
}
|
||||
r0, _, e1 := Syscall6(SYS_EPOLL_PWAIT, uintptr(epfd), uintptr(_p0), uintptr(len(events)), uintptr(msec), 0, 0)
|
||||
n = int(r0)
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func Eventfd(initval uint, flags int) (fd int, err error) {
|
||||
r0, _, e1 := Syscall(SYS_EVENTFD2, uintptr(initval), uintptr(flags), 0)
|
||||
fd = int(r0)
|
||||
|
||||
17
vendor/golang.org/x/sys/unix/zsyscall_linux_386.go
generated
vendored
17
vendor/golang.org/x/sys/unix/zsyscall_linux_386.go
generated
vendored
@@ -45,23 +45,6 @@ func Tee(rfd int, wfd int, len int, flags int) (n int64, err error) {
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func EpollWait(epfd int, events []EpollEvent, msec int) (n int, err error) {
|
||||
var _p0 unsafe.Pointer
|
||||
if len(events) > 0 {
|
||||
_p0 = unsafe.Pointer(&events[0])
|
||||
} else {
|
||||
_p0 = unsafe.Pointer(&_zero)
|
||||
}
|
||||
r0, _, e1 := Syscall6(SYS_EPOLL_WAIT, uintptr(epfd), uintptr(_p0), uintptr(len(events)), uintptr(msec), 0, 0)
|
||||
n = int(r0)
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func Fadvise(fd int, offset int64, length int64, advice int) (err error) {
|
||||
_, _, e1 := Syscall6(SYS_FADVISE64_64, uintptr(fd), uintptr(offset), uintptr(offset>>32), uintptr(length), uintptr(length>>32), uintptr(advice))
|
||||
if e1 != 0 {
|
||||
|
||||
17
vendor/golang.org/x/sys/unix/zsyscall_linux_amd64.go
generated
vendored
17
vendor/golang.org/x/sys/unix/zsyscall_linux_amd64.go
generated
vendored
@@ -45,23 +45,6 @@ func Tee(rfd int, wfd int, len int, flags int) (n int64, err error) {
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func EpollWait(epfd int, events []EpollEvent, msec int) (n int, err error) {
|
||||
var _p0 unsafe.Pointer
|
||||
if len(events) > 0 {
|
||||
_p0 = unsafe.Pointer(&events[0])
|
||||
} else {
|
||||
_p0 = unsafe.Pointer(&_zero)
|
||||
}
|
||||
r0, _, e1 := Syscall6(SYS_EPOLL_WAIT, uintptr(epfd), uintptr(_p0), uintptr(len(events)), uintptr(msec), 0, 0)
|
||||
n = int(r0)
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func Fadvise(fd int, offset int64, length int64, advice int) (err error) {
|
||||
_, _, e1 := Syscall6(SYS_FADVISE64, uintptr(fd), uintptr(offset), uintptr(length), uintptr(advice), 0, 0)
|
||||
if e1 != 0 {
|
||||
|
||||
17
vendor/golang.org/x/sys/unix/zsyscall_linux_arm.go
generated
vendored
17
vendor/golang.org/x/sys/unix/zsyscall_linux_arm.go
generated
vendored
@@ -213,23 +213,6 @@ func sendmsg(s int, msg *Msghdr, flags int) (n int, err error) {
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func EpollWait(epfd int, events []EpollEvent, msec int) (n int, err error) {
|
||||
var _p0 unsafe.Pointer
|
||||
if len(events) > 0 {
|
||||
_p0 = unsafe.Pointer(&events[0])
|
||||
} else {
|
||||
_p0 = unsafe.Pointer(&_zero)
|
||||
}
|
||||
r0, _, e1 := Syscall6(SYS_EPOLL_WAIT, uintptr(epfd), uintptr(_p0), uintptr(len(events)), uintptr(msec), 0, 0)
|
||||
n = int(r0)
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func Fchown(fd int, uid int, gid int) (err error) {
|
||||
_, _, e1 := Syscall(SYS_FCHOWN32, uintptr(fd), uintptr(uid), uintptr(gid))
|
||||
if e1 != 0 {
|
||||
|
||||
17
vendor/golang.org/x/sys/unix/zsyscall_linux_arm64.go
generated
vendored
17
vendor/golang.org/x/sys/unix/zsyscall_linux_arm64.go
generated
vendored
@@ -45,23 +45,6 @@ func Tee(rfd int, wfd int, len int, flags int) (n int64, err error) {
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func EpollWait(epfd int, events []EpollEvent, msec int) (n int, err error) {
|
||||
var _p0 unsafe.Pointer
|
||||
if len(events) > 0 {
|
||||
_p0 = unsafe.Pointer(&events[0])
|
||||
} else {
|
||||
_p0 = unsafe.Pointer(&_zero)
|
||||
}
|
||||
r0, _, e1 := Syscall6(SYS_EPOLL_PWAIT, uintptr(epfd), uintptr(_p0), uintptr(len(events)), uintptr(msec), 0, 0)
|
||||
n = int(r0)
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func Fadvise(fd int, offset int64, length int64, advice int) (err error) {
|
||||
_, _, e1 := Syscall6(SYS_FADVISE64, uintptr(fd), uintptr(offset), uintptr(length), uintptr(advice), 0, 0)
|
||||
if e1 != 0 {
|
||||
|
||||
17
vendor/golang.org/x/sys/unix/zsyscall_linux_loong64.go
generated
vendored
17
vendor/golang.org/x/sys/unix/zsyscall_linux_loong64.go
generated
vendored
@@ -45,23 +45,6 @@ func Tee(rfd int, wfd int, len int, flags int) (n int64, err error) {
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func EpollWait(epfd int, events []EpollEvent, msec int) (n int, err error) {
|
||||
var _p0 unsafe.Pointer
|
||||
if len(events) > 0 {
|
||||
_p0 = unsafe.Pointer(&events[0])
|
||||
} else {
|
||||
_p0 = unsafe.Pointer(&_zero)
|
||||
}
|
||||
r0, _, e1 := Syscall6(SYS_EPOLL_PWAIT, uintptr(epfd), uintptr(_p0), uintptr(len(events)), uintptr(msec), 0, 0)
|
||||
n = int(r0)
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func Fadvise(fd int, offset int64, length int64, advice int) (err error) {
|
||||
_, _, e1 := Syscall6(SYS_FADVISE64, uintptr(fd), uintptr(offset), uintptr(length), uintptr(advice), 0, 0)
|
||||
if e1 != 0 {
|
||||
|
||||
17
vendor/golang.org/x/sys/unix/zsyscall_linux_mips.go
generated
vendored
17
vendor/golang.org/x/sys/unix/zsyscall_linux_mips.go
generated
vendored
@@ -45,23 +45,6 @@ func Tee(rfd int, wfd int, len int, flags int) (n int64, err error) {
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func EpollWait(epfd int, events []EpollEvent, msec int) (n int, err error) {
|
||||
var _p0 unsafe.Pointer
|
||||
if len(events) > 0 {
|
||||
_p0 = unsafe.Pointer(&events[0])
|
||||
} else {
|
||||
_p0 = unsafe.Pointer(&_zero)
|
||||
}
|
||||
r0, _, e1 := Syscall6(SYS_EPOLL_WAIT, uintptr(epfd), uintptr(_p0), uintptr(len(events)), uintptr(msec), 0, 0)
|
||||
n = int(r0)
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func Fadvise(fd int, offset int64, length int64, advice int) (err error) {
|
||||
_, _, e1 := Syscall9(SYS_FADVISE64, uintptr(fd), 0, uintptr(offset>>32), uintptr(offset), uintptr(length>>32), uintptr(length), uintptr(advice), 0, 0)
|
||||
if e1 != 0 {
|
||||
|
||||
17
vendor/golang.org/x/sys/unix/zsyscall_linux_mips64.go
generated
vendored
17
vendor/golang.org/x/sys/unix/zsyscall_linux_mips64.go
generated
vendored
@@ -45,23 +45,6 @@ func Tee(rfd int, wfd int, len int, flags int) (n int64, err error) {
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func EpollWait(epfd int, events []EpollEvent, msec int) (n int, err error) {
|
||||
var _p0 unsafe.Pointer
|
||||
if len(events) > 0 {
|
||||
_p0 = unsafe.Pointer(&events[0])
|
||||
} else {
|
||||
_p0 = unsafe.Pointer(&_zero)
|
||||
}
|
||||
r0, _, e1 := Syscall6(SYS_EPOLL_WAIT, uintptr(epfd), uintptr(_p0), uintptr(len(events)), uintptr(msec), 0, 0)
|
||||
n = int(r0)
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func Fadvise(fd int, offset int64, length int64, advice int) (err error) {
|
||||
_, _, e1 := Syscall6(SYS_FADVISE64, uintptr(fd), uintptr(offset), uintptr(length), uintptr(advice), 0, 0)
|
||||
if e1 != 0 {
|
||||
|
||||
17
vendor/golang.org/x/sys/unix/zsyscall_linux_mips64le.go
generated
vendored
17
vendor/golang.org/x/sys/unix/zsyscall_linux_mips64le.go
generated
vendored
@@ -45,23 +45,6 @@ func Tee(rfd int, wfd int, len int, flags int) (n int64, err error) {
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func EpollWait(epfd int, events []EpollEvent, msec int) (n int, err error) {
|
||||
var _p0 unsafe.Pointer
|
||||
if len(events) > 0 {
|
||||
_p0 = unsafe.Pointer(&events[0])
|
||||
} else {
|
||||
_p0 = unsafe.Pointer(&_zero)
|
||||
}
|
||||
r0, _, e1 := Syscall6(SYS_EPOLL_WAIT, uintptr(epfd), uintptr(_p0), uintptr(len(events)), uintptr(msec), 0, 0)
|
||||
n = int(r0)
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func Fadvise(fd int, offset int64, length int64, advice int) (err error) {
|
||||
_, _, e1 := Syscall6(SYS_FADVISE64, uintptr(fd), uintptr(offset), uintptr(length), uintptr(advice), 0, 0)
|
||||
if e1 != 0 {
|
||||
|
||||
17
vendor/golang.org/x/sys/unix/zsyscall_linux_mipsle.go
generated
vendored
17
vendor/golang.org/x/sys/unix/zsyscall_linux_mipsle.go
generated
vendored
@@ -45,23 +45,6 @@ func Tee(rfd int, wfd int, len int, flags int) (n int64, err error) {
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func EpollWait(epfd int, events []EpollEvent, msec int) (n int, err error) {
|
||||
var _p0 unsafe.Pointer
|
||||
if len(events) > 0 {
|
||||
_p0 = unsafe.Pointer(&events[0])
|
||||
} else {
|
||||
_p0 = unsafe.Pointer(&_zero)
|
||||
}
|
||||
r0, _, e1 := Syscall6(SYS_EPOLL_WAIT, uintptr(epfd), uintptr(_p0), uintptr(len(events)), uintptr(msec), 0, 0)
|
||||
n = int(r0)
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func Fadvise(fd int, offset int64, length int64, advice int) (err error) {
|
||||
_, _, e1 := Syscall9(SYS_FADVISE64, uintptr(fd), 0, uintptr(offset), uintptr(offset>>32), uintptr(length), uintptr(length>>32), uintptr(advice), 0, 0)
|
||||
if e1 != 0 {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user