lib/promrelabel: optimize action: labelmap for non-trivial regexs

This commit is contained in:
Aliaksandr Valialkin 2022-09-30 10:43:31 +03:00
parent 17289ff481
commit 899d2c40fb
No known key found for this signature in database
GPG key ID: A72BEC6CD3D0DED1
3 changed files with 40 additions and 19 deletions

View file

@ -6,6 +6,7 @@ import (
"strconv" "strconv"
"strings" "strings"
"github.com/VictoriaMetrics/VictoriaMetrics/lib/bytesutil"
"github.com/VictoriaMetrics/VictoriaMetrics/lib/envtemplate" "github.com/VictoriaMetrics/VictoriaMetrics/lib/envtemplate"
"github.com/VictoriaMetrics/VictoriaMetrics/lib/fs" "github.com/VictoriaMetrics/VictoriaMetrics/lib/fs"
"github.com/VictoriaMetrics/VictoriaMetrics/lib/logger" "github.com/VictoriaMetrics/VictoriaMetrics/lib/logger"
@ -346,7 +347,7 @@ func parseRelabelConfig(rc *RelabelConfig) (*parsedRelabelConfig, error) {
return nil, fmt.Errorf("`labels` config cannot be applied to `action=%s`; it is applied only to `action=graphite`", action) return nil, fmt.Errorf("`labels` config cannot be applied to `action=%s`; it is applied only to `action=graphite`", action)
} }
} }
return &parsedRelabelConfig{ prc := &parsedRelabelConfig{
SourceLabels: sourceLabels, SourceLabels: sourceLabels,
Separator: separator, Separator: separator,
TargetLabel: targetLabel, TargetLabel: targetLabel,
@ -365,7 +366,9 @@ func parseRelabelConfig(rc *RelabelConfig) (*parsedRelabelConfig, error) {
hasCaptureGroupInTargetLabel: strings.Contains(targetLabel, "$"), hasCaptureGroupInTargetLabel: strings.Contains(targetLabel, "$"),
hasCaptureGroupInReplacement: strings.Contains(replacement, "$"), hasCaptureGroupInReplacement: strings.Contains(replacement, "$"),
hasLabelReferenceInReplacement: strings.Contains(replacement, "{{"), hasLabelReferenceInReplacement: strings.Contains(replacement, "{{"),
}, nil }
prc.stringReplacer = bytesutil.NewFastStringTransformer(prc.replaceFullStringSlow)
return prc, nil
} }
func isDefaultRegex(expr string) bool { func isDefaultRegex(expr string) bool {

View file

@ -161,6 +161,11 @@ func TestParseRelabelConfigsSuccess(t *testing.T) {
if err != nil { if err != nil {
t.Fatalf("unexpected error: %s", err) t.Fatalf("unexpected error: %s", err)
} }
if pcs != nil {
for _, prc := range pcs.prcs {
prc.stringReplacer = nil
}
}
if !reflect.DeepEqual(pcs, pcsExpected) { if !reflect.DeepEqual(pcs, pcsExpected) {
t.Fatalf("unexpected pcs; got\n%#v\nwant\n%#v", pcs, pcsExpected) t.Fatalf("unexpected pcs; got\n%#v\nwant\n%#v", pcs, pcsExpected)
} }

View file

@ -35,6 +35,8 @@ type parsedRelabelConfig struct {
hasCaptureGroupInTargetLabel bool hasCaptureGroupInTargetLabel bool
hasCaptureGroupInReplacement bool hasCaptureGroupInReplacement bool
hasLabelReferenceInReplacement bool hasLabelReferenceInReplacement bool
stringReplacer *bytesutil.FastStringTransformer
} }
// String returns human-readable representation for prc. // String returns human-readable representation for prc.
@ -305,8 +307,8 @@ func (prc *parsedRelabelConfig) apply(labels []prompbmarshal.Label, labelsOffset
case "labelmap": case "labelmap":
// Replace label names with the `replacement` if they match `regex` // Replace label names with the `replacement` if they match `regex`
for _, label := range src { for _, label := range src {
labelName, ok := prc.replaceFullString(label.Name, prc.Replacement, prc.hasCaptureGroupInReplacement) labelName := prc.replaceFullStringFast(label.Name)
if ok { if labelName != label.Name {
labels = setLabelValue(labels, labelsOffset, labelName, label.Value) labels = setLabelValue(labels, labelsOffset, labelName, label.Value)
} }
} }
@ -360,16 +362,23 @@ func (prc *parsedRelabelConfig) apply(labels []prompbmarshal.Label, labelsOffset
} }
} }
func (prc *parsedRelabelConfig) replaceFullString(s, replacement string, hasCaptureGroupInReplacement bool) (string, bool) { // replaceFullStringFast replaces s with the replacement if s matches '^regex$'.
//
// s is returned as is if it doesn't match '^regex$'.
func (prc *parsedRelabelConfig) replaceFullStringFast(s string) string {
prefix, complete := prc.regexOriginal.LiteralPrefix() prefix, complete := prc.regexOriginal.LiteralPrefix()
if complete && !hasCaptureGroupInReplacement { replacement := prc.Replacement
if complete && !prc.hasCaptureGroupInReplacement {
if s == prefix { if s == prefix {
return replacement, true // Fast path - s matches literal regex
return replacement
} }
return s, false // Fast path - s doesn't match literal regex
return s
} }
if !strings.HasPrefix(s, prefix) { if !strings.HasPrefix(s, prefix) {
return s, false // Fast path - s doesn't match literl prefix from regex
return s
} }
if replacement == "$1" { if replacement == "$1" {
// Fast path for commonly used rule for deleting label prefixes such as: // Fast path for commonly used rule for deleting label prefixes such as:
@ -383,29 +392,33 @@ func (prc *parsedRelabelConfig) replaceFullString(s, replacement string, hasCapt
reSuffix := reStr[len(prefix):] reSuffix := reStr[len(prefix):]
switch reSuffix { switch reSuffix {
case "(.*)": case "(.*)":
return suffix, true return suffix
case "(.+)": case "(.+)":
if len(suffix) > 0 { if len(suffix) > 0 {
return suffix, true return suffix
} }
return s, false return s
} }
} }
} }
// Slow path - handle the rest of cases.
return prc.stringReplacer.Transform(s)
}
// replaceFullStringSlow replaces s with the replacement if s matches '^regex$'.
//
// s is returned as is if it doesn't match '^regex$'.
func (prc *parsedRelabelConfig) replaceFullStringSlow(s string) string {
if re := prc.regex; re.HasPrefix() && !re.MatchString(s) { if re := prc.regex; re.HasPrefix() && !re.MatchString(s) {
// Fast path - regex mismatch // Fast path - regex mismatch
return s, false return s
} }
// Slow path - regexp processing // Slow path - regexp processing
match := prc.RegexAnchored.FindStringSubmatchIndex(s) match := prc.RegexAnchored.FindStringSubmatchIndex(s)
if match == nil { if match == nil {
return s, false return s
} }
bb := relabelBufPool.Get() return prc.expandCaptureGroups(prc.Replacement, s, match)
bb.B = prc.RegexAnchored.ExpandString(bb.B[:0], replacement, s, match)
result := string(bb.B)
relabelBufPool.Put(bb)
return result, true
} }
func (prc *parsedRelabelConfig) replaceStringSubmatches(s, replacement string, hasCaptureGroupInReplacement bool) (string, bool) { func (prc *parsedRelabelConfig) replaceStringSubmatches(s, replacement string, hasCaptureGroupInReplacement bool) (string, bool) {