Skip to content

policy: Pre-parse cached selectors, index by namespace#42008

Merged
jrajahalme merged 11 commits intocilium:mainfrom
jrajahalme:cidr-label-perf
Dec 2, 2025
Merged

policy: Pre-parse cached selectors, index by namespace#42008
jrajahalme merged 11 commits intocilium:mainfrom
jrajahalme:cidr-label-perf

Conversation

@jrajahalme
Copy link
Copy Markdown
Member

@jrajahalme jrajahalme commented Oct 3, 2025

Parse selector requirement keys into labels when inserting into selector cache. Previously the labels of each existing cached selector (and netip.Prefix for a CIDR selector) were re-parsed and re-allocated on each match operation, used when new selectors or identities are added to the selector cache. This was essentially an O(n^2) operation on policy ingestion and identity updates.

Index cached selectors and identities by namespace so that it is possible to avoid futile matches rather than making the match operation itself fail early for a non-matching namespace. This speeds up namespaced policy ingestion and identity updates.

These changes make CIDR policy resolution in the new BenchmarkResolveCIDRPolicyRules ~4x faster, using 90% less memory and 96% less allocations.

@jrajahalme jrajahalme requested review from a team as code owners October 3, 2025 13:43
@jrajahalme jrajahalme added the kind/performance There is a performance impact of this. label Oct 3, 2025
@jrajahalme jrajahalme requested a review from squeed October 3, 2025 13:43
@jrajahalme jrajahalme added sig/policy Impacts whether traffic is allowed or denied based on user-defined policies. release-note/misc This PR makes changes that have no direct user impact. labels Oct 3, 2025
@marseel
Copy link
Copy Markdown
Member

marseel commented Oct 3, 2025

/scale-100

@jrajahalme
Copy link
Copy Markdown
Member Author

/test

@jrajahalme jrajahalme changed the title policy: Pre-parse labelIdentitySelector policy: Pre-parse cached selectors, index by namespace Oct 7, 2025
@jrajahalme
Copy link
Copy Markdown
Member Author

/test

@squeed
Copy link
Copy Markdown
Contributor

squeed commented Oct 8, 2025

(note: I see at least one "wip" commit message)

@jrajahalme
Copy link
Copy Markdown
Member Author

(note: I see at least one "wip" commit message)

Was a commit squash artefact, will fix :-)

@jrajahalme jrajahalme requested a review from a team as a code owner October 14, 2025 09:38
@jrajahalme jrajahalme requested a review from squeed October 14, 2025 09:38
@jrajahalme
Copy link
Copy Markdown
Member Author

/test

@jrajahalme
Copy link
Copy Markdown
Member Author

/test

@jrajahalme
Copy link
Copy Markdown
Member Author

/test

Use world label in CIDR selectors instead of a zero-length prefixes. Thus
a CIDR "0.0.0.0/0" selector will translate to a "reserved:world" selector
(in an IPv6 disabled configuration). This makes it possible to remove the
addition of world selectors when wildcard selectors are added.

Do not add extra selectors with "reserved:world" label requirement (or
dual-stack equivalents) when a CIDR selecting all IPv4 or IPv6 addresses
is ingested. Adding the extra world label selector will no longer expand
the set of selected identities, since the world selector has already been
used instead of a zero-length CIDR selector.

The removed logic was also partially based on string matching on forms
that would not match for selectors with a CIDR "1.1.1.1/0" for example,
even though the resulting CIDR selector would be the same
("cidr.0.0.0.0/0").

This change allows CIDR selector parsing to internal
representation to be simplified, as each CIDR selector will map to
exactly one internal EndpointSelector.

Signed-off-by: Jarno Rajahalme <[email protected]>
Add a new LabelMatcher interface with a sole LookupLabel function that
uses a pre-parsed *Label as an argument. Implement it in Labels,
LabelArray, and in a new K8sSet, that can be used to cast bare sting maps
to a LabelMatcher.

Use iteration index into LabelArray rather than using a temporary
Label. This makes a diffence due to LookupLabel used in some inner loops
in policy selector matching.

Signed-off-by: Jarno Rajahalme <[email protected]>
Use the cached string and requirements with pre-parsed labels instead of
api.EndpointSelector in labelIdentitySelector.

labels.LabelArray is changed to add HasLabel(), GetLabel() and
LookupLabel() functions that take a pre-parsed Label as an argument
instead of a string.

This makes CIDR policy resolution in BenchmarkResolveCIDRPolicyRules 70%
faster, with 90% less memory used and 95% less allocations.

Signed-off-by: Jarno Rajahalme <[email protected]>
Consider selector possibly selecting any namespace if the selector
requirement has any operator than "In", "Equals", or "DoubleEquals". For
example, if the requirement is "NotIn", then any namespace *not in* the
values would be selected. In this kind of cases we skip namespace
filtering and perform a full match.

Signed-off-by: Jarno Rajahalme <[email protected]>
Store the fqdn identity label into `fqdnSelector` so that it does not
need to be re-created on every match operation.

Signed-off-by: Jarno Rajahalme <[email protected]>
Refactor scIdentityCache to a struct.

Keep scIdentities in a map via a pointer so that it is possible to point
to them via a secondary index to be added in the next commit.

Signed-off-by: Jarno Rajahalme <[email protected]>
Add a namespace index to identities so that ones with non-matching
identities can be trivially skipped when adding or removing selectors
to/from the selector cache.

Signed-off-by: Jarno Rajahalme <[email protected]>
Index cached selectors by namespace so that ones with non-matching
namespaces can be trivially skipped when adding or deleting identities.

Signed-off-by: Jarno Rajahalme <[email protected]>
@jrajahalme jrajahalme changed the title policy: Pre-parse cached selectors, index by namespace, use statedb/part.Map policy: Pre-parse cached selectors, index by namespace Nov 22, 2025
@jrajahalme
Copy link
Copy Markdown
Member Author

Removed part.Map change (last commit), as it triggered consistent ci-ingress failure. Will investigate and post as a separate PR.

@jrajahalme jrajahalme removed request for a team, joamaki and marseel November 22, 2025 10:53
@jrajahalme
Copy link
Copy Markdown
Member Author

/test

Copy link
Copy Markdown
Member

@sayboras sayboras left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good for cilium/proxy related files

Copy link
Copy Markdown
Contributor

@joamaki joamaki left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sig-k8s & sig-lb LGTM

@jrajahalme
Copy link
Copy Markdown
Member Author

/test

Keep pre-computed Requirements only in Selector implementations in
pkg/policy/types. Use these in places where EndpointSelector was used for
matching in production code previously.

This simplifies policy/api code, and keeps the faster matching in one
place (policy/types.Selector implementations). These same Selectors are
now also used internally in SelectorCache for updating the cached
selections.

Note that the matching interface of api.EndpointSelector has been moved
to unit test code. Due to dependency cycle avoidance the unit test
matching logic in policy/api can not use the new matching logic in
policy/types. For selector matching in production code, create a
types.LabelSelector from api.EndpointSelector and use that for
matching. Note that for this to work the api.EndpointSelector MUST HAVE
the key prefixed with a label source (e.g., "k8s."), otherwise any dot
separated prefix will be parsed as the label's source and removed from
the label key!

API selector types now all have a SelectorKey() method, used for getting
a key string before a new Selectors are created from them. This method
should be fast, and api.EndpointSelector keeps the internal
cachedLabelSelectorString member for this purpose. This cached string is
still updated after each modification to api.EndpointSelector.

PeerSelector interface used to be a "marker" interface with
IsPeerSelector() function that did nothing. Since we added another
function (SelectorKey()) to the interface, it is not a pure marker
anymore, and IsPeerSelector() function is no longer needed, so remove it.

Since also subject selectors can be cached in selector cache, rename
PeerSelector interface as APISelector, as it must be implemented by all
policy.api types that can be used as types.Selectors.

Stop hauling logger to selector matching functions, as they use validated
inputs and should never fail. To help avoid breaking this invariant, use
logging.DefaultSlogLogger as a "soft panic" to fail Cilium CI if these
are hit during CI runs.

Signed-off-by: Jarno Rajahalme <[email protected]>
@jrajahalme
Copy link
Copy Markdown
Member Author

Squashed last three commits, added slogloggercheck with reason to use DefaultSlogLogger.

@jrajahalme
Copy link
Copy Markdown
Member Author

/test

value = v.Value
}
return
// Get implements labels.LabelMatcher; label source is ignored
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit:

Suggested change
// Get implements labels.LabelMatcher; label source is ignored
// GetLabel implements labels.LabelMatcher; label source is ignored

// Has implements k8sLabels.Labels.
func (l labelsMatcher) Has(label string) (exists bool) {
return labels.Labels(l).HasLabelWithKey(label)
// Has implements labels.LabelMatcher.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// Has implements labels.LabelMatcher.
// HasLabel implements labels.LabelMatcher.

// Lookup implements k8sLabels.Labels.
func (l labelsMatcher) Lookup(label string) (value string, exists bool) {
v, ok := labels.Labels(l)[label]
// Lookup implements labels.LabelMatcher
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// Lookup implements labels.LabelMatcher
// LookupLabel implements labels.LabelMatcher

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

kind/performance There is a performance impact of this. ready-to-merge This PR has passed all tests and received consensus from code owners to merge. release-note/misc This PR makes changes that have no direct user impact. sig/policy Impacts whether traffic is allowed or denied based on user-defined policies.

Projects

No open projects
Status: Released

Development

Successfully merging this pull request may close these issues.