Skip to content

Commit 1b35cce

Browse files
Release SearchContext referenced resources via safer pattern (#127485)
No need for two fields for this, using the future pattern is also much safer and easier to reason about and automatically unlinks objects on close.
1 parent 5965b27 commit 1b35cce

File tree

2 files changed

+15
-13
lines changed

2 files changed

+15
-13
lines changed

server/src/main/java/org/elasticsearch/search/DefaultSearchContext.java

+3-1
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,13 @@
2121
import org.apache.lucene.search.Query;
2222
import org.apache.lucene.search.TotalHits;
2323
import org.apache.lucene.util.NumericUtils;
24+
import org.elasticsearch.action.ActionListener;
2425
import org.elasticsearch.action.search.SearchType;
2526
import org.elasticsearch.cluster.routing.IndexRouting;
2627
import org.elasticsearch.common.breaker.CircuitBreaker;
2728
import org.elasticsearch.common.lucene.search.Queries;
2829
import org.elasticsearch.core.Nullable;
30+
import org.elasticsearch.core.Releasables;
2931
import org.elasticsearch.core.TimeValue;
3032
import org.elasticsearch.index.IndexMode;
3133
import org.elasticsearch.index.IndexService;
@@ -212,7 +214,7 @@ final class DefaultSearchContext extends SearchContext {
212214
minimumDocsPerSlice
213215
);
214216
}
215-
releasables.addAll(List.of(engineSearcher, searcher));
217+
closeFuture.addListener(ActionListener.releasing(Releasables.wrap(engineSearcher, searcher)));
216218
this.relativeTimeSupplier = relativeTimeSupplier;
217219
this.timeout = timeout;
218220
searchExecutionContext = indexService.newSearchExecutionContext(

server/src/main/java/org/elasticsearch/search/internal/SearchContext.java

+12-12
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,13 @@
1111
import org.apache.lucene.search.FieldDoc;
1212
import org.apache.lucene.search.Query;
1313
import org.apache.lucene.search.TotalHits;
14+
import org.elasticsearch.action.ActionListener;
1415
import org.elasticsearch.action.search.SearchType;
16+
import org.elasticsearch.action.support.SubscribableListener;
1517
import org.elasticsearch.common.breaker.CircuitBreaker;
1618
import org.elasticsearch.core.Assertions;
1719
import org.elasticsearch.core.Nullable;
1820
import org.elasticsearch.core.Releasable;
19-
import org.elasticsearch.core.Releasables;
2021
import org.elasticsearch.core.TimeValue;
2122
import org.elasticsearch.index.cache.bitset.BitsetFilterCache;
2223
import org.elasticsearch.index.mapper.IdLoader;
@@ -56,8 +57,6 @@
5657
import java.util.List;
5758
import java.util.Map;
5859
import java.util.Set;
59-
import java.util.concurrent.CopyOnWriteArrayList;
60-
import java.util.concurrent.atomic.AtomicBoolean;
6160

6261
/**
6362
* This class encapsulates the state needed to execute a search. It holds a reference to the
@@ -71,13 +70,16 @@ public abstract class SearchContext implements Releasable {
7170
public static final int TRACK_TOTAL_HITS_DISABLED = -1;
7271
public static final int DEFAULT_TRACK_TOTAL_HITS_UP_TO = 10000;
7372

74-
protected final List<Releasable> releasables = new CopyOnWriteArrayList<>();
75-
76-
private final AtomicBoolean closed = new AtomicBoolean(false);
73+
protected final SubscribableListener<Void> closeFuture = new SubscribableListener<>();
7774

7875
{
7976
if (Assertions.ENABLED) {
80-
releasables.add(LeakTracker.wrap(() -> { assert closed.get(); }));
77+
closeFuture.addListener(ActionListener.releasing(LeakTracker.wrap(new Releasable() {
78+
@Override
79+
public void close() {
80+
// empty instance that will actually get GC'ed so that the leak tracker works
81+
}
82+
})));
8183
}
8284
}
8385
private InnerHitsContext innerHitsContext;
@@ -109,9 +111,7 @@ public final List<Runnable> getCancellationChecks() {
109111

110112
@Override
111113
public final void close() {
112-
if (closed.compareAndSet(false, true)) {
113-
Releasables.close(releasables);
114-
}
114+
closeFuture.onResponse(null);
115115
}
116116

117117
/**
@@ -399,8 +399,8 @@ public final boolean checkRealMemoryCB(int locallyAccumulatedBytes, String label
399399
* Adds a releasable that will be freed when this context is closed.
400400
*/
401401
public void addReleasable(Releasable releasable) { // TODO most Releasables are managed by their callers. We probably don't need this.
402-
assert closed.get() == false;
403-
releasables.add(releasable);
402+
assert closeFuture.isDone() == false;
403+
closeFuture.addListener(ActionListener.releasing(releasable));
404404
}
405405

406406
/**

0 commit comments

Comments
 (0)