This commit is contained in:
Dimitar Dimitrov 2025-08-05 15:05:10 -07:00 committed by GitHub
commit 1ef5706e06
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 51 additions and 0 deletions

View File

@ -16,6 +16,8 @@ package labels
import (
"bytes"
"strconv"
"go.uber.org/atomic"
)
// MatchType is an enum for label matching types.
@ -78,6 +80,26 @@ func MustNewMatcher(mt MatchType, name, val string) *Matcher {
return m
}
// NewMatcherWithTimeTracker returns a matcher which can track the time spent running regular expression matchers.
// duration is incremented when the MatchType is MatchRegexp or MatchNotRegexp.
// duration is incremented every 64 Matcher.Matches() invocations and multiplied by 64;
// the assumption is that all previous 63 invocations took the same time.
func NewMatcherWithTimeTracker(t MatchType, n, v string, duration *atomic.Duration) (*Matcher, error) {
m := &Matcher{
Type: t,
Name: n,
Value: v,
}
if t == MatchRegexp || t == MatchNotRegexp {
re, err := NewFastRegexMatcherWithTimeTracker(v, duration)
if err != nil {
return nil, err
}
m.re = re
}
return m, nil
}
func (m *Matcher) String() string {
// Start a buffer with a pre-allocated size on stack to cover most needs.
var bytea [1024]byte

View File

@ -16,11 +16,13 @@ package labels
import (
"slices"
"strings"
"time"
"unicode"
"unicode/utf8"
"github.com/grafana/regexp"
"github.com/grafana/regexp/syntax"
"go.uber.org/atomic"
"golang.org/x/text/unicode/norm"
)
@ -86,6 +88,33 @@ func NewFastRegexMatcher(v string) (*FastRegexMatcher, error) {
return m, nil
}
// NewFastRegexMatcherWithTimeTracker returns a matcher which will track the time spent running the matcher.
// duration is incremented every 64 Matcher.Matches() invocations and multiplied by 64;
// the assumption is that all previous 63 invocations took the same time.
func NewFastRegexMatcherWithTimeTracker(regex string, duration *atomic.Duration) (*FastRegexMatcher, error) {
m, err := NewFastRegexMatcher(regex)
if err != nil {
return nil, err
}
withDifferentObserver := *m
sampler := atomic.NewInt64(-1)
oldMatchString := m.matchString
withDifferentObserver.matchString = func(s string) bool {
const sampleRate = 64
if tick := sampler.Inc(); tick%sampleRate == 0 {
defer func(start time.Time) {
d := time.Since(start)
if tick != 0 {
d *= sampleRate
}
duration.Add(d)
}(time.Now())
}
return oldMatchString(s)
}
return &withDifferentObserver, nil
}
// compileMatchStringFunction returns the function to run by MatchString().
func (m *FastRegexMatcher) compileMatchStringFunction() func(string) bool {
// If the only optimization available is the string matcher, then we can just run it.