Skip to content

Commit 2e99ef5

Browse files
Release SearchContext referenced resources via safer pattern
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 d65f34d commit 2e99ef5

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,12 +21,14 @@
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;
2930
import org.elasticsearch.core.Releasable;
31+
import org.elasticsearch.core.Releasables;
3032
import org.elasticsearch.core.TimeValue;
3133
import org.elasticsearch.index.IndexMode;
3234
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)