Skip to content

Aggs: Avoid OOMs by accounting memory on cardinality agg reduction phase - #152773

Merged
ivancea merged 10 commits into
elastic:mainfrom
ivancea:aggs-prevent-hll-oom
Jul 6, 2026
Merged

ivancea merged 10 commits into
elastic:mainfrom
ivancea:aggs-prevent-hll-oom

Conversation

@ivancea

@ivancea ivancea commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

Fixes #150290

  • Adds the CB'd BigArrays to HyperLogLogPlus class in InternalCartinality
  • Clones it on get() with a non-accounting BigArrays. InternalAggregations don't have a safe open-close lifecycle, and keeping it there would leak memory

@ivancea ivancea added >bug :Analytics/Aggregations Aggregations Team:Analytics Meta label for analytical engine team (ESQL/Aggs/Geo) v9.5.0 labels Jul 2, 2026
@elasticsearchmachine

Copy link
Copy Markdown
Collaborator

Hi @ivancea, I've created a changelog YAML for you.

@github-actions

github-actions Bot commented Jul 2, 2026 •

Copy link
Copy Markdown
Contributor

🔍 Preview links for changed docs

⏳ Building and deploying preview... View progress

This comment will be updated with preview links when the build is complete.

@github-actions

github-actions Bot commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

ℹ️ Important: Docs version tagging

👋 Thanks for updating the docs! Just a friendly reminder that our docs are now cumulative. This means all 9.x versions are documented on the same page and published off of the main branch, instead of creating separate pages for each minor version.

We use applies_to tags to mark version-specific features and changes.

Expand for a quick overview

When to use applies_to tags:

✅ At the page level to indicate which products/deployments the content applies to (mandatory)
✅ When features change state (e.g. preview, ga) in a specific version
✅ When availability differs across deployments and environments

What NOT to do:

❌ Don't remove or replace information that applies to an older version
❌ Don't add new information that applies to a specific version without an applies_to tag
❌ Don't forget that applies_to tags can be used at the page, section, and inline level

🤔 Need help?

? null
: reduceContext.bigArrays().breakerService().getBreaker(CircuitBreaker.REQUEST);
if (breaker != null) {
breaker.addEstimateBytesAndMaybeBreak(cloneBytes, "cardinality reduce result clone");

@ivancea ivancea Jul 2, 2026 •

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

This will peak memory usage by x2 for this HLL+. Classes like CardinalityAggregator do this same thing, without the peak accounting:

// We need to build a copy because the returned Aggregation needs remain usable after
// this Aggregator (and its HLL++ counters) is released.
AbstractHyperLogLogPlusPlus copy = counts.clone(owningBucketOrdinal, BigArrays.NON_RECYCLING_INSTANCE);
return new InternalCardinality(name, copy, metadata());

Options I see:

  • Double accounting for the cloned HLL+ without bigarrays (May trip)
  • No double accounting (May OOM)
  • Manual accounting (May get outdated or be inconsistent, but we can fix it after each call by calling ramBytesUsed() on the HLL+)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Double counting these is fine with me.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

It's not great, but we'll do our best with _search aggs these days.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

This PR fixes an aggregation reduction memory-accounting gap in InternalCardinality by ensuring HyperLogLog++ merge allocations are tracked by the REQUEST circuit breaker during coordinator reduction, preventing untracked growth that can lead to OOMs.

Changes:

  • Use reduceContext.bigArrays() (rather than BigArrays.NON_RECYCLING_INSTANCE) when constructing the coordinator-side HyperLogLogPlusPlus used for reduction, so merges are REQUEST-breaker aware.
  • Add peak-memory accounting for the final “result clone” step and ensure the reduction state is released via AggregatorReducer#close().
  • Add focused tests validating reduction trips the REQUEST breaker and that clone-time peak accounting is enforced.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated no comments.

File Description
server/src/main/java/org/elasticsearch/search/aggregations/metrics/InternalCardinality.java Route reduction allocations through reduceContext.bigArrays() and add breaker-charged peak accounting for cloning + proper release on close.
server/src/main/java/org/elasticsearch/search/aggregations/metrics/HyperLogLogPlusPlus.java Add ramBytesUsed() helpers to support clone peak-memory estimation.
server/src/test/java/org/elasticsearch/search/aggregations/metrics/InternalCardinalityTests.java Add tests asserting REQUEST breaker usage during reduction and peak-memory accounting during result cloning.
docs/changelog/152773.yaml Add changelog entry for the fix.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@ivancea
ivancea marked this pull request as ready for review July 3, 2026 09:42
@elasticsearchmachine

Copy link
Copy Markdown
Collaborator

Pinging @elastic/es-analytical-engine (Team:Analytics)

@craigtaverner craigtaverner left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

LGTM, with some questions/suggestions.

Comment thread docs/changelog/152773.yaml Outdated
issues:
- 150290
pr: 152773
summary: "Aggs: Account memory on `InternalCardinality` reduction"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

As a changelog entry this is perhaps a bit cryptic. Is there a way to describe this so that users understand the change better? Perhaps "Improve memory usage protections on aggregations in the coordinator"?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Changed

try {
AbstractHyperLogLogPlusPlus result = toRelease.clone(0, BigArrays.NON_RECYCLING_INSTANCE);
reduced = null;
Releasables.close(toRelease);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Curious why we don't just close reduced here since toRelease is the same reference? Is it that the close can fail, and we don't want to repeat the close on line 125 again?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Yes, it's to avoid that double-close.

That said, I'll improve it a bit by moving the temp var assignation here, and add a micro-comment:

HyperLogLogPlusPlus toRelease = reduced;
reduced = null;
Releasables.close(toRelease);

}
CircuitBreakerService breakerService = LimitedBreaker.service(
CircuitBreaker.REQUEST,
ByteSizeValue.ofBytes(reducedHllBytes * 3 / 2)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Any value in testing that we don't get an exception if the CB limit is more than 2x the reducedHllBytes?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Not bad to avoid over-counting I suppose. Added!

@ivancea ivancea changed the title Aggs: Account memory on InternalCardinality reduction Aggs: Avoid OOMs by account memory on cardinality agg reduction phase Jul 3, 2026
@ivancea ivancea added auto-backport Automatically create backport pull requests when merged v9.4.4 v9.3.8 v8.19.19 labels Jul 6, 2026
@ivancea ivancea removed the v9.3.8 label Jul 6, 2026
@ivancea ivancea changed the title Aggs: Avoid OOMs by account memory on cardinality agg reduction phase Aggs: Avoid OOMs by accounting memory on cardinality agg reduction phase Jul 6, 2026
@ivancea
ivancea merged commit c27d292 into elastic:main Jul 6, 2026
6 of 9 checks passed
@ivancea
ivancea deleted the aggs-prevent-hll-oom branch July 6, 2026 15:42
@elasticsearchmachine

Copy link
Copy Markdown
Collaborator

💔 Backport failed

Status Branch Result
✅ 9.4
❌ 8.19 Commit could not be cherrypicked due to conflicts

You can use sqren/backport to manually backport by running backport --upstream elastic/elasticsearch --pr 152773

elasticsearchmachine pushed a commit that referenced this pull request Jul 6, 2026
…ase (#152773) (#152991)

Fixes #150290

- Adds the CB'd `BigArrays` to `HyperLogLogPlus` class in `InternalCartinality`
- Clones it on get() with a non-accounting `BigArrays`. `InternalAggregation`s don't have a safe open-close lifecycle, and keeping it there would leak memory
ivancea added a commit that referenced this pull request Jul 7, 2026
…ase (#152773) (#152995)

Fixes #150290

- Adds the CB'd `BigArrays` to `HyperLogLogPlus` class in `InternalCartinality`
- Clones it on get() with a non-accounting `BigArrays`. `InternalAggregation`s don't have a safe open-close lifecycle, and keeping it there would leak memory
burqen pushed a commit to burqen/elasticsearch that referenced this pull request Jul 7, 2026
…ase (elastic#152773)

Fixes elastic#150290

- Adds the CB'd `BigArrays` to `HyperLogLogPlus` class in `InternalCartinality`
- Clones it on get() with a non-accounting `BigArrays`. `InternalAggregation`s don't have a safe open-close lifecycle, and keeping it there would leak memory
@ivancea ivancea added the v9.3.8 label Jul 8, 2026
elasticsearchmachine pushed a commit that referenced this pull request Jul 8, 2026
…ion phase (#153246)

* Aggs: Avoid OOMs by accounting memory on cardinality agg reduction phase (#152773)

Fixes #150290

- Adds the CB'd `BigArrays` to `HyperLogLogPlus` class in `InternalCartinality`
- Clones it on get() with a non-accounting `BigArrays`. `InternalAggregation`s don't have a safe open-close lifecycle, and keeping it there would leak memory

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

Labels

:Analytics/Aggregations Aggregations auto-backport Automatically create backport pull requests when merged >bug Team:Analytics Meta label for analytical engine team (ESQL/Aggs/Geo) v8.19.19 v9.3.8 v9.4.4 v9.5.0

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Aggs: InternalCardinality coordinator reduction bypasses the REQUEST circuit breaker, causing OOM

5 participants