public RefCounted getSearcher()

in solr/core/src/java/org/apache/solr/core/SolrCore.java [2506:2754]


  public RefCounted<SolrIndexSearcher> getSearcher(
      boolean forceNew,
      boolean returnSearcher,
      final Future<Void>[] waitSearcher,
      boolean updateHandlerReopens) {
    // it may take some time to open an index.... we may need to make
    // sure that two threads aren't trying to open one at the same time
    // if it isn't necessary.

    synchronized (searcherLock) {
      for (; ; ) { // this loop is so we can retry in the event that we exceed maxWarmingSearchers
        // see if we can return the current searcher
        if (_searcher != null && !forceNew) {
          if (returnSearcher) {
            _searcher.incref();
            return _searcher;
          } else {
            return null;
          }
        }

        // check to see if we can wait for someone else's searcher to be set
        if (onDeckSearchers > 0 && !forceNew && _searcher == null) {
          try {
            searcherLock.wait();
          } catch (InterruptedException e) {
            if (log.isInfoEnabled()) {
              log.info("Interrupted waiting for searcherLock", e);
            }
          }
        }

        // check again: see if we can return right now
        if (_searcher != null && !forceNew) {
          if (returnSearcher) {
            _searcher.incref();
            return _searcher;
          } else {
            return null;
          }
        }

        // At this point, we know we need to open a new searcher...
        // first: increment count to signal other threads that we are
        //        opening a new searcher.
        onDeckSearchers++;
        newSearcherCounter.inc();
        if (onDeckSearchers < 1) {
          // should never happen... just a sanity check
          log.error("ERROR!!! onDeckSearchers is {}", onDeckSearchers);
          onDeckSearchers = 1; // reset
        } else if (onDeckSearchers > maxWarmingSearchers) {
          onDeckSearchers--;
          newSearcherMaxReachedCounter.inc();
          try {
            searcherLock.wait();
          } catch (InterruptedException e) {
            if (log.isInfoEnabled()) {
              log.info("Interrupted waiting for searcherLock", e);
            }
          }
          continue; // go back to the top of the loop and retry
        } else if (onDeckSearchers > 1) {
          log.warn("PERFORMANCE WARNING: Overlapping onDeckSearchers={}", onDeckSearchers);
        }

        break; // I can now exit the loop and proceed to open a searcher
      }
    }

    // a signal to decrement onDeckSearchers if something goes wrong.
    final boolean[] decrementOnDeckCount = new boolean[] {true};
    RefCounted<SolrIndexSearcher> currSearcherHolder = null; // searcher we are autowarming from
    RefCounted<SolrIndexSearcher> searchHolder = null;
    boolean success = false;

    openSearcherLock.lock();
    Timer.Context timerContext = newSearcherTimer.time();
    try {
      searchHolder = openNewSearcher(updateHandlerReopens, false);
      // the searchHolder will be incremented once already (and it will eventually be assigned to
      // _searcher when registered) increment it again if we are going to return it to the caller.
      if (returnSearcher) {
        searchHolder.incref();
      }

      final RefCounted<SolrIndexSearcher> newSearchHolder = searchHolder;
      final SolrIndexSearcher newSearcher = newSearchHolder.get();

      boolean alreadyRegistered = false;
      synchronized (searcherLock) {
        if (_searcher == null) {
          // if there isn't a current searcher then we may
          // want to register this one before warming is complete instead of waiting.
          if (solrConfig.useColdSearcher) {
            registerSearcher(newSearchHolder);
            decrementOnDeckCount[0] = false;
            alreadyRegistered = true;
          }
        } else {
          // get a reference to the current searcher for purposes of autowarming.
          currSearcherHolder = _searcher;
          currSearcherHolder.incref();
        }
      }

      final SolrIndexSearcher currSearcher =
          currSearcherHolder == null ? null : currSearcherHolder.get();

      Future<Void> future = null;

      // if the underlying searcher has not changed, no warming is needed
      if (newSearcher != currSearcher) {

        // warm the new searcher based on the current searcher.
        // should this go before the other event handlers or after?
        if (currSearcher != null) {
          future =
              searcherExecutor.submit(
                  () -> {
                    Timer.Context warmupContext = newSearcherWarmupTimer.time();
                    try {
                      newSearcher.warm(currSearcher);
                    } catch (Throwable e) {
                      log.error("Exception warming new searcher", e);
                      if (e instanceof Error) {
                        throw (Error) e;
                      }
                    } finally {
                      warmupContext.close();
                    }
                    return null;
                  });
        }

        if (currSearcher == null) {
          future =
              searcherExecutor.submit(
                  () -> {
                    newSearcher.bootstrapFirstSearcher();
                    for (SolrEventListener listener : firstSearcherListeners) {
                      try {
                        listener.newSearcher(newSearcher, null);
                      } catch (Throwable e) {
                        log.error("Exception firing listener {} for new searcher", listener, e);
                        if (e instanceof Error) {
                          throw e;
                        }
                      }
                    }
                    return null;
                  });
        }

        if (currSearcher != null) {
          future =
              searcherExecutor.submit(
                  () -> {
                    for (SolrEventListener listener : newSearcherListeners) {
                      try {
                        listener.newSearcher(newSearcher, currSearcher);
                      } catch (Throwable e) {
                        log.error("Exception firing listener {} for new searcher", listener, e);
                        if (e instanceof Error) {
                          throw (Error) e;
                        }
                      }
                    }
                    return null;
                  });
        }
      }

      // WARNING: this code assumes a single threaded executor (that all tasks
      // queued will finish first).
      final RefCounted<SolrIndexSearcher> currSearcherHolderF = currSearcherHolder;
      if (!alreadyRegistered) {
        future =
            searcherExecutor.submit(
                () -> {
                  try {
                    // registerSearcher will decrement onDeckSearchers and
                    // do a notify, even if it fails.
                    registerSearcher(newSearchHolder);
                  } catch (Throwable e) {
                    log.error("Exception registering new searcher", e);
                    if (e instanceof Error) {
                      throw (Error) e;
                    }
                  } finally {
                    // we are all done with the old searcher we used
                    // for warming...
                    if (currSearcherHolderF != null) currSearcherHolderF.decref();
                  }
                  return null;
                });
      }

      if (waitSearcher != null) {
        waitSearcher[0] = future;
      }

      success = true;

      // Return the searcher as the warming tasks run in parallel
      // callers may wait on the waitSearcher future returned.
      return returnSearcher ? newSearchHolder : null;

    } catch (Exception e) {
      if (e instanceof RuntimeException) throw (RuntimeException) e;
      throw new SolrException(ErrorCode.SERVER_ERROR, e);
    } finally {

      timerContext.close();

      if (!success) {
        newSearcherOtherErrorsCounter.inc();
        synchronized (searcherLock) {
          onDeckSearchers--;

          if (onDeckSearchers < 0) {
            // sanity check... should never happen
            log.error("ERROR!!! onDeckSearchers after decrement={}", onDeckSearchers);
            onDeckSearchers = 0; // try and recover
          }
          // if we failed, we need to wake up at least one waiter to continue the process
          searcherLock.notify();
        }

        if (currSearcherHolder != null) {
          currSearcherHolder.decref();
        }

        if (searchHolder != null) {
          // decrement 1 for _searcher (searchHolder will never become _searcher now)
          searchHolder.decref();
          if (returnSearcher) {
            // decrement 1 because we won't be returning the searcher to the user
            searchHolder.decref();
          }
        }
      }

      // we want to do this after we decrement onDeckSearchers so another thread
      // doesn't increment first and throw a false warning.
      openSearcherLock.unlock();
      newSearcherReady = true;
    }
  }