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;
}
}