/* * 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.action.bulk; import org.elasticsearch.ElasticsearchException; import org.elasticsearch.Version; import org.elasticsearch.action.DocWriteRequest; import org.elasticsearch.action.DocWriteResponse; import org.elasticsearch.action.bulk.TransportShardBulkAction.ReplicaItemExecutionMode; import org.elasticsearch.action.delete.DeleteRequest; import org.elasticsearch.action.delete.DeleteResponse; import org.elasticsearch.action.index.IndexRequest; import org.elasticsearch.action.index.IndexResponse; import org.elasticsearch.action.support.WriteRequest.RefreshPolicy; import org.elasticsearch.action.support.replication.ReplicationOperation; import org.elasticsearch.action.update.UpdateHelper; import org.elasticsearch.action.update.UpdateRequest; import org.elasticsearch.action.update.UpdateResponse; import org.elasticsearch.client.Requests; import org.elasticsearch.cluster.metadata.IndexMetaData; import org.elasticsearch.common.lucene.uid.Versions; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.index.VersionType; import org.elasticsearch.index.engine.Engine; import org.elasticsearch.index.engine.VersionConflictEngineException; import org.elasticsearch.index.mapper.MapperService; import org.elasticsearch.index.mapper.Mapping; import org.elasticsearch.index.shard.IndexShard; import org.elasticsearch.index.shard.IndexShardTestCase; import org.elasticsearch.index.shard.ShardId; import org.elasticsearch.index.translog.Translog; import org.elasticsearch.rest.RestStatus; import org.mockito.ArgumentCaptor; import java.io.IOException; import java.util.concurrent.atomic.AtomicInteger; import static org.elasticsearch.action.bulk.TransportShardBulkAction.replicaItemExecutionMode; import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.CoreMatchers.not; import static org.hamcrest.Matchers.containsString; import static org.mockito.Mockito.any; import static org.mockito.Mockito.anyBoolean; import static org.mockito.Mockito.anyLong; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; public class TransportShardBulkActionTests extends IndexShardTestCase { private final ShardId shardId = new ShardId("index", "_na_", 0); private final Settings idxSettings = Settings.builder() .put("index.number_of_shards", 1) .put("index.number_of_replicas", 0) .put("index.version.created", Version.CURRENT.id) .build(); private IndexMetaData indexMetaData() throws IOException { return IndexMetaData.builder("index") .putMapping("type", "{\"properties\":{\"foo\":{\"type\":\"text\",\"fields\":" + "{\"keyword\":{\"type\":\"keyword\",\"ignore_above\":256}}}}}") .settings(idxSettings) .primaryTerm(0, 1).build(); } public void testShouldExecuteReplicaItem() throws Exception { // Successful index request should be replicated DocWriteRequest writeRequest = new IndexRequest("index", "type", "id") .source(Requests.INDEX_CONTENT_TYPE, "foo", "bar"); DocWriteResponse response = new IndexResponse(shardId, "type", "id", 1, 17, 1, randomBoolean()); BulkItemRequest request = new BulkItemRequest(0, writeRequest); request.setPrimaryResponse(new BulkItemResponse(0, DocWriteRequest.OpType.INDEX, response)); assertThat(replicaItemExecutionMode(request, 0), equalTo(ReplicaItemExecutionMode.NORMAL)); // Failed index requests without sequence no should not be replicated writeRequest = new IndexRequest("index", "type", "id") .source(Requests.INDEX_CONTENT_TYPE, "foo", "bar"); request = new BulkItemRequest(0, writeRequest); request.setPrimaryResponse( new BulkItemResponse(0, DocWriteRequest.OpType.INDEX, new BulkItemResponse.Failure("index", "type", "id", new IllegalArgumentException("i died")))); assertThat(replicaItemExecutionMode(request, 0), equalTo(ReplicaItemExecutionMode.NOOP)); // Failed index requests with sequence no should be replicated request = new BulkItemRequest(0, writeRequest); request.setPrimaryResponse( new BulkItemResponse(0, DocWriteRequest.OpType.INDEX, new BulkItemResponse.Failure("index", "type", "id", new IllegalArgumentException( "i died after sequence no was generated"), 1))); assertThat(replicaItemExecutionMode(request, 0), equalTo(ReplicaItemExecutionMode.FAILURE)); // NOOP requests should not be replicated writeRequest = new UpdateRequest("index", "type", "id"); response = new UpdateResponse(shardId, "type", "id", 1, DocWriteResponse.Result.NOOP); request = new BulkItemRequest(0, writeRequest); request.setPrimaryResponse(new BulkItemResponse(0, DocWriteRequest.OpType.UPDATE, response)); assertThat(replicaItemExecutionMode(request, 0), equalTo(ReplicaItemExecutionMode.NOOP)); } public void testExecuteBulkIndexRequest() throws Exception { IndexMetaData metaData = indexMetaData(); IndexShard shard = newStartedShard(true); BulkItemRequest[] items = new BulkItemRequest[1]; boolean create = randomBoolean(); DocWriteRequest writeRequest = new IndexRequest("index", "type", "id") .source(Requests.INDEX_CONTENT_TYPE, "foo", "bar") .create(create); BulkItemRequest primaryRequest = new BulkItemRequest(0, writeRequest); items[0] = primaryRequest; BulkShardRequest bulkShardRequest = new BulkShardRequest(shardId, RefreshPolicy.NONE, items); Translog.Location location = new Translog.Location(0, 0, 0); UpdateHelper updateHelper = null; Translog.Location newLocation = TransportShardBulkAction.executeBulkItemRequest(metaData, shard, bulkShardRequest, location, 0, updateHelper, threadPool::absoluteTimeInMillis, new NoopMappingUpdatePerformer()); // Translog should change, since there were no problems assertThat(newLocation, not(location)); BulkItemResponse primaryResponse = bulkShardRequest.items()[0].getPrimaryResponse(); assertThat(primaryResponse.getItemId(), equalTo(0)); assertThat(primaryResponse.getId(), equalTo("id")); assertThat(primaryResponse.getOpType(), equalTo(create ? DocWriteRequest.OpType.CREATE : DocWriteRequest.OpType.INDEX)); assertFalse(primaryResponse.isFailed()); // Assert that the document actually made it there assertDocCount(shard, 1); writeRequest = new IndexRequest("index", "type", "id") .source(Requests.INDEX_CONTENT_TYPE, "foo", "bar") .create(true); primaryRequest = new BulkItemRequest(0, writeRequest); items[0] = primaryRequest; bulkShardRequest = new BulkShardRequest(shardId, RefreshPolicy.NONE, items); Translog.Location secondLocation = TransportShardBulkAction.executeBulkItemRequest( metaData, shard, bulkShardRequest, newLocation, 0, updateHelper, threadPool::absoluteTimeInMillis, new NoopMappingUpdatePerformer()); // Translog should not change, since the document was not indexed due to a version conflict assertThat(secondLocation, equalTo(newLocation)); BulkItemRequest replicaRequest = bulkShardRequest.items()[0]; primaryResponse = bulkShardRequest.items()[0].getPrimaryResponse(); assertThat(primaryResponse.getItemId(), equalTo(0)); assertThat(primaryResponse.getId(), equalTo("id")); assertThat(primaryResponse.getOpType(), equalTo(DocWriteRequest.OpType.CREATE)); // Should be failed since the document already exists assertTrue(primaryResponse.isFailed()); BulkItemResponse.Failure failure = primaryResponse.getFailure(); assertThat(failure.getIndex(), equalTo("index")); assertThat(failure.getType(), equalTo("type")); assertThat(failure.getId(), equalTo("id")); assertThat(failure.getCause().getClass(), equalTo(VersionConflictEngineException.class)); assertThat(failure.getCause().getMessage(), containsString("version conflict, document already exists (current version [1])")); assertThat(failure.getStatus(), equalTo(RestStatus.CONFLICT)); assertThat(replicaRequest, equalTo(primaryRequest)); // Assert that the document count is still 1 assertDocCount(shard, 1); closeShards(shard); } public void testExecuteBulkIndexRequestWithRejection() throws Exception { IndexMetaData metaData = indexMetaData(); IndexShard shard = newStartedShard(true); BulkItemRequest[] items = new BulkItemRequest[1]; DocWriteRequest writeRequest = new IndexRequest("index", "type", "id") .source(Requests.INDEX_CONTENT_TYPE, "foo", "bar"); items[0] = new BulkItemRequest(0, writeRequest); BulkShardRequest bulkShardRequest = new BulkShardRequest(shardId, RefreshPolicy.NONE, items); Translog.Location location = new Translog.Location(0, 0, 0); UpdateHelper updateHelper = null; // Pretend the mappings haven't made it to the node yet, and throw a rejection Exception err = new ReplicationOperation.RetryOnPrimaryException(shardId, "rejection"); try { TransportShardBulkAction.executeBulkItemRequest(metaData, shard, bulkShardRequest, location, 0, updateHelper, threadPool::absoluteTimeInMillis, new ThrowingMappingUpdatePerformer(err)); fail("should have thrown a retry exception"); } catch (ReplicationOperation.RetryOnPrimaryException e) { assertThat(e, equalTo(err)); } closeShards(shard); } public void testExecuteBulkIndexRequestWithConflictingMappings() throws Exception { IndexMetaData metaData = indexMetaData(); IndexShard shard = newStartedShard(true); BulkItemRequest[] items = new BulkItemRequest[1]; DocWriteRequest writeRequest = new IndexRequest("index", "type", "id") .source(Requests.INDEX_CONTENT_TYPE, "foo", "bar"); items[0] = new BulkItemRequest(0, writeRequest); BulkShardRequest bulkShardRequest = new BulkShardRequest(shardId, RefreshPolicy.NONE, items); Translog.Location location = new Translog.Location(0, 0, 0); UpdateHelper updateHelper = null; // Return a mapping conflict (IAE) when trying to update the mapping Exception err = new IllegalArgumentException("mapping conflict"); Translog.Location newLocation = TransportShardBulkAction.executeBulkItemRequest(metaData, shard, bulkShardRequest, location, 0, updateHelper, threadPool::absoluteTimeInMillis, new ThrowingMappingUpdatePerformer(err)); // Translog shouldn't change, as there were conflicting mappings assertThat(newLocation, equalTo(location)); BulkItemResponse primaryResponse = bulkShardRequest.items()[0].getPrimaryResponse(); // Since this was not a conflict failure, the primary response // should be filled out with the failure information assertThat(primaryResponse.getItemId(), equalTo(0)); assertThat(primaryResponse.getId(), equalTo("id")); assertThat(primaryResponse.getOpType(), equalTo(DocWriteRequest.OpType.INDEX)); assertTrue(primaryResponse.isFailed()); assertThat(primaryResponse.getFailureMessage(), containsString("mapping conflict")); BulkItemResponse.Failure failure = primaryResponse.getFailure(); assertThat(failure.getIndex(), equalTo("index")); assertThat(failure.getType(), equalTo("type")); assertThat(failure.getId(), equalTo("id")); assertThat(failure.getCause(), equalTo(err)); assertThat(failure.getStatus(), equalTo(RestStatus.BAD_REQUEST)); closeShards(shard); } public void testExecuteBulkDeleteRequest() throws Exception { IndexMetaData metaData = indexMetaData(); IndexShard shard = newStartedShard(true); BulkItemRequest[] items = new BulkItemRequest[1]; DocWriteRequest writeRequest = new DeleteRequest("index", "type", "id"); items[0] = new BulkItemRequest(0, writeRequest); BulkShardRequest bulkShardRequest = new BulkShardRequest(shardId, RefreshPolicy.NONE, items); Translog.Location location = new Translog.Location(0, 0, 0); UpdateHelper updateHelper = null; Translog.Location newLocation = TransportShardBulkAction.executeBulkItemRequest(metaData, shard, bulkShardRequest, location, 0, updateHelper, threadPool::absoluteTimeInMillis, new NoopMappingUpdatePerformer()); // Translog changes, even though the document didn't exist assertThat(newLocation, not(location)); BulkItemRequest replicaRequest = bulkShardRequest.items()[0]; DocWriteRequest replicaDeleteRequest = replicaRequest.request(); BulkItemResponse primaryResponse = replicaRequest.getPrimaryResponse(); DeleteResponse response = primaryResponse.getResponse(); // Any version can be matched on replica assertThat(replicaDeleteRequest.version(), equalTo(Versions.MATCH_ANY)); assertThat(replicaDeleteRequest.versionType(), equalTo(VersionType.INTERNAL)); assertThat(primaryResponse.getItemId(), equalTo(0)); assertThat(primaryResponse.getId(), equalTo("id")); assertThat(primaryResponse.getOpType(), equalTo(DocWriteRequest.OpType.DELETE)); assertFalse(primaryResponse.isFailed()); assertThat(response.getResult(), equalTo(DocWriteResponse.Result.NOT_FOUND)); assertThat(response.getShardId(), equalTo(shard.shardId())); assertThat(response.getIndex(), equalTo("index")); assertThat(response.getType(), equalTo("type")); assertThat(response.getId(), equalTo("id")); assertThat(response.getVersion(), equalTo(1L)); assertThat(response.getSeqNo(), equalTo(0L)); assertThat(response.forcedRefresh(), equalTo(false)); // Now do the same after indexing the document, it should now find and delete the document indexDoc(shard, "type", "id", "{\"foo\": \"bar\"}"); writeRequest = new DeleteRequest("index", "type", "id"); items[0] = new BulkItemRequest(0, writeRequest); bulkShardRequest = new BulkShardRequest(shardId, RefreshPolicy.NONE, items); location = newLocation; newLocation = TransportShardBulkAction.executeBulkItemRequest(metaData, shard, bulkShardRequest, location, 0, updateHelper, threadPool::absoluteTimeInMillis, new NoopMappingUpdatePerformer()); // Translog changes, because the document was deleted assertThat(newLocation, not(location)); replicaRequest = bulkShardRequest.items()[0]; replicaDeleteRequest = replicaRequest.request(); primaryResponse = replicaRequest.getPrimaryResponse(); response = primaryResponse.getResponse(); // Any version can be matched on replica assertThat(replicaDeleteRequest.version(), equalTo(Versions.MATCH_ANY)); assertThat(replicaDeleteRequest.versionType(), equalTo(VersionType.INTERNAL)); assertThat(primaryResponse.getItemId(), equalTo(0)); assertThat(primaryResponse.getId(), equalTo("id")); assertThat(primaryResponse.getOpType(), equalTo(DocWriteRequest.OpType.DELETE)); assertFalse(primaryResponse.isFailed()); assertThat(response.getResult(), equalTo(DocWriteResponse.Result.DELETED)); assertThat(response.getShardId(), equalTo(shard.shardId())); assertThat(response.getIndex(), equalTo("index")); assertThat(response.getType(), equalTo("type")); assertThat(response.getId(), equalTo("id")); assertThat(response.getVersion(), equalTo(3L)); assertThat(response.getSeqNo(), equalTo(2L)); assertThat(response.forcedRefresh(), equalTo(false)); assertDocCount(shard, 0); closeShards(shard); } public void testNoopUpdateReplicaRequest() throws Exception { DocWriteRequest writeRequest = new IndexRequest("index", "type", "id") .source(Requests.INDEX_CONTENT_TYPE, "field", "value"); BulkItemRequest replicaRequest = new BulkItemRequest(0, writeRequest); DocWriteResponse noopUpdateResponse = new UpdateResponse(shardId, "index", "id", 0, DocWriteResponse.Result.NOOP); BulkItemResultHolder noopResults = new BulkItemResultHolder(noopUpdateResponse, null, replicaRequest); Translog.Location location = new Translog.Location(0, 0, 0); BulkItemRequest[] items = new BulkItemRequest[0]; BulkShardRequest bulkShardRequest = new BulkShardRequest(shardId, RefreshPolicy.NONE, items); BulkItemResponse primaryResponse = TransportShardBulkAction.createPrimaryResponse( noopResults, DocWriteRequest.OpType.UPDATE, bulkShardRequest); Translog.Location newLocation = TransportShardBulkAction.calculateTranslogLocation(location, noopResults); // Basically nothing changes in the request since it's a noop assertThat(newLocation, equalTo(location)); assertThat(primaryResponse.getItemId(), equalTo(0)); assertThat(primaryResponse.getId(), equalTo("id")); assertThat(primaryResponse.getOpType(), equalTo(DocWriteRequest.OpType.UPDATE)); assertThat(primaryResponse.getResponse(), equalTo(noopUpdateResponse)); assertThat(primaryResponse.getResponse().getResult(), equalTo(DocWriteResponse.Result.NOOP)); } public void testUpdateReplicaRequestWithFailure() throws Exception { DocWriteRequest writeRequest = new IndexRequest("index", "type", "id") .source(Requests.INDEX_CONTENT_TYPE, "field", "value"); BulkItemRequest replicaRequest = new BulkItemRequest(0, writeRequest); Exception err = new ElasticsearchException("I'm dead <(x.x)>"); Engine.IndexResult indexResult = new Engine.IndexResult(err, 0, 0); BulkItemResultHolder failedResults = new BulkItemResultHolder(null, indexResult, replicaRequest); Translog.Location location = new Translog.Location(0, 0, 0); BulkItemRequest[] items = new BulkItemRequest[0]; BulkShardRequest bulkShardRequest = new BulkShardRequest(shardId, RefreshPolicy.NONE, items); BulkItemResponse primaryResponse = TransportShardBulkAction.createPrimaryResponse( failedResults, DocWriteRequest.OpType.UPDATE, bulkShardRequest); Translog.Location newLocation = TransportShardBulkAction.calculateTranslogLocation(location, failedResults); // Since this was not a conflict failure, the primary response // should be filled out with the failure information assertThat(newLocation, equalTo(location)); assertThat(primaryResponse.getItemId(), equalTo(0)); assertThat(primaryResponse.getId(), equalTo("id")); assertThat(primaryResponse.getOpType(), equalTo(DocWriteRequest.OpType.INDEX)); assertTrue(primaryResponse.isFailed()); assertThat(primaryResponse.getFailureMessage(), containsString("I'm dead <(x.x)>")); BulkItemResponse.Failure failure = primaryResponse.getFailure(); assertThat(failure.getIndex(), equalTo("index")); assertThat(failure.getType(), equalTo("type")); assertThat(failure.getId(), equalTo("id")); assertThat(failure.getCause(), equalTo(err)); assertThat(failure.getStatus(), equalTo(RestStatus.INTERNAL_SERVER_ERROR)); } public void testUpdateReplicaRequestWithConflictFailure() throws Exception { DocWriteRequest writeRequest = new IndexRequest("index", "type", "id") .source(Requests.INDEX_CONTENT_TYPE, "field", "value"); BulkItemRequest replicaRequest = new BulkItemRequest(0, writeRequest); Exception err = new VersionConflictEngineException(shardId, "type", "id", "I'm conflicted <(;_;)>"); Engine.IndexResult indexResult = new Engine.IndexResult(err, 0, 0); BulkItemResultHolder failedResults = new BulkItemResultHolder(null, indexResult, replicaRequest); Translog.Location location = new Translog.Location(0, 0, 0); BulkItemRequest[] items = new BulkItemRequest[0]; BulkShardRequest bulkShardRequest = new BulkShardRequest(shardId, RefreshPolicy.NONE, items); BulkItemResponse primaryResponse = TransportShardBulkAction.createPrimaryResponse( failedResults, DocWriteRequest.OpType.UPDATE, bulkShardRequest); Translog.Location newLocation = TransportShardBulkAction.calculateTranslogLocation(location, failedResults); // Since this was not a conflict failure, the primary response // should be filled out with the failure information assertThat(newLocation, equalTo(location)); assertThat(primaryResponse.getItemId(), equalTo(0)); assertThat(primaryResponse.getId(), equalTo("id")); assertThat(primaryResponse.getOpType(), equalTo(DocWriteRequest.OpType.INDEX)); assertTrue(primaryResponse.isFailed()); assertThat(primaryResponse.getFailureMessage(), containsString("I'm conflicted <(;_;)>")); BulkItemResponse.Failure failure = primaryResponse.getFailure(); assertThat(failure.getIndex(), equalTo("index")); assertThat(failure.getType(), equalTo("type")); assertThat(failure.getId(), equalTo("id")); assertThat(failure.getCause(), equalTo(err)); assertThat(failure.getStatus(), equalTo(RestStatus.CONFLICT)); } public void testUpdateReplicaRequestWithSuccess() throws Exception { DocWriteRequest writeRequest = new IndexRequest("index", "type", "id") .source(Requests.INDEX_CONTENT_TYPE, "field", "value"); BulkItemRequest replicaRequest = new BulkItemRequest(0, writeRequest); boolean created = randomBoolean(); Translog.Location resultLocation = new Translog.Location(42, 42, 42); Engine.IndexResult indexResult = new FakeResult(1, 1, created, resultLocation); DocWriteResponse indexResponse = new IndexResponse(shardId, "index", "id", 1, 17, 1, created); BulkItemResultHolder goodResults = new BulkItemResultHolder(indexResponse, indexResult, replicaRequest); Translog.Location originalLocation = new Translog.Location(21, 21, 21); BulkItemRequest[] items = new BulkItemRequest[0]; BulkShardRequest bulkShardRequest = new BulkShardRequest(shardId, RefreshPolicy.NONE, items); BulkItemResponse primaryResponse = TransportShardBulkAction.createPrimaryResponse( goodResults, DocWriteRequest.OpType.INDEX, bulkShardRequest); Translog.Location newLocation = TransportShardBulkAction.calculateTranslogLocation(originalLocation, goodResults); // Check that the translog is successfully advanced assertThat(newLocation, equalTo(resultLocation)); // Since this was not a conflict failure, the primary response // should be filled out with the failure information assertThat(primaryResponse.getItemId(), equalTo(0)); assertThat(primaryResponse.getId(), equalTo("id")); assertThat(primaryResponse.getOpType(), equalTo(DocWriteRequest.OpType.INDEX)); DocWriteResponse response = primaryResponse.getResponse(); assertThat(response.status(), equalTo(created ? RestStatus.CREATED : RestStatus.OK)); } public void testCalculateTranslogLocation() throws Exception { final Translog.Location original = new Translog.Location(0, 0, 0); DocWriteRequest writeRequest = new IndexRequest("index", "type", "id") .source(Requests.INDEX_CONTENT_TYPE, "field", "value"); BulkItemRequest replicaRequest = new BulkItemRequest(0, writeRequest); BulkItemResultHolder results = new BulkItemResultHolder(null, null, replicaRequest); assertThat(TransportShardBulkAction.calculateTranslogLocation(original, results), equalTo(original)); boolean created = randomBoolean(); DocWriteResponse indexResponse = new IndexResponse(shardId, "index", "id", 1, 17, 1, created); Translog.Location newLocation = new Translog.Location(1, 1, 1); final long version = randomNonNegativeLong(); final long seqNo = randomNonNegativeLong(); final long primaryTerm = randomIntBetween(1, 16); Engine.IndexResult indexResult = new IndexResultWithLocation(version, seqNo, primaryTerm, created, newLocation); results = new BulkItemResultHolder(indexResponse, indexResult, replicaRequest); assertThat(TransportShardBulkAction.calculateTranslogLocation(original, results), equalTo(newLocation)); } public void testNoOpReplicationOnPrimaryDocumentFailure() throws Exception { final IndexShard shard = spy(newStartedShard(false)); BulkItemRequest itemRequest = new BulkItemRequest(0, new IndexRequest("index", "type") .source(Requests.INDEX_CONTENT_TYPE, "foo", "bar") ); final String failureMessage = "simulated primary failure"; itemRequest.setPrimaryResponse(new BulkItemResponse(0, randomFrom( DocWriteRequest.OpType.CREATE, DocWriteRequest.OpType.DELETE, DocWriteRequest.OpType.INDEX ), new BulkItemResponse.Failure("index", "type", "1", new IOException(failureMessage), 1L) )); BulkItemRequest[] itemRequests = new BulkItemRequest[1]; itemRequests[0] = itemRequest; BulkShardRequest bulkShardRequest = new BulkShardRequest( shard.shardId(), RefreshPolicy.NONE, itemRequests); TransportShardBulkAction.performOnReplica(bulkShardRequest, shard); ArgumentCaptor<Engine.NoOp> noOp = ArgumentCaptor.forClass(Engine.NoOp.class); verify(shard, times(1)).markSeqNoAsNoOp(noOp.capture()); final Engine.NoOp noOpValue = noOp.getValue(); assertThat(noOpValue.seqNo(), equalTo(1L)); assertThat(noOpValue.reason(), containsString(failureMessage)); closeShards(shard); } public void testMappingUpdateParsesCorrectNumberOfTimes() throws Exception { IndexMetaData metaData = indexMetaData(); logger.info("--> metadata.getIndex(): {}", metaData.getIndex()); final IndexShard shard = spy(newStartedShard(true)); IndexRequest request = new IndexRequest("index", "type", "id") .source(Requests.INDEX_CONTENT_TYPE, "foo", "bar"); final AtomicInteger updateCalled = new AtomicInteger(0); final AtomicInteger verifyCalled = new AtomicInteger(0); TransportShardBulkAction.executeIndexRequestOnPrimary(request, shard, new MappingUpdatePerformer() { @Override public void updateMappings(Mapping update, ShardId shardId, String type) throws Exception { // There should indeed be a mapping update assertNotNull(update); updateCalled.incrementAndGet(); } @Override public void verifyMappings(Mapping update, ShardId shardId) throws Exception { // No-op, will be called logger.info("--> verifying mappings noop"); verifyCalled.incrementAndGet(); } }); assertThat("mappings were \"updated\" once", updateCalled.get(), equalTo(1)); assertThat("mappings were \"verified\" once", verifyCalled.get(), equalTo(1)); // Verify that the shard "prepared" the operation twice verify(shard, times(2)).prepareIndexOnPrimary(any(), anyLong(), any(), anyLong(), anyBoolean()); // Update the mapping, so the next mapping updater doesn't do anything final MapperService mapperService = shard.mapperService(); logger.info("--> mapperService.index(): {}", mapperService.index()); mapperService.updateMapping(metaData); TransportShardBulkAction.executeIndexRequestOnPrimary(request, shard, new MappingUpdatePerformer() { @Override public void updateMappings(Mapping update, ShardId shardId, String type) throws Exception { fail("should not have had to update the mappings"); } @Override public void verifyMappings(Mapping update, ShardId shardId) throws Exception { fail("should not have had to update the mappings"); } }); // Verify that the shard "prepared" the operation only once (2 for previous invocations plus // 1 for this execution) verify(shard, times(3)).prepareIndexOnPrimary(any(), anyLong(), any(), anyLong(), anyBoolean()); closeShards(shard); } public class IndexResultWithLocation extends Engine.IndexResult { private final Translog.Location location; public IndexResultWithLocation(long version, long seqNo, long primaryTerm, boolean created, Translog.Location newLocation) { super(version, seqNo, created); this.location = newLocation; } @Override public Translog.Location getTranslogLocation() { return this.location; } } public void testPrepareIndexOpOnReplica() throws Exception { IndexMetaData metaData = indexMetaData(); IndexShard shard = newStartedShard(false); DocWriteResponse primaryResponse = new IndexResponse(shardId, "index", "id", 1, 17, 1, randomBoolean()); IndexRequest request = new IndexRequest("index", "type", "id") .source(Requests.INDEX_CONTENT_TYPE, "field", "value"); Engine.Index op = TransportShardBulkAction.prepareIndexOperationOnReplica( primaryResponse, request, shard); assertThat(op.version(), equalTo(primaryResponse.getVersion())); assertThat(op.seqNo(), equalTo(primaryResponse.getSeqNo())); assertThat(op.versionType(), equalTo(VersionType.EXTERNAL)); closeShards(shard); } /** * Fake IndexResult that has a settable translog location */ private static class FakeResult extends Engine.IndexResult { private final Translog.Location location; protected FakeResult(long version, long seqNo, boolean created, Translog.Location location) { super(version, seqNo, created); this.location = location; } @Override public Translog.Location getTranslogLocation() { return this.location; } } /** Doesn't perform any mapping updates */ public static class NoopMappingUpdatePerformer implements MappingUpdatePerformer { public void updateMappings(Mapping update, ShardId shardId, String type) throws Exception { } public void verifyMappings(Mapping update, ShardId shardId) throws Exception { } } /** Always throw the given exception */ private class ThrowingMappingUpdatePerformer implements MappingUpdatePerformer { private final Exception e; ThrowingMappingUpdatePerformer(Exception e) { this.e = e; } public void updateMappings(Mapping update, ShardId shardId, String type) throws Exception { throw e; } public void verifyMappings(Mapping update, ShardId shardId) throws Exception { fail("should not have gotten to this point"); } } /** Always throw the given exception */ private class ThrowingVerifyingMappingUpdatePerformer implements MappingUpdatePerformer { private final Exception e; ThrowingVerifyingMappingUpdatePerformer(Exception e) { this.e = e; } public void updateMappings(Mapping update, ShardId shardId, String type) throws Exception { } public void verifyMappings(Mapping update, ShardId shardId) throws Exception { throw e; } } }