95
95
import java .util .concurrent .locks .ReentrantLock ;
96
96
import java .util .concurrent .locks .ReentrantReadWriteLock ;
97
97
import java .util .function .BiFunction ;
98
+ import java .util .function .Function ;
98
99
import java .util .stream .Stream ;
99
100
100
101
import static org .elasticsearch .index .seqno .SequenceNumbers .UNASSIGNED_PRIMARY_TERM ;
@@ -588,31 +589,17 @@ protected final GetResult getFromSearcher(Get get, BiFunction<String, SearcherSc
588
589
589
590
public abstract GetResult get (Get get , BiFunction <String , SearcherScope , Searcher > searcherFactory ) throws EngineException ;
590
591
591
-
592
592
/**
593
- * Returns a new searcher instance. The consumer of this
594
- * API is responsible for releasing the returned searcher in a
595
- * safe manner, preferably in a try/finally block.
596
- *
597
- * @param source the source API or routing that triggers this searcher acquire
598
- *
599
- * @see Searcher#close()
593
+ * Acquires a point-in-time reader that can be used to create {@link Engine.Searcher}s on demand.
600
594
*/
601
- public final Searcher acquireSearcher ( String source ) throws EngineException {
602
- return acquireSearcher ( source , SearcherScope .EXTERNAL );
595
+ public final SearcherSupplier acquireSearcherSupplier ( Function < Searcher , Searcher > wrapper ) throws EngineException {
596
+ return acquireSearcherSupplier ( wrapper , SearcherScope .EXTERNAL );
603
597
}
604
598
605
599
/**
606
- * Returns a new searcher instance. The consumer of this
607
- * API is responsible for releasing the returned searcher in a
608
- * safe manner, preferably in a try/finally block.
609
- *
610
- * @param source the source API or routing that triggers this searcher acquire
611
- * @param scope the scope of this searcher ie. if the searcher will be used for get or search purposes
612
- *
613
- * @see Searcher#close()
600
+ * Acquires a point-in-time reader that can be used to create {@link Engine.Searcher}s on demand.
614
601
*/
615
- public Searcher acquireSearcher ( String source , SearcherScope scope ) throws EngineException {
602
+ public SearcherSupplier acquireSearcherSupplier ( Function < Searcher , Searcher > wrapper , SearcherScope scope ) throws EngineException {
616
603
/* Acquire order here is store -> manager since we need
617
604
* to make sure that the store is not closed before
618
605
* the searcher is acquired. */
@@ -621,35 +608,64 @@ public Searcher acquireSearcher(String source, SearcherScope scope) throws Engin
621
608
}
622
609
Releasable releasable = store ::decRef ;
623
610
try {
624
- assert assertSearcherIsWarmedUp (source , scope );
625
611
ReferenceManager <ElasticsearchDirectoryReader > referenceManager = getReferenceManager (scope );
626
- final ElasticsearchDirectoryReader acquire = referenceManager .acquire ();
612
+ ElasticsearchDirectoryReader acquire = referenceManager .acquire ();
627
613
AtomicBoolean released = new AtomicBoolean (false );
628
- Searcher engineSearcher = new Searcher (source , acquire ,
629
- engineConfig .getSimilarity (), engineConfig .getQueryCache (), engineConfig .getQueryCachingPolicy (),
630
- () -> {
631
- if (released .compareAndSet (false , true )) {
632
- try {
633
- referenceManager .release (acquire );
634
- } finally {
635
- store .decRef ();
614
+ SearcherSupplier reader = new SearcherSupplier (wrapper ) {
615
+ @ Override
616
+ public Searcher acquireSearcherInternal (String source ) {
617
+ assert assertSearcherIsWarmedUp (source , scope );
618
+ return new Searcher (source , acquire , engineConfig .getSimilarity (), engineConfig .getQueryCache (),
619
+ engineConfig .getQueryCachingPolicy (), () -> {});
620
+ }
621
+
622
+ @ Override
623
+ public void close () {
624
+ if (released .compareAndSet (false , true )) {
625
+ try {
626
+ referenceManager .release (acquire );
627
+ } catch (IOException e ) {
628
+ throw new UncheckedIOException ("failed to close" , e );
629
+ } catch (AlreadyClosedException e ) {
630
+ // This means there's a bug somewhere: don't suppress it
631
+ throw new AssertionError (e );
632
+ } finally {
633
+ store .decRef ();
634
+ }
635
+ } else {
636
+ /* In general, readers should never be released twice or this would break reference counting. There is one rare case
637
+ * when it might happen though: when the request and the Reaper thread would both try to release it in a very short
638
+ * amount of time, this is why we only log a warning instead of throwing an exception. */
639
+ logger .warn ("Reader was released twice" , new IllegalStateException ("Double release" ));
636
640
}
637
- } else {
638
- /* In general, readers should never be released twice or this would break reference counting. There is one rare case
639
- * when it might happen though: when the request and the Reaper thread would both try to release it in a very short
640
- * amount of time, this is why we only log a warning instead of throwing an exception. */
641
- logger .warn ("Searcher was released twice" , new IllegalStateException ("Double release" ));
642
641
}
643
- }) ;
642
+ } ;
644
643
releasable = null ; // success - hand over the reference to the engine reader
645
- return engineSearcher ;
644
+ return reader ;
646
645
} catch (AlreadyClosedException ex ) {
647
646
throw ex ;
648
647
} catch (Exception ex ) {
649
- maybeFailEngine ("acquire_searcher " , ex );
648
+ maybeFailEngine ("acquire_reader " , ex );
650
649
ensureOpen (ex ); // throw EngineCloseException here if we are already closed
651
- logger .error (() -> new ParameterizedMessage ("failed to acquire searcher, source {}" , source ), ex );
652
- throw new EngineException (shardId , "failed to acquire searcher, source " + source , ex );
650
+ logger .error (() -> new ParameterizedMessage ("failed to acquire reader" ), ex );
651
+ throw new EngineException (shardId , "failed to acquire reader" , ex );
652
+ } finally {
653
+ Releasables .close (releasable );
654
+ }
655
+ }
656
+
657
+ public final Searcher acquireSearcher (String source ) throws EngineException {
658
+ return acquireSearcher (source , SearcherScope .EXTERNAL );
659
+ }
660
+
661
+ public Searcher acquireSearcher (String source , SearcherScope scope ) throws EngineException {
662
+ SearcherSupplier releasable = null ;
663
+ try {
664
+ SearcherSupplier reader = releasable = acquireSearcherSupplier (Function .identity (), scope );
665
+ Searcher searcher = reader .acquireSearcher (source );
666
+ releasable = null ;
667
+ return new Searcher (source , searcher .getDirectoryReader (), searcher .getSimilarity (),
668
+ searcher .getQueryCache (), searcher .getQueryCachingPolicy (), () -> Releasables .close (searcher , reader ));
653
669
} finally {
654
670
Releasables .close (releasable );
655
671
}
@@ -1158,6 +1174,21 @@ default void onFailedEngine(String reason, @Nullable Exception e) {
1158
1174
}
1159
1175
}
1160
1176
1177
+ public abstract static class SearcherSupplier implements Releasable {
1178
+ private final Function <Searcher , Searcher > wrapper ;
1179
+
1180
+ public SearcherSupplier (Function <Searcher , Searcher > wrapper ) {
1181
+ this .wrapper = wrapper ;
1182
+ }
1183
+
1184
+ public final Searcher acquireSearcher (String source ) {
1185
+ final Searcher searcher = acquireSearcherInternal (source );
1186
+ return "can_match" .equals (source ) ? searcher : wrapper .apply (searcher );
1187
+ }
1188
+
1189
+ protected abstract Searcher acquireSearcherInternal (String source );
1190
+ }
1191
+
1161
1192
public static final class Searcher extends IndexSearcher implements Releasable {
1162
1193
private final String source ;
1163
1194
private final Closeable onClose ;
0 commit comments