Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Runtime override of tenant-specific active series custom trackers #1188

Merged
merged 98 commits into from
Apr 5, 2022
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
Show all changes
98 commits
Select commit Hold shift + click to select a range
9139200
Initial implementation
gubjanos Feb 11, 2022
ceaecba
fixing test
gubjanos Feb 11, 2022
31f697b
More straightworward logic
gubjanos Feb 15, 2022
6318a97
Some tests
gubjanos Feb 15, 2022
2be8c3b
Clearing out tests
gubjanos Feb 15, 2022
6386997
Removing unused code
gubjanos Feb 15, 2022
f4dd1d5
Smaller fixes
gubjanos Feb 15, 2022
34d4953
Fixes based on oleg's review
gubjanos Feb 15, 2022
46d96bf
Simplifying diff logic by comparing current and present logic every t…
gubjanos Feb 15, 2022
c729776
Making serialization fail if matchers are not parseable
gubjanos Feb 16, 2022
c5d38a4
Proper checking of series matcher equality
gubjanos Feb 16, 2022
2821436
Changing slice allocation based on oleg's suggestion
gubjanos Feb 16, 2022
4a39127
'interning' default matchers to avoid memory leak
gubjanos Feb 16, 2022
a65aaf1
Support flag based config if no runtime config provided
gubjanos Feb 16, 2022
4fd52f1
Adding test cases for backward compatibility and overwrite check
gubjanos Feb 16, 2022
d987078
Cleaning up
gubjanos Feb 16, 2022
cd7bc5e
Early-exit when pointers reference the same object
gubjanos Feb 16, 2022
96aca48
Changing equality check to string concatenation based as it is much f…
gubjanos Feb 16, 2022
b552d66
Matcher configuration fix + adjusting test
gubjanos Feb 17, 2022
bb4e8b1
Adding active_series_custom_tracker tests
gubjanos Feb 17, 2022
fa925be
Adding active_series_test
gubjanos Feb 17, 2022
c651fca
Fixes based on oleg's suggestions
gubjanos Feb 17, 2022
28b4ff0
fixup
gubjanos Feb 17, 2022
e2250a9
fixup
gubjanos Feb 18, 2022
378c86e
Incorporating oleg's suggestions
gubjanos Feb 18, 2022
858863a
Adding more tests
gubjanos Feb 21, 2022
66e6ea1
cleanup
gubjanos Feb 21, 2022
1fcd42c
Adding testcase for empty default overwrite and fixing this corner case
gubjanos Feb 21, 2022
36cb4a4
Merge branch 'main' into runtime-matchers
gubjanos Feb 21, 2022
3dd657e
Test fixes
gubjanos Feb 21, 2022
5209623
Test fixes, lint fixes
gubjanos Feb 22, 2022
5da5bce
Adding more testcases
gubjanos Feb 22, 2022
203d66e
Changing assert.NoError to require.NoError when immediate halting is …
gubjanos Feb 22, 2022
60f67ce
lint fix and merge resolve fix
gubjanos Feb 22, 2022
40ea210
Reintroducing ActiveSeriesCustomTrackerConfig because ingester config…
gubjanos Feb 22, 2022
c8ee2c9
Adding more context to asm.key comment
gubjanos Feb 22, 2022
8cde596
Changing back flag based description as it allows multiple flags
gubjanos Feb 22, 2022
47c7a0c
changing back to exact wording
gubjanos Feb 22, 2022
9e1ef8e
fixup
gubjanos Feb 22, 2022
bbf54a5
Using config type instead of map[string]string in tests, cleanup
gubjanos Feb 22, 2022
42870e5
Adding test case for metric cleanup
gubjanos Feb 22, 2022
79304c5
Changing ActiveSeriesCustomTrackerConfig to contain []labelMatchers
gubjanos Feb 23, 2022
c076c04
Adding assumption to deserialization test, and adding comment to Stri…
gubjanos Feb 23, 2022
20cb06f
Trying to speed up comparison
gubjanos Feb 23, 2022
7d0792c
Make custom trackers be unmarshaled as a map
colega Feb 23, 2022
2bc4da1
Refactor config parsing, make string key static
colega Feb 23, 2022
23e84eb
No need to store asm.key since config.string is static now
colega Feb 23, 2022
ed1bdfa
Make config.String() be a valid flag value.
colega Feb 23, 2022
b490ca8
Move config to same file as overrides, and rename file.
colega Feb 23, 2022
712cbbe
Updating help template
gubjanos Feb 23, 2022
ef3b51b
Creating ActiveSeriesMatchers only at replace time
gubjanos Feb 23, 2022
d324631
Revert "Creating ActiveSeriesMatchers only at replace time"
gubjanos Feb 23, 2022
07ae65c
Creating ActiveSeriesMatchers only at replace time
gubjanos Feb 24, 2022
78e325a
Update tools/doc-generator/parser.go
gubjanos Feb 24, 2022
80b593b
Changes based on oleg's comments
gubjanos Feb 24, 2022
30ae4f6
Replacing matchings for active series and including test case for this
gubjanos Feb 25, 2022
dc4b3e3
Rework and implementing oleg and pstibrany's suggestions
gubjanos Feb 28, 2022
0132477
Moving active series to separate package
gubjanos Mar 1, 2022
6c332a9
Moving tenant specific configuration to limits, updating tests
gubjanos Mar 1, 2022
9820074
Lint fixes, file renaming
gubjanos Mar 2, 2022
843ee36
Merge branch 'main' into runtime-matchers
gubjanos Mar 2, 2022
3035b4f
Use plain struct for config, not pointer
colega Mar 2, 2022
44f9fdc
CustomTrackersConfig annotation fix
gubjanos Mar 3, 2022
bfe3f6d
Adding documentation on limits
gubjanos Mar 3, 2022
e289c56
Moving active serreis flag to limits, removing runtime default, fixin…
gubjanos Mar 3, 2022
62fd2ef
Adding custom trackers loading metrics and tests for it
gubjanos Mar 4, 2022
23a8996
Adjustments based on Oleg's comments, and introducing single source of
gubjanos Mar 16, 2022
d247dec
Adding back ActiveSeriesCustomTrackersConfig to ingester for backward…
gubjanos Mar 16, 2022
3f942cd
Merge branch 'main' into runtime-matchers
gubjanos Mar 16, 2022
e551d87
lintfix
gubjanos Mar 16, 2022
21e7671
Test fix
gubjanos Mar 16, 2022
34ac0a5
Rework based on Oleg's suggestions
gubjanos Mar 17, 2022
f7136fb
Update pkg/ingester/activeseries/active_series_test.go
gubjanos Mar 17, 2022
7ca8b1e
Update pkg/ingester/activeseries/active_series_test.go
gubjanos Mar 17, 2022
e14d4db
Removing accidentally added file
gubjanos Mar 17, 2022
69054d3
Update pkg/ingester/activeseries/active_series.go
gubjanos Mar 18, 2022
47326c0
Reworking active series trackers to get time from external calls
gubjanos Mar 22, 2022
fd15e6a
Addressing review on comments and minor cleanup
gubjanos Mar 24, 2022
9e0a0e2
Changing active series deprecation override location and adding unit …
gubjanos Mar 29, 2022
5d12046
Adding TODO to remove module unit tests with Mimir 2.2 and adding hig…
gubjanos Mar 29, 2022
f24c6d2
Removing accidentally commited file (again...)
gubjanos Mar 29, 2022
9d6aeb7
Merge branch 'main' into runtime-matchers
gubjanos Mar 29, 2022
df0bb8d
Removing unused method clear.
gubjanos Mar 29, 2022
c0149a7
Updating config-descriptor
gubjanos Mar 29, 2022
583d9da
WIP Fixing data race
gubjanos Mar 30, 2022
273179a
Fixing mimir_test.go
gubjanos Mar 30, 2022
c5311bb
Referencing issue in test to explain why sleep is needed
gubjanos Mar 30, 2022
a8a230f
Lint fix
gubjanos Mar 30, 2022
737a08b
Adding entry to CHANGELOG.md
gubjanos Mar 31, 2022
444d0a9
Fixup for the changelog
gubjanos Mar 31, 2022
5fb103f
More precise wording for the changelog
gubjanos Mar 31, 2022
5aed13d
Merge branch 'main' into runtime-matchers
gubjanos Mar 31, 2022
b04a320
Comment fix based on Oleg's suggestion
gubjanos Mar 31, 2022
62e3b90
Merge branch 'main' into runtime-matchers
gubjanos Apr 1, 2022
28e3b2a
Lintfix after online editor conflict resolve
gubjanos Apr 1, 2022
9f9487c
Merge branch 'main' into runtime-matchers
gubjanos Apr 4, 2022
a043d12
Modifications based on pstibrany's comments
gubjanos Apr 4, 2022
48bb8ea
Fixing reference-help
gubjanos Apr 4, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 0 additions & 2 deletions cmd/mimir/help-all.txt.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -909,8 +909,6 @@ Usage of ./cmd/mimir/mimir:
Expected number of samples per timeseries, used for preallocations. (default 10)
-ingester-client.expected-timeseries int
Expected number of timeseries per request, used for preallocations. (default 100)
-ingester.active-series-custom-trackers value
Additional active series metrics, matching the provided matchers. Matchers should be in form <name>:<matcher>, like 'foobar:{foo="bar"}'. Multiple matchers can be provided either providing the flag multiple times or providing multiple semicolon-separated values to a single flag.
-ingester.active-series-metrics-enabled
Enable tracking of active series and export them as metrics. (default true)
-ingester.active-series-metrics-idle-timeout duration
Expand Down
2 changes: 0 additions & 2 deletions cmd/mimir/help.txt.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -371,8 +371,6 @@ Usage of ./cmd/mimir/mimir:
Expected number of samples per timeseries, used for preallocations. (default 10)
-ingester-client.expected-timeseries int
Expected number of timeseries per request, used for preallocations. (default 100)
-ingester.active-series-custom-trackers value
Additional active series metrics, matching the provided matchers. Matchers should be in form <name>:<matcher>, like 'foobar:{foo="bar"}'. Multiple matchers can be provided either providing the flag multiple times or providing multiple semicolon-separated values to a single flag.
-ingester.active-series-metrics-enabled
Enable tracking of active series and export them as metrics. (default true)
-ingester.availability-zone string
Expand Down
19 changes: 17 additions & 2 deletions pkg/ingester/active_series.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,9 @@ const (

// ActiveSeries is keeping track of recently active series for a single tenant.
type ActiveSeries struct {
asm *ActiveSeriesMatchers
stripes [numActiveSeriesStripes]activeSeriesStripe
asm *ActiveSeriesMatchers
gubjanos marked this conversation as resolved.
Show resolved Hide resolved
stripes [numActiveSeriesStripes]activeSeriesStripe
lastUpdate time.Time
gubjanos marked this conversation as resolved.
Show resolved Hide resolved
}

// activeSeriesStripe holds a subset of the series timestamps for a single tenant.
Expand Down Expand Up @@ -63,6 +64,20 @@ func NewActiveSeries(asm *ActiveSeriesMatchers) *ActiveSeries {
return c
}

func (c *ActiveSeries) ReloadSeriesMatchers(asm *ActiveSeriesMatchers) {
c.asm = asm
for i := 0; i < numActiveSeriesStripes; i++ {
c.stripes[i].mu.Lock()
defer c.stripes[i].mu.Unlock()
}
gubjanos marked this conversation as resolved.
Show resolved Hide resolved

for i := 0; i < numActiveSeriesStripes; i++ {
c.stripes[i].asm = asm
c.stripes[i].activeMatching = makeIntSliceIfNotEmpty(len(asm.MatcherNames()))
gubjanos marked this conversation as resolved.
Show resolved Hide resolved
}
c.lastUpdate = time.Now()
}

// Updates series timestamp to 'now'. Function is called to make a copy of labels if entry doesn't exist yet.
func (c *ActiveSeries) UpdateSeries(series labels.Labels, now time.Time, labelsCopy func(labels.Labels) labels.Labels) {
fp := fingerprint(series)
Expand Down
147 changes: 110 additions & 37 deletions pkg/ingester/ingester.go
Original file line number Diff line number Diff line change
Expand Up @@ -121,10 +121,10 @@ type Config struct {

RateUpdatePeriod time.Duration `yaml:"rate_update_period" category:"advanced"`

ActiveSeriesMetricsEnabled bool `yaml:"active_series_metrics_enabled"`
ActiveSeriesMetricsUpdatePeriod time.Duration `yaml:"active_series_metrics_update_period" category:"advanced"`
ActiveSeriesMetricsIdleTimeout time.Duration `yaml:"active_series_metrics_idle_timeout" category:"advanced"`
ActiveSeriesCustomTrackers ActiveSeriesCustomTrackersConfig `yaml:"active_series_custom_trackers" doc:"description=Additional custom trackers for active metrics. If there are active series matching a provided matcher (map value), the count will be exposed in the custom trackers metric labeled using the tracker name (map key). Zero valued counts are not exposed (and removed when they go back to zero)."`
ActiveSeriesMetricsEnabled bool `yaml:"active_series_metrics_enabled"`
ActiveSeriesMetricsUpdatePeriod time.Duration `yaml:"active_series_metrics_update_period" category:"advanced"`
ActiveSeriesMetricsIdleTimeout time.Duration `yaml:"active_series_metrics_idle_timeout" category:"advanced"`
RuntimeMatchersConfigFn func() *RuntimeMatchersConfig `yaml:"-"`

ExemplarsUpdatePeriod time.Duration `yaml:"exemplars_update_period" category:"experimental"`

Expand Down Expand Up @@ -153,7 +153,6 @@ func (cfg *Config) RegisterFlags(f *flag.FlagSet) {
f.BoolVar(&cfg.ActiveSeriesMetricsEnabled, "ingester.active-series-metrics-enabled", true, "Enable tracking of active series and export them as metrics.")
f.DurationVar(&cfg.ActiveSeriesMetricsUpdatePeriod, "ingester.active-series-metrics-update-period", 1*time.Minute, "How often to update active series metrics.")
f.DurationVar(&cfg.ActiveSeriesMetricsIdleTimeout, "ingester.active-series-metrics-idle-timeout", 10*time.Minute, "After what time a series is considered to be inactive.")
f.Var(&cfg.ActiveSeriesCustomTrackers, "ingester.active-series-custom-trackers", "Additional active series metrics, matching the provided matchers. Matchers should be in form <name>:<matcher>, like 'foobar:{foo=\"bar\"}'. Multiple matchers can be provided either providing the flag multiple times or providing multiple semicolon-separated values to a single flag.")

f.BoolVar(&cfg.StreamChunksWhenUsingBlocks, "ingester.stream-chunks-when-using-blocks", true, "Stream chunks from ingesters to queriers.")
f.DurationVar(&cfg.ExemplarsUpdatePeriod, "ingester.exemplars-update-period", 15*time.Second, "Period with which to update per-user max exemplars.")
Expand Down Expand Up @@ -198,8 +197,6 @@ type Ingester struct {
metrics *ingesterMetrics
logger log.Logger

activeSeriesMatcher *ActiveSeriesMatchers

lifecycler *ring.Lifecycler
limits *validation.Overrides
limiter *Limiter
Expand Down Expand Up @@ -237,6 +234,9 @@ type Ingester struct {
// Rate of pushed samples. Used to limit global samples push rate.
ingestionRate *util_math.EwmaRate
inflightPushRequests atomic.Int64

// Last seen activeSeriesConfiguration. Needed for diff based updating.
runtimeMatchersConfig *RuntimeMatchersConfig
}

func newIngester(cfg Config, limits *validation.Overrides, registerer prometheus.Registerer, logger log.Logger) (*Ingester, error) {
Expand All @@ -250,18 +250,18 @@ func newIngester(cfg Config, limits *validation.Overrides, registerer prometheus
}

return &Ingester{
cfg: cfg,
limits: limits,
logger: logger,
activeSeriesMatcher: &ActiveSeriesMatchers{},

tsdbs: make(map[string]*userTSDB),
usersMetadata: make(map[string]*userMetricsMetadata),
bucket: bucketClient,
tsdbMetrics: newTSDBMetrics(registerer),
forceCompactTrigger: make(chan requestWithUsersAndCallback),
shipTrigger: make(chan requestWithUsersAndCallback),
seriesHashCache: hashcache.NewSeriesHashCache(cfg.BlocksStorageConfig.TSDB.SeriesHashCacheMaxBytes),
cfg: cfg,
limits: limits,
logger: logger,

tsdbs: make(map[string]*userTSDB),
usersMetadata: make(map[string]*userMetricsMetadata),
bucket: bucketClient,
tsdbMetrics: newTSDBMetrics(registerer),
forceCompactTrigger: make(chan requestWithUsersAndCallback),
shipTrigger: make(chan requestWithUsersAndCallback),
seriesHashCache: hashcache.NewSeriesHashCache(cfg.BlocksStorageConfig.TSDB.SeriesHashCacheMaxBytes),
runtimeMatchersConfig: &RuntimeMatchersConfig{},
}, nil
}

Expand All @@ -279,13 +279,7 @@ func New(cfg Config, clientConfig client.Config, limits *validation.Overrides, r
}
i.clientConfig = clientConfig
i.ingestionRate = util_math.NewEWMARate(0.2, instanceIngestionRateTickInterval)
i.metrics = newIngesterMetrics(registerer, cfg.ActiveSeriesMetricsEnabled, i.activeSeriesMatcher.MatcherNames(), i.getInstanceLimits, i.ingestionRate, &i.inflightPushRequests)

asm, err := NewActiveSeriesMatchers(cfg.ActiveSeriesCustomTrackers)
if err != nil {
return nil, err
}
i.activeSeriesMatcher = asm
i.metrics = newIngesterMetrics(registerer, cfg.ActiveSeriesMetricsEnabled, i.getInstanceLimits, i.ingestionRate, &i.inflightPushRequests)

// Replace specific metrics which we can't directly track but we need to read
// them from the underlying system (ie. TSDB).
Expand Down Expand Up @@ -333,7 +327,7 @@ func NewForFlusher(cfg Config, limits *validation.Overrides, registerer promethe
if err != nil {
return nil, err
}
i.metrics = newIngesterMetrics(registerer, false, nil, i.getInstanceLimits, nil, &i.inflightPushRequests)
i.metrics = newIngesterMetrics(registerer, false, i.getInstanceLimits, nil, &i.inflightPushRequests)

i.shipperIngesterID = "flusher"

Expand Down Expand Up @@ -465,6 +459,7 @@ func (i *Ingester) updateLoop(ctx context.Context) error {
i.applyExemplarsSettings()

case <-activeSeriesTickerChan:
i.reloadConfig(time.Now())
i.updateActiveSeries(time.Now())

case <-ctx.Done():
Expand All @@ -475,6 +470,66 @@ func (i *Ingester) updateLoop(ctx context.Context) error {
}
}

func (i *Ingester) reloadConfig(now time.Time) {
newConfig := i.getRuntimeMatchersConfig()
defaultMatchersEquals := i.runtimeMatchersConfig.DefaultMatchers.String() == newConfig.DefaultMatchers.String()

// it is crucial to only reload matchers which have been changed, as this function runs even if there is no change in config
for _, userID := range i.getTSDBUsers() {
userDB := i.getTSDB(userID)
if userDB == nil {
continue
}
oldValue, oldOk := i.runtimeMatchersConfig.TenantSpecificMatchers[userID]
newValue, newOk := newConfig.TenantSpecificMatchers[userID]
var replaceValue ActiveSeriesCustomTrackersConfig = nil
if !newOk && !oldOk && !defaultMatchersEquals {
// default changed and no overwrite
replaceValue = newConfig.DefaultMatchers
} else if newOk && !oldOk {
// tenant specific added
replaceValue = newValue
} else if !newOk && oldOk {
// tenant specific removed
replaceValue = newConfig.DefaultMatchers
} else if oldValue.String() != newValue.String() {
// tenant specific changed
replaceValue = newValue
}
gubjanos marked this conversation as resolved.
Show resolved Hide resolved
if replaceValue != nil {
err := i.ReplaceMatchers(replaceValue, userDB)
if err != nil {
level.Error(i.logger).Log("msg", "failed to update config", "user", userID, "err", err)
}
}
}

i.runtimeMatchersConfig = newConfig
}

func (i *Ingester) ReplaceMatchers(config ActiveSeriesCustomTrackersConfig, userDB *userTSDB) error {
newMatcher, err := NewActiveSeriesMatchers(config)
if err != nil {
return err
}
i.metrics.deletePerUserCustomTrackerMetrics(userDB.userID, userDB.activeSeries.asm.names)
userDB.activeSeries.ReloadSeriesMatchers(newMatcher)
return nil
}

func (i *Ingester) getRuntimeMatchersConfig() *RuntimeMatchersConfig {
if i.cfg.RuntimeMatchersConfigFn == nil {
return &RuntimeMatchersConfig{}
}

r := i.cfg.RuntimeMatchersConfigFn()
if r == nil {
return &RuntimeMatchersConfig{}
}

return r
}

func (i *Ingester) updateActiveSeries(now time.Time) {
purgeTime := now.Add(-i.cfg.ActiveSeriesMetricsIdleTimeout)
for _, userID := range i.getTSDBUsers() {
Expand All @@ -490,14 +545,19 @@ func (i *Ingester) updateActiveSeries(now time.Time) {
} else {
i.metrics.activeSeriesPerUser.DeleteLabelValues(userID)
}
for idx, name := range i.activeSeriesMatcher.MatcherNames() {
// We only set the metrics for matchers that actually exist, to avoid increasing cardinality with zero valued metrics.
if activeMatching[idx] > 0 {
i.metrics.activeSeriesCustomTrackersPerUser.WithLabelValues(userID, name).Set(float64(activeMatching[idx]))
} else {
i.metrics.activeSeriesCustomTrackersPerUser.DeleteLabelValues(userID, name)
if userDB.activeSeries.lastUpdate.IsZero() || userDB.activeSeries.lastUpdate.After(now.Add(i.cfg.ActiveSeriesMetricsIdleTimeout)) {
gubjanos marked this conversation as resolved.
Show resolved Hide resolved
// Do not publish metrics until the new matcher setup had time to catch up
// LastUpdate is Zero when it never get updated
for idx, name := range userDB.activeSeries.asm.names {
// We only set the metrics for matchers that actually exist, to avoid increasing cardinality with zero valued metrics.
if activeMatching[idx] > 0 {
i.metrics.activeSeriesCustomTrackersPerUser.WithLabelValues(userID, name).Set(float64(activeMatching[idx]))
} else {
i.metrics.activeSeriesCustomTrackersPerUser.DeleteLabelValues(userID, name)
}
}
}

}
}

Expand Down Expand Up @@ -1444,10 +1504,24 @@ func (i *Ingester) createTSDB(userID string) (*userTSDB, error) {
userLogger := util_log.WithUserID(userID, i.logger)

blockRanges := i.cfg.BlocksStorageConfig.TSDB.BlockRanges.ToMilliseconds()
var activeSeriesMathers *ActiveSeriesMatchers
gubjanos marked this conversation as resolved.
Show resolved Hide resolved
matchersCfg := i.getRuntimeMatchersConfig()
val, ok := matchersCfg.TenantSpecificMatchers[userID]
var matchers ActiveSeriesCustomTrackersConfig = nil
if ok {
matchers = val
} else {
matchers = matchersCfg.DefaultMatchers
}
gubjanos marked this conversation as resolved.
Show resolved Hide resolved
activeSeriesMathers, err := NewActiveSeriesMatchers(matchers)
gubjanos marked this conversation as resolved.
Show resolved Hide resolved
if err != nil {
level.Error(i.logger).Log("msg", "failed to apply runtime matchers", "user", userID, "err", err)
activeSeriesMathers = &ActiveSeriesMatchers{}
}
gubjanos marked this conversation as resolved.
Show resolved Hide resolved

userDB := &userTSDB{
userID: userID,
activeSeries: NewActiveSeries(i.activeSeriesMatcher),
activeSeries: NewActiveSeries(activeSeriesMathers),
seriesInMetric: newMetricCounter(i.limiter, i.cfg.getIgnoreSeriesLimitForMetricNamesMap()),
ingestedAPISamples: util_math.NewEWMARate(0.2, i.cfg.RateUpdatePeriod),
ingestedRuleSamples: util_math.NewEWMARate(0.2, i.cfg.RateUpdatePeriod),
Expand Down Expand Up @@ -1570,9 +1644,7 @@ func (i *Ingester) closeAllTSDB() {

i.metrics.memUsers.Dec()
i.metrics.activeSeriesPerUser.DeleteLabelValues(userID)
for _, name := range i.metrics.activeSeriesCustomTrackerNames {
i.metrics.activeSeriesCustomTrackersPerUser.DeleteLabelValues(userID, name)
}
i.metrics.deletePerUserCustomTrackerMetrics(userID, db.activeSeries.asm.names)
}(userDB)
}

Expand Down Expand Up @@ -1969,6 +2041,7 @@ func (i *Ingester) closeAndDeleteUserTSDBIfIdle(userID string) tsdbCloseCheckRes

i.deleteUserMetadata(userID)
i.metrics.deletePerUserMetrics(userID)
i.metrics.deletePerUserCustomTrackerMetrics(userID, userDB.activeSeries.asm.names)

validation.DeletePerUserValidationMetrics(userID, i.logger)

Expand Down