Grafana Mimir provides horizontally scalable, highly available, multi-tenant, long-term storage for Prometheus.
Find a file
Jon Kartago Lamida 8dbb85e739
MQE: Improvement to labels deduplication (#14148)
<!--  Thanks for sending a pull request!  Before submitting:

1. Read our CONTRIBUTING.md guide
2. Rebase your PR if it gets out of sync with main
-->

#### What this PR does


Follow up of
https://github.com/grafana/mimir/pull/13806#discussion_r2660110934

The initial implementation in above PR was not accurate because we
didn't handle hash collision when increasing memory labels consumption
hence we can't accurately balancing reducing memory consumption when
handing series to the query engine. This PR do the following update to
make memory tracking for labels accurate:

* Handle hash collision and only increase memory consumption for unique
labels
* Add MemoryTrackingSeriesSet wrapper to wrap the merged result from
store-gateway and ingester whose results are already deduplicated the
labels via MergeSeriesSet
* Revert responsibility of QueryLimiter to the state before PR #13806,
just to handle series limit
* Labels deduplication and increasing memory consumption for labels are
handled by SeriesDeduplicator.

~For MQE, SeriesDeduplicator is initialised in Selector and passed into
context, hence deduplication will be effective per-selector. As an
example for query `foo` + `foo`, there will two SeriesDeduplicator for
each binary operand that will increase memory consumption for unique
labels for each binary operand. Once the series are fetched from
queriers (ingester and store gateway), we pass it to a wrapper
MemoryTrackingSeriesSet which will deccrease memory consumption for
already unique labels for the given selector.~

~For tenant federation case, we override the SeriesDeduplicator so that
it will unique for each tenant. As an example if there are two federated
tenants, there will be two SeriesDeduplicator that will do increase in
which each queriers result will be wrapped by different
MemoryTrackingSeriesSet for memory decrease.~

~Last we also add Deduplicator, together with
UnlimitedMemoryTrackingMiddleware to track memory consumption for Labels
in the case of `/series` and `/read` endpoint call. Without adding
Deduplicator there, we will get negative memory consumption when
iterating the series result from those 2 endpoints.~

`querier.MemoryTrackingQueryable` handles all SeriesLabelsDeduplicator
initialisation.

Some tests need to be updated by passing UnlimitedMemoryTracker to
context or by adding custom queryable to simulate when we have to
increase/decrease labels memory consumption.

## Memory Benchmark

We can see the more duplicates the B/op is smaller. No duplicates 21
B/op, 50% duplicates 10B/op and 90% duplicates 1 B/op only. Which means
less allocations the more duplicate labels in the results because we
reused the labels.

<details>

```
go test -bench='BenchmarkSeriesDeduplicator_Deduplicate_WithCallerDedup_(NoDuplicates|50pct|90pct)$' \                                                                                                                                                                                                                                   -benchmem \
    -benchtime=5000x \
    -count=5 \
    -run=^$ \
    ./pkg/util/limiter/
goos: darwin
goarch: arm64
pkg: github.com/grafana/mimir/pkg/util/limiter
cpu: Apple M4 Pro
BenchmarkSeriesDeduplicator_Deduplicate_WithCallerDedup_NoDuplicates-14    	    5000	     19866 ns/op	      21 B/op	       0 allocs/op
BenchmarkSeriesDeduplicator_Deduplicate_WithCallerDedup_NoDuplicates-14    	    5000	     19553 ns/op	      21 B/op	       0 allocs/op
BenchmarkSeriesDeduplicator_Deduplicate_WithCallerDedup_NoDuplicates-14    	    5000	     19202 ns/op	      21 B/op	       0 allocs/op
BenchmarkSeriesDeduplicator_Deduplicate_WithCallerDedup_NoDuplicates-14    	    5000	     17423 ns/op	      21 B/op	       0 allocs/op
BenchmarkSeriesDeduplicator_Deduplicate_WithCallerDedup_NoDuplicates-14    	    5000	     17633 ns/op	      21 B/op	       0 allocs/op
BenchmarkSeriesDeduplicator_Deduplicate_WithCallerDedup_90pct-14           	    5000	     18806 ns/op	       1 B/op	       0 allocs/op
BenchmarkSeriesDeduplicator_Deduplicate_WithCallerDedup_90pct-14           	    5000	     19545 ns/op	       1 B/op	       0 allocs/op
BenchmarkSeriesDeduplicator_Deduplicate_WithCallerDedup_90pct-14           	    5000	     18882 ns/op	       1 B/op	       0 allocs/op
BenchmarkSeriesDeduplicator_Deduplicate_WithCallerDedup_90pct-14           	    5000	     20637 ns/op	       1 B/op	       0 allocs/op
BenchmarkSeriesDeduplicator_Deduplicate_WithCallerDedup_90pct-14           	    5000	     21143 ns/op	       1 B/op	       0 allocs/op
BenchmarkSeriesDeduplicator_Deduplicate_WithCallerDedup_50pct-14           	    5000	     17031 ns/op	      10 B/op	       0 allocs/op
BenchmarkSeriesDeduplicator_Deduplicate_WithCallerDedup_50pct-14           	    5000	     17139 ns/op	      10 B/op	       0 allocs/op
BenchmarkSeriesDeduplicator_Deduplicate_WithCallerDedup_50pct-14           	    5000	     16878 ns/op	      10 B/op	       0 allocs/op
BenchmarkSeriesDeduplicator_Deduplicate_WithCallerDedup_50pct-14           	    5000	     20526 ns/op	      10 B/op	       0 allocs/op
BenchmarkSeriesDeduplicator_Deduplicate_WithCallerDedup_50pct-14           	    5000	     17965 ns/op	      10 B/op	       0 allocs/op
```
</details>

#### Which issue(s) this PR fixes or relates to

Fixes https://github.com/grafana/mimir-squad/issues/3280

#### Checklist

- [x] Tests updated.
- [ ] Documentation added.
- [ ] `CHANGELOG.md` updated - the order of entries should be
`[CHANGE]`, `[FEATURE]`, `[ENHANCEMENT]`, `[BUGFIX]`. If changelog entry
is not needed, please add the `changelog-not-needed` label to the PR.
- [ ]
[`about-versioning.md`](https://github.com/grafana/mimir/blob/main/docs/sources/mimir/configure/about-versioning.md)
updated with experimental features.






<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Medium Risk**
> Touches core query limiting and query streaming paths
(ingester/store-gateway/distributor) and changes how label memory is
accounted for; behavior should be equivalent but regressions could
affect query rejection limits or memory tracking accuracy.
> 
> **Overview**
> Improves per-query labels memory accounting by introducing
`SeriesLabelsDeduplicator` (context-scoped, hash-collision aware) and
moving labels dedup + label-memory increases out of `QueryLimiter`.
> 
> Query paths that build/stream series labels (distributor
`MetricsForLabelMatchers`, ingester streaming, and store-gateway
streaming) now deduplicate labels via the new deduplicator, then apply
`QueryLimiter.AddSeries()` strictly for max-series enforcement. A new
`querier.MemoryTrackingQueryable` wraps MQE queryables so `Select()`
installs the deduplicator and wraps results in
`MemoryTrackingSeriesSet`; several call sites drop older
`MemoryTrackingSeriesSet` wrappers to avoid double-wrapping, and ruler’s
dedicated “unlimited memory tracker queryable” is removed in favor of
injecting the memory tracker via the manager context.
> 
> Tests/benchmarks are updated to ensure contexts include the new
deduplicator (and where needed a memory tracker), and `QueryLimiter`
internals/tests are simplified after removing
label-return/memory-tracking responsibilities.
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
6e78c009e2. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
2026-02-13 13:41:09 +08:00
.config/go Remove Go telemetry from git repository, ignore it. (#9122) 2024-08-28 07:50:45 +02:00
.github Vendor changes into backend-enterprise on every merge to main (#14322) 2026-02-13 08:11:38 +11:00
cmd Update dskit to latest (#14347) 2026-02-12 13:52:33 -08:00
development dev: allow mimir-microservices-mode to work with public Mimir images (#14291) 2026-02-10 13:00:08 -05:00
docs Update dskit to latest (#14347) 2026-02-12 13:52:33 -08:00
images Update logo image to be less jagged (#1484) 2022-03-15 14:05:49 +00:00
integration storage/ingest: Add support for Kafka SCRAM auth (#14307) 2026-02-11 16:08:02 +01:00
mimir-build-image Automatically update tools in build image and development Dockerfiles (#14321) 2026-02-12 14:53:03 +11:00
operations Update dskit to latest (#14347) 2026-02-12 13:52:33 -08:00
packaging packaging: Switch to bitnamilegacy jsonnet (#14313) 2026-02-10 17:09:47 -06:00
pkg MQE: Improvement to labels deduplication (#14148) 2026-02-13 13:41:09 +08:00
tools Release: Add prerequisite check that the version string is well-formed (#14311) 2026-02-10 12:15:20 -06:00
vendor Update dskit to latest (#14347) 2026-02-12 13:52:33 -08:00
.gitattributes Exclude vendor/ from PR size (#2229) 2020-03-09 12:00:15 +01:00
.gitconfig Rename prometheus-private to mimir-prometheus (#843) 2022-01-21 15:55:07 +00:00
.gitignore Remove standalone mimir-continuous-test binary and image (#13097) 2025-10-22 10:09:56 -04:00
.golangci.yml mimirpb: copy custom values and ensure via exhaustruct (#13849) 2025-12-18 15:17:42 +01:00
.lintignore Remove old website (#1135) 2022-02-09 15:44:34 +01:00
.prettierignore Move the mimir-distributed helm chart into the mimir repository (#1925) 2022-05-30 11:02:02 +02:00
ADOPTERS.md Update ADOPTERS.md (#9620) 2024-10-15 09:31:57 +02:00
AGENTS.md Reconcile agent instructions (#14207) 2026-01-30 17:21:55 +01:00
CHANGELOG.md MQE: Don't apply projections to unsupported aggregations (#14326) 2026-02-12 09:03:43 -05:00
CLAUDE.md Reconcile agent instructions (#14207) 2026-01-30 17:21:55 +01:00
CODE_OF_CONDUCT.md Fix code of conduct (#922) 2022-01-27 15:36:57 +01:00
CODEOWNERS codeowners: kick grafanabot out (#11194) 2025-04-11 07:46:16 -04:00
CONTRIBUTING.md fixes link (#1476) 2022-03-14 17:06:57 +01:00
go.mod Update dskit to latest (#14347) 2026-02-12 13:52:33 -08:00
go.sum Update dskit to latest (#14347) 2026-02-12 13:52:33 -08:00
GOVERNANCE.md Updates governance to reflect special handling for Grafana Cloud Hosted Metrics engineers 2026-01-21 18:13:34 +01:00
LICENSE Apply standard Grafana Labs governance and license (#22) 2021-08-05 14:40:22 +02:00
LICENSING.md Change license for operations folder to Apache2. (#5753) 2023-08-22 12:27:49 +02:00
MAINTAINERS.md Remove dimitarvdimitrov from the maintainers list (#13870) 2025-12-22 14:57:25 +00:00
Makefile Automatically update tools in build image and development Dockerfiles (#14321) 2026-02-12 14:53:03 +11:00
Makefile.local.example Push all images to Docker Hub (#1204) 2022-02-16 16:27:05 +00:00
README.md Fix broken link in readme, and add redirect to docs. (#6115) 2023-09-25 09:51:02 +02:00
RELEASE.md Remove GEM mentions from release documentation (#13255) 2025-10-30 12:43:44 +01:00
renovate.json5 Automatically update tools in build image and development Dockerfiles (#14321) 2026-02-12 14:53:03 +11:00
VERSION Prepare Mimir 3.0.3 release (#14317) 2026-02-11 11:21:09 -06:00

Grafana Mimir

Grafana Mimir logo

Grafana Mimir is an open source software project that provides a scalable long-term storage for Prometheus. Some of the core strengths of Grafana Mimir include:

  • Easy to install and maintain: Grafana Mimirs extensive documentation, tutorials, and deployment tooling make it quick to get started. Using its monolithic mode, you can get Grafana Mimir up and running with just one binary and no additional dependencies. Once deployed, the best-practice dashboards, alerts, and runbooks packaged with Grafana Mimir make it easy to monitor the health of the system.
  • Massive scalability: You can run Grafana Mimir's horizontally-scalable architecture across multiple machines, resulting in the ability to process orders of magnitude more time series than a single Prometheus instance. Internal testing shows that Grafana Mimir handles up to 1 billion active time series.
  • Global view of metrics: Grafana Mimir enables you to run queries that aggregate series from multiple Prometheus instances, giving you a global view of your systems. Its query engine extensively parallelizes query execution, so that even the highest-cardinality queries complete with blazing speed.
  • Cheap, durable metric storage: Grafana Mimir uses object storage for long-term data storage, allowing it to take advantage of this ubiquitous, cost-effective, high-durability technology. It is compatible with multiple object store implementations, including AWS S3, Google Cloud Storage, Azure Blob Storage, OpenStack Swift, as well as any S3-compatible object storage.
  • High availability: Grafana Mimir replicates incoming metrics, ensuring that no data is lost in the event of machine failure. Its horizontally scalable architecture also means that it can be restarted, upgraded, or downgraded with zero downtime, which means no interruptions to metrics ingestion or querying.
  • Natively multi-tenant: Grafana Mimirs multi-tenant architecture enables you to isolate data and queries from independent teams or business units, making it possible for these groups to share the same cluster. Advanced limits and quality-of-service controls ensure that capacity is shared fairly among tenants.

Migrating to Grafana Mimir

If you're migrating to Grafana Mimir, refer to the following documents:

Deploying Grafana Mimir

For information about how to deploy Grafana Mimir, refer to Deploy Grafana Mimir.

Getting started

If youre new to Grafana Mimir, read the Get started guide.

Before deploying Grafana Mimir in a production environment, read:

  1. An overview of Grafana Mimirs architecture
  2. Configure Grafana Mimir
  3. Run Grafana Mimir in production

Documentation

Refer to the following links to access Grafana Mimir documentation:

Contributing

To contribute to Grafana Mimir, refer to Contributing to Grafana Mimir.

Join the Grafana Mimir discussion

If you have any questions or feedback regarding Grafana Mimir, join the Grafana Mimir Discussion. Alternatively, consider joining the monthly Grafana Mimir Community Call.

Your feedback is always welcome, and you can also share it via the #mimir Slack channel.

License

Grafana Mimir is distributed under AGPL-3.0-only.