/* * Licensed to Elasticsearch under one or more contributor * license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright * ownership. Elasticsearch 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.elasticsearch.index.reindex; import org.elasticsearch.action.bulk.BulkItemResponse.Failure; import org.elasticsearch.action.index.IndexRequestBuilder; import org.elasticsearch.test.junit.annotations.TestLogging; import java.util.ArrayList; import java.util.List; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; import static org.elasticsearch.action.DocWriteRequest.OpType.CREATE; import static org.hamcrest.Matchers.both; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.either; import static org.hamcrest.Matchers.greaterThan; import static org.hamcrest.Matchers.lessThanOrEqualTo; /** * Tests failure capturing and abort-on-failure behavior of reindex. */ @TestLogging("_root:DEBUG") public class ReindexFailureTests extends ReindexTestCase { public void testFailuresCauseAbortDefault() throws Exception { /* * Create the destination index such that the copy will cause a mapping * conflict on every request. */ indexRandom(true, client().prepareIndex("dest", "test", "test").setSource("test", 10) /* Its a string in the source! */); indexDocs(100); ReindexRequestBuilder copy = reindex().source("source").destination("dest"); /* * Set the search size to something very small to cause there to be * multiple batches for this request so we can assert that we abort on * the first batch. */ copy.source().setSize(1); BulkByScrollResponse response = copy.get(); assertThat(response, matcher() .batches(1) .failures(both(greaterThan(0)).and(lessThanOrEqualTo(maximumNumberOfShards())))); for (Failure failure: response.getBulkFailures()) { assertThat(failure.getMessage(), containsString("NumberFormatException[For input string: \"words words\"]")); } } public void testAbortOnVersionConflict() throws Exception { // Just put something in the way of the copy. indexRandom(true, client().prepareIndex("dest", "test", "1").setSource("test", "test")); indexDocs(100); ReindexRequestBuilder copy = reindex().source("source").destination("dest").abortOnVersionConflict(true); // CREATE will cause the conflict to prevent the write. copy.destination().setOpType(CREATE); BulkByScrollResponse response = copy.get(); assertThat(response, matcher().batches(1).versionConflicts(1).failures(1).created(99)); for (Failure failure: response.getBulkFailures()) { assertThat(failure.getMessage(), containsString("VersionConflictEngineException[[test][")); } } /** * Make sure that search failures get pushed back to the user as failures of * the whole process. We do lose some information about how far along the * process got, but its important that they see these failures. */ public void testResponseOnSearchFailure() throws Exception { /* * Attempt to trigger a reindex failure by deleting the source index out * from under it. */ int attempt = 1; while (attempt < 5) { indexDocs(100); ReindexRequestBuilder copy = reindex().source("source").destination("dest"); copy.source().setSize(10); Future<BulkByScrollResponse> response = copy.execute(); client().admin().indices().prepareDelete("source").get(); try { response.get(); logger.info("Didn't trigger a reindex failure on the {} attempt", attempt); attempt++; } catch (ExecutionException e) { logger.info("Triggered a reindex failure on the {} attempt: {}", attempt, e.getMessage()); assertThat(e.getMessage(), either(containsString("all shards failed")) .or(containsString("No search context found")) .or(containsString("no such index")) ); return; } } assumeFalse("Wasn't able to trigger a reindex failure in " + attempt + " attempts.", true); } private void indexDocs(int count) throws Exception { List<IndexRequestBuilder> docs = new ArrayList<IndexRequestBuilder>(count); for (int i = 0; i < count; i++) { docs.add(client().prepareIndex("source", "test", Integer.toString(i)).setSource("test", "words words")); } indexRandom(true, docs); } }