Add dedicated retention lease exceptions (#38754)

When a retention lease already exists on an add retention lease
invocation, or a retention lease is not found on a renew retention lease
invocation today we throw an illegal argument exception. This puts a
burden on the caller to catch that specific exception and parse the
message. This commit relieves the burden from the caller by adding
dedicated exception types for these situations.
This commit is contained in:
Jason Tedor 2019-02-12 00:31:29 -05:00
parent b97c74bbab
commit c7cdd6a46a
No known key found for this signature in database
GPG Key ID: FA89F05560F16BC5
6 changed files with 175 additions and 4 deletions

View File

@ -1012,7 +1012,17 @@ public class ElasticsearchException extends RuntimeException implements ToXConte
SNAPSHOT_IN_PROGRESS_EXCEPTION(org.elasticsearch.snapshots.SnapshotInProgressException.class, SNAPSHOT_IN_PROGRESS_EXCEPTION(org.elasticsearch.snapshots.SnapshotInProgressException.class,
org.elasticsearch.snapshots.SnapshotInProgressException::new, 151, Version.V_6_7_0), org.elasticsearch.snapshots.SnapshotInProgressException::new, 151, Version.V_6_7_0),
NO_SUCH_REMOTE_CLUSTER_EXCEPTION(org.elasticsearch.transport.NoSuchRemoteClusterException.class, 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<? extends ElasticsearchException> exceptionClass; final Class<? extends ElasticsearchException> exceptionClass;
final CheckedFunction<StreamInput, ? extends ElasticsearchException, IOException> constructor; final CheckedFunction<StreamInput, ? extends ElasticsearchException, IOException> constructor;

View File

@ -237,7 +237,7 @@ public class ReplicationTracker extends AbstractIndexShardComponent implements L
synchronized (this) { synchronized (this) {
assert primaryMode; assert primaryMode;
if (retentionLeases.contains(id)) { 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); retentionLease = new RetentionLease(id, retainingSequenceNumber, currentTimeMillisSupplier.getAsLong(), source);
retentionLeases = new RetentionLeases( 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) { public synchronized RetentionLease renewRetentionLease(final String id, final long retainingSequenceNumber, final String source) {
assert primaryMode; assert primaryMode;
if (retentionLeases.contains(id) == false) { if (retentionLeases.contains(id) == false) {
throw new IllegalArgumentException("retention lease with ID [" + id + "] does not exist"); throw new RetentionLeaseNotFoundException(id);
} }
final RetentionLease retentionLease = final RetentionLease retentionLease =
new RetentionLease(id, retainingSequenceNumber, currentTimeMillisSupplier.getAsLong(), source); new RetentionLease(id, retainingSequenceNumber, currentTimeMillisSupplier.getAsLong(), source);
@ -295,7 +295,7 @@ public class ReplicationTracker extends AbstractIndexShardComponent implements L
synchronized (this) { synchronized (this) {
assert primaryMode; assert primaryMode;
if (retentionLeases.contains(id) == false) { if (retentionLeases.contains(id) == false) {
throw new IllegalArgumentException("retention lease with ID [" + id + "] does not exist"); throw new RetentionLeaseNotFoundException(id);
} }
retentionLeases = new RetentionLeases( retentionLeases = new RetentionLeases(
operationPrimaryTerm, operationPrimaryTerm,

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -61,6 +61,8 @@ import org.elasticsearch.env.ShardLockObtainFailedException;
import org.elasticsearch.index.Index; import org.elasticsearch.index.Index;
import org.elasticsearch.index.engine.RecoveryEngineException; import org.elasticsearch.index.engine.RecoveryEngineException;
import org.elasticsearch.index.query.QueryShardException; 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.IllegalIndexShardStateException;
import org.elasticsearch.index.shard.IndexShardState; import org.elasticsearch.index.shard.IndexShardState;
import org.elasticsearch.index.shard.ShardId; import org.elasticsearch.index.shard.ShardId;
@ -812,6 +814,8 @@ public class ExceptionSerializationTests extends ESTestCase {
ids.put(150, CoordinationStateRejectedException.class); ids.put(150, CoordinationStateRejectedException.class);
ids.put(151, SnapshotInProgressException.class); ids.put(151, SnapshotInProgressException.class);
ids.put(152, NoSuchRemoteClusterException.class); ids.put(152, NoSuchRemoteClusterException.class);
ids.put(153, RetentionLeaseAlreadyExistsException.class);
ids.put(154, RetentionLeaseNotFoundException.class);
Map<Class<? extends ElasticsearchException>, Integer> reverse = new HashMap<>(); Map<Class<? extends ElasticsearchException>, Integer> reverse = new HashMap<>();
for (Map.Entry<Integer, Class<? extends ElasticsearchException>> entry : ids.entrySet()) { for (Map.Entry<Integer, Class<? extends ElasticsearchException>> entry : ids.entrySet()) {

View File

@ -41,10 +41,12 @@ import java.util.stream.Collectors;
import static org.elasticsearch.index.seqno.SequenceNumbers.UNASSIGNED_SEQ_NO; import static org.elasticsearch.index.seqno.SequenceNumbers.UNASSIGNED_SEQ_NO;
import static org.hamcrest.Matchers.contains; import static org.hamcrest.Matchers.contains;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.empty; import static org.hamcrest.Matchers.empty;
import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.hasItem; import static org.hamcrest.Matchers.hasItem;
import static org.hamcrest.Matchers.hasSize; import static org.hamcrest.Matchers.hasSize;
import static org.hamcrest.Matchers.hasToString;
public class ReplicationTrackerRetentionLeaseTests extends ReplicationTrackerTestCase { 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() { public void testAddRetentionLeaseCausesRetentionLeaseSync() {
final AllocationId allocationId = AllocationId.newInitializing(); final AllocationId allocationId = AllocationId.newInitializing();
final Map<String, Long> retainingSequenceNumbers = new HashMap<>(); final Map<String, Long> 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() { public void testRemoveRetentionLeaseCausesRetentionLeaseSync() {
final AllocationId allocationId = AllocationId.newInitializing(); final AllocationId allocationId = AllocationId.newInitializing();
final Map<String, Long> retainingSequenceNumbers = new HashMap<>(); final Map<String, Long> retainingSequenceNumbers = new HashMap<>();