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"
"strings"
"github.com/VictoriaMetrics/VictoriaMetrics/lib/bytesutil"
"github.com/VictoriaMetrics/VictoriaMetrics/lib/envtemplate"
"github.com/VictoriaMetrics/VictoriaMetrics/lib/fs"
"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 &parsedRelabelConfig{
prc := &parsedRelabelConfig{
SourceLabels: sourceLabels,
Separator: separator,
TargetLabel: targetLabel,
@ -365,7 +366,9 @@ func parseRelabelConfig(rc *RelabelConfig) (*parsedRelabelConfig, error) {
hasCaptureGroupInTargetLabel: strings.Contains(targetLabel, "$"),
hasCaptureGroupInReplacement: strings.Contains(replacement, "$"),
hasLabelReferenceInReplacement: strings.Contains(replacement, "{{"),
}, nil
}
prc.stringReplacer = bytesutil.NewFastStringTransformer(prc.replaceFullStringSlow)
return prc, nil
}
func isDefaultRegex(expr string) bool {

View file

@ -161,6 +161,11 @@ func TestParseRelabelConfigsSuccess(t *testing.T) {
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
if pcs != nil {
for _, prc := range pcs.prcs {
prc.stringReplacer = nil
}
}
if !reflect.DeepEqual(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
hasCaptureGroupInReplacement bool
hasLabelReferenceInReplacement bool
stringReplacer *bytesutil.FastStringTransformer
}
// String returns human-readable representation for prc.
@ -305,8 +307,8 @@ func (prc *parsedRelabelConfig) apply(labels []prompbmarshal.Label, labelsOffset
case "labelmap":
// Replace label names with the `replacement` if they match `regex`
for _, label := range src {
labelName, ok := prc.replaceFullString(label.Name, prc.Replacement, prc.hasCaptureGroupInReplacement)
if ok {
labelName := prc.replaceFullStringFast(label.Name)
if labelName != label.Name {
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()
if complete && !hasCaptureGroupInReplacement {
replacement := prc.Replacement
if complete && !prc.hasCaptureGroupInReplacement {
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) {
return s, false
// Fast path - s doesn't match literl prefix from regex
return s
}
if replacement == "$1" {
// 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):]
switch reSuffix {
case "(.*)":
return suffix, true
return suffix
case "(.+)":
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) {
// Fast path - regex mismatch
return s, false
return s
}
// Slow path - regexp processing
match := prc.RegexAnchored.FindStringSubmatchIndex(s)
if match == nil {
return s, false
return s
}
bb := relabelBufPool.Get()
bb.B = prc.RegexAnchored.ExpandString(bb.B[:0], replacement, s, match)
result := string(bb.B)
relabelBufPool.Put(bb)
return result, true
return prc.expandCaptureGroups(prc.Replacement, s, match)
}
func (prc *parsedRelabelConfig) replaceStringSubmatches(s, replacement string, hasCaptureGroupInReplacement bool) (string, bool) {