Files
Max Kotliar be3eddf425 lib/prompb: Merge prompbmarshal logic into prompb
The prompb and prompbmarshal share exactly the same models and provide
marshal and unmarshale capabilities for them. This creates duplication
(changes in one model has to be made in another, case with metadata) and
confusion where for example you compare same looking models but golang
says they are not the same (because of the type).

This commit merge prompbmarshal logic into prompb so the rest of the
code is aligned on prompb models.

Moves samplesPool and labelsPool to WriteRequestUnmarshaller.
Make WriteRequest struct clean from unmarshal logic.

The benchmark shows no significant changes:

$benchstat prompbmarshal.bench prompb2.bench
goos: darwin
goarch: arm64
pkg: github.com/VictoriaMetrics/VictoriaMetrics/lib/prompb
cpu: Apple M1 Pro
                                 │ prompbmarshal.bench │           prompb2.bench            │
                                 │       sec/op        │   sec/op     vs base               │
WriteRequestUnmarshalProtobuf-10           189.2µ ± 5%   190.8µ ± 8%       ~ (p=0.579 n=10)
WriteRequestMarshalProtobuf-10             145.3µ ± 7%   143.6µ ± 2%       ~ (p=0.143 n=10)
geomean                                    165.8µ        165.5µ       -0.14%

                                 │ prompbmarshal.bench │            prompb2.bench            │
                                 │         B/s         │     B/s       vs base               │
WriteRequestUnmarshalProtobuf-10          50.42Mi ± 5%   49.99Mi ± 8%       ~ (p=0.593 n=10)
WriteRequestMarshalProtobuf-10            65.64Mi ± 7%   66.39Mi ± 2%       ~ (p=0.143 n=10)
geomean                                   57.53Mi        57.61Mi       +0.14%

                                 │ prompbmarshal.bench │            prompb2.bench             │
                                 │        B/op         │     B/op       vs base               │
WriteRequestUnmarshalProtobuf-10         27.70Ki ±  4%   26.90Ki ±  7%       ~ (p=0.190 n=10)
WriteRequestMarshalProtobuf-10           3.267Ki ± 12%   3.273Ki ± 12%       ~ (p=0.971 n=10)
geomean                                  9.514Ki         9.383Ki        -1.38%

                                 │ prompbmarshal.bench │            prompb2.bench            │
                                 │      allocs/op      │ allocs/op   vs base                 │
WriteRequestUnmarshalProtobuf-10          0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=10) ¹
WriteRequestMarshalProtobuf-10            0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=10) ¹
geomean                                              ²               +0.00%                ²
¹ all samples are equal
² summaries must be >0 to compute geomean
2025-07-31 01:04:11 +03:00

135 lines
5.0 KiB
Go

package timeserieslimits
import (
"time"
"github.com/VictoriaMetrics/metrics"
"github.com/VictoriaMetrics/VictoriaMetrics/lib/atomicutil"
"github.com/VictoriaMetrics/VictoriaMetrics/lib/logger"
"github.com/VictoriaMetrics/VictoriaMetrics/lib/prompb"
)
var (
enabled bool
// The maximum length of label name.
//
// Samples with longer names are ignored.
maxLabelNameLen = 256
// The maximum length of label value.
//
// Samples with longer label values are ignored.
maxLabelValueLen = 4 * 1024
// The maximum number of labels per each timeseries.
//
// Samples with exceeding amount of labels are ignored.
maxLabelsPerTimeseries = 40
)
// Init prepares package for usage
func Init(inputMaxLabelsPerTimeseries, inputMaxLabelNameLen, inputMaxLabelValueLen int) {
maxLabelsPerTimeseries = inputMaxLabelsPerTimeseries
maxLabelNameLen = inputMaxLabelNameLen
maxLabelValueLen = inputMaxLabelValueLen
enabled = maxLabelsPerTimeseries > 0 || maxLabelNameLen > 0 || maxLabelValueLen > 0
_ = metrics.GetOrCreateGauge(`vm_rows_ignored_total{reason="too_many_labels"}`, func() float64 {
return float64(ignoredSeriesWithTooManyLabels.Load())
})
_ = metrics.GetOrCreateGauge(`vm_rows_ignored_total{reason="too_long_label_name"}`, func() float64 {
return float64(ignoredSeriesWithTooLongLabelName.Load())
})
_ = metrics.GetOrCreateGauge(`vm_rows_ignored_total{reason="too_long_label_value"}`, func() float64 {
return float64(ignoredSeriesWithTooLongLabelValue.Load())
})
}
var (
ignoredSeriesWithTooManyLabelsLogTicker = time.NewTicker(5 * time.Second)
ignoredSeriesWithTooLongLabelNameLogTicker = time.NewTicker(5 * time.Second)
ignoredSeriesWithTooLongLabelValueLogTicker = time.NewTicker(5 * time.Second)
)
var (
// ignoredSeriesWithTooManyLabels is the number of ignored series with too many labels
ignoredSeriesWithTooManyLabels atomicutil.Uint64
// ignoredSeriesWithTooLongLabelName is the number of ignored series which contain labels with too long names
ignoredSeriesWithTooLongLabelName atomicutil.Uint64
// ignoredSeriesWithTooLongLabelValue is the number of ignored series which contain labels with too long values
ignoredSeriesWithTooLongLabelValue atomicutil.Uint64
)
func trackIgnoredSeriesWithTooManyLabels(labels []prompb.Label) {
ignoredSeriesWithTooManyLabels.Add(1)
select {
case <-ignoredSeriesWithTooManyLabelsLogTicker.C:
// Do not call logger.WithThrottler() here, since this will result in increased CPU usage
// because prompb.LabelsToString() will be called with each trackIgnoredSeriesWithTooManyLabels call.
logger.Warnf("ignoring series with %d labels for %s; either reduce the number of labels for this metric "+
"or increase -maxLabelsPerTimeseries=%d cmd-line flag value",
len(labels), prompb.LabelsToString(labels), maxLabelsPerTimeseries)
default:
}
}
func trackIgnoredSeriesWithTooLongLabelValue(l *prompb.Label, labels []prompb.Label) {
ignoredSeriesWithTooLongLabelValue.Add(1)
select {
case <-ignoredSeriesWithTooLongLabelValueLogTicker.C:
// Do not call logger.WithThrottler() here, since this will result in increased CPU usage
// because prompb.LabelsToString() will be called with each trackIgnoredSeriesWithTooLongLabelValue call.
logger.Warnf("ignoring series with %s=%q label for %s; label value length=%d exceeds -maxLabelValueLen=%d; "+
"either reduce the label value length or increase -maxLabelValueLen command-line flag value",
l.Name, l.Value, prompb.LabelsToString(labels), len(l.Value), maxLabelValueLen)
default:
}
}
func trackIgnoredSeriesWithTooLongLabelName(l *prompb.Label, labels []prompb.Label) {
ignoredSeriesWithTooLongLabelName.Add(1)
select {
case <-ignoredSeriesWithTooLongLabelNameLogTicker.C:
// Do not call logger.WithThrottler() here, since this will result in increased CPU usage
// because prompb.LabelsToString() will be called with each trackIgnoredSeriesWithTooLongLabelName call.
logger.Warnf("ignoring series with label %q for %s; label name length=%d exceeds max allowed %d - consider reducing label name length.",
l.Name, prompb.LabelsToString(labels), len(l.Name), maxLabelNameLen)
default:
}
}
// Enabled returns true if any of limits is configured
func Enabled() bool {
return enabled
}
// IsExceeding checks if passed labels exceed one of the limits:
// * Maximum allowed labels limit
// * Maximum allowed label name length limit
// * Maximum allowed label value length limit
//
// increments metrics and shows warning in logs
func IsExceeding(labels []prompb.Label) bool {
if maxLabelsPerTimeseries > 0 && len(labels) > maxLabelsPerTimeseries {
trackIgnoredSeriesWithTooManyLabels(labels)
return true
}
if maxLabelNameLen == 0 && maxLabelValueLen == 0 {
return false
}
for _, l := range labels {
if maxLabelNameLen > 0 && len(l.Name) > maxLabelNameLen {
trackIgnoredSeriesWithTooLongLabelName(&l, labels)
return true
}
if maxLabelValueLen > 0 && len(l.Value) > maxLabelValueLen {
trackIgnoredSeriesWithTooLongLabelValue(&l, labels)
return true
}
}
return false
}