SOLR-4127: Added explicit error message if users attempt Atomic document updates with either updateLog or DistribUpdateProcessor

git-svn-id: https://svn.apache.org/repos/asf/lucene/dev/trunk@1420297 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Chris M. Hostetter 2012-12-11 17:25:02 +00:00
parent c02c9dd262
commit 0b3a7900fc
5 changed files with 146 additions and 12 deletions

View File

@ -312,6 +312,9 @@ Bug Fixes
same way as on startup when collection.configName is not explicitly passed. same way as on startup when collection.configName is not explicitly passed.
(Po Rui, Mark Miller) (Po Rui, Mark Miller)
* SOLR-4127: Added explicit error message if users attempt Atomic document
updates with either updateLog or DistribUpdateProcessor. (hossman)
Other Changes Other Changes
---------------------- ----------------------

View File

@ -457,11 +457,22 @@ public class DistributedUpdateProcessor extends UpdateRequestProcessor {
private boolean versionAdd(AddUpdateCommand cmd) throws IOException { private boolean versionAdd(AddUpdateCommand cmd) throws IOException {
BytesRef idBytes = cmd.getIndexedId(); BytesRef idBytes = cmd.getIndexedId();
if (vinfo == null || idBytes == null) { if (idBytes == null) {
super.processAdd(cmd); super.processAdd(cmd);
return false; return false;
} }
if (vinfo == null) {
if (isAtomicUpdate(cmd)) {
throw new SolrException
(SolrException.ErrorCode.BAD_REQUEST,
"Atomic document updates are not supported unless <updateLog/> is configured");
} else {
super.processAdd(cmd);
return false;
}
}
// This is only the hash for the bucket, and must be based only on the uniqueKey (i.e. do not use a pluggable hash here) // This is only the hash for the bucket, and must be based only on the uniqueKey (i.e. do not use a pluggable hash here)
int bucketHash = Hash.murmurhash3_x86_32(idBytes.bytes, idBytes.offset, idBytes.length, 0); int bucketHash = Hash.murmurhash3_x86_32(idBytes.bytes, idBytes.offset, idBytes.length, 0);
@ -580,21 +591,26 @@ public class DistributedUpdateProcessor extends UpdateRequestProcessor {
return false; return false;
} }
/**
* Utility method that examines the SolrInputDocument in an AddUpdateCommand
* and returns true if the documents contains atomic update instructions.
*/
public static boolean isAtomicUpdate(final AddUpdateCommand cmd) {
SolrInputDocument sdoc = cmd.getSolrInputDocument();
for (SolrInputField sif : sdoc.values()) {
if (sif.getValue() instanceof Map) {
return true;
}
}
return false;
}
// TODO: may want to switch to using optimistic locking in the future for better concurrency // TODO: may want to switch to using optimistic locking in the future for better concurrency
// that's why this code is here... need to retry in a loop closely around/in versionAdd // that's why this code is here... need to retry in a loop closely around/in versionAdd
boolean getUpdatedDocument(AddUpdateCommand cmd, long versionOnUpdate) throws IOException { boolean getUpdatedDocument(AddUpdateCommand cmd, long versionOnUpdate) throws IOException {
if (!isAtomicUpdate(cmd)) return false;
SolrInputDocument sdoc = cmd.getSolrInputDocument(); SolrInputDocument sdoc = cmd.getSolrInputDocument();
boolean update = false;
for (SolrInputField sif : sdoc.values()) {
if (sif.getValue() instanceof Map) {
update = true;
break;
}
}
if (!update) return false;
BytesRef id = cmd.getIndexedId(); BytesRef id = cmd.getIndexedId();
SolrInputDocument oldDoc = RealTimeGetComponent.getInputDocument(cmd.getReq().getCore(), id); SolrInputDocument oldDoc = RealTimeGetComponent.getInputDocument(cmd.getReq().getCore(), id);

View File

@ -18,7 +18,8 @@
package org.apache.solr.update.processor; package org.apache.solr.update.processor;
import java.io.IOException; import java.io.IOException;
import org.apache.solr.common.SolrException;
import org.apache.solr.common.SolrException.ErrorCode;
import org.apache.solr.request.SolrQueryRequest; import org.apache.solr.request.SolrQueryRequest;
import org.apache.solr.response.SolrQueryResponse; import org.apache.solr.response.SolrQueryResponse;
import org.apache.solr.update.*; import org.apache.solr.update.*;
@ -58,6 +59,13 @@ class RunUpdateProcessor extends UpdateRequestProcessor
@Override @Override
public void processAdd(AddUpdateCommand cmd) throws IOException { public void processAdd(AddUpdateCommand cmd) throws IOException {
if (DistributedUpdateProcessor.isAtomicUpdate(cmd)) {
throw new SolrException
(SolrException.ErrorCode.BAD_REQUEST,
"RunUpdateProcessor has recieved an AddUpdateCommand containing a document that appears to still contain Atomic document update operations, most likely because DistributedUpdateProcessorFactory was explicitly disabled from this updateRequestProcessorChain");
}
updateHandler.addDoc(cmd); updateHandler.addDoc(cmd);
super.processAdd(cmd); super.processAdd(cmd);
changesSinceCommit = true; changesSinceCommit = true;

View File

@ -22,6 +22,14 @@
<directoryFactory name="DirectoryFactory" class="${solr.directoryFactory:solr.RAMDirectoryFactory}"/> <directoryFactory name="DirectoryFactory" class="${solr.directoryFactory:solr.RAMDirectoryFactory}"/>
<dataDir>${solr.data.dir:}</dataDir> <dataDir>${solr.data.dir:}</dataDir>
<!-- an update processor the explicitly excludes distrib to test
clean errors when people attempt atomic updates w/o it
-->
<updateRequestProcessorChain name="nodistrib" >
<processor class="solr.NoOpDistributingUpdateProcessorFactory" />
<processor class="solr.RunUpdateProcessorFactory" />
</updateRequestProcessorChain>
<requestHandler name="standard" class="solr.StandardRequestHandler"> <requestHandler name="standard" class="solr.StandardRequestHandler">
</requestHandler> </requestHandler>

View File

@ -0,0 +1,99 @@
/*
* 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;
import org.apache.solr.SolrTestCaseJ4;
import org.apache.solr.update.DirectUpdateHandler2;
import org.apache.solr.common.SolrException;
public class TestAtomicUpdateErrorCases extends SolrTestCaseJ4 {
public void testUpdateNoTLog() throws Exception {
try {
initCore("solrconfig.xml","schema15.xml");
UpdateHandler uh = h.getCore().getUpdateHandler();
assertTrue("this test requires DirectUpdateHandler2",
uh instanceof DirectUpdateHandler2);
assertNull("this test requires that the updateLog not be enabled, it " +
"seems that someone modified the configs",
((DirectUpdateHandler2)uh).getUpdateLog());
// creating docs should work fine
addAndGetVersion(sdoc("id", "1", "val_i", "42"), null);
assertU(commit());
try {
ignoreException("updateLog");
// updating docs should fail
addAndGetVersion(sdoc("id", "1", "val_i", map("inc",-666)), null);
fail("didn't get error about needing updateLog");
} catch (SolrException ex) {
assertEquals(400, ex.code());
// if the message doesn't match our expectation, wrap & rethrow
if (ex.getMessage().indexOf("unless <updateLog/> is configured") < 0) {
throw new RuntimeException("exception message is not expected", ex);
}
} finally {
resetExceptionIgnores();
}
} finally {
deleteCore();
}
}
public void testUpdateNoDistribProcessor() throws Exception {
try {
initCore("solrconfig-tlog.xml","schema15.xml");
assertNotNull("this test requires an update chain named 'nodistrib'",
h.getCore().getUpdateProcessingChain("nodistrib"));
// creating docs should work fine
addAndGetVersion(sdoc("id", "1", "val_i", "42"),
params("update.chain","nodistrib"));
assertU(commit());
try {
ignoreException("DistributedUpdateProcessorFactory");
// updating docs should fail
addAndGetVersion(sdoc("id", "1", "val_i", map("inc",-666)),
params("update.chain","nodistrib"));
fail("didn't get error about needing DistributedUpdateProcessorFactory");
} catch (SolrException ex) {
assertEquals(400, ex.code());
// if the message doesn't match our expectation, wrap & rethrow
if (ex.getMessage().indexOf("DistributedUpdateProcessorFactory") < 0) {
throw new RuntimeException("exception message is not expected", ex);
}
} finally {
resetExceptionIgnores();
}
} finally {
deleteCore();
}
}
}