From 022276de97aef970b7c477769b393753ef09e6e8 Mon Sep 17 00:00:00 2001 From: Andrew McDermott Date: Wed, 10 Mar 2021 11:45:57 +0000 Subject: [PATCH 1/2] optimisation: cache compiled regexp's --- pkg/router/template/template_helper.go | 42 ++++++++++++++++++++++++-- 1 file changed, 40 insertions(+), 2 deletions(-) diff --git a/pkg/router/template/template_helper.go b/pkg/router/template/template_helper.go index 1081fd650..c16ee10ec 100644 --- a/pkg/router/template/template_helper.go +++ b/pkg/router/template/template_helper.go @@ -10,6 +10,7 @@ import ( "sort" "strconv" "strings" + "sync" "text/template" "time" @@ -31,9 +32,46 @@ func isTrue(s string) bool { return v } +// compiledRegexp is the store of already compiled regular +// expressions. +var compiledRegexp sync.Map + +// cachedRegexpCompile will compile pattern using regexp.Compile and +// adds it to the compiledRegexp store if it is not already present. +// It will return an error error if pattern is an invalid regular +// expression. If pattern already exists in the store then no +// compilation is necessary and the existing compiled regexp is +// returned. This provides a huge performance improvement as repeated +// calls to compiling a regular expression is enormous. See: +// https://bugzilla.redhat.com/show_bug.cgi?id=1937972 +func cachedRegexpCompile(pattern string) (*regexp.Regexp, error) { + v, ok := compiledRegexp.Load(pattern) + if !ok { + log.V(7).Info("compiling regexp", "pattern", pattern) + re, err := regexp.Compile(pattern) + if err != nil { + return nil, err + } + compiledRegexp.Store(pattern, re) + return re, nil + } + return v.(*regexp.Regexp), nil +} + +// matchString reports whether the string s contains any match in +// pattern. Repeated re-compilations of the regular expression +// (pattern) are avoided by utilising the cachedRegexpCompile store. +func matchString(pattern string, s string) (bool, error) { + re, err := cachedRegexpCompile(pattern) + if err != nil { + return false, err + } + return re.MatchString(s), nil +} + func firstMatch(pattern string, values ...string) string { log.V(7).Info("firstMatch called", "pattern", pattern, "values", values) - if re, err := regexp.Compile(`\A(?:` + pattern + `)\z`); err == nil { + if re, err := cachedRegexpCompile(`\A(?:` + pattern + `)\z`); err == nil { for _, value := range values { if re.MatchString(value) { log.V(7).Info("firstMatch returning", "value", value) @@ -80,7 +118,7 @@ func matchValues(s string, allowedValues ...string) bool { func matchPattern(pattern, s string) bool { log.V(7).Info("matchPattern called", "pattern", pattern, "s", s) - status, err := regexp.MatchString(`\A(?:`+pattern+`)\z`, s) + status, err := matchString(`\A(?:`+pattern+`)\z`, s) if err == nil { log.V(7).Info("matchPattern returning", "foundMatch", status) return status From 7cdaead92adf3d930fe21778d9eb71e6ef75800e Mon Sep 17 00:00:00 2001 From: Andrew McDermott Date: Thu, 11 Mar 2021 18:56:32 +0000 Subject: [PATCH 2/2] Log router write config time --- pkg/router/template/router.go | 1 + 1 file changed, 1 insertion(+) diff --git a/pkg/router/template/router.go b/pkg/router/template/router.go index cbcb7f11b..29e316d6f 100644 --- a/pkg/router/template/router.go +++ b/pkg/router/template/router.go @@ -453,6 +453,7 @@ func (r *templateRouter) commitAndReload() error { reloadStart := time.Now() err := r.writeConfig() r.metricWriteConfig.Observe(float64(time.Now().Sub(reloadStart)) / float64(time.Second)) + log.V(4).Info("writeConfig", "duration", time.Now().Sub(reloadStart).String()) return err }(); err != nil { return err