Skip to content

Allow GC of closed search contexts in ES|QL - #155418

Merged
dnhatn merged 5 commits into
elastic:mainfrom
dnhatn:search-contex-gced
Jul 29, 2026
Merged

dnhatn merged 5 commits into
elastic:mainfrom
dnhatn:search-contex-gced

Conversation

@dnhatn

@dnhatn dnhatn commented Jul 29, 2026 •

Copy link
Copy Markdown
Member

ES|QL already closes search contexts as soon as compute no longer needs them. However, AcquiredSearchContexts kept those closed contexts in its global array, so their in-memory Lucene/codec structures could not be GCed. Queries target many shards could therefore retain a large amount of memory and even OOM.

Relates #139693

@elasticsearchmachine

Copy link
Copy Markdown
Collaborator

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

@github-actions

github-actions Bot commented Jul 29, 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

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?

@dnhatn dnhatn added the auto-backport Automatically create backport pull requests when merged label Jul 29, 2026
@dnhatn
dnhatn marked this pull request as ready for review July 29, 2026 18:17
@elasticsearchmachine elasticsearchmachine added the Team:Analytics Meta label for analytical engine team (ESQL/Aggs/Geo) label Jul 29, 2026
@elasticsearchmachine

Copy link
Copy Markdown
Collaborator

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

@GalLalouche GalLalouche 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.

Thanks @dnhatn! I have a (few) design suggestion(s) that would make this a bit cleaner. WDYT?


@Override
public SearchContext searchContext() {
throw new AlreadyClosedException("ComputeSearchContext at index [" + index() + "] was already released");

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.

Nit: Extract the error message to a common helper


private static class AlreadyReleasedComputeSearchContext extends ComputeSearchContext {
AlreadyReleasedComputeSearchContext(int index) {
super(index, null);

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.

This feels wrong, design-wise, and an abuse of ComputeSearchContext/violating LSP (having to pass the null to a non-nullable field is the especially smelly part!). I see a few of ways around it:

  1. Define an interface both ComputeSearchContext and AlreadyReleasedComputeSearchContext implement. That's a large blast radius though (if you want to keep it to a minimum, you can name the new interface ComputeSearchContext and rename the class to something else).
  2. Slightly easier, change ComputeSearchContext's close method to also nullify the SearchContext.
  3. Easiest, but least clean IMO: add a protected constructor to ComputeSearchContext that only takes the index, and handles set the null on its own. At least that way you avoid the smelliness mentioned above.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I pushed ebf380e

final int idx = nextAddIndex++;
cse.addReleasable(() -> {
synchronized (AcquiredSearchContexts.this) {
// Allow GC of closed search contexts as soon as they are released.

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.

I definitely missed this, since I assumed the overhead of maintain the SearchContext is negligible. For my own education, how heavy is this?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

It can be expensive if a shard has many segments and fields. In the customer's heap dump, many search contexts use more than 20 MB.

@dnhatn
dnhatn requested a review from GalLalouche July 29, 2026 19:20

@GalLalouche GalLalouche 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 a small suggestion.

*/
class ComputeSearchContext implements Releasable {
private final int index;
private final SearchContext searchContext;

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.

Suggestion: Mark this as @Nullable, perhaps with a comment explaining when it is nullable.

@GalLalouche GalLalouche 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 a small suggestion.

@dnhatn
dnhatn removed request for jimczi and martijnvg July 29, 2026 20:37
@dnhatn

dnhatn commented Jul 29, 2026

Copy link
Copy Markdown
Member Author

Thanks Gal!

@dnhatn
dnhatn merged commit efa7ca9 into elastic:main Jul 29, 2026
43 checks passed
@dnhatn
dnhatn deleted the search-contex-gced branch July 29, 2026 22:30
@elasticsearchmachine

Copy link
Copy Markdown
Collaborator

💔 Backport failed

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

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

@dnhatn

dnhatn commented Jul 29, 2026

Copy link
Copy Markdown
Member Author

💚 All backports created successfully

Status Branch Result
✅ 9.5
✅ 9.4

Questions ?

Please refer to the Backport tool documentation

elasticsearchmachine pushed a commit that referenced this pull request Jul 29, 2026
ES|QL already closes search contexts as soon as compute no longer needs
them. However, AcquiredSearchContexts kept those closed contexts in its
global array, so their in-memory Lucene/codec structures could not be
GCed. Queries target many shards could therefore retain a large amount
of memory and even OOM.

Relates #139693

(cherry picked from commit efa7ca9)
dnhatn added a commit to dnhatn/elasticsearch that referenced this pull request Jul 30, 2026
ES|QL already closes search contexts as soon as compute no longer needs
them. However, AcquiredSearchContexts kept those closed contexts in its
global array, so their in-memory Lucene/codec structures could not be
GCed. Queries target many shards could therefore retain a large amount
of memory and even OOM.

Relates elastic#139693

(cherry picked from commit efa7ca9)
elasticsearchmachine pushed a commit that referenced this pull request Jul 30, 2026
ES|QL already closes search contexts as soon as compute no longer needs
them. However, AcquiredSearchContexts kept those closed contexts in its
global array, so their in-memory Lucene/codec structures could not be
GCed. Queries target many shards could therefore retain a large amount
of memory and even OOM.

Relates #139693

(cherry picked from commit efa7ca9)
tballison pushed a commit to tballison/elasticsearch that referenced this pull request Aug 4, 2026
ES|QL already closes search contexts as soon as compute no longer needs 
them. However, AcquiredSearchContexts kept those closed contexts in its
global array, so their in-memory Lucene/codec structures could not be
GCed. Queries target many shards could therefore retain a large amount
of memory and even OOM.

Relates elastic#139693
tveasey pushed a commit to tveasey/elasticsearch that referenced this pull request Aug 6, 2026
ES|QL already closes search contexts as soon as compute no longer needs 
them. However, AcquiredSearchContexts kept those closed contexts in its
global array, so their in-memory Lucene/codec structures could not be
GCed. Queries target many shards could therefore retain a large amount
of memory and even OOM.

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

Labels

:Analytics/ES|QL AKA ESQL auto-backport Automatically create backport pull requests when merged >bug Team:Analytics Meta label for analytical engine team (ESQL/Aggs/Geo) v9.4.5 v9.5.1 v9.6.0

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants