Uh oh!
There was an error while loading. Please reload this page.
perf: optimise regex patterns compilation - #1114
Conversation
Claude finished @mbevc1's task in 3m 43s —— View job Review of the latest push ( |
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
mbevc1
commented
Aug 20, 2026
Fixed as suggested and addressed latest feedback:
|
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
0b13e69 to
cc29ce9CompareUh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
ccb46ba to
1714d4dCompareSeveral hot paths recompiled the same regex patterns on every call, so filtering or scanning N items compiled the same patterns N times. - filters: ResourceFilterOptions.Compile returns a CompiledResourceFilter, which the AWS (Lambda, ECS clusters and services), Kubernetes and Cloud Run snapshot paths now build once per snapshot instead of once per resource name. Filtering 100 names against two patterns drops from ~348,000 to ~25,000 ns/op and from 5,000 to 108 allocs/op. - digest: the constant SHA256 fingerprint pattern is compiled at package level rather than on every ValidateDigest call. - jira: the default issue key pattern and the ^-\d helper are compiled at package level. A pattern built from project keys is still compiled per call, which cannot panic because validateJiraProjectKeys has already rejected any key that could carry regex metacharacters. An invalid pattern is still reported exactly when it was before: only when matching a name reaches it. A name settled by ExcludeNames/IncludeNames, or by an earlier pattern that already matched, is filtered without the invalid pattern being consulted, so a filter that worked before still works. Compiling therefore cannot fail, and ShouldInclude returns the error. Two clean-ups follow from the above: - kube: filterNamespaces matched names in one goroutine per namespace behind a mutex, and carried an error channel plus a cancellable context solely to report a compile failure out of those goroutines. With the patterns compiled up front the goroutine body cannot fail and the remaining work is microseconds of CPU, so it runs in a plain loop and the returned order is now deterministic. The pod fetching in GetPodsData stays concurrent. - gitview: MatchPatternInCommitMessageORBranchName had no production callers and called MustCompile on a caller-supplied pattern, so an invalid pattern would panic instead of returning an error. Removed; its search semantics are covered by the attest jira search-text test. Closes#826
- filters: Compile clones the literal name slices, so the immutability that CompiledResourceFilter's doc comment promises - and that makes sharing one compiled filter across the ECS goroutines safe - holds for the literals and not just the patterns. Covered by a test that fails without the clone. - kube: filterNamespaces' four-way length check is exactly !filter.IsSet(), as getFilteredLambdaFuncs already spells it. Collapsing it also keeps ResourceFilterOptions.IsSet() in production use. - attest jira: validateJiraProjectKeys compiled a constant pattern on every invocation, with error handling for a failure that cannot happen. It is now a package-level jiraProjectKeyRegexp, which internal/jira's jiraIssueKeyRegexp doc comment can name instead of repeating the literal. - digest: the pattern constant is validSha256Pattern rather than validSha256regex, which was neither idiomatic Go nor distinguishable at a glance from the compiled validSha256Regexp beside it. - kube test: TestFilterNamespaces asserted with ElementsMatch, which cannot fail on ordering, while this change claims the returned order is deterministic. It now builds the expectation in the cluster's listing order and asserts equality, with a guard so that assertion cannot pass vacuously. Also adds the k8s case for an invalid pattern behind an excluded literal name: the snapshot still fails there, because the cluster's own namespaces are not in ExcludeNames and one of them always reaches the pattern. Refs #826
processAnnotations re-compiled a constant pattern for every annotation key, which is the same shape as the digest and jira changes except that this one is inside the loop rather than once per invocation. Hoist it to a package-level annotationKeyRegexp, matching jiraProjectKeyRegexp. The pattern and the error message are unchanged, so the existing --annotate cases still pin the behaviour. This is the last constant pattern compiled inline outside tests; the remaining regexp.Compile calls all take a user-supplied pattern and have to stay dynamic. Also clone the namespace list in filterNamespaces' early return, which handed back the caller's slice fifteen lines above a Compile that clones precisely so callers cannot alias. Nothing mutates it today, so this is for consistency rather than a fix. Refs #826
MakeJiraIssueKeyPattern interpolated project keys into a regex verbatim, so "the pattern always compiles" held only because validateJiraProjectKeys had already rejected anything containing a metacharacter. That contract lived in a comment in cmd/kosli and was relied on by a MustCompile in internal/jira, two packages away from the check enforcing it. QuoteMeta each key instead, which makes it a property of the function rather than a promise its callers have to keep - and lets MakeJiraIssueKeyPattern, which is exported, be safe on its own terms. No behaviour change for a key a Jira project could actually have: none of [A-Za-z0-9_] is a metacharacter, so quoting leaves every valid key untouched and the existing pattern expectations are unchanged. Adds a case for keys that do carry metacharacters, which fails without the quoting. Refs #826
Follow-up to the quoting change, addressing review of it.
makeJiraIssueKeyPattern is now unexported. Its "" return means "no issue key
can match", but it is the one sentinel that inverts when used: MustCompile("")
succeeds and matches at every position, so a caller who compiles it gets
match-everything, the widening the sentinel exists to prevent. Only
jiraIssueKeyRegexp stood between the two, and the function had just two
references, both in this package. Its rationale also sat inside the function
body, so go doc printed a bare signature for a return value with three
meanings; that is now a doc comment listing all three and saying the empty one
must not be compiled.
Keys that all drop out no longer fall back to the default pattern. Asking for
no project in particular and asking for projects that all turn out to be
unusable are different questions: the first means every project, the second
means none can match. Answering the second with the default would widen a
caller who named projects to every project, and on an attestation path the keys
that widening invents are then looked up and attested.
Blank keys are dropped and the rest trimmed. An empty key interpolates an empty
alternative that matches any -[0-9]+, so {"EX", ""} reported -41284 out of
CVE-2026-41284 as an issue key; a whitespace-only key does the same one column
over, since a space is not a metacharacter for QuoteMeta to escape; and an
untrimmed " EX" reports " EX-12", which is not a key any project has. None of
this is reachable from the CLI - validateJiraProjectKeys rejects all three
before FindJiraIssueKeys sees them - so the guard is kept to one line and one
test case rather than covered exhaustively.
Also in internal/kube's TestFilterNamespaces: drop the namespaces from the
invalid-pattern case, which created and deleted two against a real cluster
without needing them, since the cluster's own namespaces reach the pattern. The
other error case keeps its namespace, which is load-bearing - it exists to be
settled by ExcludeNames while the others still reach the invalid pattern.
And state plainly what TestCompiledFilterConcurrentShouldInclude does establish:
every goroutine writes its own slot and reads only immutable state, so its
assertions check that concurrent matching returns the right answers, not that
the sharing is synchronised.
Refs #8261714d4d to
a032f6fCompareUh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Several hot paths recompiled the same regex patterns on every call, so filtering or scanning N items compiled the same patterns N times.
An invalid pattern is still reported exactly when it was before: only when matching a name reaches it. A name settled by ExcludeNames/IncludeNames, or by an earlier pattern that already matched, is filtered without the invalid pattern being consulted, so a filter that worked before still works. Compiling therefore cannot fail, and ShouldInclude returns the error.
Two clean-ups follow from the above:
Closes#826
Checklist
charts/k8s-reporter/) updated, if needed. Note: these changes live in a separate PR