From d136081040b4a0564dee952d2e568ccd39dc133b Mon Sep 17 00:00:00 2001 From: Aliaksandr Valialkin Date: Mon, 22 Feb 2021 16:33:55 +0200 Subject: [PATCH] lib/promrelabel: add more optimizations for relabeling for common cases --- app/vmagent/remotewrite/relabel.go | 12 +- app/vmagent/remotewrite/remotewrite.go | 12 +- app/vminsert/relabel/relabel.go | 26 +- lib/promrelabel/config.go | 100 ++-- lib/promrelabel/config_test.go | 43 +- lib/promrelabel/relabel.go | 185 +++---- lib/promrelabel/relabel_test.go | 646 ++++++++++++------------- lib/promrelabel/relabel_timing_test.go | 350 +++++++------- lib/promscrape/config.go | 12 +- lib/promscrape/config_test.go | 20 +- lib/promscrape/scrapework.go | 15 +- lib/promscrape/scrapework_test.go | 55 ++- 12 files changed, 751 insertions(+), 725 deletions(-) diff --git a/app/vmagent/remotewrite/relabel.go b/app/vmagent/remotewrite/relabel.go index 875694173..ad7c967f6 100644 --- a/app/vmagent/remotewrite/relabel.go +++ b/app/vmagent/remotewrite/relabel.go @@ -41,7 +41,7 @@ func loadRelabelConfigs() (*relabelConfigs, error) { return nil, fmt.Errorf("too many -remoteWrite.urlRelabelConfig args: %d; it mustn't exceed the number of -remoteWrite.url args: %d", len(*relabelConfigPaths), len(*remoteWriteURLs)) } - rcs.perURL = make([][]promrelabel.ParsedRelabelConfig, len(*remoteWriteURLs)) + rcs.perURL = make([]*promrelabel.ParsedConfigs, len(*remoteWriteURLs)) for i, path := range *relabelConfigPaths { if len(path) == 0 { // Skip empty relabel config. @@ -57,8 +57,8 @@ func loadRelabelConfigs() (*relabelConfigs, error) { } type relabelConfigs struct { - global []promrelabel.ParsedRelabelConfig - perURL [][]promrelabel.ParsedRelabelConfig + global *promrelabel.ParsedConfigs + perURL []*promrelabel.ParsedConfigs } // initLabelsGlobal must be called after parsing command-line flags. @@ -79,8 +79,8 @@ func initLabelsGlobal() { } } -func (rctx *relabelCtx) applyRelabeling(tss []prompbmarshal.TimeSeries, extraLabels []prompbmarshal.Label, prcs []promrelabel.ParsedRelabelConfig) []prompbmarshal.TimeSeries { - if len(extraLabels) == 0 && len(prcs) == 0 { +func (rctx *relabelCtx) applyRelabeling(tss []prompbmarshal.TimeSeries, extraLabels []prompbmarshal.Label, pcs *promrelabel.ParsedConfigs) []prompbmarshal.TimeSeries { + if len(extraLabels) == 0 && pcs.Len() == 0 { // Nothing to change. return tss } @@ -100,7 +100,7 @@ func (rctx *relabelCtx) applyRelabeling(tss []prompbmarshal.TimeSeries, extraLab labels = append(labels, *extraLabel) } } - labels = promrelabel.ApplyRelabelConfigs(labels, labelsLen, prcs, true) + labels = pcs.Apply(labels, labelsLen, true) if len(labels) == labelsLen { // Drop the current time series, since relabeling removed all the labels. continue diff --git a/app/vmagent/remotewrite/remotewrite.go b/app/vmagent/remotewrite/remotewrite.go index 824ea9ad7..4e5ef6751 100644 --- a/app/vmagent/remotewrite/remotewrite.go +++ b/app/vmagent/remotewrite/remotewrite.go @@ -142,8 +142,8 @@ func Stop() { func Push(wr *prompbmarshal.WriteRequest) { var rctx *relabelCtx rcs := allRelabelConfigs.Load().(*relabelConfigs) - prcsGlobal := rcs.global - if len(prcsGlobal) > 0 || len(labelsGlobal) > 0 { + pcsGlobal := rcs.global + if pcsGlobal.Len() > 0 || len(labelsGlobal) > 0 { rctx = getRelabelCtx() } tss := wr.Timeseries @@ -167,7 +167,7 @@ func Push(wr *prompbmarshal.WriteRequest) { } if rctx != nil { tssBlockLen := len(tssBlock) - tssBlock = rctx.applyRelabeling(tssBlock, labelsGlobal, prcsGlobal) + tssBlock = rctx.applyRelabeling(tssBlock, labelsGlobal, pcsGlobal) globalRelabelMetricsDropped.Add(tssBlockLen - len(tssBlock)) } for _, rwctx := range rwctxs { @@ -240,8 +240,8 @@ func (rwctx *remoteWriteCtx) Push(tss []prompbmarshal.TimeSeries) { var rctx *relabelCtx var v *[]prompbmarshal.TimeSeries rcs := allRelabelConfigs.Load().(*relabelConfigs) - prcs := rcs.perURL[rwctx.idx] - if len(prcs) > 0 { + pcs := rcs.perURL[rwctx.idx] + if pcs.Len() > 0 { rctx = getRelabelCtx() // Make a copy of tss before applying relabeling in order to prevent // from affecting time series for other remoteWrite.url configs. @@ -250,7 +250,7 @@ func (rwctx *remoteWriteCtx) Push(tss []prompbmarshal.TimeSeries) { v = tssRelabelPool.Get().(*[]prompbmarshal.TimeSeries) tss = append(*v, tss...) tssLen := len(tss) - tss = rctx.applyRelabeling(tss, nil, prcs) + tss = rctx.applyRelabeling(tss, nil, pcs) rwctx.relabelMetricsDropped.Add(tssLen - len(tss)) } pss := rwctx.pss diff --git a/app/vminsert/relabel/relabel.go b/app/vminsert/relabel/relabel.go index 11b9cc774..b9a2e62e4 100644 --- a/app/vminsert/relabel/relabel.go +++ b/app/vminsert/relabel/relabel.go @@ -19,11 +19,11 @@ var relabelConfig = flag.String("relabelConfig", "", "Optional path to a file wi // Init must be called after flag.Parse and before using the relabel package. func Init() { - prcs, err := loadRelabelConfig() + pcs, err := loadRelabelConfig() if err != nil { logger.Fatalf("cannot load relabelConfig: %s", err) } - prcsGlobal.Store(&prcs) + pcsGlobal.Store(pcs) if len(*relabelConfig) == 0 { return } @@ -31,34 +31,34 @@ func Init() { go func() { for range sighupCh { logger.Infof("received SIGHUP; reloading -relabelConfig=%q...", *relabelConfig) - prcs, err := loadRelabelConfig() + pcs, err := loadRelabelConfig() if err != nil { logger.Errorf("cannot load the updated relabelConfig: %s; preserving the previous config", err) continue } - prcsGlobal.Store(&prcs) + pcsGlobal.Store(pcs) logger.Infof("successfully reloaded -relabelConfig=%q", *relabelConfig) } }() } -var prcsGlobal atomic.Value +var pcsGlobal atomic.Value -func loadRelabelConfig() ([]promrelabel.ParsedRelabelConfig, error) { +func loadRelabelConfig() (*promrelabel.ParsedConfigs, error) { if len(*relabelConfig) == 0 { return nil, nil } - prcs, err := promrelabel.LoadRelabelConfigs(*relabelConfig) + pcs, err := promrelabel.LoadRelabelConfigs(*relabelConfig) if err != nil { return nil, fmt.Errorf("error when reading -relabelConfig=%q: %w", *relabelConfig, err) } - return prcs, nil + return pcs, nil } // HasRelabeling returns true if there is global relabeling. func HasRelabeling() bool { - prcs := prcsGlobal.Load().(*[]promrelabel.ParsedRelabelConfig) - return len(*prcs) > 0 + pcs := pcsGlobal.Load().(*promrelabel.ParsedConfigs) + return pcs.Len() > 0 } // Ctx holds relabeling context. @@ -77,8 +77,8 @@ func (ctx *Ctx) Reset() { // // The returned labels are valid until the next call to ApplyRelabeling. func (ctx *Ctx) ApplyRelabeling(labels []prompb.Label) []prompb.Label { - prcs := prcsGlobal.Load().(*[]promrelabel.ParsedRelabelConfig) - if len(*prcs) == 0 { + pcs := pcsGlobal.Load().(*promrelabel.ParsedConfigs) + if pcs.Len() == 0 { // There are no relabeling rules. return labels } @@ -97,7 +97,7 @@ func (ctx *Ctx) ApplyRelabeling(labels []prompb.Label) []prompb.Label { } // Apply relabeling - tmpLabels = promrelabel.ApplyRelabelConfigs(tmpLabels, 0, *prcs, true) + tmpLabels = pcs.Apply(tmpLabels, 0, true) ctx.tmpLabels = tmpLabels if len(tmpLabels) == 0 { metricsDropped.Inc() diff --git a/lib/promrelabel/config.go b/lib/promrelabel/config.go index abd9419a4..29d624174 100644 --- a/lib/promrelabel/config.go +++ b/lib/promrelabel/config.go @@ -23,38 +23,78 @@ type RelabelConfig struct { Action string `yaml:"action,omitempty"` } +// ParsedConfigs represents parsed relabel configs. +type ParsedConfigs struct { + prcs []*parsedRelabelConfig +} + +// Len returns the number of relabel configs in pcs. +func (pcs *ParsedConfigs) Len() int { + if pcs == nil { + return 0 + } + return len(pcs.prcs) +} + +// String returns human-readabale representation for pcs. +func (pcs *ParsedConfigs) String() string { + if pcs == nil { + return "" + } + var sb strings.Builder + for _, prc := range pcs.prcs { + fmt.Fprintf(&sb, "%s", prc.String()) + } + return sb.String() +} + // LoadRelabelConfigs loads relabel configs from the given path. -func LoadRelabelConfigs(path string) ([]ParsedRelabelConfig, error) { +func LoadRelabelConfigs(path string) (*ParsedConfigs, error) { data, err := ioutil.ReadFile(path) if err != nil { return nil, fmt.Errorf("cannot read `relabel_configs` from %q: %w", path, err) } data = envtemplate.Replace(data) - var rcs []RelabelConfig - if err := yaml.UnmarshalStrict(data, &rcs); err != nil { + pcs, err := ParseRelabelConfigsData(data) + if err != nil { return nil, fmt.Errorf("cannot unmarshal `relabel_configs` from %q: %w", path, err) } - return ParseRelabelConfigs(nil, rcs) + return pcs, nil +} + +// ParseRelabelConfigsData parses relabel configs from the given data. +func ParseRelabelConfigsData(data []byte) (*ParsedConfigs, error) { + var rcs []RelabelConfig + if err := yaml.UnmarshalStrict(data, &rcs); err != nil { + return nil, err + } + return ParseRelabelConfigs(rcs) } // ParseRelabelConfigs parses rcs to dst. -func ParseRelabelConfigs(dst []ParsedRelabelConfig, rcs []RelabelConfig) ([]ParsedRelabelConfig, error) { +func ParseRelabelConfigs(rcs []RelabelConfig) (*ParsedConfigs, error) { if len(rcs) == 0 { - return dst, nil + return nil, nil } + prcs := make([]*parsedRelabelConfig, len(rcs)) for i := range rcs { - var err error - dst, err = parseRelabelConfig(dst, &rcs[i]) + prc, err := parseRelabelConfig(&rcs[i]) if err != nil { - return dst, fmt.Errorf("error when parsing `relabel_config` #%d: %w", i+1, err) + return nil, fmt.Errorf("error when parsing `relabel_config` #%d: %w", i+1, err) } + prcs[i] = prc } - return dst, nil + return &ParsedConfigs{ + prcs: prcs, + }, nil } -var defaultRegexForRelabelConfig = regexp.MustCompile("^(.*)$") +var ( + defaultOriginalRegexForRelabelConfig = regexp.MustCompile(".*") + defaultRegexForRelabelConfig = regexp.MustCompile("^(.*)$") +) -func parseRelabelConfig(dst []ParsedRelabelConfig, rc *RelabelConfig) ([]ParsedRelabelConfig, error) { +func parseRelabelConfig(rc *RelabelConfig) (*parsedRelabelConfig, error) { sourceLabels := rc.SourceLabels separator := ";" if rc.Separator != nil { @@ -62,6 +102,7 @@ func parseRelabelConfig(dst []ParsedRelabelConfig, rc *RelabelConfig) ([]ParsedR } targetLabel := rc.TargetLabel regexCompiled := defaultRegexForRelabelConfig + regexOriginalCompiled := defaultOriginalRegexForRelabelConfig if rc.Regex != nil { regex := *rc.Regex if rc.Action != "replace_all" && rc.Action != "labelmap_all" { @@ -69,9 +110,14 @@ func parseRelabelConfig(dst []ParsedRelabelConfig, rc *RelabelConfig) ([]ParsedR } re, err := regexp.Compile(regex) if err != nil { - return dst, fmt.Errorf("cannot parse `regex` %q: %w", regex, err) + return nil, fmt.Errorf("cannot parse `regex` %q: %w", regex, err) } regexCompiled = re + reOriginal, err := regexp.Compile(*rc.Regex) + if err != nil { + return nil, fmt.Errorf("cannot parse `regex` %q: %w", *rc.Regex, err) + } + regexOriginalCompiled = reOriginal } modulus := rc.Modulus replacement := "$1" @@ -85,49 +131,49 @@ func parseRelabelConfig(dst []ParsedRelabelConfig, rc *RelabelConfig) ([]ParsedR switch action { case "replace": if targetLabel == "" { - return dst, fmt.Errorf("missing `target_label` for `action=replace`") + return nil, fmt.Errorf("missing `target_label` for `action=replace`") } case "replace_all": if len(sourceLabels) == 0 { - return dst, fmt.Errorf("missing `source_labels` for `action=replace_all`") + return nil, fmt.Errorf("missing `source_labels` for `action=replace_all`") } if targetLabel == "" { - return dst, fmt.Errorf("missing `target_label` for `action=replace_all`") + return nil, fmt.Errorf("missing `target_label` for `action=replace_all`") } case "keep_if_equal": if len(sourceLabels) < 2 { - return dst, fmt.Errorf("`source_labels` must contain at least two entries for `action=keep_if_equal`; got %q", sourceLabels) + return nil, fmt.Errorf("`source_labels` must contain at least two entries for `action=keep_if_equal`; got %q", sourceLabels) } case "drop_if_equal": if len(sourceLabels) < 2 { - return dst, fmt.Errorf("`source_labels` must contain at least two entries for `action=drop_if_equal`; got %q", sourceLabels) + return nil, fmt.Errorf("`source_labels` must contain at least two entries for `action=drop_if_equal`; got %q", sourceLabels) } case "keep": if len(sourceLabels) == 0 { - return dst, fmt.Errorf("missing `source_labels` for `action=keep`") + return nil, fmt.Errorf("missing `source_labels` for `action=keep`") } case "drop": if len(sourceLabels) == 0 { - return dst, fmt.Errorf("missing `source_labels` for `action=drop`") + return nil, fmt.Errorf("missing `source_labels` for `action=drop`") } case "hashmod": if len(sourceLabels) == 0 { - return dst, fmt.Errorf("missing `source_labels` for `action=hashmod`") + return nil, fmt.Errorf("missing `source_labels` for `action=hashmod`") } if targetLabel == "" { - return dst, fmt.Errorf("missing `target_label` for `action=hashmod`") + return nil, fmt.Errorf("missing `target_label` for `action=hashmod`") } if modulus < 1 { - return dst, fmt.Errorf("unexpected `modulus` for `action=hashmod`: %d; must be greater than 0", modulus) + return nil, fmt.Errorf("unexpected `modulus` for `action=hashmod`: %d; must be greater than 0", modulus) } case "labelmap": case "labelmap_all": case "labeldrop": case "labelkeep": default: - return dst, fmt.Errorf("unknown `action` %q", action) + return nil, fmt.Errorf("unknown `action` %q", action) } - dst = append(dst, ParsedRelabelConfig{ + return &parsedRelabelConfig{ SourceLabels: sourceLabels, Separator: separator, TargetLabel: targetLabel, @@ -136,8 +182,8 @@ func parseRelabelConfig(dst []ParsedRelabelConfig, rc *RelabelConfig) ([]ParsedR Replacement: replacement, Action: action, + regexOriginal: regexOriginalCompiled, hasCaptureGroupInTargetLabel: strings.Contains(targetLabel, "$"), hasCaptureGroupInReplacement: strings.Contains(replacement, "$"), - }) - return dst, nil + }, nil } diff --git a/lib/promrelabel/config_test.go b/lib/promrelabel/config_test.go index 3a1ae1477..f514d4d9a 100644 --- a/lib/promrelabel/config_test.go +++ b/lib/promrelabel/config_test.go @@ -7,12 +7,12 @@ import ( func TestLoadRelabelConfigsSuccess(t *testing.T) { path := "testdata/relabel_configs_valid.yml" - prcs, err := LoadRelabelConfigs(path) + pcs, err := LoadRelabelConfigs(path) if err != nil { t.Fatalf("cannot load relabel configs from %q: %s", path, err) } - if len(prcs) != 9 { - t.Fatalf("unexpected number of relabel configs loaded from %q; got %d; want %d", path, len(prcs), 9) + if n := pcs.Len(); n != 9 { + t.Fatalf("unexpected number of relabel configs loaded from %q; got %d; want %d", path, n, 9) } } @@ -23,7 +23,7 @@ func TestLoadRelabelConfigsFailure(t *testing.T) { if err == nil { t.Fatalf("expecting non-nil error") } - if len(rcs) != 0 { + if rcs.Len() != 0 { t.Fatalf("unexpected non-empty rcs: %#v", rcs) } } @@ -36,14 +36,14 @@ func TestLoadRelabelConfigsFailure(t *testing.T) { } func TestParseRelabelConfigsSuccess(t *testing.T) { - f := func(rcs []RelabelConfig, prcsExpected []ParsedRelabelConfig) { + f := func(rcs []RelabelConfig, pcsExpected *ParsedConfigs) { t.Helper() - prcs, err := ParseRelabelConfigs(nil, rcs) + pcs, err := ParseRelabelConfigs(rcs) if err != nil { t.Fatalf("unexected error: %s", err) } - if !reflect.DeepEqual(prcs, prcsExpected) { - t.Fatalf("unexpected prcs; got\n%#v\nwant\n%#v", prcs, prcsExpected) + if !reflect.DeepEqual(pcs, pcsExpected) { + t.Fatalf("unexpected pcs; got\n%#v\nwant\n%#v", pcs, pcsExpected) } } f(nil, nil) @@ -52,16 +52,19 @@ func TestParseRelabelConfigsSuccess(t *testing.T) { SourceLabels: []string{"foo", "bar"}, TargetLabel: "xxx", }, - }, []ParsedRelabelConfig{ - { - SourceLabels: []string{"foo", "bar"}, - Separator: ";", - TargetLabel: "xxx", - Regex: defaultRegexForRelabelConfig, - Replacement: "$1", - Action: "replace", + }, &ParsedConfigs{ + prcs: []*parsedRelabelConfig{ + { + SourceLabels: []string{"foo", "bar"}, + Separator: ";", + TargetLabel: "xxx", + Regex: defaultRegexForRelabelConfig, + Replacement: "$1", + Action: "replace", - hasCaptureGroupInReplacement: true, + regexOriginal: defaultOriginalRegexForRelabelConfig, + hasCaptureGroupInReplacement: true, + }, }, }) } @@ -69,12 +72,12 @@ func TestParseRelabelConfigsSuccess(t *testing.T) { func TestParseRelabelConfigsFailure(t *testing.T) { f := func(rcs []RelabelConfig) { t.Helper() - prcs, err := ParseRelabelConfigs(nil, rcs) + pcs, err := ParseRelabelConfigs(rcs) if err == nil { t.Fatalf("expecting non-nil error") } - if len(prcs) > 0 { - t.Fatalf("unexpected non-empty prcs: %#v", prcs) + if pcs.Len() > 0 { + t.Fatalf("unexpected non-empty pcs: %#v", pcs) } } t.Run("invalid-regex", func(t *testing.T) { diff --git a/lib/promrelabel/relabel.go b/lib/promrelabel/relabel.go index 95cd0d924..a161e55cc 100644 --- a/lib/promrelabel/relabel.go +++ b/lib/promrelabel/relabel.go @@ -12,10 +12,10 @@ import ( xxhash "github.com/cespare/xxhash/v2" ) -// ParsedRelabelConfig contains parsed `relabel_config`. +// parsedRelabelConfig contains parsed `relabel_config`. // // See https://prometheus.io/docs/prometheus/latest/configuration/configuration/#relabel_config -type ParsedRelabelConfig struct { +type parsedRelabelConfig struct { SourceLabels []string Separator string TargetLabel string @@ -24,29 +24,32 @@ type ParsedRelabelConfig struct { Replacement string Action string + regexOriginal *regexp.Regexp hasCaptureGroupInTargetLabel bool hasCaptureGroupInReplacement bool } // String returns human-readable representation for prc. -func (prc *ParsedRelabelConfig) String() string { +func (prc *parsedRelabelConfig) String() string { return fmt.Sprintf("SourceLabels=%s, Separator=%s, TargetLabel=%s, Regex=%s, Modulus=%d, Replacement=%s, Action=%s", prc.SourceLabels, prc.Separator, prc.TargetLabel, prc.Regex.String(), prc.Modulus, prc.Replacement, prc.Action) } -// ApplyRelabelConfigs applies prcs to labels starting from the labelsOffset. +// Apply applies pcs to labels starting from the labelsOffset. // // If isFinalize is set, then FinalizeLabels is called on the labels[labelsOffset:]. // // The returned labels at labels[labelsOffset:] are sorted. -func ApplyRelabelConfigs(labels []prompbmarshal.Label, labelsOffset int, prcs []ParsedRelabelConfig, isFinalize bool) []prompbmarshal.Label { - for i := range prcs { - tmp := applyRelabelConfig(labels, labelsOffset, &prcs[i]) - if len(tmp) == labelsOffset { - // All the labels have been removed. - return tmp +func (pcs *ParsedConfigs) Apply(labels []prompbmarshal.Label, labelsOffset int, isFinalize bool) []prompbmarshal.Label { + if pcs != nil { + for _, prc := range pcs.prcs { + tmp := prc.apply(labels, labelsOffset) + if len(tmp) == labelsOffset { + // All the labels have been removed. + return tmp + } + labels = tmp } - labels = tmp } labels = removeEmptyLabels(labels, labelsOffset) if isFinalize { @@ -106,10 +109,10 @@ func FinalizeLabels(dst, src []prompbmarshal.Label) []prompbmarshal.Label { return dst } -// applyRelabelConfig applies relabeling according to prc. +// apply applies relabeling according to prc. // // See https://prometheus.io/docs/prometheus/latest/configuration/configuration/#relabel_config -func applyRelabelConfig(labels []prompbmarshal.Label, labelsOffset int, prc *ParsedRelabelConfig) []prompbmarshal.Label { +func (prc *parsedRelabelConfig) apply(labels []prompbmarshal.Label, labelsOffset int) []prompbmarshal.Label { src := labels[labelsOffset:] switch prc.Action { case "replace": @@ -150,22 +153,13 @@ func applyRelabelConfig(labels []prompbmarshal.Label, labelsOffset int, prc *Par case "replace_all": bb := relabelBufPool.Get() bb.B = concatLabelValues(bb.B[:0], src, prc.SourceLabels, prc.Separator) - if prefix, complete := prc.Regex.LiteralPrefix(); complete && !prc.hasCaptureGroupInReplacement { - // Fast path - string replacement without regexp. - sourceStr := string(bb.B) - relabelBufPool.Put(bb) - valueStr := strings.ReplaceAll(sourceStr, prefix, prc.Replacement) - return setLabelValue(labels, labelsOffset, prc.TargetLabel, valueStr) - } - if !prc.Regex.Match(bb.B) { - // Fast path - nothing to replace. - relabelBufPool.Put(bb) - return labels - } - sourceStr := string(bb.B) // Make a copy of bb, since it can be returned from ReplaceAllString + sourceStr := string(bb.B) relabelBufPool.Put(bb) - valueStr := prc.Regex.ReplaceAllString(sourceStr, prc.Replacement) - return setLabelValue(labels, labelsOffset, prc.TargetLabel, valueStr) + valueStr, ok := prc.replaceStringSubmatches(sourceStr, prc.Replacement, prc.hasCaptureGroupInReplacement) + if ok { + labels = setLabelValue(labels, labelsOffset, prc.TargetLabel, valueStr) + } + return labels case "keep_if_equal": // Keep the entry if all the label values in source_labels are equal. // For example: @@ -193,14 +187,7 @@ func applyRelabelConfig(labels []prompbmarshal.Label, labelsOffset int, prc *Par case "keep": bb := relabelBufPool.Get() bb.B = concatLabelValues(bb.B[:0], src, prc.SourceLabels, prc.Separator) - keep := false - if prefix, complete := prc.Regex.LiteralPrefix(); complete { - // Fast path - simple string match - keep = prefix == string(bb.B) - } else { - // Slow path - match by regexp - keep = prc.Regex.Match(bb.B) - } + keep := prc.matchString(bytesutil.ToUnsafeString(bb.B)) relabelBufPool.Put(bb) if !keep { return labels[:labelsOffset] @@ -209,14 +196,7 @@ func applyRelabelConfig(labels []prompbmarshal.Label, labelsOffset int, prc *Par case "drop": bb := relabelBufPool.Get() bb.B = concatLabelValues(bb.B[:0], src, prc.SourceLabels, prc.Separator) - drop := false - if prefix, complete := prc.Regex.LiteralPrefix(); complete { - // Fast path - simple string match - drop = prefix == string(bb.B) - } else { - // Slow path - match by regexp - drop = prc.Regex.Match(bb.B) - } + drop := prc.matchString(bytesutil.ToUnsafeString(bb.B)) relabelBufPool.Put(bb) if drop { return labels[:labelsOffset] @@ -232,42 +212,23 @@ func applyRelabelConfig(labels []prompbmarshal.Label, labelsOffset int, prc *Par case "labelmap": for i := range src { label := &src[i] - match := prc.Regex.FindStringSubmatchIndex(label.Name) - if match == nil { - continue + labelName, ok := prc.replaceFullString(label.Name, prc.Replacement, prc.hasCaptureGroupInReplacement) + if ok { + labels = setLabelValue(labels, labelsOffset, labelName, label.Value) } - value := relabelBufPool.Get() - value.B = prc.Regex.ExpandString(value.B[:0], prc.Replacement, label.Name, match) - labelName := string(value.B) - relabelBufPool.Put(value) - labels = setLabelValue(labels, labelsOffset, labelName, label.Value) } return labels case "labelmap_all": for i := range src { label := &src[i] - if prefix, complete := prc.Regex.LiteralPrefix(); complete && !prc.hasCaptureGroupInReplacement { - // Fast path - replace without regexp - label.Name = strings.ReplaceAll(label.Name, prefix, prc.Replacement) - } else { - // Slow path - replace with regexp. - if !prc.Regex.MatchString(label.Name) { - continue - } - label.Name = prc.Regex.ReplaceAllString(label.Name, prc.Replacement) - } + label.Name, _ = prc.replaceStringSubmatches(label.Name, prc.Replacement, prc.hasCaptureGroupInReplacement) } return labels case "labeldrop": keepSrc := true for i := range src { label := &src[i] - if prefix, complete := prc.Regex.LiteralPrefix(); complete { - if prefix == label.Name { - keepSrc = false - break - } - } else if prc.Regex.MatchString(label.Name) { + if prc.matchString(label.Name) { keepSrc = false break } @@ -278,11 +239,7 @@ func applyRelabelConfig(labels []prompbmarshal.Label, labelsOffset int, prc *Par dst := labels[:labelsOffset] for i := range src { label := &src[i] - if prefix, complete := prc.Regex.LiteralPrefix(); complete { - if prefix != label.Name { - dst = append(dst, *label) - } - } else if !prc.Regex.MatchString(label.Name) { + if !prc.matchString(label.Name) { dst = append(dst, *label) } } @@ -291,12 +248,7 @@ func applyRelabelConfig(labels []prompbmarshal.Label, labelsOffset int, prc *Par keepSrc := true for i := range src { label := &src[i] - if prefix, complete := prc.Regex.LiteralPrefix(); complete { - if prefix != label.Name { - keepSrc = false - break - } - } else if !prc.Regex.MatchString(src[i].Name) { + if !prc.matchString(label.Name) { keepSrc = false break } @@ -307,11 +259,7 @@ func applyRelabelConfig(labels []prompbmarshal.Label, labelsOffset int, prc *Par dst := labels[:labelsOffset] for i := range src { label := &src[i] - if prefix, complete := prc.Regex.LiteralPrefix(); complete { - if prefix == label.Name { - dst = append(dst, *label) - } - } else if prc.Regex.MatchString(label.Name) { + if prc.matchString(label.Name) { dst = append(dst, *label) } } @@ -322,7 +270,74 @@ func applyRelabelConfig(labels []prompbmarshal.Label, labelsOffset int, prc *Par } } -func (prc *ParsedRelabelConfig) expandCaptureGroups(template, source string, match []int) string { +func (prc *parsedRelabelConfig) replaceFullString(s, replacement string, hasCaptureGroupInReplacement bool) (string, bool) { + prefix, complete := prc.regexOriginal.LiteralPrefix() + if complete && !hasCaptureGroupInReplacement { + if s == prefix { + return replacement, true + } + return s, false + } + if !strings.HasPrefix(s, prefix) { + return s, false + } + if replacement == "$1" { + // Fast path for commonly used rule for deleting label prefixes such as: + // + // - action: labelmap + // regex: __meta_kubernetes_node_label_(.+) + // + reStr := prc.regexOriginal.String() + if strings.HasPrefix(reStr, prefix) { + suffix := s[len(prefix):] + reSuffix := reStr[len(prefix):] + switch reSuffix { + case "(.*)": + return suffix, true + case "(.+)": + if len(suffix) > 0 { + return suffix, true + } + return s, false + } + } + } + // Slow path - regexp processing + match := prc.Regex.FindStringSubmatchIndex(s) + if match == nil { + return s, false + } + bb := relabelBufPool.Get() + bb.B = prc.Regex.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) { + 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 + } + if !re.MatchString(s) { + return s, false + } + return re.ReplaceAllString(s, replacement), true +} + +func (prc *parsedRelabelConfig) matchString(s string) bool { + prefix, complete := prc.regexOriginal.LiteralPrefix() + if complete { + return prefix == s + } + return strings.HasPrefix(s, prefix) && prc.Regex.MatchString(s) +} + +func (prc *parsedRelabelConfig) expandCaptureGroups(template, source string, match []int) string { bb := relabelBufPool.Get() bb.B = prc.Regex.ExpandString(bb.B[:0], template, source, match) s := string(bb.B) diff --git a/lib/promrelabel/relabel_test.go b/lib/promrelabel/relabel_test.go index 6185f029b..cc06887e7 100644 --- a/lib/promrelabel/relabel_test.go +++ b/lib/promrelabel/relabel_test.go @@ -2,24 +2,27 @@ package promrelabel import ( "reflect" - "regexp" "testing" "github.com/VictoriaMetrics/VictoriaMetrics/lib/prompbmarshal" ) func TestApplyRelabelConfigs(t *testing.T) { - f := func(prcs []ParsedRelabelConfig, labels []prompbmarshal.Label, isFinalize bool, resultExpected []prompbmarshal.Label) { + f := func(config string, labels []prompbmarshal.Label, isFinalize bool, resultExpected []prompbmarshal.Label) { t.Helper() - result := ApplyRelabelConfigs(labels, 0, prcs, isFinalize) + pcs, err := ParseRelabelConfigsData([]byte(config)) + if err != nil { + t.Fatalf("cannot parse %q: %s", config, err) + } + result := pcs.Apply(labels, 0, isFinalize) if !reflect.DeepEqual(result, resultExpected) { t.Fatalf("unexpected result; got\n%v\nwant\n%v", result, resultExpected) } } t.Run("empty_relabel_configs", func(t *testing.T) { - f(nil, nil, false, nil) - f(nil, nil, true, nil) - f(nil, []prompbmarshal.Label{ + f("", nil, false, nil) + f("", nil, true, nil) + f("", []prompbmarshal.Label{ { Name: "foo", Value: "bar", @@ -30,7 +33,7 @@ func TestApplyRelabelConfigs(t *testing.T) { Value: "bar", }, }) - f(nil, []prompbmarshal.Label{ + f("", []prompbmarshal.Label{ { Name: "foo", Value: "bar", @@ -55,35 +58,20 @@ func TestApplyRelabelConfigs(t *testing.T) { }) }) t.Run("replace-miss", func(t *testing.T) { - f([]ParsedRelabelConfig{ - { - Action: "replace", - TargetLabel: "bar", - Regex: defaultRegexForRelabelConfig, - Replacement: "$1", - hasCaptureGroupInReplacement: true, - }, - }, nil, false, []prompbmarshal.Label{}) - f([]ParsedRelabelConfig{ - { - Action: "replace", - SourceLabels: []string{"foo"}, - TargetLabel: "bar", - Regex: defaultRegexForRelabelConfig, - Replacement: "$1", - hasCaptureGroupInReplacement: true, - }, - }, nil, false, []prompbmarshal.Label{}) - f([]ParsedRelabelConfig{ - { - Action: "replace", - SourceLabels: []string{"foo"}, - TargetLabel: "bar", - Regex: defaultRegexForRelabelConfig, - Replacement: "$1", - hasCaptureGroupInReplacement: true, - }, - }, []prompbmarshal.Label{ + f(` +- action: replace + target_label: bar +`, nil, false, []prompbmarshal.Label{}) + f(` +- action: replace + source_labels: ["foo"] + target_label: bar +`, nil, false, []prompbmarshal.Label{}) + f(` +- action: replace + source_labels: ["foo"] + target_label: "bar" +`, []prompbmarshal.Label{ { Name: "xxx", Value: "yyy", @@ -94,16 +82,12 @@ func TestApplyRelabelConfigs(t *testing.T) { Value: "yyy", }, }) - f([]ParsedRelabelConfig{ - { - Action: "replace", - SourceLabels: []string{"foo"}, - TargetLabel: "bar", - Regex: regexp.MustCompile(".+"), - Replacement: "$1", - hasCaptureGroupInReplacement: true, - }, - }, []prompbmarshal.Label{ + f(` +- action: replace + source_labels: ["foo"] + target_label: "bar" + regex: ".+" +`, []prompbmarshal.Label{ { Name: "xxx", Value: "yyy", @@ -116,17 +100,12 @@ func TestApplyRelabelConfigs(t *testing.T) { }) }) t.Run("replace-hit", func(t *testing.T) { - f([]ParsedRelabelConfig{ - { - Action: "replace", - SourceLabels: []string{"xxx", "foo"}, - Separator: ";", - TargetLabel: "bar", - Regex: defaultRegexForRelabelConfig, - Replacement: "a-$1-b", - hasCaptureGroupInReplacement: true, - }, - }, []prompbmarshal.Label{ + f(` +- action: replace + source_labels: ["xxx", "foo"] + target_label: "bar" + replacement: "a-$1-b" +`, []prompbmarshal.Label{ { Name: "xxx", Value: "yyy", @@ -143,18 +122,12 @@ func TestApplyRelabelConfigs(t *testing.T) { }) }) t.Run("replace-hit-target-label-with-capture-group", func(t *testing.T) { - f([]ParsedRelabelConfig{ - { - Action: "replace", - SourceLabels: []string{"xxx", "foo"}, - Separator: ";", - TargetLabel: "bar-$1", - Regex: defaultRegexForRelabelConfig, - Replacement: "a-$1-b", - hasCaptureGroupInTargetLabel: true, - hasCaptureGroupInReplacement: true, - }, - }, []prompbmarshal.Label{ + f(` +- action: replace + source_labels: ["xxx", "foo"] + target_label: "bar-$1" + replacement: "a-$1-b" +`, []prompbmarshal.Label{ { Name: "xxx", Value: "yyy", @@ -171,35 +144,21 @@ func TestApplyRelabelConfigs(t *testing.T) { }) }) t.Run("replace_all-miss", func(t *testing.T) { - f([]ParsedRelabelConfig{ - { - Action: "replace_all", - TargetLabel: "bar", - Regex: defaultRegexForRelabelConfig, - Replacement: "$1", - hasCaptureGroupInReplacement: true, - }, - }, nil, false, []prompbmarshal.Label{}) - f([]ParsedRelabelConfig{ - { - Action: "replace_all", - SourceLabels: []string{"foo"}, - TargetLabel: "bar", - Regex: defaultRegexForRelabelConfig, - Replacement: "$1", - hasCaptureGroupInReplacement: true, - }, - }, nil, false, []prompbmarshal.Label{}) - f([]ParsedRelabelConfig{ - { - Action: "replace_all", - SourceLabels: []string{"foo"}, - TargetLabel: "bar", - Regex: defaultRegexForRelabelConfig, - Replacement: "$1", - hasCaptureGroupInReplacement: true, - }, - }, []prompbmarshal.Label{ + f(` +- action: replace_all + source_labels: [foo] + target_label: "bar" +`, nil, false, []prompbmarshal.Label{}) + f(` +- action: replace_all + source_labels: ["foo"] + target_label: "bar" +`, nil, false, []prompbmarshal.Label{}) + f(` +- action: replace_all + source_labels: ["foo"] + target_label: "bar" +`, []prompbmarshal.Label{ { Name: "xxx", Value: "yyy", @@ -210,16 +169,12 @@ func TestApplyRelabelConfigs(t *testing.T) { Value: "yyy", }, }) - f([]ParsedRelabelConfig{ - { - Action: "replace_all", - SourceLabels: []string{"foo"}, - TargetLabel: "bar", - Regex: regexp.MustCompile(".+"), - Replacement: "$1", - hasCaptureGroupInReplacement: true, - }, - }, []prompbmarshal.Label{ + f(` +- action: replace_all + source_labels: ["foo"] + target_label: "bar" + regex: ".+" +`, []prompbmarshal.Label{ { Name: "xxx", Value: "yyy", @@ -232,16 +187,13 @@ func TestApplyRelabelConfigs(t *testing.T) { }) }) t.Run("replace_all-hit", func(t *testing.T) { - f([]ParsedRelabelConfig{ - { - Action: "replace_all", - SourceLabels: []string{"xxx"}, - Separator: ";", - TargetLabel: "xxx", - Regex: regexp.MustCompile("-"), - Replacement: ".", - }, - }, []prompbmarshal.Label{ + f(` +- action: replace_all + source_labels: ["xxx"] + target_label: "xxx" + regex: "-" + replacement: "." +`, []prompbmarshal.Label{ { Name: "xxx", Value: "a-b-c", @@ -254,17 +206,13 @@ func TestApplyRelabelConfigs(t *testing.T) { }) }) t.Run("replace_all-regex-hit", func(t *testing.T) { - f([]ParsedRelabelConfig{ - { - Action: "replace_all", - SourceLabels: []string{"xxx", "foo"}, - Separator: ";", - TargetLabel: "xxx", - Regex: regexp.MustCompile("(;)"), - Replacement: "-$1-", - hasCaptureGroupInReplacement: true, - }, - }, []prompbmarshal.Label{ + f(` +- action: replace_all + source_labels: ["xxx", "foo"] + target_label: "xxx" + regex: "(;)" + replacement: "-$1-" +`, []prompbmarshal.Label{ { Name: "xxx", Value: "y;y", @@ -277,24 +225,16 @@ func TestApplyRelabelConfigs(t *testing.T) { }) }) t.Run("replace-add-multi-labels", func(t *testing.T) { - f([]ParsedRelabelConfig{ - { - Action: "replace", - SourceLabels: []string{"xxx"}, - TargetLabel: "bar", - Regex: defaultRegexForRelabelConfig, - Replacement: "a-$1", - hasCaptureGroupInReplacement: true, - }, - { - Action: "replace", - SourceLabels: []string{"bar"}, - TargetLabel: "zar", - Regex: defaultRegexForRelabelConfig, - Replacement: "b-$1", - hasCaptureGroupInReplacement: true, - }, - }, []prompbmarshal.Label{ + f(` +- action: replace + source_labels: ["xxx"] + target_label: "bar" + replacement: "a-$1" +- action: replace + source_labels: ["bar"] + target_label: "zar" + replacement: "b-$1" +`, []prompbmarshal.Label{ { Name: "xxx", Value: "yyy", @@ -323,16 +263,12 @@ func TestApplyRelabelConfigs(t *testing.T) { }) }) t.Run("replace-self", func(t *testing.T) { - f([]ParsedRelabelConfig{ - { - Action: "replace", - SourceLabels: []string{"foo"}, - TargetLabel: "foo", - Regex: defaultRegexForRelabelConfig, - Replacement: "a-$1", - hasCaptureGroupInReplacement: true, - }, - }, []prompbmarshal.Label{ + f(` +- action: replace + source_labels: ["foo"] + target_label: "foo" + replacement: "a-$1" +`, []prompbmarshal.Label{ { Name: "foo", Value: "aaxx", @@ -345,14 +281,11 @@ func TestApplyRelabelConfigs(t *testing.T) { }) }) t.Run("replace-missing-source", func(t *testing.T) { - f([]ParsedRelabelConfig{ - { - Action: "replace", - TargetLabel: "foo", - Regex: defaultRegexForRelabelConfig, - Replacement: "foobar", - }, - }, []prompbmarshal.Label{}, true, []prompbmarshal.Label{ + f(` +- action: replace + target_label: foo + replacement: "foobar" +`, []prompbmarshal.Label{}, true, []prompbmarshal.Label{ { Name: "foo", Value: "foobar", @@ -360,18 +293,14 @@ func TestApplyRelabelConfigs(t *testing.T) { }) }) t.Run("keep_if_equal-miss", func(t *testing.T) { - f([]ParsedRelabelConfig{ - { - Action: "keep_if_equal", - SourceLabels: []string{"foo", "bar"}, - }, - }, nil, true, nil) - f([]ParsedRelabelConfig{ - { - Action: "keep_if_equal", - SourceLabels: []string{"xxx", "bar"}, - }, - }, []prompbmarshal.Label{ + f(` +- action: keep_if_equal + source_labels: ["foo", "bar"] +`, nil, true, nil) + f(` +- action: keep_if_equal + source_labels: ["xxx", "bar"] +`, []prompbmarshal.Label{ { Name: "xxx", Value: "yyy", @@ -379,12 +308,10 @@ func TestApplyRelabelConfigs(t *testing.T) { }, true, []prompbmarshal.Label{}) }) t.Run("keep_if_equal-hit", func(t *testing.T) { - f([]ParsedRelabelConfig{ - { - Action: "keep_if_equal", - SourceLabels: []string{"xxx", "bar"}, - }, - }, []prompbmarshal.Label{ + f(` +- action: keep_if_equal + source_labels: ["xxx", "bar"] +`, []prompbmarshal.Label{ { Name: "xxx", Value: "yyy", @@ -405,18 +332,14 @@ func TestApplyRelabelConfigs(t *testing.T) { }) }) t.Run("drop_if_equal-miss", func(t *testing.T) { - f([]ParsedRelabelConfig{ - { - Action: "drop_if_equal", - SourceLabels: []string{"foo", "bar"}, - }, - }, nil, true, nil) - f([]ParsedRelabelConfig{ - { - Action: "drop_if_equal", - SourceLabels: []string{"xxx", "bar"}, - }, - }, []prompbmarshal.Label{ + f(` +- action: drop_if_equal + source_labels: ["foo", "bar"] +`, nil, true, nil) + f(` +- action: drop_if_equal + source_labels: ["xxx", "bar"] +`, []prompbmarshal.Label{ { Name: "xxx", Value: "yyy", @@ -429,12 +352,10 @@ func TestApplyRelabelConfigs(t *testing.T) { }) }) t.Run("drop_if_equal-hit", func(t *testing.T) { - f([]ParsedRelabelConfig{ - { - Action: "drop_if_equal", - SourceLabels: []string{"xxx", "bar"}, - }, - }, []prompbmarshal.Label{ + f(` +- action: drop_if_equal + source_labels: [xxx, bar] +`, []prompbmarshal.Label{ { Name: "xxx", Value: "yyy", @@ -446,20 +367,16 @@ func TestApplyRelabelConfigs(t *testing.T) { }, true, []prompbmarshal.Label{}) }) t.Run("keep-miss", func(t *testing.T) { - f([]ParsedRelabelConfig{ - { - Action: "keep", - SourceLabels: []string{"foo"}, - Regex: regexp.MustCompile(".+"), - }, - }, nil, true, nil) - f([]ParsedRelabelConfig{ - { - Action: "keep", - SourceLabels: []string{"foo"}, - Regex: regexp.MustCompile(".+"), - }, - }, []prompbmarshal.Label{ + f(` +- action: keep + source_labels: [foo] + regex: ".+" +`, nil, true, nil) + f(` +- action: keep + source_labels: [foo] + regex: ".+" +`, []prompbmarshal.Label{ { Name: "xxx", Value: "yyy", @@ -467,13 +384,11 @@ func TestApplyRelabelConfigs(t *testing.T) { }, true, []prompbmarshal.Label{}) }) t.Run("keep-hit", func(t *testing.T) { - f([]ParsedRelabelConfig{ - { - Action: "keep", - SourceLabels: []string{"foo"}, - Regex: regexp.MustCompile("yyy"), - }, - }, []prompbmarshal.Label{ + f(` +- action: keep + source_labels: [foo] + regex: "yyy" +`, []prompbmarshal.Label{ { Name: "foo", Value: "yyy", @@ -486,13 +401,11 @@ func TestApplyRelabelConfigs(t *testing.T) { }) }) t.Run("keep-hit-regexp", func(t *testing.T) { - f([]ParsedRelabelConfig{ - { - Action: "keep", - SourceLabels: []string{"foo"}, - Regex: regexp.MustCompile(".+"), - }, - }, []prompbmarshal.Label{ + f(` +- action: keep + source_labels: ["foo"] + regex: ".+" +`, []prompbmarshal.Label{ { Name: "foo", Value: "yyy", @@ -505,20 +418,16 @@ func TestApplyRelabelConfigs(t *testing.T) { }) }) t.Run("drop-miss", func(t *testing.T) { - f([]ParsedRelabelConfig{ - { - Action: "drop", - SourceLabels: []string{"foo"}, - Regex: regexp.MustCompile(".+"), - }, - }, nil, false, nil) - f([]ParsedRelabelConfig{ - { - Action: "drop", - SourceLabels: []string{"foo"}, - Regex: regexp.MustCompile(".+"), - }, - }, []prompbmarshal.Label{ + f(` +- action: drop + source_labels: [foo] + regex: ".+" +`, nil, false, nil) + f(` +- action: drop + source_labels: [foo] + regex: ".+" +`, []prompbmarshal.Label{ { Name: "xxx", Value: "yyy", @@ -531,13 +440,11 @@ func TestApplyRelabelConfigs(t *testing.T) { }) }) t.Run("drop-hit", func(t *testing.T) { - f([]ParsedRelabelConfig{ - { - Action: "drop", - SourceLabels: []string{"foo"}, - Regex: regexp.MustCompile("yyy"), - }, - }, []prompbmarshal.Label{ + f(` +- action: drop + source_labels: [foo] + regex: yyy +`, []prompbmarshal.Label{ { Name: "foo", Value: "yyy", @@ -545,13 +452,11 @@ func TestApplyRelabelConfigs(t *testing.T) { }, true, []prompbmarshal.Label{}) }) t.Run("drop-hit-regexp", func(t *testing.T) { - f([]ParsedRelabelConfig{ - { - Action: "drop", - SourceLabels: []string{"foo"}, - Regex: regexp.MustCompile(".+"), - }, - }, []prompbmarshal.Label{ + f(` +- action: drop + source_labels: [foo] + regex: ".+" +`, []prompbmarshal.Label{ { Name: "foo", Value: "yyy", @@ -559,14 +464,12 @@ func TestApplyRelabelConfigs(t *testing.T) { }, true, []prompbmarshal.Label{}) }) t.Run("hashmod-miss", func(t *testing.T) { - f([]ParsedRelabelConfig{ - { - Action: "hashmod", - SourceLabels: []string{"foo"}, - TargetLabel: "aaa", - Modulus: 123, - }, - }, []prompbmarshal.Label{ + f(` +- action: hashmod + source_labels: [foo] + target_label: aaa + modulus: 123 +`, []prompbmarshal.Label{ { Name: "xxx", Value: "yyy", @@ -583,14 +486,12 @@ func TestApplyRelabelConfigs(t *testing.T) { }) }) t.Run("hashmod-hit", func(t *testing.T) { - f([]ParsedRelabelConfig{ - { - Action: "hashmod", - SourceLabels: []string{"foo"}, - TargetLabel: "aaa", - Modulus: 123, - }, - }, []prompbmarshal.Label{ + f(` +- action: hashmod + source_labels: [foo] + target_label: aaa + modulus: 123 +`, []prompbmarshal.Label{ { Name: "foo", Value: "yyy", @@ -606,14 +507,97 @@ func TestApplyRelabelConfigs(t *testing.T) { }, }) }) - t.Run("labelmap", func(t *testing.T) { - f([]ParsedRelabelConfig{ + t.Run("labelmap-copy-label", func(t *testing.T) { + f(` +- action: labelmap + regex: "foo" + replacement: "bar" +`, []prompbmarshal.Label{ { - Action: "labelmap", - Regex: regexp.MustCompile("foo(.+)"), - Replacement: "$1-x", + Name: "foo", + Value: "yyy", }, - }, []prompbmarshal.Label{ + { + Name: "foobar", + Value: "aaa", + }, + }, true, []prompbmarshal.Label{ + { + Name: "bar", + Value: "yyy", + }, + { + Name: "foo", + Value: "yyy", + }, + { + Name: "foobar", + Value: "aaa", + }, + }) + }) + t.Run("labelmap-remove-prefix-dot-star", func(t *testing.T) { + f(` +- action: labelmap + regex: "foo(.*)" +`, []prompbmarshal.Label{ + { + Name: "xoo", + Value: "yyy", + }, + { + Name: "foobar", + Value: "aaa", + }, + }, true, []prompbmarshal.Label{ + { + Name: "bar", + Value: "aaa", + }, + { + Name: "foobar", + Value: "aaa", + }, + { + Name: "xoo", + Value: "yyy", + }, + }) + }) + t.Run("labelmap-remove-prefix-dot-plus", func(t *testing.T) { + f(` +- action: labelmap + regex: "foo(.+)" +`, []prompbmarshal.Label{ + { + Name: "foo", + Value: "yyy", + }, + { + Name: "foobar", + Value: "aaa", + }, + }, true, []prompbmarshal.Label{ + { + Name: "bar", + Value: "aaa", + }, + { + Name: "foo", + Value: "yyy", + }, + { + Name: "foobar", + Value: "aaa", + }, + }) + }) + t.Run("labelmap-regex", func(t *testing.T) { + f(` +- action: labelmap + regex: "foo(.+)" + replacement: "$1-x" +`, []prompbmarshal.Label{ { Name: "foo", Value: "yyy", @@ -638,13 +622,11 @@ func TestApplyRelabelConfigs(t *testing.T) { }) }) t.Run("labelmap_all", func(t *testing.T) { - f([]ParsedRelabelConfig{ - { - Action: "labelmap_all", - Regex: regexp.MustCompile(`\.`), - Replacement: "-", - }, - }, []prompbmarshal.Label{ + f(` +- action: labelmap_all + regex: "\\." + replacement: "-" +`, []prompbmarshal.Label{ { Name: "foo.bar.baz", Value: "yyy", @@ -665,13 +647,11 @@ func TestApplyRelabelConfigs(t *testing.T) { }) }) t.Run("labelmap_all-regexp", func(t *testing.T) { - f([]ParsedRelabelConfig{ - { - Action: "labelmap_all", - Regex: regexp.MustCompile(`ba(.)`), - Replacement: "${1}ss", - }, - }, []prompbmarshal.Label{ + f(` +- action: labelmap_all + regex: "ba(.)" + replacement: "${1}ss" +`, []prompbmarshal.Label{ { Name: "foo.bar.baz", Value: "yyy", @@ -692,12 +672,10 @@ func TestApplyRelabelConfigs(t *testing.T) { }) }) t.Run("labeldrop", func(t *testing.T) { - f([]ParsedRelabelConfig{ - { - Action: "labeldrop", - Regex: regexp.MustCompile("dropme"), - }, - }, []prompbmarshal.Label{ + f(` +- action: labeldrop + regex: dropme +`, []prompbmarshal.Label{ { Name: "aaa", Value: "bbb", @@ -708,12 +686,10 @@ func TestApplyRelabelConfigs(t *testing.T) { Value: "bbb", }, }) - f([]ParsedRelabelConfig{ - { - Action: "labeldrop", - Regex: regexp.MustCompile("dropme"), - }, - }, []prompbmarshal.Label{ + f(` +- action: labeldrop + regex: dropme +`, []prompbmarshal.Label{ { Name: "xxx", Value: "yyy", @@ -738,12 +714,10 @@ func TestApplyRelabelConfigs(t *testing.T) { }) }) t.Run("labeldrop-regexp", func(t *testing.T) { - f([]ParsedRelabelConfig{ - { - Action: "labeldrop", - Regex: regexp.MustCompile("dropme.*"), - }, - }, []prompbmarshal.Label{ + f(` +- action: labeldrop + regex: "dropme.*" +`, []prompbmarshal.Label{ { Name: "aaa", Value: "bbb", @@ -754,12 +728,10 @@ func TestApplyRelabelConfigs(t *testing.T) { Value: "bbb", }, }) - f([]ParsedRelabelConfig{ - { - Action: "labeldrop", - Regex: regexp.MustCompile("dropme.*"), - }, - }, []prompbmarshal.Label{ + f(` +- action: labeldrop + regex: "dropme.*" +`, []prompbmarshal.Label{ { Name: "xxx", Value: "yyy", @@ -784,12 +756,10 @@ func TestApplyRelabelConfigs(t *testing.T) { }) }) t.Run("labelkeep", func(t *testing.T) { - f([]ParsedRelabelConfig{ - { - Action: "labelkeep", - Regex: regexp.MustCompile("keepme"), - }, - }, []prompbmarshal.Label{ + f(` +- action: labelkeep + regex: "keepme" +`, []prompbmarshal.Label{ { Name: "keepme", Value: "aaa", @@ -800,12 +770,10 @@ func TestApplyRelabelConfigs(t *testing.T) { Value: "aaa", }, }) - f([]ParsedRelabelConfig{ - { - Action: "labelkeep", - Regex: regexp.MustCompile("keepme"), - }, - }, []prompbmarshal.Label{ + f(` +- action: labelkeep + regex: keepme +`, []prompbmarshal.Label{ { Name: "keepme", Value: "aaa", @@ -826,12 +794,10 @@ func TestApplyRelabelConfigs(t *testing.T) { }) }) t.Run("labelkeep-regexp", func(t *testing.T) { - f([]ParsedRelabelConfig{ - { - Action: "labelkeep", - Regex: regexp.MustCompile("keepme.*"), - }, - }, []prompbmarshal.Label{ + f(` +- action: labelkeep + regex: "keepme.*" +`, []prompbmarshal.Label{ { Name: "keepme", Value: "aaa", @@ -842,12 +808,10 @@ func TestApplyRelabelConfigs(t *testing.T) { Value: "aaa", }, }) - f([]ParsedRelabelConfig{ - { - Action: "labelkeep", - Regex: regexp.MustCompile("keepme.*"), - }, - }, []prompbmarshal.Label{ + f(` +- action: labelkeep + regex: "keepme.*" +`, []prompbmarshal.Label{ { Name: "keepme", Value: "aaa", diff --git a/lib/promrelabel/relabel_timing_test.go b/lib/promrelabel/relabel_timing_test.go index fb0a0207c..4904bb0ef 100644 --- a/lib/promrelabel/relabel_timing_test.go +++ b/lib/promrelabel/relabel_timing_test.go @@ -2,7 +2,6 @@ package promrelabel import ( "fmt" - "regexp" "testing" "github.com/VictoriaMetrics/VictoriaMetrics/lib/prompbmarshal" @@ -10,15 +9,11 @@ import ( func BenchmarkApplyRelabelConfigs(b *testing.B) { b.Run("replace-label-copy", func(b *testing.B) { - prcs := []ParsedRelabelConfig{ - { - Action: "replace", - SourceLabels: []string{"id"}, - TargetLabel: "__name__", - Regex: defaultRegexForRelabelConfig, - Replacement: "$1", - }, - } + pcs := mustParseRelabelConfigs(` +- action: replace + source_labels: [id] + target_label: __name__ +`) labelsOrig := []prompbmarshal.Label{ { Name: "__name__", @@ -35,7 +30,7 @@ func BenchmarkApplyRelabelConfigs(b *testing.B) { var labels []prompbmarshal.Label for pb.Next() { labels = append(labels[:0], labelsOrig...) - labels = ApplyRelabelConfigs(labels, 0, prcs, true) + labels = pcs.Apply(labels, 0, true) if len(labels) != len(labelsOrig) { panic(fmt.Errorf("unexpected number of labels; got %d; want %d; labels:\n%#v", len(labels), len(labelsOrig), labels)) } @@ -55,14 +50,11 @@ func BenchmarkApplyRelabelConfigs(b *testing.B) { }) }) b.Run("replace-set-label", func(b *testing.B) { - prcs := []ParsedRelabelConfig{ - { - Action: "replace", - TargetLabel: "__name__", - Regex: defaultRegexForRelabelConfig, - Replacement: "foobar", - }, - } + pcs := mustParseRelabelConfigs(` +- action: replace + target_label: __name__ + replacement: foobar +`) labelsOrig := []prompbmarshal.Label{ { Name: "__name__", @@ -79,7 +71,7 @@ func BenchmarkApplyRelabelConfigs(b *testing.B) { var labels []prompbmarshal.Label for pb.Next() { labels = append(labels[:0], labelsOrig...) - labels = ApplyRelabelConfigs(labels, 0, prcs, true) + labels = pcs.Apply(labels, 0, true) if len(labels) != len(labelsOrig) { panic(fmt.Errorf("unexpected number of labels; got %d; want %d; labels:\n%#v", len(labels), len(labelsOrig), labels)) } @@ -99,14 +91,11 @@ func BenchmarkApplyRelabelConfigs(b *testing.B) { }) }) b.Run("replace-add-label", func(b *testing.B) { - prcs := []ParsedRelabelConfig{ - { - Action: "replace", - TargetLabel: "aaa", - Regex: defaultRegexForRelabelConfig, - Replacement: "foobar", - }, - } + pcs := mustParseRelabelConfigs(` +- action: replace + target_label: aaa + replacement: foobar +`) labelsOrig := []prompbmarshal.Label{ { Name: "__name__", @@ -119,7 +108,7 @@ func BenchmarkApplyRelabelConfigs(b *testing.B) { var labels []prompbmarshal.Label for pb.Next() { labels = append(labels[:0], labelsOrig...) - labels = ApplyRelabelConfigs(labels, 0, prcs, true) + labels = pcs.Apply(labels, 0, true) if len(labels) != 2 { panic(fmt.Errorf("unexpected number of labels; got %d; want %d; labels:\n%#v", len(labels), 2, labels)) } @@ -139,15 +128,12 @@ func BenchmarkApplyRelabelConfigs(b *testing.B) { }) }) b.Run("replace-mismatch", func(b *testing.B) { - prcs := []ParsedRelabelConfig{ - { - Action: "replace", - SourceLabels: []string{"non-existing-label"}, - TargetLabel: "id", - Regex: regexp.MustCompile("(foobar)-.*"), - Replacement: "$1", - }, - } + pcs := mustParseRelabelConfigs(` +- action: replace + source_labels: ["non-existing-label"] + target_label: id + regex: "(foobar)-.*" +`) labelsOrig := []prompbmarshal.Label{ { Name: "__name__", @@ -164,7 +150,7 @@ func BenchmarkApplyRelabelConfigs(b *testing.B) { var labels []prompbmarshal.Label for pb.Next() { labels = append(labels[:0], labelsOrig...) - labels = ApplyRelabelConfigs(labels, 0, prcs, true) + labels = pcs.Apply(labels, 0, true) if len(labels) != len(labelsOrig) { panic(fmt.Errorf("unexpected number of labels; got %d; want %d; labels:\n%#v", len(labels), len(labelsOrig), labels)) } @@ -184,15 +170,12 @@ func BenchmarkApplyRelabelConfigs(b *testing.B) { }) }) b.Run("replace-match-regex", func(b *testing.B) { - prcs := []ParsedRelabelConfig{ - { - Action: "replace", - SourceLabels: []string{"id"}, - TargetLabel: "id", - Regex: regexp.MustCompile("(foobar)-.*"), - Replacement: "$1", - }, - } + pcs := mustParseRelabelConfigs(` +- action: replace + source_labels: [id] + target_label: id + regex: "(foobar)-.*" +`) labelsOrig := []prompbmarshal.Label{ { Name: "__name__", @@ -209,7 +192,7 @@ func BenchmarkApplyRelabelConfigs(b *testing.B) { var labels []prompbmarshal.Label for pb.Next() { labels = append(labels[:0], labelsOrig...) - labels = ApplyRelabelConfigs(labels, 0, prcs, true) + labels = pcs.Apply(labels, 0, true) if len(labels) != len(labelsOrig) { panic(fmt.Errorf("unexpected number of labels; got %d; want %d; labels:\n%#v", len(labels), len(labelsOrig), labels)) } @@ -229,13 +212,11 @@ func BenchmarkApplyRelabelConfigs(b *testing.B) { }) }) b.Run("drop-mismatch", func(b *testing.B) { - prcs := []ParsedRelabelConfig{ - { - Action: "drop", - SourceLabels: []string{"non-existing-label"}, - Regex: regexp.MustCompile("(foobar)-.*"), - }, - } + pcs := mustParseRelabelConfigs(` +- action: drop + source_labels: ["non-existing-label"] + regex: "(foobar)-.*" +`) labelsOrig := []prompbmarshal.Label{ { Name: "__name__", @@ -252,7 +233,7 @@ func BenchmarkApplyRelabelConfigs(b *testing.B) { var labels []prompbmarshal.Label for pb.Next() { labels = append(labels[:0], labelsOrig...) - labels = ApplyRelabelConfigs(labels, 0, prcs, true) + labels = pcs.Apply(labels, 0, true) if len(labels) != len(labelsOrig) { panic(fmt.Errorf("unexpected number of labels; got %d; want %d; labels:\n%#v", len(labels), len(labelsOrig), labels)) } @@ -272,13 +253,11 @@ func BenchmarkApplyRelabelConfigs(b *testing.B) { }) }) b.Run("drop-match", func(b *testing.B) { - prcs := []ParsedRelabelConfig{ - { - Action: "drop", - SourceLabels: []string{"id"}, - Regex: regexp.MustCompile("yes"), - }, - } + pcs := mustParseRelabelConfigs(` +- action: drop + source_labels: [id] + regex: yes +`) labelsOrig := []prompbmarshal.Label{ { Name: "__name__", @@ -295,7 +274,7 @@ func BenchmarkApplyRelabelConfigs(b *testing.B) { var labels []prompbmarshal.Label for pb.Next() { labels = append(labels[:0], labelsOrig...) - labels = ApplyRelabelConfigs(labels, 0, prcs, true) + labels = pcs.Apply(labels, 0, true) if len(labels) != 0 { panic(fmt.Errorf("unexpected number of labels; got %d; want %d; labels:\n%#v", len(labels), 0, labels)) } @@ -303,13 +282,11 @@ func BenchmarkApplyRelabelConfigs(b *testing.B) { }) }) b.Run("drop-match-regexp", func(b *testing.B) { - prcs := []ParsedRelabelConfig{ - { - Action: "drop", - SourceLabels: []string{"id"}, - Regex: regexp.MustCompile("(foobar)-.*"), - }, - } + pcs := mustParseRelabelConfigs(` +- action: drop + source_labels: [id] + regex: "(foobar)-.*" +`) labelsOrig := []prompbmarshal.Label{ { Name: "__name__", @@ -326,7 +303,7 @@ func BenchmarkApplyRelabelConfigs(b *testing.B) { var labels []prompbmarshal.Label for pb.Next() { labels = append(labels[:0], labelsOrig...) - labels = ApplyRelabelConfigs(labels, 0, prcs, true) + labels = pcs.Apply(labels, 0, true) if len(labels) != 0 { panic(fmt.Errorf("unexpected number of labels; got %d; want %d; labels:\n%#v", len(labels), 0, labels)) } @@ -334,13 +311,11 @@ func BenchmarkApplyRelabelConfigs(b *testing.B) { }) }) b.Run("keep-mismatch", func(b *testing.B) { - prcs := []ParsedRelabelConfig{ - { - Action: "keep", - SourceLabels: []string{"non-existing-label"}, - Regex: regexp.MustCompile("(foobar)-.*"), - }, - } + pcs := mustParseRelabelConfigs(` +- action: keep + source_labels: ["non-existing-label"] + regex: "(foobar)-.*" +`) labelsOrig := []prompbmarshal.Label{ { Name: "__name__", @@ -357,7 +332,7 @@ func BenchmarkApplyRelabelConfigs(b *testing.B) { var labels []prompbmarshal.Label for pb.Next() { labels = append(labels[:0], labelsOrig...) - labels = ApplyRelabelConfigs(labels, 0, prcs, true) + labels = pcs.Apply(labels, 0, true) if len(labels) != 0 { panic(fmt.Errorf("unexpected number of labels; got %d; want %d; labels:\n%#v", len(labels), 0, labels)) } @@ -365,13 +340,11 @@ func BenchmarkApplyRelabelConfigs(b *testing.B) { }) }) b.Run("keep-match", func(b *testing.B) { - prcs := []ParsedRelabelConfig{ - { - Action: "keep", - SourceLabels: []string{"id"}, - Regex: regexp.MustCompile("yes"), - }, - } + pcs := mustParseRelabelConfigs(` +- action: keep + source_labels: [id] + regex: yes +`) labelsOrig := []prompbmarshal.Label{ { Name: "__name__", @@ -388,7 +361,7 @@ func BenchmarkApplyRelabelConfigs(b *testing.B) { var labels []prompbmarshal.Label for pb.Next() { labels = append(labels[:0], labelsOrig...) - labels = ApplyRelabelConfigs(labels, 0, prcs, true) + labels = pcs.Apply(labels, 0, true) if len(labels) != len(labelsOrig) { panic(fmt.Errorf("unexpected number of labels; got %d; want %d; labels:\n%#v", len(labels), len(labelsOrig), labels)) } @@ -408,13 +381,11 @@ func BenchmarkApplyRelabelConfigs(b *testing.B) { }) }) b.Run("keep-match-regexp", func(b *testing.B) { - prcs := []ParsedRelabelConfig{ - { - Action: "keep", - SourceLabels: []string{"id"}, - Regex: regexp.MustCompile("(foobar)-.*"), - }, - } + pcs := mustParseRelabelConfigs(` +- action: keep + source_labels: [id] + regex: "(foobar)-.*" +`) labelsOrig := []prompbmarshal.Label{ { Name: "__name__", @@ -431,7 +402,7 @@ func BenchmarkApplyRelabelConfigs(b *testing.B) { var labels []prompbmarshal.Label for pb.Next() { labels = append(labels[:0], labelsOrig...) - labels = ApplyRelabelConfigs(labels, 0, prcs, true) + labels = pcs.Apply(labels, 0, true) if len(labels) != len(labelsOrig) { panic(fmt.Errorf("unexpected number of labels; got %d; want %d; labels:\n%#v", len(labels), len(labelsOrig), labels)) } @@ -451,12 +422,10 @@ func BenchmarkApplyRelabelConfigs(b *testing.B) { }) }) b.Run("labeldrop-mismatch", func(b *testing.B) { - prcs := []ParsedRelabelConfig{ - { - Action: "labeldrop", - Regex: regexp.MustCompile("non-existing-label"), - }, - } + pcs := mustParseRelabelConfigs(` +- action: labeldrop + regex: "non-existing-label" +`) labelsOrig := []prompbmarshal.Label{ { Name: "__name__", @@ -473,7 +442,7 @@ func BenchmarkApplyRelabelConfigs(b *testing.B) { var labels []prompbmarshal.Label for pb.Next() { labels = append(labels[:0], labelsOrig...) - labels = ApplyRelabelConfigs(labels, 0, prcs, true) + labels = pcs.Apply(labels, 0, true) if len(labels) != len(labelsOrig) { panic(fmt.Errorf("unexpected number of labels; got %d; want %d; labels:\n%#v", len(labels), len(labelsOrig), labels)) } @@ -493,12 +462,10 @@ func BenchmarkApplyRelabelConfigs(b *testing.B) { }) }) b.Run("labeldrop-match", func(b *testing.B) { - prcs := []ParsedRelabelConfig{ - { - Action: "labeldrop", - Regex: regexp.MustCompile("id"), - }, - } + pcs := mustParseRelabelConfigs(` +- action: labeldrop + regex: id +`) labelsOrig := []prompbmarshal.Label{ { Name: "__name__", @@ -515,7 +482,7 @@ func BenchmarkApplyRelabelConfigs(b *testing.B) { var labels []prompbmarshal.Label for pb.Next() { labels = append(labels[:0], labelsOrig...) - labels = ApplyRelabelConfigs(labels, 0, prcs, true) + labels = pcs.Apply(labels, 0, true) if len(labels) != 1 { panic(fmt.Errorf("unexpected number of labels; got %d; want %d; labels:\n%#v", len(labels), 1, labels)) } @@ -529,12 +496,10 @@ func BenchmarkApplyRelabelConfigs(b *testing.B) { }) }) b.Run("labeldrop-match-regexp", func(b *testing.B) { - prcs := []ParsedRelabelConfig{ - { - Action: "labeldrop", - Regex: regexp.MustCompile("id.*"), - }, - } + pcs := mustParseRelabelConfigs(` +- action: labeldrop + regex: "id.*" +`) labelsOrig := []prompbmarshal.Label{ { Name: "__name__", @@ -551,7 +516,7 @@ func BenchmarkApplyRelabelConfigs(b *testing.B) { var labels []prompbmarshal.Label for pb.Next() { labels = append(labels[:0], labelsOrig...) - labels = ApplyRelabelConfigs(labels, 0, prcs, true) + labels = pcs.Apply(labels, 0, true) if len(labels) != 1 { panic(fmt.Errorf("unexpected number of labels; got %d; want %d; labels:\n%#v", len(labels), 1, labels)) } @@ -565,12 +530,10 @@ func BenchmarkApplyRelabelConfigs(b *testing.B) { }) }) b.Run("labelkeep-mismatch", func(b *testing.B) { - prcs := []ParsedRelabelConfig{ - { - Action: "labelkeep", - Regex: regexp.MustCompile("non-existing-label"), - }, - } + pcs := mustParseRelabelConfigs(` +- action: labelkeep + regex: "non-existing-label" +`) labelsOrig := []prompbmarshal.Label{ { Name: "__name__", @@ -587,7 +550,7 @@ func BenchmarkApplyRelabelConfigs(b *testing.B) { var labels []prompbmarshal.Label for pb.Next() { labels = append(labels[:0], labelsOrig...) - labels = ApplyRelabelConfigs(labels, 0, prcs, true) + labels = pcs.Apply(labels, 0, true) if len(labels) != 0 { panic(fmt.Errorf("unexpected number of labels; got %d; want %d; labels:\n%#v", len(labels), 0, labels)) } @@ -595,12 +558,10 @@ func BenchmarkApplyRelabelConfigs(b *testing.B) { }) }) b.Run("labelkeep-match", func(b *testing.B) { - prcs := []ParsedRelabelConfig{ - { - Action: "labelkeep", - Regex: regexp.MustCompile("id"), - }, - } + pcs := mustParseRelabelConfigs(` +- action: labelkeep + regex: id +`) labelsOrig := []prompbmarshal.Label{ { Name: "__name__", @@ -617,7 +578,7 @@ func BenchmarkApplyRelabelConfigs(b *testing.B) { var labels []prompbmarshal.Label for pb.Next() { labels = append(labels[:0], labelsOrig...) - labels = ApplyRelabelConfigs(labels, 0, prcs, true) + labels = pcs.Apply(labels, 0, true) if len(labels) != 1 { panic(fmt.Errorf("unexpected number of labels; got %d; want %d; labels:\n%#v", len(labels), 1, labels)) } @@ -631,12 +592,10 @@ func BenchmarkApplyRelabelConfigs(b *testing.B) { }) }) b.Run("labelkeep-match-regexp", func(b *testing.B) { - prcs := []ParsedRelabelConfig{ - { - Action: "labelkeep", - Regex: regexp.MustCompile("id.*"), - }, - } + pcs := mustParseRelabelConfigs(` +- action: labelkeep + regex: "id.*" +`) labelsOrig := []prompbmarshal.Label{ { Name: "__name__", @@ -653,7 +612,7 @@ func BenchmarkApplyRelabelConfigs(b *testing.B) { var labels []prompbmarshal.Label for pb.Next() { labels = append(labels[:0], labelsOrig...) - labels = ApplyRelabelConfigs(labels, 0, prcs, true) + labels = pcs.Apply(labels, 0, true) if len(labels) != 1 { panic(fmt.Errorf("unexpected number of labels; got %d; want %d; labels:\n%#v", len(labels), 1, labels)) } @@ -666,19 +625,12 @@ func BenchmarkApplyRelabelConfigs(b *testing.B) { } }) }) - b.Run("labelmap", func(b *testing.B) { - prcs := []ParsedRelabelConfig{ - { - Action: "labelmap", - Regex: regexp.MustCompile("a(.*)"), - Replacement: "$1", - }, - } + b.Run("labelmap-mismatch", func(b *testing.B) { + pcs := mustParseRelabelConfigs(` +- action: labelmap + regex: "a(.*)" +`) labelsOrig := []prompbmarshal.Label{ - { - Name: "aabc", - Value: "foobar-random-string-here", - }, { Name: "foo", Value: "bar", @@ -690,8 +642,38 @@ func BenchmarkApplyRelabelConfigs(b *testing.B) { var labels []prompbmarshal.Label for pb.Next() { labels = append(labels[:0], labelsOrig...) - labels = ApplyRelabelConfigs(labels, 0, prcs, true) - if len(labels) != 3 { + labels = pcs.Apply(labels, 0, true) + if len(labels) != 1 { + panic(fmt.Errorf("unexpected number of labels; got %d; want %d; labels:\n%#v", len(labels), 3, labels)) + } + if labels[0].Name != "foo" { + panic(fmt.Errorf("unexpected label name; got %q; want %q", labels[0].Name, "foo")) + } + if labels[0].Value != "bar" { + panic(fmt.Errorf("unexpected label value; got %q; want %q", labels[0].Value, "bar")) + } + } + }) + }) + b.Run("labelmap-match-remove-prefix", func(b *testing.B) { + pcs := mustParseRelabelConfigs(` +- action: labelmap + regex: "a(.*)" +`) + labelsOrig := []prompbmarshal.Label{ + { + Name: "aabc", + Value: "foobar-random-string-here", + }, + } + b.ReportAllocs() + b.SetBytes(1) + b.RunParallel(func(pb *testing.PB) { + var labels []prompbmarshal.Label + for pb.Next() { + labels = append(labels[:0], labelsOrig...) + labels = pcs.Apply(labels, 0, true) + if len(labels) != 2 { panic(fmt.Errorf("unexpected number of labels; got %d; want %d; labels:\n%#v", len(labels), 3, labels)) } if labels[0].Name != "aabc" { @@ -706,24 +688,52 @@ func BenchmarkApplyRelabelConfigs(b *testing.B) { if labels[1].Value != "foobar-random-string-here" { panic(fmt.Errorf("unexpected label value; got %q; want %q", labels[1].Value, "foobar-random-string-here")) } - if labels[2].Name != "foo" { - panic(fmt.Errorf("unexpected label name; got %q; want %q", labels[2].Name, "foo")) + } + }) + }) + b.Run("labelmap-match-regexp", func(b *testing.B) { + pcs := mustParseRelabelConfigs(` +- action: labelmap + regex: "(.*)bc" +`) + labelsOrig := []prompbmarshal.Label{ + { + Name: "aabc", + Value: "foobar-random-string-here", + }, + } + b.ReportAllocs() + b.SetBytes(1) + b.RunParallel(func(pb *testing.PB) { + var labels []prompbmarshal.Label + for pb.Next() { + labels = append(labels[:0], labelsOrig...) + labels = pcs.Apply(labels, 0, true) + if len(labels) != 2 { + panic(fmt.Errorf("unexpected number of labels; got %d; want %d; labels:\n%#v", len(labels), 3, labels)) } - if labels[2].Value != "bar" { - panic(fmt.Errorf("unexpected label value; got %q; want %q", labels[2].Value, "bar")) + if labels[0].Name != "aa" { + panic(fmt.Errorf("unexpected label name; got %q; want %q", labels[0].Name, "aa")) + } + if labels[0].Value != "foobar-random-string-here" { + panic(fmt.Errorf("unexpected label value; got %q; want %q", labels[0].Value, "foobar-random-string-here")) + } + if labels[1].Name != "aabc" { + panic(fmt.Errorf("unexpected label name; got %q; want %q", labels[1].Name, "aabc")) + } + if labels[1].Value != "foobar-random-string-here" { + panic(fmt.Errorf("unexpected label value; got %q; want %q", labels[1].Value, "foobar-random-string-here")) } } }) }) b.Run("hashmod", func(b *testing.B) { - prcs := []ParsedRelabelConfig{ - { - Action: "hashmod", - SourceLabels: []string{"id"}, - TargetLabel: "id", - Modulus: 23, - }, - } + pcs := mustParseRelabelConfigs(` +- action: hashmod + source_labels: [id] + target_label: id + modulus: 23 +`) labelsOrig := []prompbmarshal.Label{ { Name: "__name__", @@ -740,7 +750,7 @@ func BenchmarkApplyRelabelConfigs(b *testing.B) { var labels []prompbmarshal.Label for pb.Next() { labels = append(labels[:0], labelsOrig...) - labels = ApplyRelabelConfigs(labels, 0, prcs, true) + labels = pcs.Apply(labels, 0, true) if len(labels) != len(labelsOrig) { panic(fmt.Errorf("unexpected number of labels; got %d; want %d; labels:\n%#v", len(labels), len(labelsOrig), labels)) } @@ -760,3 +770,11 @@ func BenchmarkApplyRelabelConfigs(b *testing.B) { }) }) } + +func mustParseRelabelConfigs(config string) *ParsedConfigs { + pcs, err := ParseRelabelConfigsData([]byte(config)) + if err != nil { + panic(fmt.Errorf("unexpected error: %w", err)) + } + return pcs +} diff --git a/lib/promscrape/config.go b/lib/promscrape/config.go index 6c1e2f502..4f97506b6 100644 --- a/lib/promscrape/config.go +++ b/lib/promscrape/config.go @@ -482,13 +482,11 @@ func getScrapeWorkConfig(sc *ScrapeConfig, baseDir string, globalCfg *GlobalConf if err != nil { return nil, fmt.Errorf("cannot parse auth config for `job_name` %q: %w", jobName, err) } - var relabelConfigs []promrelabel.ParsedRelabelConfig - relabelConfigs, err = promrelabel.ParseRelabelConfigs(relabelConfigs[:0], sc.RelabelConfigs) + relabelConfigs, err := promrelabel.ParseRelabelConfigs(sc.RelabelConfigs) if err != nil { return nil, fmt.Errorf("cannot parse `relabel_configs` for `job_name` %q: %w", jobName, err) } - var metricRelabelConfigs []promrelabel.ParsedRelabelConfig - metricRelabelConfigs, err = promrelabel.ParseRelabelConfigs(metricRelabelConfigs[:0], sc.MetricRelabelConfigs) + metricRelabelConfigs, err := promrelabel.ParseRelabelConfigs(sc.MetricRelabelConfigs) if err != nil { return nil, fmt.Errorf("cannot parse `metric_relabel_configs` for `job_name` %q: %w", jobName, err) } @@ -527,8 +525,8 @@ type scrapeWorkConfig struct { honorLabels bool honorTimestamps bool externalLabels map[string]string - relabelConfigs []promrelabel.ParsedRelabelConfig - metricRelabelConfigs []promrelabel.ParsedRelabelConfig + relabelConfigs *promrelabel.ParsedConfigs + metricRelabelConfigs *promrelabel.ParsedConfigs sampleLimit int disableCompression bool disableKeepAlive bool @@ -695,7 +693,7 @@ func appendScrapeWork(dst []*ScrapeWork, swc *scrapeWorkConfig, target string, e // Reduce memory usage by interning all the strings in originalLabels. internLabelStrings(originalLabels) } - labels = promrelabel.ApplyRelabelConfigs(labels, 0, swc.relabelConfigs, false) + labels = swc.relabelConfigs.Apply(labels, 0, false) labels = promrelabel.RemoveMetaLabels(labels[:0], labels) // Remove references to already deleted labels, so GC could clean strings for label name and label value past len(labels). // This should reduce memory usage when relabeling creates big number of temporary labels with long names and/or values. diff --git a/lib/promscrape/config_test.go b/lib/promscrape/config_test.go index a1dbc5f57..f2273ccc4 100644 --- a/lib/promscrape/config_test.go +++ b/lib/promscrape/config_test.go @@ -4,13 +4,11 @@ import ( "crypto/tls" "fmt" "reflect" - "regexp" "testing" "time" "github.com/VictoriaMetrics/VictoriaMetrics/lib/promauth" "github.com/VictoriaMetrics/VictoriaMetrics/lib/prompbmarshal" - "github.com/VictoriaMetrics/VictoriaMetrics/lib/promrelabel" ) func TestLoadStaticConfigs(t *testing.T) { @@ -1034,13 +1032,6 @@ scrape_configs: }, }) - prcs, err := promrelabel.ParseRelabelConfigs(nil, []promrelabel.RelabelConfig{{ - SourceLabels: []string{"foo"}, - TargetLabel: "abc", - }}) - if err != nil { - t.Fatalf("unexpected error when parsing relabel configs: %s", err) - } f(` scrape_configs: - job_name: foo @@ -1076,9 +1067,12 @@ scrape_configs: Value: "foo", }, }, - AuthConfig: &promauth.Config{}, - MetricRelabelConfigs: prcs, - jobNameOriginal: "foo", + AuthConfig: &promauth.Config{}, + MetricRelabelConfigs: mustParseRelabelConfigs(` +- source_labels: [foo] + target_label: abc +`), + jobNameOriginal: "foo", }, }) f(` @@ -1374,8 +1368,6 @@ scrape_configs: }) } -var defaultRegexForRelabelConfig = regexp.MustCompile("^(.*)$") - func equalStaticConfigForScrapeWorks(a, b []*ScrapeWork) bool { if len(a) != len(b) { return false diff --git a/lib/promscrape/scrapework.go b/lib/promscrape/scrapework.go index adff00c2c..313734d6b 100644 --- a/lib/promscrape/scrapework.go +++ b/lib/promscrape/scrapework.go @@ -6,7 +6,6 @@ import ( "math" "math/bits" "strconv" - "strings" "sync" "time" @@ -76,7 +75,7 @@ type ScrapeWork struct { ProxyURL proxy.URL // Optional `metric_relabel_configs`. - MetricRelabelConfigs []promrelabel.ParsedRelabelConfig + MetricRelabelConfigs *promrelabel.ParsedConfigs // The maximum number of metrics to scrape after relabeling. SampleLimit int @@ -105,18 +104,10 @@ func (sw *ScrapeWork) key() string { key := fmt.Sprintf("ScrapeURL=%s, ScrapeInterval=%s, ScrapeTimeout=%s, HonorLabels=%v, HonorTimestamps=%v, Labels=%s, "+ "AuthConfig=%s, MetricRelabelConfigs=%s, SampleLimit=%d, DisableCompression=%v, DisableKeepAlive=%v, StreamParse=%v, ScrapeAlignInterval=%s", sw.ScrapeURL, sw.ScrapeInterval, sw.ScrapeTimeout, sw.HonorLabels, sw.HonorTimestamps, sw.LabelsString(), - sw.AuthConfig.String(), sw.metricRelabelConfigsString(), sw.SampleLimit, sw.DisableCompression, sw.DisableKeepAlive, sw.StreamParse, sw.ScrapeAlignInterval) + sw.AuthConfig.String(), sw.MetricRelabelConfigs.String(), sw.SampleLimit, sw.DisableCompression, sw.DisableKeepAlive, sw.StreamParse, sw.ScrapeAlignInterval) return key } -func (sw *ScrapeWork) metricRelabelConfigsString() string { - var sb strings.Builder - for _, prc := range sw.MetricRelabelConfigs { - fmt.Fprintf(&sb, "%s", prc.String()) - } - return sb.String() -} - // Job returns job for the ScrapeWork func (sw *ScrapeWork) Job() string { return promrelabel.GetLabelValueByName(sw.Labels, "job") @@ -503,7 +494,7 @@ func (sw *scrapeWork) addRowToTimeseries(wc *writeRequestCtx, r *parser.Row, tim labelsLen := len(wc.labels) wc.labels = appendLabels(wc.labels, r.Metric, r.Tags, sw.Config.Labels, sw.Config.HonorLabels) if needRelabel { - wc.labels = promrelabel.ApplyRelabelConfigs(wc.labels, labelsLen, sw.Config.MetricRelabelConfigs, true) + wc.labels = sw.Config.MetricRelabelConfigs.Apply(wc.labels, labelsLen, true) } else { wc.labels = promrelabel.FinalizeLabels(wc.labels[:labelsLen], wc.labels[labelsLen:]) promrelabel.SortLabels(wc.labels[labelsLen:]) diff --git a/lib/promscrape/scrapework_test.go b/lib/promscrape/scrapework_test.go index 3e9fb9577..74b6f67e8 100644 --- a/lib/promscrape/scrapework_test.go +++ b/lib/promscrape/scrapework_test.go @@ -2,7 +2,6 @@ package promscrape import ( "fmt" - "regexp" "strings" "testing" @@ -102,7 +101,8 @@ func TestScrapeWorkScrapeInternalSuccess(t *testing.T) { sw.PushData = func(wr *prompbmarshal.WriteRequest) { pushDataCalls++ if len(wr.Timeseries) > len(timeseriesExpected) { - pushDataErr = fmt.Errorf("too many time series obtained; got %d; want %d", len(wr.Timeseries), len(timeseriesExpected)) + pushDataErr = fmt.Errorf("too many time series obtained; got %d; want %d\ngot\n%+v\nwant\n%+v", + len(wr.Timeseries), len(timeseriesExpected), wr.Timeseries, timeseriesExpected) return } tsExpected := timeseriesExpected[:len(wr.Timeseries)] @@ -271,20 +271,14 @@ func TestScrapeWorkScrapeInternalSuccess(t *testing.T) { Value: "foo.com", }, }, - MetricRelabelConfigs: []promrelabel.ParsedRelabelConfig{ - { - SourceLabels: []string{"__address__", "job"}, - Separator: "/", - TargetLabel: "instance", - Regex: defaultRegexForRelabelConfig, - Replacement: "$1", - Action: "replace", - }, - { - Action: "labeldrop", - Regex: regexp.MustCompile("^c$"), - }, - }, + MetricRelabelConfigs: mustParseRelabelConfigs(` +- action: replace + source_labels: ["__address__", "job"] + separator: "/" + target_label: "instance" +- action: labeldrop + regex: c +`), }, ` foo{bar="baz",job="xx",instance="foo.com/xx"} 34.44 123 bar{a="b",job="xx",instance="foo.com/xx"} -3e4 123 @@ -311,18 +305,15 @@ func TestScrapeWorkScrapeInternalSuccess(t *testing.T) { Value: "foo.com", }, }, - MetricRelabelConfigs: []promrelabel.ParsedRelabelConfig{ - { - Action: "drop", - SourceLabels: []string{"a", "c"}, - Regex: regexp.MustCompile("^bd$"), - }, - { - Action: "drop", - SourceLabels: []string{"__name__"}, - Regex: regexp.MustCompile("^(dropme|up)$"), - }, - }, + MetricRelabelConfigs: mustParseRelabelConfigs(` +- action: drop + separator: "" + source_labels: [a, c] + regex: "^bd$" +- action: drop + source_labels: [__name__] + regex: "dropme|up" +`), }, ` foo{bar="baz",job="xx",instance="foo.com"} 34.44 123 up{job="xx",instance="foo.com"} 1 123 @@ -440,3 +431,11 @@ func timeseriesToString(ts *prompbmarshal.TimeSeries) string { fmt.Fprintf(&sb, "%g %d", s.Value, s.Timestamp) return sb.String() } + +func mustParseRelabelConfigs(config string) *promrelabel.ParsedConfigs { + pcs, err := promrelabel.ParseRelabelConfigsData([]byte(config)) + if err != nil { + panic(fmt.Errorf("cannot parse %q: %w", config, err)) + } + return pcs +}