lib/promrelabel: use regexutil.PromRegex for regex matching in actions labeldrop,labelkeep,drop and keep

This makes possible optimizing additional cases inside regexutil.PromRegex
This commit is contained in:
Aliaksandr Valialkin 2022-08-26 15:23:41 +03:00
parent 7afe8450fc
commit 4c6916f32a
No known key found for this signature in database
GPG key ID: A72BEC6CD3D0DED1
7 changed files with 85 additions and 93 deletions

View file

@ -8,6 +8,7 @@ import (
"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/regexutil" "github.com/VictoriaMetrics/VictoriaMetrics/lib/regexutil"
"gopkg.in/yaml.v2" "gopkg.in/yaml.v2"
) )
@ -189,6 +190,13 @@ func ParseRelabelConfigs(rcs []RelabelConfig, relabelDebug bool) (*ParsedConfigs
var ( var (
defaultOriginalRegexForRelabelConfig = regexp.MustCompile(".*") defaultOriginalRegexForRelabelConfig = regexp.MustCompile(".*")
defaultRegexForRelabelConfig = regexp.MustCompile("^(.*)$") defaultRegexForRelabelConfig = regexp.MustCompile("^(.*)$")
defaultPromRegex = func() *regexutil.PromRegex {
pr, err := regexutil.NewPromRegex(".*")
if err != nil {
panic(fmt.Errorf("BUG: unexpected error: %s", err))
}
return pr
}()
) )
func parseRelabelConfig(rc *RelabelConfig) (*parsedRelabelConfig, error) { func parseRelabelConfig(rc *RelabelConfig) (*parsedRelabelConfig, error) {
@ -202,9 +210,9 @@ func parseRelabelConfig(rc *RelabelConfig) (*parsedRelabelConfig, error) {
action = "replace" action = "replace"
} }
targetLabel := rc.TargetLabel targetLabel := rc.TargetLabel
regexCompiled := defaultRegexForRelabelConfig regexAnchored := defaultRegexForRelabelConfig
regexOriginalCompiled := defaultOriginalRegexForRelabelConfig regexOriginalCompiled := defaultOriginalRegexForRelabelConfig
var regexOrValues []string promRegex := defaultPromRegex
if rc.Regex != nil && !isDefaultRegex(rc.Regex.S) { if rc.Regex != nil && !isDefaultRegex(rc.Regex.S) {
regex := rc.Regex.S regex := rc.Regex.S
regexOrig := regex regexOrig := regex
@ -216,13 +224,16 @@ func parseRelabelConfig(rc *RelabelConfig) (*parsedRelabelConfig, error) {
if err != nil { if err != nil {
return nil, fmt.Errorf("cannot parse `regex` %q: %w", regex, err) return nil, fmt.Errorf("cannot parse `regex` %q: %w", regex, err)
} }
regexCompiled = re regexAnchored = re
reOriginal, err := regexp.Compile(regexOrig) reOriginal, err := regexp.Compile(regexOrig)
if err != nil { if err != nil {
return nil, fmt.Errorf("cannot parse `regex` %q: %w", regexOrig, err) return nil, fmt.Errorf("cannot parse `regex` %q: %w", regexOrig, err)
} }
regexOriginalCompiled = reOriginal regexOriginalCompiled = reOriginal
regexOrValues = regexutil.GetOrValues(regexOrig) promRegex, err = regexutil.NewPromRegex(regexOrig)
if err != nil {
logger.Panicf("BUG: cannot parse already parsed regex %q: %s", regexOrig, err)
}
} }
modulus := rc.Modulus modulus := rc.Modulus
replacement := "$1" replacement := "$1"
@ -335,20 +346,20 @@ func parseRelabelConfig(rc *RelabelConfig) (*parsedRelabelConfig, error) {
} }
} }
return &parsedRelabelConfig{ return &parsedRelabelConfig{
SourceLabels: sourceLabels, SourceLabels: sourceLabels,
Separator: separator, Separator: separator,
TargetLabel: targetLabel, TargetLabel: targetLabel,
Regex: regexCompiled, RegexAnchored: regexAnchored,
Modulus: modulus, Modulus: modulus,
Replacement: replacement, Replacement: replacement,
Action: action, Action: action,
If: rc.If, If: rc.If,
graphiteMatchTemplate: graphiteMatchTemplate, graphiteMatchTemplate: graphiteMatchTemplate,
graphiteLabelRules: graphiteLabelRules, graphiteLabelRules: graphiteLabelRules,
regex: promRegex,
regexOriginal: regexOriginalCompiled, regexOriginal: regexOriginalCompiled,
regexOrValues: regexOrValues,
hasCaptureGroupInTargetLabel: strings.Contains(targetLabel, "$"), hasCaptureGroupInTargetLabel: strings.Contains(targetLabel, "$"),
hasCaptureGroupInReplacement: strings.Contains(replacement, "$"), hasCaptureGroupInReplacement: strings.Contains(replacement, "$"),

View file

@ -126,7 +126,7 @@ func TestParsedConfigsString(t *testing.T) {
TargetLabel: "foo", TargetLabel: "foo",
SourceLabels: []string{"aaa"}, SourceLabels: []string{"aaa"},
}, },
}, "[SourceLabels=[aaa], Separator=;, TargetLabel=foo, Regex=^(.*)$, Modulus=0, Replacement=$1, Action=replace, If=, "+ }, "[SourceLabels=[aaa], Separator=;, TargetLabel=foo, Regex=.*, Modulus=0, Replacement=$1, Action=replace, If=, "+
"graphiteMatchTemplate=<nil>, graphiteLabelRules=[]], relabelDebug=false") "graphiteMatchTemplate=<nil>, graphiteLabelRules=[]], relabelDebug=false")
var ie IfExpression var ie IfExpression
if err := ie.Parse("{foo=~'bar'}"); err != nil { if err := ie.Parse("{foo=~'bar'}"); err != nil {
@ -141,7 +141,7 @@ func TestParsedConfigsString(t *testing.T) {
}, },
If: &ie, If: &ie,
}, },
}, "[SourceLabels=[], Separator=;, TargetLabel=, Regex=^(.*)$, Modulus=0, Replacement=$1, Action=graphite, If={foo=~'bar'}, "+ }, "[SourceLabels=[], Separator=;, TargetLabel=, Regex=.*, Modulus=0, Replacement=$1, Action=graphite, If={foo=~'bar'}, "+
"graphiteMatchTemplate=foo.*.bar, graphiteLabelRules=[replaceTemplate=$1-zz, targetLabel=job]], relabelDebug=false") "graphiteMatchTemplate=foo.*.bar, graphiteLabelRules=[replaceTemplate=$1-zz, targetLabel=job]], relabelDebug=false")
f([]RelabelConfig{ f([]RelabelConfig{
{ {
@ -150,7 +150,7 @@ func TestParsedConfigsString(t *testing.T) {
TargetLabel: "x", TargetLabel: "x",
If: &ie, If: &ie,
}, },
}, "[SourceLabels=[foo bar], Separator=;, TargetLabel=x, Regex=^(.*)$, Modulus=0, Replacement=$1, Action=replace, If={foo=~'bar'}, "+ }, "[SourceLabels=[foo bar], Separator=;, TargetLabel=x, Regex=.*, Modulus=0, Replacement=$1, Action=replace, If={foo=~'bar'}, "+
"graphiteMatchTemplate=<nil>, graphiteLabelRules=[]], relabelDebug=false") "graphiteMatchTemplate=<nil>, graphiteLabelRules=[]], relabelDebug=false")
} }
@ -174,13 +174,14 @@ func TestParseRelabelConfigsSuccess(t *testing.T) {
}, &ParsedConfigs{ }, &ParsedConfigs{
prcs: []*parsedRelabelConfig{ prcs: []*parsedRelabelConfig{
{ {
SourceLabels: []string{"foo", "bar"}, SourceLabels: []string{"foo", "bar"},
Separator: ";", Separator: ";",
TargetLabel: "xxx", TargetLabel: "xxx",
Regex: defaultRegexForRelabelConfig, RegexAnchored: defaultRegexForRelabelConfig,
Replacement: "$1", Replacement: "$1",
Action: "replace", Action: "replace",
regex: defaultPromRegex,
regexOriginal: defaultOriginalRegexForRelabelConfig, regexOriginal: defaultOriginalRegexForRelabelConfig,
hasCaptureGroupInReplacement: true, hasCaptureGroupInReplacement: true,
}, },

View file

@ -9,6 +9,7 @@ import (
"github.com/VictoriaMetrics/VictoriaMetrics/lib/bytesutil" "github.com/VictoriaMetrics/VictoriaMetrics/lib/bytesutil"
"github.com/VictoriaMetrics/VictoriaMetrics/lib/logger" "github.com/VictoriaMetrics/VictoriaMetrics/lib/logger"
"github.com/VictoriaMetrics/VictoriaMetrics/lib/prompbmarshal" "github.com/VictoriaMetrics/VictoriaMetrics/lib/prompbmarshal"
"github.com/VictoriaMetrics/VictoriaMetrics/lib/regexutil"
"github.com/cespare/xxhash/v2" "github.com/cespare/xxhash/v2"
) )
@ -16,20 +17,20 @@ import (
// //
// See https://prometheus.io/docs/prometheus/latest/configuration/configuration/#relabel_config // See https://prometheus.io/docs/prometheus/latest/configuration/configuration/#relabel_config
type parsedRelabelConfig struct { type parsedRelabelConfig struct {
SourceLabels []string SourceLabels []string
Separator string Separator string
TargetLabel string TargetLabel string
Regex *regexp.Regexp RegexAnchored *regexp.Regexp
Modulus uint64 Modulus uint64
Replacement string Replacement string
Action string Action string
If *IfExpression If *IfExpression
graphiteMatchTemplate *graphiteMatchTemplate graphiteMatchTemplate *graphiteMatchTemplate
graphiteLabelRules []graphiteLabelRule graphiteLabelRules []graphiteLabelRule
regex *regexutil.PromRegex
regexOriginal *regexp.Regexp regexOriginal *regexp.Regexp
regexOrValues []string
hasCaptureGroupInTargetLabel bool hasCaptureGroupInTargetLabel bool
hasCaptureGroupInReplacement bool hasCaptureGroupInReplacement bool
@ -39,7 +40,8 @@ type parsedRelabelConfig struct {
// String returns human-readable representation for prc. // 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, If=%s, graphiteMatchTemplate=%s, graphiteLabelRules=%s", return fmt.Sprintf("SourceLabels=%s, Separator=%s, TargetLabel=%s, Regex=%s, Modulus=%d, Replacement=%s, Action=%s, If=%s, graphiteMatchTemplate=%s, graphiteLabelRules=%s",
prc.SourceLabels, prc.Separator, prc.TargetLabel, prc.Regex, prc.Modulus, prc.Replacement, prc.Action, prc.If, prc.graphiteMatchTemplate, prc.graphiteLabelRules) prc.SourceLabels, prc.Separator, prc.TargetLabel, prc.regexOriginal, prc.Modulus, prc.Replacement,
prc.Action, prc.If, prc.graphiteMatchTemplate, prc.graphiteLabelRules)
} }
// Apply applies pcs to labels starting from the labelsOffset. // Apply applies pcs to labels starting from the labelsOffset.
@ -183,7 +185,7 @@ func (prc *parsedRelabelConfig) apply(labels []prompbmarshal.Label, labelsOffset
replacement = string(bb.B) replacement = string(bb.B)
} }
bb.B = concatLabelValues(bb.B[:0], src, prc.SourceLabels, prc.Separator) bb.B = concatLabelValues(bb.B[:0], src, prc.SourceLabels, prc.Separator)
if prc.Regex == defaultRegexForRelabelConfig && !prc.hasCaptureGroupInTargetLabel { if prc.RegexAnchored == defaultRegexForRelabelConfig && !prc.hasCaptureGroupInTargetLabel {
if replacement == "$1" { if replacement == "$1" {
// Fast path for the rule that copies source label values to destination: // Fast path for the rule that copies source label values to destination:
// - source_labels: [...] // - source_labels: [...]
@ -201,7 +203,7 @@ func (prc *parsedRelabelConfig) apply(labels []prompbmarshal.Label, labelsOffset
return labels return labels
} }
} }
match := prc.Regex.FindSubmatchIndex(bb.B) match := prc.RegexAnchored.FindSubmatchIndex(bb.B)
if match == nil { if match == nil {
// Fast path - nothing to replace. // Fast path - nothing to replace.
relabelBufPool.Put(bb) relabelBufPool.Put(bb)
@ -253,7 +255,7 @@ func (prc *parsedRelabelConfig) apply(labels []prompbmarshal.Label, labelsOffset
return labels return labels
case "keep": case "keep":
// Keep the target if `source_labels` joined with `separator` match the `regex`. // Keep the target if `source_labels` joined with `separator` match the `regex`.
if prc.Regex == defaultRegexForRelabelConfig { if prc.RegexAnchored == defaultRegexForRelabelConfig {
// Fast path for the case with `if` and without explicitly set `regex`: // Fast path for the case with `if` and without explicitly set `regex`:
// //
// - action: keep // - action: keep
@ -263,7 +265,7 @@ func (prc *parsedRelabelConfig) apply(labels []prompbmarshal.Label, labelsOffset
} }
bb := relabelBufPool.Get() bb := relabelBufPool.Get()
bb.B = concatLabelValues(bb.B[:0], src, prc.SourceLabels, prc.Separator) bb.B = concatLabelValues(bb.B[:0], src, prc.SourceLabels, prc.Separator)
keep := prc.matchString(bytesutil.ToUnsafeString(bb.B)) keep := prc.regex.MatchString(bytesutil.ToUnsafeString(bb.B))
relabelBufPool.Put(bb) relabelBufPool.Put(bb)
if !keep { if !keep {
return labels[:labelsOffset] return labels[:labelsOffset]
@ -271,7 +273,7 @@ func (prc *parsedRelabelConfig) apply(labels []prompbmarshal.Label, labelsOffset
return labels return labels
case "drop": case "drop":
// Drop the target if `source_labels` joined with `separator` don't match the `regex`. // Drop the target if `source_labels` joined with `separator` don't match the `regex`.
if prc.Regex == defaultRegexForRelabelConfig { if prc.RegexAnchored == defaultRegexForRelabelConfig {
// Fast path for the case with `if` and without explicitly set `regex`: // Fast path for the case with `if` and without explicitly set `regex`:
// //
// - action: drop // - action: drop
@ -281,7 +283,7 @@ func (prc *parsedRelabelConfig) apply(labels []prompbmarshal.Label, labelsOffset
} }
bb := relabelBufPool.Get() bb := relabelBufPool.Get()
bb.B = concatLabelValues(bb.B[:0], src, prc.SourceLabels, prc.Separator) bb.B = concatLabelValues(bb.B[:0], src, prc.SourceLabels, prc.Separator)
drop := prc.matchString(bytesutil.ToUnsafeString(bb.B)) drop := prc.regex.MatchString(bytesutil.ToUnsafeString(bb.B))
relabelBufPool.Put(bb) relabelBufPool.Put(bb)
if drop { if drop {
return labels[:labelsOffset] return labels[:labelsOffset]
@ -317,7 +319,7 @@ func (prc *parsedRelabelConfig) apply(labels []prompbmarshal.Label, labelsOffset
dst := labels[:labelsOffset] dst := labels[:labelsOffset]
for i := range src { for i := range src {
label := &src[i] label := &src[i]
if !prc.matchString(label.Name) { if !prc.regex.MatchString(label.Name) {
dst = append(dst, *label) dst = append(dst, *label)
} }
} }
@ -327,7 +329,7 @@ func (prc *parsedRelabelConfig) apply(labels []prompbmarshal.Label, labelsOffset
dst := labels[:labelsOffset] dst := labels[:labelsOffset]
for i := range src { for i := range src {
label := &src[i] label := &src[i]
if prc.matchString(label.Name) { if prc.regex.MatchString(label.Name) {
dst = append(dst, *label) dst = append(dst, *label)
} }
} }
@ -387,12 +389,12 @@ func (prc *parsedRelabelConfig) replaceFullString(s, replacement string, hasCapt
} }
} }
// Slow path - regexp processing // Slow path - regexp processing
match := prc.Regex.FindStringSubmatchIndex(s) match := prc.RegexAnchored.FindStringSubmatchIndex(s)
if match == nil { if match == nil {
return s, false return s, false
} }
bb := relabelBufPool.Get() bb := relabelBufPool.Get()
bb.B = prc.Regex.ExpandString(bb.B[:0], replacement, s, match) bb.B = prc.RegexAnchored.ExpandString(bb.B[:0], replacement, s, match)
result := string(bb.B) result := string(bb.B)
relabelBufPool.Put(bb) relabelBufPool.Put(bb)
return result, true return result, true
@ -413,39 +415,9 @@ func (prc *parsedRelabelConfig) replaceStringSubmatches(s, replacement string, h
return re.ReplaceAllString(s, replacement), true return re.ReplaceAllString(s, replacement), true
} }
func (prc *parsedRelabelConfig) matchString(s string) bool {
if len(prc.regexOrValues) > 0 {
for _, orValue := range prc.regexOrValues {
if s == orValue {
return true
}
}
return false
}
prefix, complete := prc.regexOriginal.LiteralPrefix()
if complete {
return prefix == s
}
if !strings.HasPrefix(s, prefix) {
return false
}
reStr := prc.regexOriginal.String()
if strings.HasPrefix(reStr, prefix) {
// Fast path for `foo.*` and `bar.+` regexps
reSuffix := reStr[len(prefix):]
switch reSuffix {
case ".+", "(.+)":
return len(s) > len(prefix)
case ".*", "(.*)":
return true
}
}
return prc.Regex.MatchString(s)
}
func (prc *parsedRelabelConfig) expandCaptureGroups(template, source string, match []int) string { func (prc *parsedRelabelConfig) expandCaptureGroups(template, source string, match []int) string {
bb := relabelBufPool.Get() bb := relabelBufPool.Get()
bb.B = prc.Regex.ExpandString(bb.B[:0], template, source, match) bb.B = prc.RegexAnchored.ExpandString(bb.B[:0], template, source, match)
s := string(bb.B) s := string(bb.B)
relabelBufPool.Put(bb) relabelBufPool.Put(bb)
return s return s

View file

@ -728,12 +728,12 @@ func TestFillLabelReferences(t *testing.T) {
f(`{{bar}}-aa{{__name__}}.{{bar}}{{non-existing-label}}`, `foo{bar="baz"}`, `baz-aafoo.baz`) f(`{{bar}}-aa{{__name__}}.{{bar}}{{non-existing-label}}`, `foo{bar="baz"}`, `baz-aafoo.baz`)
} }
func TestRegexpMatchStringSuccess(t *testing.T) { func TestRegexMatchStringSuccess(t *testing.T) {
f := func(pattern, s string) { f := func(pattern, s string) {
t.Helper() t.Helper()
prc := newTestRegexRelabelConfig(pattern) prc := newTestRegexRelabelConfig(pattern)
if !prc.matchString(s) { if !prc.regex.MatchString(s) {
t.Fatalf("unexpected matchString(%q) result; got false; want true", s) t.Fatalf("unexpected MatchString(%q) result; got false; want true", s)
} }
} }
f("", "") f("", "")
@ -753,8 +753,8 @@ func TestRegexpMatchStringFailure(t *testing.T) {
f := func(pattern, s string) { f := func(pattern, s string) {
t.Helper() t.Helper()
prc := newTestRegexRelabelConfig(pattern) prc := newTestRegexRelabelConfig(pattern)
if prc.matchString(s) { if prc.regex.MatchString(s) {
t.Fatalf("unexpected matchString(%q) result; got true; want false", s) t.Fatalf("unexpected MatchString(%q) result; got true; want false", s)
} }
} }
f("", "foo") f("", "foo")

View file

@ -16,7 +16,7 @@ func BenchmarkMatchRegexPrefixDotPlusMatchOptimized(b *testing.B) {
b.SetBytes(1) b.SetBytes(1)
b.RunParallel(func(pb *testing.PB) { b.RunParallel(func(pb *testing.PB) {
for pb.Next() { for pb.Next() {
if !prc.matchString(s) { if !prc.regex.MatchString(s) {
panic(fmt.Errorf("unexpected string mismatch for pattern=%q, s=%q", pattern, s)) panic(fmt.Errorf("unexpected string mismatch for pattern=%q, s=%q", pattern, s))
} }
} }
@ -46,7 +46,7 @@ func BenchmarkMatchRegexPrefixDotPlusMismatchOptimized(b *testing.B) {
b.SetBytes(1) b.SetBytes(1)
b.RunParallel(func(pb *testing.PB) { b.RunParallel(func(pb *testing.PB) {
for pb.Next() { for pb.Next() {
if prc.matchString(s) { if prc.regex.MatchString(s) {
panic(fmt.Errorf("unexpected string match for pattern=%q, s=%q", pattern, s)) panic(fmt.Errorf("unexpected string match for pattern=%q, s=%q", pattern, s))
} }
} }
@ -76,7 +76,7 @@ func BenchmarkMatchRegexPrefixDotStarMatchOptimized(b *testing.B) {
b.SetBytes(1) b.SetBytes(1)
b.RunParallel(func(pb *testing.PB) { b.RunParallel(func(pb *testing.PB) {
for pb.Next() { for pb.Next() {
if !prc.matchString(s) { if !prc.regex.MatchString(s) {
panic(fmt.Errorf("unexpected string mismatch for pattern=%q, s=%q", pattern, s)) panic(fmt.Errorf("unexpected string mismatch for pattern=%q, s=%q", pattern, s))
} }
} }
@ -106,7 +106,7 @@ func BenchmarkMatchRegexPrefixDotStarMismatchOptimized(b *testing.B) {
b.SetBytes(1) b.SetBytes(1)
b.RunParallel(func(pb *testing.PB) { b.RunParallel(func(pb *testing.PB) {
for pb.Next() { for pb.Next() {
if prc.matchString(s) { if prc.regex.MatchString(s) {
panic(fmt.Errorf("unexpected string match for pattern=%q, s=%q", pattern, s)) panic(fmt.Errorf("unexpected string match for pattern=%q, s=%q", pattern, s))
} }
} }
@ -136,7 +136,7 @@ func BenchmarkMatchRegexSingleValueMatchOptimized(b *testing.B) {
b.SetBytes(1) b.SetBytes(1)
b.RunParallel(func(pb *testing.PB) { b.RunParallel(func(pb *testing.PB) {
for pb.Next() { for pb.Next() {
if !prc.matchString(s) { if !prc.regex.MatchString(s) {
panic(fmt.Errorf("unexpected string mismatch for pattern=%q, s=%q", pattern, s)) panic(fmt.Errorf("unexpected string mismatch for pattern=%q, s=%q", pattern, s))
} }
} }
@ -166,7 +166,7 @@ func BenchmarkMatchRegexSingleValueMismatchOptimized(b *testing.B) {
b.SetBytes(1) b.SetBytes(1)
b.RunParallel(func(pb *testing.PB) { b.RunParallel(func(pb *testing.PB) {
for pb.Next() { for pb.Next() {
if prc.matchString(s) { if prc.regex.MatchString(s) {
panic(fmt.Errorf("unexpected string match for pattern=%q, s=%q", pattern, s)) panic(fmt.Errorf("unexpected string match for pattern=%q, s=%q", pattern, s))
} }
} }
@ -196,7 +196,7 @@ func BenchmarkMatchRegexOrValuesMatchOptimized(b *testing.B) {
b.SetBytes(1) b.SetBytes(1)
b.RunParallel(func(pb *testing.PB) { b.RunParallel(func(pb *testing.PB) {
for pb.Next() { for pb.Next() {
if !prc.matchString(s) { if !prc.regex.MatchString(s) {
panic(fmt.Errorf("unexpected string mismatch for pattern=%q, s=%q", pattern, s)) panic(fmt.Errorf("unexpected string mismatch for pattern=%q, s=%q", pattern, s))
} }
} }
@ -226,7 +226,7 @@ func BenchmarkMatchRegexOrValuesMismatchOptimized(b *testing.B) {
b.SetBytes(1) b.SetBytes(1)
b.RunParallel(func(pb *testing.PB) { b.RunParallel(func(pb *testing.PB) {
for pb.Next() { for pb.Next() {
if prc.matchString(s) { if prc.regex.MatchString(s) {
panic(fmt.Errorf("unexpected string match for pattern=%q, s=%q", pattern, s)) panic(fmt.Errorf("unexpected string match for pattern=%q, s=%q", pattern, s))
} }
} }

View file

@ -16,7 +16,7 @@ import (
type PromRegex struct { type PromRegex struct {
// prefix contains literal prefix for regex. // prefix contains literal prefix for regex.
// For example, prefix="foo" for regex="foo(a|b)" // For example, prefix="foo" for regex="foo(a|b)"
prefix string prefix string
// Suffix contains regex suffix left after removing the prefix. // Suffix contains regex suffix left after removing the prefix.
// For example, suffix="a|b" for regex="foo(a|b)" // For example, suffix="a|b" for regex="foo(a|b)"
@ -51,12 +51,12 @@ func NewPromRegex(expr string) (*PromRegex, error) {
suffixExpr := "^(?:" + suffix + ")$" suffixExpr := "^(?:" + suffix + ")$"
reSuffix := regexp.MustCompile(suffixExpr) reSuffix := regexp.MustCompile(suffixExpr)
pr := &PromRegex{ pr := &PromRegex{
prefix: prefix, prefix: prefix,
suffix: suffix, suffix: suffix,
substrDotStar: substrDotStar, substrDotStar: substrDotStar,
substrDotPlus: substrDotPlus, substrDotPlus: substrDotPlus,
orValues: orValues, orValues: orValues,
reSuffix: reSuffix, reSuffix: reSuffix,
} }
return pr, nil return pr, nil
} }
@ -87,7 +87,7 @@ func (pr *PromRegex) MatchString(s string) bool {
if pr.substrDotPlus != "" { if pr.substrDotPlus != "" {
// Fast path - pr contains ".+someText.+" // Fast path - pr contains ".+someText.+"
n := strings.Index(s, pr.substrDotPlus) n := strings.Index(s, pr.substrDotPlus)
return n > 0 && n + len(pr.substrDotPlus) < len(s) return n > 0 && n+len(pr.substrDotPlus) < len(s)
} }
switch pr.suffix { switch pr.suffix {
case ".*": case ".*":
@ -116,4 +116,3 @@ func getSubstringLiteral(expr, prefixSuffix string) string {
} }
return prefix return prefix
} }

View file

@ -19,6 +19,15 @@ func BenchmarkPromRegexMatchString(b *testing.B) {
b.Run("unpotimized-prefix-mismatch", func(b *testing.B) { b.Run("unpotimized-prefix-mismatch", func(b *testing.B) {
benchmarkPromRegexMatchString(b, "foo(bar.*|baz)", "zfoobarz", false) benchmarkPromRegexMatchString(b, "foo(bar.*|baz)", "zfoobarz", false)
}) })
b.Run("dot-star-match", func(b *testing.B) {
benchmarkPromRegexMatchString(b, ".*", "foo", true)
})
b.Run("dot-plus-match", func(b *testing.B) {
benchmarkPromRegexMatchString(b, ".+", "foo", true)
})
b.Run("dot-plus-mismatch", func(b *testing.B) {
benchmarkPromRegexMatchString(b, ".+", "", false)
})
b.Run("literal-match", func(b *testing.B) { b.Run("literal-match", func(b *testing.B) {
benchmarkPromRegexMatchString(b, "foo", "foo", true) benchmarkPromRegexMatchString(b, "foo", "foo", true)
}) })