Skip to content

Commit ff6985e

Browse files
committed
Allow SearchOperationListeners to validate a search context (#24650)
This commit adds a new method to the SearchOperationListener that allows implementers to validate the SearchContext immediately after it is retrieved from the active contexts. The listener may throw a runtime exception if it deems the SearchContext is not valid and that the use of the context should be terminated.
1 parent e67b026 commit ff6985e

File tree

3 files changed

+76
-4
lines changed

3 files changed

+76
-4
lines changed

core/src/main/java/org/elasticsearch/index/shard/SearchOperationListener.java

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import org.apache.logging.log4j.Logger;
2222
import org.apache.logging.log4j.message.ParameterizedMessage;
2323
import org.apache.logging.log4j.util.Supplier;
24+
import org.elasticsearch.ExceptionsHelper;
2425
import org.elasticsearch.search.internal.SearchContext;
2526

2627
import java.util.List;
@@ -104,6 +105,14 @@ public interface SearchOperationListener {
104105
*/
105106
default void onFreeScrollContext(SearchContext context) {};
106107

108+
/**
109+
* Executed prior to using a {@link SearchContext} that has been retrieved
110+
* from the active contexts. If the context is deemed invalid a runtime
111+
* exception can be thrown, which will prevent the context from being used.
112+
* @param context the context retrieved from the active contexts
113+
*/
114+
default void validateSearchContext(SearchContext context) {}
115+
107116
/**
108117
* A Composite listener that multiplexes calls to each of the listeners methods.
109118
*/
@@ -225,5 +234,18 @@ public void onFreeScrollContext(SearchContext context) {
225234
}
226235
}
227236
}
237+
238+
@Override
239+
public void validateSearchContext(SearchContext context) {
240+
Exception exception = null;
241+
for (SearchOperationListener listener : listeners) {
242+
try {
243+
listener.validateSearchContext(context);
244+
} catch (Exception e) {
245+
exception = ExceptionsHelper.useOrSuppress(exception, e);
246+
}
247+
}
248+
ExceptionsHelper.reThrowIfNotNull(exception);
249+
}
228250
}
229251
}

core/src/main/java/org/elasticsearch/search/SearchService.java

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -437,7 +437,15 @@ private SearchContext findContext(long id) throws SearchContextMissingException
437437
if (context == null) {
438438
throw new SearchContextMissingException(id);
439439
}
440-
return context;
440+
441+
SearchOperationListener operationListener = context.indexShard().getSearchOperationListener();
442+
try {
443+
operationListener.validateSearchContext(context);
444+
return context;
445+
} catch (Exception e) {
446+
processFailure(context, e);
447+
throw e;
448+
}
441449
}
442450

443451
final SearchContext createAndPutContext(ShardSearchRequest request) throws IOException {

core/src/test/java/org/elasticsearch/index/shard/SearchOperationListenerTests.java

Lines changed: 45 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,6 @@
1818
*/
1919
package org.elasticsearch.index.shard;
2020

21-
import org.apache.lucene.index.Term;
22-
import org.elasticsearch.client.Client;
23-
import org.elasticsearch.index.engine.Engine;
2421
import org.elasticsearch.search.internal.SearchContext;
2522
import org.elasticsearch.test.ESTestCase;
2623
import org.elasticsearch.test.TestSearchContext;
@@ -32,6 +29,9 @@
3229
import java.util.List;
3330
import java.util.concurrent.atomic.AtomicInteger;
3431

32+
import static org.hamcrest.Matchers.not;
33+
import static org.hamcrest.Matchers.sameInstance;
34+
3535
public class SearchOperationListenerTests extends ESTestCase {
3636

3737
// this test also tests if calls are correct if one or more listeners throw exceptions
@@ -46,6 +46,7 @@ public void testListenersAreExecuted() {
4646
AtomicInteger freeContext = new AtomicInteger();
4747
AtomicInteger newScrollContext = new AtomicInteger();
4848
AtomicInteger freeScrollContext = new AtomicInteger();
49+
AtomicInteger validateSearchContext = new AtomicInteger();
4950
AtomicInteger timeInNanos = new AtomicInteger(randomIntBetween(0, 10));
5051
SearchOperationListener listener = new SearchOperationListener() {
5152
@Override
@@ -109,17 +110,26 @@ public void onFreeScrollContext(SearchContext context) {
109110
assertNotNull(context);
110111
freeScrollContext.incrementAndGet();
111112
}
113+
114+
@Override
115+
public void validateSearchContext(SearchContext context) {
116+
assertNotNull(context);
117+
validateSearchContext.incrementAndGet();
118+
}
112119
};
113120

114121
SearchOperationListener throwingListener = (SearchOperationListener) Proxy.newProxyInstance(
115122
SearchOperationListener.class.getClassLoader(),
116123
new Class[]{SearchOperationListener.class},
117124
(a,b,c) -> { throw new RuntimeException();});
125+
int throwingListeners = 0;
118126
final List<SearchOperationListener> indexingOperationListeners = new ArrayList<>(Arrays.asList(listener, listener));
119127
if (randomBoolean()) {
120128
indexingOperationListeners.add(throwingListener);
129+
throwingListeners++;
121130
if (randomBoolean()) {
122131
indexingOperationListeners.add(throwingListener);
132+
throwingListeners++;
123133
}
124134
}
125135
Collections.shuffle(indexingOperationListeners, random());
@@ -137,6 +147,7 @@ public void onFreeScrollContext(SearchContext context) {
137147
assertEquals(0, newScrollContext.get());
138148
assertEquals(0, freeContext.get());
139149
assertEquals(0, freeScrollContext.get());
150+
assertEquals(0, validateSearchContext.get());
140151

141152
compositeListener.onFetchPhase(ctx, timeInNanos.get());
142153
assertEquals(0, preFetch.get());
@@ -149,6 +160,7 @@ public void onFreeScrollContext(SearchContext context) {
149160
assertEquals(0, newScrollContext.get());
150161
assertEquals(0, freeContext.get());
151162
assertEquals(0, freeScrollContext.get());
163+
assertEquals(0, validateSearchContext.get());
152164

153165
compositeListener.onPreQueryPhase(ctx);
154166
assertEquals(0, preFetch.get());
@@ -161,6 +173,7 @@ public void onFreeScrollContext(SearchContext context) {
161173
assertEquals(0, newScrollContext.get());
162174
assertEquals(0, freeContext.get());
163175
assertEquals(0, freeScrollContext.get());
176+
assertEquals(0, validateSearchContext.get());
164177

165178
compositeListener.onPreFetchPhase(ctx);
166179
assertEquals(2, preFetch.get());
@@ -173,6 +186,7 @@ public void onFreeScrollContext(SearchContext context) {
173186
assertEquals(0, newScrollContext.get());
174187
assertEquals(0, freeContext.get());
175188
assertEquals(0, freeScrollContext.get());
189+
assertEquals(0, validateSearchContext.get());
176190

177191
compositeListener.onFailedFetchPhase(ctx);
178192
assertEquals(2, preFetch.get());
@@ -185,6 +199,7 @@ public void onFreeScrollContext(SearchContext context) {
185199
assertEquals(0, newScrollContext.get());
186200
assertEquals(0, freeContext.get());
187201
assertEquals(0, freeScrollContext.get());
202+
assertEquals(0, validateSearchContext.get());
188203

189204
compositeListener.onFailedQueryPhase(ctx);
190205
assertEquals(2, preFetch.get());
@@ -197,6 +212,7 @@ public void onFreeScrollContext(SearchContext context) {
197212
assertEquals(0, newScrollContext.get());
198213
assertEquals(0, freeContext.get());
199214
assertEquals(0, freeScrollContext.get());
215+
assertEquals(0, validateSearchContext.get());
200216

201217
compositeListener.onNewContext(ctx);
202218
assertEquals(2, preFetch.get());
@@ -209,6 +225,7 @@ public void onFreeScrollContext(SearchContext context) {
209225
assertEquals(0, newScrollContext.get());
210226
assertEquals(0, freeContext.get());
211227
assertEquals(0, freeScrollContext.get());
228+
assertEquals(0, validateSearchContext.get());
212229

213230
compositeListener.onNewScrollContext(ctx);
214231
assertEquals(2, preFetch.get());
@@ -221,6 +238,7 @@ public void onFreeScrollContext(SearchContext context) {
221238
assertEquals(2, newScrollContext.get());
222239
assertEquals(0, freeContext.get());
223240
assertEquals(0, freeScrollContext.get());
241+
assertEquals(0, validateSearchContext.get());
224242

225243
compositeListener.onFreeContext(ctx);
226244
assertEquals(2, preFetch.get());
@@ -233,6 +251,7 @@ public void onFreeScrollContext(SearchContext context) {
233251
assertEquals(2, newScrollContext.get());
234252
assertEquals(2, freeContext.get());
235253
assertEquals(0, freeScrollContext.get());
254+
assertEquals(0, validateSearchContext.get());
236255

237256
compositeListener.onFreeScrollContext(ctx);
238257
assertEquals(2, preFetch.get());
@@ -245,5 +264,28 @@ public void onFreeScrollContext(SearchContext context) {
245264
assertEquals(2, newScrollContext.get());
246265
assertEquals(2, freeContext.get());
247266
assertEquals(2, freeScrollContext.get());
267+
assertEquals(0, validateSearchContext.get());
268+
269+
if (throwingListeners == 0) {
270+
compositeListener.validateSearchContext(ctx);
271+
} else {
272+
RuntimeException expected = expectThrows(RuntimeException.class, () -> compositeListener.validateSearchContext(ctx));
273+
assertNull(expected.getMessage());
274+
assertEquals(throwingListeners - 1, expected.getSuppressed().length);
275+
if (throwingListeners > 1) {
276+
assertThat(expected.getSuppressed()[0], not(sameInstance(expected)));
277+
}
278+
}
279+
assertEquals(2, preFetch.get());
280+
assertEquals(2, preQuery.get());
281+
assertEquals(2, failedFetch.get());
282+
assertEquals(2, failedQuery.get());
283+
assertEquals(2, onQuery.get());
284+
assertEquals(2, onFetch.get());
285+
assertEquals(2, newContext.get());
286+
assertEquals(2, newScrollContext.get());
287+
assertEquals(2, freeContext.get());
288+
assertEquals(2, freeScrollContext.get());
289+
assertEquals(2, validateSearchContext.get());
248290
}
249291
}

0 commit comments

Comments
 (0)