diff --git a/server/src/main/java/org/elasticsearch/ElasticsearchException.java b/server/src/main/java/org/elasticsearch/ElasticsearchException.java index 2e04d03438e..861228d2217 100644 --- a/server/src/main/java/org/elasticsearch/ElasticsearchException.java +++ b/server/src/main/java/org/elasticsearch/ElasticsearchException.java @@ -1012,7 +1012,17 @@ public class ElasticsearchException extends RuntimeException implements ToXConte SNAPSHOT_IN_PROGRESS_EXCEPTION(org.elasticsearch.snapshots.SnapshotInProgressException.class, org.elasticsearch.snapshots.SnapshotInProgressException::new, 151, Version.V_6_7_0), NO_SUCH_REMOTE_CLUSTER_EXCEPTION(org.elasticsearch.transport.NoSuchRemoteClusterException.class, - org.elasticsearch.transport.NoSuchRemoteClusterException::new, 152, Version.V_6_7_0); + org.elasticsearch.transport.NoSuchRemoteClusterException::new, 152, Version.V_6_7_0), + RETENTION_LEASE_ALREADY_EXISTS_EXCEPTION( + org.elasticsearch.index.seqno.RetentionLeaseAlreadyExistsException.class, + org.elasticsearch.index.seqno.RetentionLeaseAlreadyExistsException::new, + 153, + Version.V_6_7_0), + RETENTION_LEASE_NOT_FOUND_EXCEPTION( + org.elasticsearch.index.seqno.RetentionLeaseNotFoundException.class, + org.elasticsearch.index.seqno.RetentionLeaseNotFoundException::new, + 154, + Version.V_6_7_0); final Class exceptionClass; final CheckedFunction constructor; diff --git a/server/src/main/java/org/elasticsearch/index/seqno/ReplicationTracker.java b/server/src/main/java/org/elasticsearch/index/seqno/ReplicationTracker.java index 1d598d3d50a..3e4e83d365e 100644 --- a/server/src/main/java/org/elasticsearch/index/seqno/ReplicationTracker.java +++ b/server/src/main/java/org/elasticsearch/index/seqno/ReplicationTracker.java @@ -237,7 +237,7 @@ public class ReplicationTracker extends AbstractIndexShardComponent implements L synchronized (this) { assert primaryMode; if (retentionLeases.contains(id)) { - throw new IllegalArgumentException("retention lease with ID [" + id + "] already exists"); + throw new RetentionLeaseAlreadyExistsException(id); } retentionLease = new RetentionLease(id, retainingSequenceNumber, currentTimeMillisSupplier.getAsLong(), source); retentionLeases = new RetentionLeases( @@ -262,7 +262,7 @@ public class ReplicationTracker extends AbstractIndexShardComponent implements L public synchronized RetentionLease renewRetentionLease(final String id, final long retainingSequenceNumber, final String source) { assert primaryMode; if (retentionLeases.contains(id) == false) { - throw new IllegalArgumentException("retention lease with ID [" + id + "] does not exist"); + throw new RetentionLeaseNotFoundException(id); } final RetentionLease retentionLease = new RetentionLease(id, retainingSequenceNumber, currentTimeMillisSupplier.getAsLong(), source); @@ -295,7 +295,7 @@ public class ReplicationTracker extends AbstractIndexShardComponent implements L synchronized (this) { assert primaryMode; if (retentionLeases.contains(id) == false) { - throw new IllegalArgumentException("retention lease with ID [" + id + "] does not exist"); + throw new RetentionLeaseNotFoundException(id); } retentionLeases = new RetentionLeases( operationPrimaryTerm, diff --git a/server/src/main/java/org/elasticsearch/index/seqno/RetentionLeaseAlreadyExistsException.java b/server/src/main/java/org/elasticsearch/index/seqno/RetentionLeaseAlreadyExistsException.java new file mode 100644 index 00000000000..aaa41a7b400 --- /dev/null +++ b/server/src/main/java/org/elasticsearch/index/seqno/RetentionLeaseAlreadyExistsException.java @@ -0,0 +1,38 @@ +/* + * 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.seqno; + +import org.elasticsearch.ResourceAlreadyExistsException; +import org.elasticsearch.common.io.stream.StreamInput; + +import java.io.IOException; +import java.util.Objects; + +public class RetentionLeaseAlreadyExistsException extends ResourceAlreadyExistsException { + + RetentionLeaseAlreadyExistsException(final String id) { + super("retention lease with ID [" + Objects.requireNonNull(id) + "] already exists"); + } + + public RetentionLeaseAlreadyExistsException(final StreamInput in) throws IOException { + super(in); + } + +} diff --git a/server/src/main/java/org/elasticsearch/index/seqno/RetentionLeaseNotFoundException.java b/server/src/main/java/org/elasticsearch/index/seqno/RetentionLeaseNotFoundException.java new file mode 100644 index 00000000000..d975077327f --- /dev/null +++ b/server/src/main/java/org/elasticsearch/index/seqno/RetentionLeaseNotFoundException.java @@ -0,0 +1,38 @@ +/* + * 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.seqno; + +import org.elasticsearch.ResourceNotFoundException; +import org.elasticsearch.common.io.stream.StreamInput; + +import java.io.IOException; +import java.util.Objects; + +public class RetentionLeaseNotFoundException extends ResourceNotFoundException { + + RetentionLeaseNotFoundException(final String id) { + super("retention lease with ID [" + Objects.requireNonNull(id) + "] not found"); + } + + public RetentionLeaseNotFoundException(final StreamInput in) throws IOException { + super(in); + } + +} diff --git a/server/src/test/java/org/elasticsearch/ExceptionSerializationTests.java b/server/src/test/java/org/elasticsearch/ExceptionSerializationTests.java index 8ac056aa41c..8f62e5c8156 100644 --- a/server/src/test/java/org/elasticsearch/ExceptionSerializationTests.java +++ b/server/src/test/java/org/elasticsearch/ExceptionSerializationTests.java @@ -61,6 +61,8 @@ import org.elasticsearch.env.ShardLockObtainFailedException; import org.elasticsearch.index.Index; import org.elasticsearch.index.engine.RecoveryEngineException; import org.elasticsearch.index.query.QueryShardException; +import org.elasticsearch.index.seqno.RetentionLeaseAlreadyExistsException; +import org.elasticsearch.index.seqno.RetentionLeaseNotFoundException; import org.elasticsearch.index.shard.IllegalIndexShardStateException; import org.elasticsearch.index.shard.IndexShardState; import org.elasticsearch.index.shard.ShardId; @@ -812,6 +814,8 @@ public class ExceptionSerializationTests extends ESTestCase { ids.put(150, CoordinationStateRejectedException.class); ids.put(151, SnapshotInProgressException.class); ids.put(152, NoSuchRemoteClusterException.class); + ids.put(153, RetentionLeaseAlreadyExistsException.class); + ids.put(154, RetentionLeaseNotFoundException.class); Map, Integer> reverse = new HashMap<>(); for (Map.Entry> entry : ids.entrySet()) { diff --git a/server/src/test/java/org/elasticsearch/index/seqno/ReplicationTrackerRetentionLeaseTests.java b/server/src/test/java/org/elasticsearch/index/seqno/ReplicationTrackerRetentionLeaseTests.java index d47b45a5ec6..a9aae80db6c 100644 --- a/server/src/test/java/org/elasticsearch/index/seqno/ReplicationTrackerRetentionLeaseTests.java +++ b/server/src/test/java/org/elasticsearch/index/seqno/ReplicationTrackerRetentionLeaseTests.java @@ -41,10 +41,12 @@ import java.util.stream.Collectors; import static org.elasticsearch.index.seqno.SequenceNumbers.UNASSIGNED_SEQ_NO; import static org.hamcrest.Matchers.contains; +import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.empty; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.hasItem; import static org.hamcrest.Matchers.hasSize; +import static org.hamcrest.Matchers.hasToString; public class ReplicationTrackerRetentionLeaseTests extends ReplicationTrackerTestCase { @@ -90,6 +92,60 @@ public class ReplicationTrackerRetentionLeaseTests extends ReplicationTrackerTes } } + public void testAddDuplicateRetentionLease() { + final AllocationId allocationId = AllocationId.newInitializing(); + long primaryTerm = randomLongBetween(1, Long.MAX_VALUE); + final ReplicationTracker replicationTracker = new ReplicationTracker( + new ShardId("test", "_na", 0), + allocationId.getId(), + IndexSettingsModule.newIndexSettings("test", Settings.EMPTY), + primaryTerm, + UNASSIGNED_SEQ_NO, + value -> {}, + () -> 0L, + (leases, listener) -> {}); + replicationTracker.updateFromMaster( + randomNonNegativeLong(), + Collections.singleton(allocationId.getId()), + routingTable(Collections.emptySet(), allocationId), + Collections.emptySet()); + replicationTracker.activatePrimaryMode(SequenceNumbers.NO_OPS_PERFORMED); + final String id = randomAlphaOfLength(8); + final long retainingSequenceNumber = randomNonNegativeLong(); + final String source = randomAlphaOfLength(8); + replicationTracker.addRetentionLease(id, retainingSequenceNumber, source, ActionListener.wrap(() -> {})); + final long nextRetaininSequenceNumber = randomLongBetween(retainingSequenceNumber, Long.MAX_VALUE); + final RetentionLeaseAlreadyExistsException e = expectThrows( + RetentionLeaseAlreadyExistsException.class, + () -> replicationTracker.addRetentionLease(id, nextRetaininSequenceNumber, source, ActionListener.wrap(() -> {}))); + assertThat(e, hasToString(containsString("retention lease with ID [" + id + "] already exists"))); + } + + public void testRenewNotFoundRetentionLease() { + final AllocationId allocationId = AllocationId.newInitializing(); + long primaryTerm = randomLongBetween(1, Long.MAX_VALUE); + final ReplicationTracker replicationTracker = new ReplicationTracker( + new ShardId("test", "_na", 0), + allocationId.getId(), + IndexSettingsModule.newIndexSettings("test", Settings.EMPTY), + primaryTerm, + UNASSIGNED_SEQ_NO, + value -> {}, + () -> 0L, + (leases, listener) -> {}); + replicationTracker.updateFromMaster( + randomNonNegativeLong(), + Collections.singleton(allocationId.getId()), + routingTable(Collections.emptySet(), allocationId), + Collections.emptySet()); + replicationTracker.activatePrimaryMode(SequenceNumbers.NO_OPS_PERFORMED); + final String id = randomAlphaOfLength(8); + final RetentionLeaseNotFoundException e = expectThrows( + RetentionLeaseNotFoundException.class, + () -> replicationTracker.renewRetentionLease(id, randomNonNegativeLong(), randomAlphaOfLength(8))); + assertThat(e, hasToString(containsString("retention lease with ID [" + id + "] not found"))); + } + public void testAddRetentionLeaseCausesRetentionLeaseSync() { final AllocationId allocationId = AllocationId.newInitializing(); final Map retainingSequenceNumbers = new HashMap<>(); @@ -188,6 +244,31 @@ public class ReplicationTrackerRetentionLeaseTests extends ReplicationTrackerTes } } + public void testRemoveNotFound() { + final AllocationId allocationId = AllocationId.newInitializing(); + long primaryTerm = randomLongBetween(1, Long.MAX_VALUE); + final ReplicationTracker replicationTracker = new ReplicationTracker( + new ShardId("test", "_na", 0), + allocationId.getId(), + IndexSettingsModule.newIndexSettings("test", Settings.EMPTY), + primaryTerm, + UNASSIGNED_SEQ_NO, + value -> {}, + () -> 0L, + (leases, listener) -> {}); + replicationTracker.updateFromMaster( + randomNonNegativeLong(), + Collections.singleton(allocationId.getId()), + routingTable(Collections.emptySet(), allocationId), + Collections.emptySet()); + replicationTracker.activatePrimaryMode(SequenceNumbers.NO_OPS_PERFORMED); + final String id = randomAlphaOfLength(8); + final RetentionLeaseNotFoundException e = expectThrows( + RetentionLeaseNotFoundException.class, + () -> replicationTracker.removeRetentionLease(id, ActionListener.wrap(() -> {}))); + assertThat(e, hasToString(containsString("retention lease with ID [" + id + "] not found"))); + } + public void testRemoveRetentionLeaseCausesRetentionLeaseSync() { final AllocationId allocationId = AllocationId.newInitializing(); final Map retainingSequenceNumbers = new HashMap<>();