Skip to content

Conversation

@nik9000
Copy link
Member

@nik9000 nik9000 commented Jan 14, 2026

This seeks to prevent out of memory errors by accounting for the memory usage of field readers. We don't have measurements for the actual memory usage, so instead we add an estimate to the breaker. An overestimate, hopefully.

This makes us circuit break when loading many many many fields on small heaps rather than crashing with an out of memory.

This also allows readers to cache things a little more aggressively, so long as they are willing to circuit break when receiving a huge number of requests.

This seaks to prevent out of memory errors by accounting for the memory
usage of field readers. We don't have measurements for the actual memory
usage, so instead we add an estiamte to the breaker. An overestimate,
hopefully.

This makes us circuit break when loading many many many fields on small
heaps rather than crashing with an out of memory.

This also allows readers to cache things a little more aggressively, so
long as they are willing to circuit break when receiving a huge number
of requests.
}

interface Reader {
interface Reader extends Releasable {
Copy link
Member Author

Choose a reason for hiding this comment

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

Here's the main change in the PR. Everything else is in service of making this Releasable and passing in CircuitBreaker.

* This implies that we need to load the value from _source. This however is very slow, especially when synthetic source is enabled.
* We're better off reading from doc_values and converting to BytesRef to satisfy the checker. This is what this block loader is for.
*/
static final class BytesRefFromLongsBlockLoader extends BlockDocValuesReader.DocValuesBlockLoader {
Copy link
Member Author

Choose a reason for hiding this comment

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

Moved it

);
}

public static class DateRangeDocValuesLoader extends BlockDocValuesReader.DocValuesBlockLoader {
Copy link
Member Author

Choose a reason for hiding this comment

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

Moved it.

}

@Override
public StoredFieldsSpec rowStrideStoredFieldSpec() {
Copy link
Member Author

Choose a reason for hiding this comment

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

Moved these methods so the inner classes are at the end of the file.

* Circuit breaker space reserved for each reader. Measured in heap dumps
* around from 3.5kb to 65kb. This is an intentional overestimate.
*/
public static final long ESTIMATED_SIZE = ByteSizeValue.ofKb(100).getBytes();
Copy link
Member Author

Choose a reason for hiding this comment

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

This 100kb is pretty controversial I think. Will want some discussion. I'd far prefer to have a good estimate, but I don't see a way to do so right now. I've seen the readers 1kb keyword fields be this large.

Copy link
Member

Choose a reason for hiding this comment

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

If SortedSetDocValues and friends would implement Accountable then we would have much better insight in the on heap memory usage. However I think this is also difficult as the memory usage might be variable as SortedSetDocValues gets used.

Additionally, should we make this configurable via a query pragma or something else?

Copy link
Member Author

Choose a reason for hiding this comment

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

Accountable
Yeah. I'd be ok doing if (thingy instanceof Accountable) here. I'd prefer not, but sometimes we can't have all we want.

Additionally, should we make this configurable via a query pragma or something else?

Hmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm.

@nik9000 nik9000 added the >bug label Jan 14, 2026
@nik9000 nik9000 marked this pull request as ready for review January 14, 2026 15:52
@elasticsearchmachine elasticsearchmachine added the Team:Analytics Meta label for analytical engine team (ESQL/Aggs/Geo) label Jan 14, 2026
@elasticsearchmachine
Copy link
Collaborator

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

@elasticsearchmachine
Copy link
Collaborator

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

Copy link
Member

@dnhatn dnhatn left a comment

Choose a reason for hiding this comment

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

@nik9000 I left a comment. Thank you for taking care of this.

@Override
public AllReader reader(LeafReaderContext context) throws IOException {
public AllReader reader(CircuitBreaker breaker, LeafReaderContext context) throws IOException {
breaker.addEstimateBytesAndMaybeBreak(ESTIMATED_SIZE, "load blocks");
Copy link
Member

Choose a reason for hiding this comment

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

getSortedNumericDocValues can throw an IOException, so we might leak the memory requested here. I think we need a try/finally block. Also, should we acquire memory in the Reader's constructor and release it in close()? For example, in BooleansBlockDocValuesReader, we release memory - should we also acquire it in the constructor?

Copy link
Member

Choose a reason for hiding this comment

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

Good point Nhat. I think applies for all getXXXDocValues(...) invocation and so try/finally blocks are also needed else where.

Copy link
Member Author

Choose a reason for hiding this comment

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

Damn. I should write a test that hits that.

I had thought "this can throw if the index is tragically corrupted" so, like, if it threw we were toast anyway. But I'll fix it.

@dnhatn dnhatn self-requested a review January 15, 2026 06:08
Copy link
Member

@martijnvg martijnvg left a comment

Choose a reason for hiding this comment

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

Thanks Nik, I think Nhat's comment should be addressed, but otherwise LGTM.

@Override
public AllReader reader(LeafReaderContext context) throws IOException {
public AllReader reader(CircuitBreaker breaker, LeafReaderContext context) throws IOException {
breaker.addEstimateBytesAndMaybeBreak(ESTIMATED_SIZE, "load blocks");
Copy link
Member

Choose a reason for hiding this comment

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

Good point Nhat. I think applies for all getXXXDocValues(...) invocation and so try/finally blocks are also needed else where.

* Circuit breaker space reserved for each reader. Measured in heap dumps
* around from 3.5kb to 65kb. This is an intentional overestimate.
*/
public static final long ESTIMATED_SIZE = ByteSizeValue.ofKb(100).getBytes();
Copy link
Member

Choose a reason for hiding this comment

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

If SortedSetDocValues and friends would implement Accountable then we would have much better insight in the on heap memory usage. However I think this is also difficult as the memory usage might be variable as SortedSetDocValues gets used.

Additionally, should we make this configurable via a query pragma or something else?

* shrug because we don't know what the script will do, and we don't know how many doc
* values it'll load. And, we're not sure much memory the script itself will actually use.
*/
public static final long ESTIMATED_SIZE = ByteSizeValue.ofKb(300).getBytes();
Copy link
Member

Choose a reason for hiding this comment

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

Scripts have additional heap memory overhead... We can always adjust it down.

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

Labels

:Analytics/ES|QL AKA ESQL >bug Team:Analytics Meta label for analytical engine team (ESQL/Aggs/Geo) v9.4.0

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants