Repository navigation
Aggs: Avoid OOMs by accounting memory on cardinality agg reduction phase - #152773
Conversation
|
Hi @ivancea, I've created a changelog YAML for you. |
🔍 Preview links for changed docs⏳ Building and deploying preview... View progress This comment will be updated with preview links when the build is complete. |
ℹ️ 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 overviewWhen to use applies_to tags:✅ At the page level to indicate which products/deployments the content applies to (mandatory) What NOT to do:❌ Don't remove or replace information that applies to an older version 🤔 Need help?
|
| ? null | ||
| : reduceContext.bigArrays().breakerService().getBreaker(CircuitBreaker.REQUEST); | ||
| if (breaker != null) { | ||
| breaker.addEstimateBytesAndMaybeBreak(cloneBytes, "cardinality reduce result clone"); |
There was a problem hiding this comment.
This will peak memory usage by x2 for this HLL+. Classes like CardinalityAggregator do this same thing, without the peak accounting:
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+)
There was a problem hiding this comment.
Double counting these is fine with me.
There was a problem hiding this comment.
It's not great, but we'll do our best with _search aggs these days.
There was a problem hiding this comment.
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 thanBigArrays.NON_RECYCLING_INSTANCE) when constructing the coordinator-sideHyperLogLogPlusPlusused 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.
|
Pinging @elastic/es-analytical-engine (Team:Analytics) |
craigtaverner
left a comment
There was a problem hiding this comment.
LGTM, with some questions/suggestions.
| issues: | ||
| - 150290 | ||
| pr: 152773 | ||
| summary: "Aggs: Account memory on `InternalCardinality` reduction" |
There was a problem hiding this comment.
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"?
| try { | ||
| AbstractHyperLogLogPlusPlus result = toRelease.clone(0, BigArrays.NON_RECYCLING_INSTANCE); | ||
| reduced = null; | ||
| Releasables.close(toRelease); |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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) |
There was a problem hiding this comment.
Any value in testing that we don't get an exception if the CB limit is more than 2x the reducedHllBytes?
There was a problem hiding this comment.
Not bad to avoid over-counting I suppose. Added!
💔 Backport failed
You can use sqren/backport to manually backport by running |
…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
…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
Fixes #150290
BigArraystoHyperLogLogPlusclass inInternalCartinalityBigArrays.InternalAggregations don't have a safe open-close lifecycle, and keeping it there would leak memory