Repository navigation
Conversation
alsoba13
marked this pull request as draft
July 30, 2026 15:06
alsoba13
marked this pull request as ready for review
July 30, 2026 18:47
alsoba13
force-pushed
the
alsoba13/sampling-with-rules
branch
from
July 31, 2026 16:54
9f9d464 to
5853ec3
Compare
aleks-p
force-pushed
the
alsoba13/sampling-with-rules
branch
from
August 5, 2026 13:45
5853ec3 to
aa36284
Compare
alsoba13
force-pushed
the
alsoba13/sampling-with-rules
branch
from
September 10, 2026 18:48
aa36284 to
e1a0af3
Compare
Contributor
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.
There are 2 total unresolved issues (including 1 from previous review).
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Want higher recall? High effort reviews run extra passes and find more bugs. A team admin can switch effort levels in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit e1a0af3. Configure here.
…pping Sampled-out profiles kept via keep-stripped-profiles were reduced to a single label-less total. Make the sampling.Stripper recording-rules aware: stacktraces targeted by a tenant's function-filter rules are kept, trimmed to the targeted frames and to the labels those rules match/group on; everything else still folds into the total. Wires a Ruler (metrics.CachedRemoteRuler / static overrides) into the distributor and the stripper. Structured as Strip = computeExceptions + stripSamples + pruneUnreferencedSymbols + compactStringTable.
Non-function ("total") recording rules match and group on labels promoted
from pprof sample labels, so a single label-less total loses them. Split the
totals per distinct combination of the labels the tenant's total rules
reference, gated per sample by the rules' deferred matchers (a matcher that
also matches "", e.g. !=, keeps its real value so it doesn't wrongly match).
Kept function-targeted samples carry those labels too, since they still count
toward the totals.
Totals are grouped with GroupSamplesByLabels and the group labels are copied
into fresh slices: the helper aliases a source sample's label slice, whose
backing array can hold labels past len() that the pprof split later restores
and indexes against the rebuilt string table (panics otherwise).
Allocate the totalKeep/funcKeep sets once and clear() them per sample instead of building a fresh map (and closure) for every sample.
A sample kept for one function rule still carries a stacktrace that other rules targeting the same function re-match at compaction. So it must keep the labels those rules need — including, for rules it does not match, the matchers that also match "" (e.g. !=): otherwise a dropped label reads as "" downstream and the rule spuriously matches the kept stacktrace, over-counting. Factor the keep-label logic into keepRuleLabels, shared by the function and total paths so the guard can't be applied to one and not the other.
Add a readable reference test exercising the full recording-rules-aware stripping behavior in one profile: function rules (with group_by, base matchers, and deferred sample-label matchers), a scoped-out rule, total rules (group_by and a != matcher), a stack hitting two functions, recursion, and non-deduped kept samples that Normalize merges downstream. Move it and its builder helpers to the top of the file so it reads as the starting point before the narrow edge-case tests.
The distributor's rule-aware stripping must use the same recording rules
the compaction observer uses, or stripping drops the stacktraces the
observer needs. The rules source was configured only under
compaction-worker.metrics-exporter, so the distributor fell back to the
static ruler even when the compaction worker read rules from the
tenant-settings client, silently diverging.
Introduce shared recording-rules.enabled and recording-rules.client-address
flags read by the query-frontend (feature flag), distributor (stripping)
and compaction-worker (metrics generation). The distributor and compaction
worker resolve their ruler from the same source, so they cannot diverge.
The deprecated compaction-worker.metrics-exporter.{enabled,rules-source.
client-address} flags are honored as a fallback, keeping existing configs
working. remote-write-address stays compaction-worker-only and mandatory
when enabled.
… failures The distributor now reads recording rules on the push path, so a slow or failing rules service must not stall pushes: only the first load per tenant blocks, later expiries refresh in the background, and failed fetches are not retried until a retry interval has passed. Fetches are bounded by a timeout.
When a tenant's rules can't be loaded, sampled-out profiles are still stripped to plain totals so writes are never blocked, but the loss is now visible through a per-tenant counter and a field on the push log line. Ruler.RecordingRules returns an error when a tenant's rules have never been loaded, which the stripper reports to the distributor.
alsoba13
force-pushed
the
alsoba13/sampling-with-rules
branch
from
October 15, 2026 17:02
e1a0af3 to
3b9e2a9
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.

What
Follows #5441. Makes the
ProfileStripperrecording-rules aware: when a profile is sampled out but kept (keep-stripped-profiles), the parts a tenant's recording rules need survive stripping so the rules keep producing correct metrics at compaction, instead of collapsing to a single label-less total.Two kinds of rule are handled ("exceptions" to the strip):
stacktrace_filter.function_name): the stacktraces reaching the targeted function are kept, trimmed to the frames that resolve to it, carrying the labels the rule matches/groups on.ProfileStrippergains aRulerdependency (declared in the sampling package,RecordingRules(tenant) ([]*RecordingRule, error); satisfied structurally bymetrics.CachedRemoteRuler/ the static-overrides ruler).initDistributorbuilds it and passes it todistributor.New, andRecordingRulesClientis added to the Distributor module deps.distributor.gostill just callsd.stripper.Strip(...).StripToTotals→Strip, nowStrip(tenantID, seriesLabels, p)— it keeps rule-targeted stacktraces too, so "ToTotals" was only half-true.Structure
computeExceptionsmatches the rules once:__profile_type__is resolved from the pprof's sample types, base-label matchers are checked now, and matchers on labels not yet present (promoted sample labels) are deferred to per-sample checks.""(e.g.!=) keeps its label with the real value, so a dropped label doesn't read as""downstream and wrongly match. This guard is shared by the function and total paths (keepRuleLabels) so they can't drift.Configuration
The distributor's rule-aware stripping must use the same rules the compaction observer uses, or stripping drops the stacktraces the observer later needs. The rules source used to live only under
compaction-worker.metrics-exporter, so the distributor silently fell back to the static-overrides ruler even when the compaction worker read rules from the remote client — diverging.This introduces a shared, single source of truth:
recording-rules.enabledandrecording-rules.client-address(empty ⇒ static rules from per-tenant overrides; set ⇒ dynamic client). Read by the query-frontend (feature flag), the distributor (rule-aware stripping) and the compaction-worker (metrics generation).compaction-worker.metrics-exporter.{enabled,rules-source.client-address}flags are deprecated but honored as a fallback (RecordingRulesConfig.ReconcileWithExporter: shared flags win, effective values written back), so existing configs keep working unchanged. A warning is logged at startup when either deprecated flag is set.remote-write-addressstays compaction-worker-only and mandatory when enabled.recording-rules.enabled; otherwise it's a plain strip-to-totals. Whether stripping happens at all remains the per-tenantkeep-stripped-profilessetting.Rules loading and availability
metrics.Ruler.RecordingRulesnow returns([]*RecordingRule, error). The error means a tenant's rules have never been loaded successfully;CachedRemoteRulerkeeps serving the last good rules (stale) when a refresh fails, so it only errors on a cold failure. The static ruler never errors.Since the distributor now reads rules on the push path,
CachedRemoteRulernever lets a slow rules service stall pushes:Writes take priority over rule data: if the rules are unavailable, the profile is still stripped to plain totals and pushed, and
Stripreports it. The distributor counts these inpyroscope_distributor_stripped_profiles_without_rules_total{tenant}and addsrecording_rules_unavailable=trueto the push log line. Those profiles lose the stacktraces the rules would have needed.The compaction worker shares this ruler, so it also gets stale-while-revalidate and the 10s failure backoff; the observer treats a rules error as no rules, as before.
When
recording-rules.enabledis set, distributors now also load tenant rules fromrecording-rules.client-address, so the settings service sees their requests as well as the compaction workers'.Label aliasing
Sample labels are reduced in place, which can leave dropped labels live in a slice's spare capacity — the pprof split's
restoreRemovedLabelsreadslabels[len:cap]and resurrects them with their pre-compaction string indices, indexing the rebuilt (smaller) string table out of range (the panic #5409 originally worked around by collapsing to a single sample). Fixed in both paths:pprof.GroupSamplesByLabels, group labels copied into fresh slices (per thefix/stripped-profile-label-aliasingapproach).retainLabelsclamps the returned slice's capacity to its length, so nothing lingers pastlen().Trade-off
Totals are grouped in-strip but not merged with the function-kept samples; identical trimmed stacks are deduped by the downstream
Normalize. SokeptSize(measured right after strip for the discarded-bytes accounting) is pre-dedup and slightly over-reports what's kept / under-reports discarded. Stored data is unaffected.Tests
sampling/profile_stripper_test.go: a readable end-to-end reference (TestProfileStripper_Strip_Overview) exercising, in one profile, function rules with group-by / base matchers / deferred sample-label matchers, a scoped-out rule, total rules with group-by and!=, a stack hitting two functions, recursion, and non-deduped kept samples — with an in/out diagram. Plus focused cases: function-targeted keep + symbol prune + re-intern; nil ruler == plain strip; total split by group-by; matcher gating;!=keeps its label on both total and function paths; a kept function sample also carries total-rule labels;TestProfileStripper_Strip_NoAliasedLabelCapacity(no sample retains aliased spare capacity), andTestProfileStripper_Strip_RulesUnavailable(ruler error → plain totals, reported).TestPushSeries_KeepStrippedProfiles_SampleSeries) driving a labeled total throughpushSeries→ split, andTestPushSeries_KeepStrippedProfiles_RulesUnavailable(the counter increments when the ruler errors).metrics/recording_rules_test.go: config resolution — shared-only, deprecated-only fallback, shared-wins-over-deprecated, and static (enabled, no address) — plus the deprecation warning.metrics/ruler_cache_test.go:CachedRemoteRuler— single-flight first load, stale served during refresh, failure backoff, stale kept when a refresh fails, recovery after a failure.