org.apache.solr.update.processor.TolerantUpdateProcessorFactory 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.update.processor;
import static org.apache.solr.update.processor.DistributingUpdateProcessorFactory.DISTRIB_UPDATE_PARAM;
import org.apache.solr.common.SolrException;
import org.apache.solr.common.SolrException.ErrorCode;
import org.apache.solr.common.util.NamedList;
import org.apache.solr.core.SolrCore;
import org.apache.solr.request.SolrQueryRequest;
import org.apache.solr.response.SolrQueryResponse;
import org.apache.solr.update.processor.DistributedUpdateProcessor.DistribPhase;
import org.apache.solr.util.plugin.SolrCoreAware;
/**
* Suppresses errors for individual add/delete commands within a single request. Instead of failing
* on the first error, at most maxErrors
errors (or unlimited if -1==maxErrors
*
) are logged and recorded the batch continues. The client will receive a status==200
*
response, which includes a list of errors that were tolerated.
*
* If more then maxErrors
occur, the first exception recorded will be re-thrown,
* Solr will respond with status==5xx
or status==4xx
(depending on the
* underlying exceptions) and it won't finish processing any more updates in the request. (ie:
* subsequent update commands in the request will not be processed even if they are valid).
*
*
maxErrors
is an int value that can be specified in the configuration and/or
* overridden per request. If unset, it will default to {@link Integer#MAX_VALUE}. Specifying an
* explicit value of -1
is supported as shorthand for {@link Integer#MAX_VALUE}, all
* other negative integer values are not supported.
*
*
An example configuration would be:
*
*
* <updateRequestProcessorChain name="tolerant-chain">
* <processor class="solr.TolerantUpdateProcessorFactory">
* <int name="maxErrors">10</int>
* </processor>
* <processor class="solr.RunUpdateProcessorFactory" />
* </updateRequestProcessorChain>
*
*
*
* The maxErrors
parameter in the above chain could be overwritten per request, for
* example:
*
*
* curl http://localhost:8983/update?update.chain=tolerant-chain&maxErrors=100 -H "Content-Type: text/xml" -d @myfile.xml
*
*
* NOTE: The behavior of this UpdateProcessofFactory in conjunction with indexing
* operations while a Shard Split is actively in progress is not well defined (or sufficiently
* tested). Users of this update processor are encouraged to either disable it, or pause updates,
* while any shard splitting is in progress (see SOLR-8881 for more details.)
*
* @since 6.1.0
*/
public class TolerantUpdateProcessorFactory extends UpdateRequestProcessorFactory
implements SolrCoreAware, UpdateRequestProcessorFactory.RunAlways {
/** Parameter that defines how many errors the UpdateRequestProcessor will tolerate */
private static final String MAX_ERRORS_PARAM = "maxErrors";
/**
* Default maxErrors value that will be use if the value is not set in configuration or in the
* request
*/
private int defaultMaxErrors = Integer.MAX_VALUE;
private boolean informed = false;
@Override
public void init(NamedList args) {
Object maxErrorsObj = args.get(MAX_ERRORS_PARAM);
if (maxErrorsObj != null) {
try {
defaultMaxErrors = Integer.parseInt(maxErrorsObj.toString());
} catch (Exception e) {
throw new SolrException(
ErrorCode.SERVER_ERROR, "Unnable to parse maxErrors parameter: " + maxErrorsObj, e);
}
if (defaultMaxErrors < -1) {
throw new SolrException(
ErrorCode.SERVER_ERROR,
"Config option '"
+ MAX_ERRORS_PARAM
+ "' must either be non-negative, or -1 to indicate 'unlimiited': "
+ maxErrorsObj.toString());
}
}
}
@Override
public void inform(SolrCore core) {
informed = true;
if (null == core.getLatestSchema().getUniqueKeyField()) {
throw new SolrException(
ErrorCode.SERVER_ERROR,
this.getClass().getName() + " requires a schema that includes a uniqueKey field.");
}
}
@Override
public UpdateRequestProcessor getInstance(
SolrQueryRequest req, SolrQueryResponse rsp, UpdateRequestProcessor next) {
assert informed : "inform(SolrCore) never called?";
// short circut if we're a replica processing commands from our leader
DistribPhase distribPhase = DistribPhase.parseParam(req.getParams().get(DISTRIB_UPDATE_PARAM));
if (DistribPhase.FROMLEADER.equals(distribPhase)) {
return next;
}
DistributedUpdateProcessorFactory.addParamToDistributedRequestWhitelist(req, MAX_ERRORS_PARAM);
int maxErrors = req.getParams().getInt(MAX_ERRORS_PARAM, defaultMaxErrors);
if (maxErrors < -1) {
throw new SolrException(
ErrorCode.BAD_REQUEST,
"'"
+ MAX_ERRORS_PARAM
+ "' must either be non-negative, or -1 to indicate 'unlimiited': "
+ maxErrors);
}
// NOTE: even if 0==maxErrors, we still inject processor into chain so responses has expected
// header info
return new TolerantUpdateProcessor(req, rsp, next, maxErrors, distribPhase);
}
}