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:
parent
b97c74bbab
commit
c7cdd6a46a
|
@ -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;
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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()) {
|
||||||
|
|
|
@ -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<>();
|
||||||
|
|
Loading…
Reference in New Issue