org.apache.solr.handler.component.SearchHandler Maven / Gradle / Ivy
Show all versions of solr-core Show documentation
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.solr.handler.component;
import static org.apache.solr.common.params.CommonParams.DISTRIB;
import static org.apache.solr.common.params.CommonParams.FAILURE;
import static org.apache.solr.common.params.CommonParams.PATH;
import static org.apache.solr.common.params.CommonParams.STATUS;
import com.codahale.metrics.Counter;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.lang.invoke.MethodHandles;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicLong;
import org.apache.lucene.index.ExitableDirectoryReader;
import org.apache.lucene.search.TotalHits;
import org.apache.solr.client.solrj.SolrRequest.SolrRequestType;
import org.apache.solr.client.solrj.SolrServerException;
import org.apache.solr.cloud.ZkController;
import org.apache.solr.common.SolrDocumentList;
import org.apache.solr.common.SolrException;
import org.apache.solr.common.params.CommonParams;
import org.apache.solr.common.params.CursorMarkParams;
import org.apache.solr.common.params.ModifiableSolrParams;
import org.apache.solr.common.params.ShardParams;
import org.apache.solr.common.util.NamedList;
import org.apache.solr.common.util.SimpleOrderedMap;
import org.apache.solr.common.util.StrUtils;
import org.apache.solr.core.CloseHook;
import org.apache.solr.core.CoreContainer;
import org.apache.solr.core.PluginInfo;
import org.apache.solr.core.SolrCore;
import org.apache.solr.handler.RequestHandlerBase;
import org.apache.solr.logging.MDCLoggingContext;
import org.apache.solr.metrics.MetricsMap;
import org.apache.solr.metrics.SolrMetricsContext;
import org.apache.solr.pkg.PackageAPI;
import org.apache.solr.pkg.PackageListeners;
import org.apache.solr.pkg.SolrPackageLoader;
import org.apache.solr.request.SolrQueryRequest;
import org.apache.solr.response.SolrQueryResponse;
import org.apache.solr.search.CursorMark;
import org.apache.solr.search.SortSpec;
import org.apache.solr.search.facet.FacetModule;
import org.apache.solr.security.AuthorizationContext;
import org.apache.solr.security.PermissionNameProvider;
import org.apache.solr.util.RTimerTree;
import org.apache.solr.util.SolrPluginUtils;
import org.apache.solr.util.circuitbreaker.CircuitBreaker;
import org.apache.solr.util.circuitbreaker.CircuitBreakerRegistry;
import org.apache.solr.util.plugin.PluginInfoInitialized;
import org.apache.solr.util.plugin.SolrCoreAware;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.MDC;
/** Refer SOLR-281 */
public class SearchHandler extends RequestHandlerBase
implements SolrCoreAware, PluginInfoInitialized, PermissionNameProvider {
static final String INIT_COMPONENTS = "components";
static final String INIT_FIRST_COMPONENTS = "first-components";
static final String INIT_LAST_COMPONENTS = "last-components";
protected static final String SHARD_HANDLER_SUFFIX = "[shard]";
private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
/**
* A counter to ensure that no RID is equal, even if they fall in the same millisecond
*
* @deprecated this was replaced by the auto-generated trace ids
*/
@Deprecated(since = "9.4")
private static final AtomicLong ridCounter = new AtomicLong();
/**
* An opt-out flag to prevent the addition of {@link CommonParams#REQUEST_ID} tracing on
* distributed queries
*
* Defaults to 'false' if not specified.
*
* @see CommonParams#DISABLE_REQUEST_ID
* @deprecated this was replaced by the auto-generated trace ids
*/
@Deprecated(since = "9.4")
private static final boolean DISABLE_REQUEST_ID_DEFAULT =
Boolean.getBoolean("solr.disableRequestId");
private HandlerMetrics metricsShard = HandlerMetrics.NO_OP;
private final Map shardPurposes = new ConcurrentHashMap<>();
protected volatile List components;
private ShardHandlerFactory shardHandlerFactory;
private PluginInfo shfInfo;
private SolrCore core;
protected List getDefaultComponents() {
ArrayList names = new ArrayList<>(8);
names.add(QueryComponent.COMPONENT_NAME);
names.add(FacetComponent.COMPONENT_NAME);
names.add(FacetModule.COMPONENT_NAME);
names.add(MoreLikeThisComponent.COMPONENT_NAME);
names.add(HighlightComponent.COMPONENT_NAME);
names.add(StatsComponent.COMPONENT_NAME);
names.add(DebugComponent.COMPONENT_NAME);
names.add(ExpandComponent.COMPONENT_NAME);
names.add(TermsComponent.COMPONENT_NAME);
return names;
}
@Override
public void init(PluginInfo info) {
init(info.initArgs);
for (PluginInfo child : info.children) {
if ("shardHandlerFactory".equals(child.type)) {
this.shfInfo = child;
break;
}
}
}
@Override
public void initializeMetrics(SolrMetricsContext parentContext, String scope) {
super.initializeMetrics(parentContext, scope);
metricsShard =
new HandlerMetrics( // will register various metrics in the context
solrMetricsContext, getCategory().toString(), scope + SHARD_HANDLER_SUFFIX);
solrMetricsContext.gauge(
new MetricsMap(map -> shardPurposes.forEach((k, v) -> map.putNoEx(k, v.getCount()))),
true,
"purposes",
getCategory().toString(),
scope + SHARD_HANDLER_SUFFIX);
}
@Override
public HandlerMetrics getMetricsForThisRequest(SolrQueryRequest req) {
return req.getParams().getBool(ShardParams.IS_SHARD, false) ? this.metricsShard : this.metrics;
}
@Override
public PermissionNameProvider.Name getPermissionName(AuthorizationContext ctx) {
return PermissionNameProvider.Name.READ_PERM;
}
/**
* Initialize the components based on name. Note, if using INIT_FIRST_COMPONENTS
or
* INIT_LAST_COMPONENTS
, then the {@link DebugComponent} will always occur last. If
* this is not desired, then one must explicitly declare all components using the
* INIT_COMPONENTS
syntax.
*/
@Override
@SuppressWarnings("unchecked")
public void inform(SolrCore core) {
this.core = core;
List c = (List) initArgs.get(INIT_COMPONENTS);
Set missing = new HashSet<>(core.getSearchComponents().checkContains(c));
List first = (List) initArgs.get(INIT_FIRST_COMPONENTS);
missing.addAll(core.getSearchComponents().checkContains(first));
List last = (List) initArgs.get(INIT_LAST_COMPONENTS);
missing.addAll(core.getSearchComponents().checkContains(last));
if (!missing.isEmpty())
throw new SolrException(
SolrException.ErrorCode.SERVER_ERROR, "Missing SearchComponents named : " + missing);
if (c != null && (first != null || last != null))
throw new SolrException(
SolrException.ErrorCode.SERVER_ERROR,
"First/Last components only valid if you do not declare 'components'");
if (shfInfo == null) {
shardHandlerFactory = core.getCoreContainer().getShardHandlerFactory();
} else {
shardHandlerFactory = core.createInitInstance(shfInfo, ShardHandlerFactory.class, null, null);
core.addCloseHook(
new CloseHook() {
@Override
public void preClose(SolrCore core) {
shardHandlerFactory.close();
}
});
shardHandlerFactory.setSecurityBuilder(
core.getCoreContainer().getPkiAuthenticationSecurityBuilder());
}
if (core.getCoreContainer().isZooKeeperAware()) {
core.getPackageListeners()
.addListener(
new PackageListeners.Listener() {
@Override
public String packageName() {
return null;
}
@Override
public Map packageDetails() {
return Collections.emptyMap();
}
@Override
public void changed(SolrPackageLoader.SolrPackage pkg, Ctx ctx) {
// we could optimize this by listening to only relevant packages,
// but it is not worth optimizing as these are lightweight objects
components = null;
}
});
}
}
@SuppressWarnings({"unchecked"})
private void initComponents() {
Object declaredComponents = initArgs.get(INIT_COMPONENTS);
List first = (List) initArgs.get(INIT_FIRST_COMPONENTS);
List last = (List) initArgs.get(INIT_LAST_COMPONENTS);
List list = null;
boolean makeDebugLast = true;
if (declaredComponents == null) {
// Use the default component list
list = getDefaultComponents();
if (first != null) {
List clist = first;
clist.addAll(list);
list = clist;
}
if (last != null) {
list.addAll(last);
}
} else {
list = (List) declaredComponents;
if (first != null || last != null) {
throw new SolrException(
SolrException.ErrorCode.SERVER_ERROR,
"First/Last components only valid if you do not declare 'components'");
}
makeDebugLast = false;
}
// Build the component list
List components = new ArrayList<>(list.size());
DebugComponent dbgCmp = null;
for (String c : list) {
SearchComponent comp = core.getSearchComponent(c);
if (comp instanceof DebugComponent && makeDebugLast == true) {
dbgCmp = (DebugComponent) comp;
} else {
components.add(comp);
log.debug("Adding component:{}", comp);
}
}
if (makeDebugLast == true && dbgCmp != null) {
components.add(dbgCmp);
log.debug("Adding debug component:{}", dbgCmp);
}
this.components = components;
}
public List getComponents() {
List result = components; // volatile read
if (result == null) {
synchronized (this) {
if (components == null) {
initComponents();
}
result = components;
}
}
return result;
}
private boolean isDistrib(SolrQueryRequest req) {
boolean isZkAware = req.getCoreContainer().isZooKeeperAware();
boolean isDistrib = req.getParams().getBool(DISTRIB, isZkAware);
if (!isDistrib) {
// for back compat, a shards param with URLs like localhost:8983/solr will mean that this
// search is distributed.
final String shards = req.getParams().get(ShardParams.SHARDS);
isDistrib = ((shards != null) && (shards.indexOf('/') > 0));
}
return isDistrib;
}
public ShardHandler getAndPrepShardHandler(SolrQueryRequest req, ResponseBuilder rb) {
ShardHandler shardHandler = null;
CoreContainer cc = req.getCoreContainer();
boolean isZkAware = cc.isZooKeeperAware();
if (rb.isDistrib) {
shardHandler = shardHandlerFactory.getShardHandler();
shardHandler.prepDistributed(rb);
if (!rb.isDistrib) {
// request is not distributed after all and so the shard handler is not needed
shardHandler = null;
}
}
if (isZkAware) {
String shardsTolerant = req.getParams().get(ShardParams.SHARDS_TOLERANT);
boolean requireZkConnected =
shardsTolerant != null && shardsTolerant.equals(ShardParams.REQUIRE_ZK_CONNECTED);
ZkController zkController = cc.getZkController();
boolean zkConnected =
zkController != null
&& !zkController.getZkClient().getConnectionManager().isLikelyExpired();
if (requireZkConnected && false == zkConnected) {
throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, "ZooKeeper is not connected");
} else {
NamedList