Skip to content

Ruler: cache parsed rule files during rule sync - #16512

Draft
karlskewes wants to merge 4 commits into
mainfrom
ruler-rule-sync-parse-cache
Draft

Ruler: cache parsed rule files during rule sync#16512
karlskewes wants to merge 4 commits into
mainfrom
ruler-rule-sync-parse-cache

Conversation

@karlskewes

@karlskewes karlskewes commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

Why

Profiling a production ruler found redundant YAML parsing on the rule sync path.
An unchanged namespace file gets fully re-parsed every time another namespace changes for that tenant.

What's Changed

  • add a rule file parse cache to FSLoader
  • gate it behind a new experimental flag, disabled by default
  • add parse cache hit/miss metrics per tenant

Benchmark

FSLoader.Load on an unchanged 20-group/5-rule namespace file, repeated calls, cache disabled vs enabled (benchstat, n=10):

                 │ cache-disabled.txt │          cache-enabled.txt           │
                 │       sec/op       │    sec/op     vs base                │
FSLoader_Load-10       4889.11µ ± 16%   62.71µ ± 16%  -98.72% (p=0.000 n=10)

                 │ cache-disabled.txt │          cache-enabled.txt           │
                 │        B/op        │     B/op      vs base                │
FSLoader_Load-10        810.28Ki ± 0%   57.25Ki ± 0%  -92.93% (p=0.000 n=10)

                 │ cache-disabled.txt │         cache-enabled.txt          │
                 │     allocs/op      │ allocs/op   vs base                │
FSLoader_Load-10         12720.0 ± 0%   225.0 ± 0%  -98.23% (p=0.000 n=10)

Notes

FSLoader has a single production call site, pkg/ruler/compat.go.
It isn't shared with any other component.

@github-actions

github-actions Bot commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

💻 Deploy preview available (Mimir):

@karlskewes

Copy link
Copy Markdown
Contributor Author

@cursor review

Comment thread pkg/ruler/mapper.go
karlskewes added a commit that referenced this pull request Sep 2, 2026
…tenants

A cache miss stored and returned the same *rulefmt.RuleGroups pointer.
rules.Manager.LoadGroups keeps SourceTenants aliased by reference, and
federated rule evaluation later sorts that slice in place
(tenant.NormalizeTenantIDs), racing with any later cache read of the
same entry. Cache a defensive copy instead.

Found by Cursor Bugbot: #16512 (comment)

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

✅ Bugbot reviewed your changes and found no new issues!

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit d45fbec. Configure here.

Add the experimental -ruler.rule-file-parse-caching-enabled flag. When
enabled, FSLoader skips rulefmt.Parse for a rule file whose exact bytes
and parse options match the last successful parse, avoiding redundant
YAML unmarshalling of unchanged namespace files on every tenant sync.
Defaults to disabled.
karlskewes added a commit that referenced this pull request Sep 2, 2026
…tenants

A cache miss stored and returned the same *rulefmt.RuleGroups pointer.
rules.Manager.LoadGroups keeps SourceTenants aliased by reference, and
federated rule evaluation later sorts that slice in place
(tenant.NormalizeTenantIDs), racing with any later cache read of the
same entry. Cache a defensive copy instead.

Found by Cursor Bugbot: #16512 (comment)
@karlskewes
karlskewes force-pushed the ruler-rule-sync-parse-cache branch from 013a9e7 to d91bd10 Compare September 2, 2026 05:39
…tenants

A cache miss stored and returned the same *rulefmt.RuleGroups pointer.
rules.Manager.LoadGroups keeps SourceTenants aliased by reference, and
federated rule evaluation later sorts that slice in place
(tenant.NormalizeTenantIDs), racing with any later cache read of the
same entry. Cache a defensive copy instead.

Found by Cursor Bugbot: #16512 (comment)
@karlskewes
karlskewes force-pushed the ruler-rule-sync-parse-cache branch from 10b89ae to 414c0ea Compare September 3, 2026 00:35
A namespace file's cache entry was never removed on rename or delete.
It was only freed when the tenant's whole FSLoader was torn down.
A tenant with ongoing namespace churn would accumulate one dead entry
per distinct path ever seen. That growth was unbounded for the life
of the tenant's manager assignment.

Split the cache into two generations, cur and prev. rules.Manager
calls Load at most once per path per Update pass. Seeing a path
already in cur means a new pass started, so rotate. A prev hit is
promoted back into cur. This keeps a stable file triggering rotation
on later passes. A path that stops being loaded ages out within two
passes, once prev is next overwritten. This bounds the cache at
roughly 2x the live file count, regardless of total distinct paths
ever used.

Known gap: a tenant whose entire namespace set changes to new paths
on every single pass, with no namespace ever stable across two
consecutive passes, never triggers a rotation. This defeats the
bound. Considered acceptable. It requires zero stable namespaces
ever, not just occasional renames.
@karlskewes

Copy link
Copy Markdown
Contributor Author

@cursor review

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

✅ Bugbot reviewed your changes and found no new issues!

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit bacb53a. Configure here.

@periklis

periklis commented Sep 4, 2026

Copy link
Copy Markdown
Contributor

@cursor review

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

✅ Bugbot reviewed your changes and found no new issues!

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit bacb53a. Configure here.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants