mirror of
https://github.com/VictoriaMetrics/VictoriaMetrics.git
synced 2024-11-21 14:44:00 +00:00
lib/promrelabel: optimize action: replace
for non-trivial regex values
Cache `action: replace` results for non-trivial regexs and return them next time instead of performing CPU-intensive regex replacement. Optimize also `action: labelmap_all` and `action: replace_all` in the same way.
This commit is contained in:
parent
f38c9db74d
commit
9fc2817f41
4 changed files with 32 additions and 21 deletions
|
@ -19,6 +19,7 @@ The following tip changes can be tested by building VictoriaMetrics components f
|
|||
|
||||
**Update note 2:** [vmalert](https://docs.victoriametrics.com/vmalert.html) changes default value for command-line flag `-datasource.queryStep` from `0s` to `5m`. The change supposed to improve reliability of the rules evaluation when evaluation interval is lower than scraping interval.
|
||||
|
||||
* FEATURE: improve [relabeling](https://docs.victoriametrics.com/vmagent.html#relabeling) performance by up to 3x if non-trivial `regex` values are used.
|
||||
* FEATURE: sanitize metric names for data ingested via [DataDog protocol](https://docs.victoriametrics.com/#how-to-send-data-from-datadog-agent) according to [DataDog metric naming](https://docs.datadoghq.com/metrics/custom_metrics/#naming-custom-metrics). The behaviour can be disabled by passing `-datadog.sanitizeMetricName=false` command-line flag. Thanks to @PerGon for [the pull request](https://github.com/VictoriaMetrics/VictoriaMetrics/pull/3105).
|
||||
* FEATURE: add `-usePromCompatibleNaming` command-line flag to [vmagent](https://docs.victoriametrics.com/vmagent.html), to single-node VictoriaMetrics and to `vminsert` component of VictoriaMetrics cluster. This flag can be used for normalizing the ingested metric names and label names to [Prometheus-compatible form](https://prometheus.io/docs/concepts/data_model/#metric-names-and-labels). If this flag is set, then all the chars unsupported by Prometheus are replaced with `_` chars in metric names and labels of the ingested samples. See [this feature request](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/3113).
|
||||
* FEATURE: accept whitespace in metric names and tags ingested via [Graphite plaintext protocol](https://docs.victoriametrics.com/#how-to-send-data-from-graphite-compatible-agents-such-as-statsd) according to [the specs](https://graphite.readthedocs.io/en/latest/tags.html). See [this issue](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/3102).
|
||||
|
|
|
@ -368,6 +368,7 @@ func parseRelabelConfig(rc *RelabelConfig) (*parsedRelabelConfig, error) {
|
|||
hasLabelReferenceInReplacement: strings.Contains(replacement, "{{"),
|
||||
}
|
||||
prc.stringReplacer = bytesutil.NewFastStringTransformer(prc.replaceFullStringSlow)
|
||||
prc.submatchReplacer = bytesutil.NewFastStringTransformer(prc.replaceStringSubmatchesSlow)
|
||||
return prc, nil
|
||||
}
|
||||
|
||||
|
|
|
@ -164,6 +164,7 @@ func TestParseRelabelConfigsSuccess(t *testing.T) {
|
|||
if pcs != nil {
|
||||
for _, prc := range pcs.prcs {
|
||||
prc.stringReplacer = nil
|
||||
prc.submatchReplacer = nil
|
||||
}
|
||||
}
|
||||
if !reflect.DeepEqual(pcs, pcsExpected) {
|
||||
|
|
|
@ -37,6 +37,7 @@ type parsedRelabelConfig struct {
|
|||
hasLabelReferenceInReplacement bool
|
||||
|
||||
stringReplacer *bytesutil.FastStringTransformer
|
||||
submatchReplacer *bytesutil.FastStringTransformer
|
||||
}
|
||||
|
||||
// String returns human-readable representation for prc.
|
||||
|
@ -211,17 +212,23 @@ func (prc *parsedRelabelConfig) apply(labels []prompbmarshal.Label, labelsOffset
|
|||
relabelBufPool.Put(bb)
|
||||
return labels
|
||||
}
|
||||
match := prc.RegexAnchored.FindSubmatchIndex(bb.B)
|
||||
if match == nil {
|
||||
// Fast path - nothing to replace.
|
||||
relabelBufPool.Put(bb)
|
||||
return labels
|
||||
var valueStr string
|
||||
if replacement == prc.Replacement {
|
||||
// Fast path - the replacement wasn't modified, so it is safe calling stringReplacer.Transform.
|
||||
valueStr = prc.stringReplacer.Transform(sourceStr)
|
||||
} else {
|
||||
// Slow path - the replacement has been modified, so the valueStr must be calculated
|
||||
// from scratch based on the new replacement value.
|
||||
match := prc.RegexAnchored.FindSubmatchIndex(bb.B)
|
||||
valueStr = prc.expandCaptureGroups(replacement, sourceStr, match)
|
||||
}
|
||||
nameStr := prc.TargetLabel
|
||||
if prc.hasCaptureGroupInTargetLabel {
|
||||
// Slow path - target_label contains regex capture groups, so the target_label
|
||||
// must be calculated from the regex match.
|
||||
match := prc.RegexAnchored.FindSubmatchIndex(bb.B)
|
||||
nameStr = prc.expandCaptureGroups(nameStr, sourceStr, match)
|
||||
}
|
||||
valueStr := prc.expandCaptureGroups(replacement, sourceStr, match)
|
||||
relabelBufPool.Put(bb)
|
||||
return setLabelValue(labels, labelsOffset, nameStr, valueStr)
|
||||
case "replace_all":
|
||||
|
@ -231,8 +238,8 @@ func (prc *parsedRelabelConfig) apply(labels []prompbmarshal.Label, labelsOffset
|
|||
bb.B = concatLabelValues(bb.B[:0], src, prc.SourceLabels, prc.Separator)
|
||||
sourceStr := string(bb.B)
|
||||
relabelBufPool.Put(bb)
|
||||
valueStr, ok := prc.replaceStringSubmatches(sourceStr, prc.Replacement, prc.hasCaptureGroupInReplacement)
|
||||
if ok {
|
||||
valueStr := prc.replaceStringSubmatchesFast(sourceStr)
|
||||
if valueStr != sourceStr {
|
||||
labels = setLabelValue(labels, labelsOffset, prc.TargetLabel, valueStr)
|
||||
}
|
||||
return labels
|
||||
|
@ -317,7 +324,7 @@ func (prc *parsedRelabelConfig) apply(labels []prompbmarshal.Label, labelsOffset
|
|||
// Replace all the occurrences of `regex` at label names with `replacement`
|
||||
for i := range src {
|
||||
label := &src[i]
|
||||
label.Name, _ = prc.replaceStringSubmatches(label.Name, prc.Replacement, prc.hasCaptureGroupInReplacement)
|
||||
label.Name = prc.replaceStringSubmatchesFast(label.Name)
|
||||
}
|
||||
return labels
|
||||
case "labeldrop":
|
||||
|
@ -421,19 +428,20 @@ func (prc *parsedRelabelConfig) replaceFullStringSlow(s string) string {
|
|||
return prc.expandCaptureGroups(prc.Replacement, s, match)
|
||||
}
|
||||
|
||||
func (prc *parsedRelabelConfig) replaceStringSubmatches(s, replacement string, hasCaptureGroupInReplacement bool) (string, bool) {
|
||||
re := prc.regexOriginal
|
||||
prefix, complete := re.LiteralPrefix()
|
||||
if complete && !hasCaptureGroupInReplacement {
|
||||
if !strings.Contains(s, prefix) {
|
||||
return s, false
|
||||
}
|
||||
return strings.ReplaceAll(s, prefix, replacement), true
|
||||
// replaceStringSubmatchesFast replaces all the regex matches with the replacement in s.
|
||||
func (prc *parsedRelabelConfig) replaceStringSubmatchesFast(s string) string {
|
||||
prefix, complete := prc.regexOriginal.LiteralPrefix()
|
||||
if complete && !prc.hasCaptureGroupInReplacement && !strings.Contains(s, prefix) {
|
||||
// Fast path - zero regex matches in s.
|
||||
return s
|
||||
}
|
||||
if !re.MatchString(s) {
|
||||
return s, false
|
||||
}
|
||||
return re.ReplaceAllString(s, replacement), true
|
||||
// Slow path - replace all the regex matches in s with the replacement.
|
||||
return prc.submatchReplacer.Transform(s)
|
||||
}
|
||||
|
||||
// replaceStringSubmatchesSlow replaces all the regex matches with the replacement in s.
|
||||
func (prc *parsedRelabelConfig) replaceStringSubmatchesSlow(s string) string {
|
||||
return prc.regexOriginal.ReplaceAllString(s, prc.Replacement)
|
||||
}
|
||||
|
||||
func (prc *parsedRelabelConfig) expandCaptureGroups(template, source string, match []int) string {
|
||||
|
|
Loading…
Reference in a new issue