Search in sources :

Example 6 with BadKeyVersionException

use of io.pravega.segmentstore.contracts.tables.BadKeyVersionException in project pravega by pravega.

the class ContainerKeyIndex method update.

/**
 * Performs a Batch Update or Removal.
 *
 * If {@link TableKeyBatch#isConditional()} returns true, this will execute an atomic Conditional Update/Removal based
 * on the condition items in the batch ({@link TableKeyBatch#getVersionedItems}. The entire TableKeyBatch will be
 * conditioned on those items, including those items that do not have a condition set. The entire TableKeyBatch will
 * either all be committed as one unit or not at all.
 *
 * Otherwise this will perform an Unconditional Update/Removal, where all the {@link TableKeyBatch.Item}s in
 * {@link TableKeyBatch#getItems()} will be applied regardless of whether they already exist or what their versions are.
 *
 * @param segment The Segment to perform the update/removal on.
 * @param batch   The {@link TableKeyBatch} to apply.
 * @param persist A Supplier that, when invoked, will persist the contents of the batch to the Segment and return
 *                a CompletableFuture to indicate when the operation is done, containing the offset at which the
 *                batch has been written to the Segment. This Future must complete successfully before the effects
 *                of the {@link TableKeyBatch} are applied to the in-memory Index or before downstream conditional
 *                updates on the affected keys are initiated.
 * @param timer   Timer for the operation.
 * @return A CompletableFuture that, when completed, will contain a list of offsets (within the Segment) where each
 * of the items in the batch has been persisted. If the update failed, it will be failed with the appropriate exception.
 * Notable exceptions:
 * <ul>
 * <li>{@link KeyNotExistsException} If a Key in the TableKeyBatch does not exist and was conditioned as having to exist.
 * <li>{@link BadKeyVersionException} If a Key does exist but had a version mismatch.
 * </ul>
 */
CompletableFuture<List<Long>> update(DirectSegmentAccess segment, TableKeyBatch batch, Supplier<CompletableFuture<Long>> persist, TimeoutTimer timer) {
    Exceptions.checkNotClosed(this.closed.get(), this);
    Supplier<CompletableFuture<List<Long>>> update;
    if (batch.isConditional()) {
        // Conditional update.
        // Collect all Cache Keys for the Update Items that have a condition on them; we need this on order to
        // serialize execution across them.
        val keys = batch.getVersionedItems().stream().map(item -> Maps.immutableEntry(segment.getSegmentId(), item.getHash())).collect(Collectors.toList());
        // Serialize the execution (queue it up to run only after all other currently queued up conditional updates
        // for touched keys have finished).
        update = () -> this.conditionalUpdateProcessor.add(keys, () -> validateConditionalUpdate(segment, batch, timer).thenComposeAsync(v -> persist.get(), this.executor).thenApplyAsync(batchOffset -> updateCache(segment, batch, batchOffset), this.executor));
    } else {
        // Unconditional update: persist the entries and update the cache.
        update = () -> persist.get().thenApplyAsync(batchOffset -> updateCache(segment, batch, batchOffset), this.executor);
    }
    // Throttle any requests, if needed.
    return this.segmentTracker.throttleIfNeeded(segment, update, batch.getLength());
}
Also used : lombok.val(lombok.val) ScheduledFuture(java.util.concurrent.ScheduledFuture) SneakyThrows(lombok.SneakyThrows) RequiredArgsConstructor(lombok.RequiredArgsConstructor) TimeoutException(java.util.concurrent.TimeoutException) SegmentProperties(io.pravega.segmentstore.contracts.SegmentProperties) BufferView(io.pravega.common.util.BufferView) Duration(java.time.Duration) Map(java.util.Map) AsyncReadResultProcessor(io.pravega.segmentstore.server.reading.AsyncReadResultProcessor) TableKey(io.pravega.segmentstore.contracts.tables.TableKey) SegmentStoreMetrics(io.pravega.segmentstore.server.SegmentStoreMetrics) Predicate(java.util.function.Predicate) NonNull(lombok.NonNull) Collection(java.util.Collection) ConcurrentHashMap(java.util.concurrent.ConcurrentHashMap) CompletionException(java.util.concurrent.CompletionException) ThreadSafe(javax.annotation.concurrent.ThreadSafe) UUID(java.util.UUID) GuardedBy(javax.annotation.concurrent.GuardedBy) Collectors(java.util.stream.Collectors) List(java.util.List) Slf4j(lombok.extern.slf4j.Slf4j) Futures(io.pravega.common.concurrent.Futures) ReadResult(io.pravega.segmentstore.contracts.ReadResult) CacheManager(io.pravega.segmentstore.server.CacheManager) ObjectClosedException(io.pravega.common.ObjectClosedException) TableAttributes(io.pravega.segmentstore.contracts.tables.TableAttributes) Getter(lombok.Getter) Exceptions(io.pravega.common.Exceptions) AtomicBoolean(java.util.concurrent.atomic.AtomicBoolean) HashMap(java.util.HashMap) CompletableFuture(java.util.concurrent.CompletableFuture) Function(java.util.function.Function) Supplier(java.util.function.Supplier) BadKeyVersionException(io.pravega.segmentstore.contracts.tables.BadKeyVersionException) ArrayList(java.util.ArrayList) HashSet(java.util.HashSet) TableSegmentNotEmptyException(io.pravega.segmentstore.contracts.tables.TableSegmentNotEmptyException) SegmentMetadata(io.pravega.segmentstore.server.SegmentMetadata) MultiKeySequentialProcessor(io.pravega.common.concurrent.MultiKeySequentialProcessor) ConditionalTableUpdateException(io.pravega.segmentstore.contracts.tables.ConditionalTableUpdateException) ScheduledExecutorService(java.util.concurrent.ScheduledExecutorService) StreamSegmentTruncatedException(io.pravega.segmentstore.contracts.StreamSegmentTruncatedException) TimeoutTimer(io.pravega.common.TimeoutTimer) KeyNotExistsException(io.pravega.segmentstore.contracts.tables.KeyNotExistsException) lombok.val(lombok.val) IOException(java.io.IOException) Maps(com.google.common.collect.Maps) Timer(io.pravega.common.Timer) TimeUnit(java.util.concurrent.TimeUnit) AtomicLong(java.util.concurrent.atomic.AtomicLong) DirectSegmentAccess(io.pravega.segmentstore.server.DirectSegmentAccess) AsyncSemaphore(io.pravega.common.concurrent.AsyncSemaphore) VisibleForTesting(com.google.common.annotations.VisibleForTesting) Collections(java.util.Collections) CompletableFuture(java.util.concurrent.CompletableFuture) AtomicLong(java.util.concurrent.atomic.AtomicLong)

Example 7 with BadKeyVersionException

use of io.pravega.segmentstore.contracts.tables.BadKeyVersionException in project pravega by pravega.

the class PravegaRequestProcessor method handleException.

private Void handleException(long requestId, String segment, long offset, String operation, Throwable u) {
    if (u == null) {
        IllegalStateException exception = new IllegalStateException("No exception to handle.");
        log.error(requestId, "Error (Segment = '{}', Operation = '{}')", segment, operation, exception);
        throw exception;
    }
    u = Exceptions.unwrap(u);
    String clientReplyStackTrace = replyWithStackTraceOnError ? Throwables.getStackTraceAsString(u) : EMPTY_STACK_TRACE;
    final Consumer<Throwable> failureHandler = t -> {
        log.error(requestId, "Error (Segment = '{}', Operation = '{}')", segment, "handling result of " + operation, t);
        connection.close();
    };
    if (u instanceof StreamSegmentExistsException) {
        log.info(requestId, "Segment '{}' already exists and cannot perform operation '{}'.", segment, operation);
        invokeSafely(connection::send, new SegmentAlreadyExists(requestId, segment, clientReplyStackTrace), failureHandler);
    } else if (u instanceof StreamSegmentNotExistsException) {
        log.warn(requestId, "Segment '{}' does not exist and cannot perform operation '{}'.", segment, operation);
        invokeSafely(connection::send, new NoSuchSegment(requestId, segment, clientReplyStackTrace, offset), failureHandler);
    } else if (u instanceof StreamSegmentSealedException) {
        log.info(requestId, "Segment '{}' is sealed and cannot perform operation '{}'.", segment, operation);
        invokeSafely(connection::send, new SegmentIsSealed(requestId, segment, clientReplyStackTrace, offset), failureHandler);
    } else if (u instanceof ContainerNotFoundException) {
        int containerId = ((ContainerNotFoundException) u).getContainerId();
        log.warn(requestId, "Wrong host. Segment = '{}' (Container {}) is not owned. Operation = '{}').", segment, containerId, operation);
        invokeSafely(connection::send, new WrongHost(requestId, segment, "", clientReplyStackTrace), failureHandler);
    } else if (u instanceof ReadCancellationException) {
        log.info(requestId, "Sending empty response on connection {} while reading segment {} due to CancellationException.", connection, segment);
        invokeSafely(connection::send, new SegmentRead(segment, offset, true, false, EMPTY_BUFFER, requestId), failureHandler);
    } else if (u instanceof CancellationException) {
        log.info(requestId, "Closing connection {} while performing {} due to {}.", connection, operation, u.toString());
        connection.close();
    } else if (u instanceof TokenExpiredException) {
        log.warn(requestId, "Expired token during operation {}", operation);
        invokeSafely(connection::send, new AuthTokenCheckFailed(requestId, clientReplyStackTrace, AuthTokenCheckFailed.ErrorCode.TOKEN_EXPIRED), failureHandler);
    } else if (u instanceof TokenException) {
        log.warn(requestId, "Token exception encountered during operation {}.", operation, u);
        invokeSafely(connection::send, new AuthTokenCheckFailed(requestId, clientReplyStackTrace, AuthTokenCheckFailed.ErrorCode.TOKEN_CHECK_FAILED), failureHandler);
    } else if (u instanceof UnsupportedOperationException) {
        log.warn(requestId, "Unsupported Operation '{}'.", operation, u);
        invokeSafely(connection::send, new OperationUnsupported(requestId, operation, clientReplyStackTrace), failureHandler);
    } else if (u instanceof BadOffsetException) {
        BadOffsetException badOffset = (BadOffsetException) u;
        log.info(requestId, "Segment '{}' is truncated and cannot perform operation '{}' at offset '{}'", segment, operation, offset);
        invokeSafely(connection::send, new SegmentIsTruncated(requestId, segment, badOffset.getExpectedOffset(), clientReplyStackTrace, offset), failureHandler);
    } else if (u instanceof TableSegmentNotEmptyException) {
        log.warn(requestId, "Table segment '{}' is not empty to perform '{}'.", segment, operation);
        invokeSafely(connection::send, new TableSegmentNotEmpty(requestId, segment, clientReplyStackTrace), failureHandler);
    } else if (u instanceof KeyNotExistsException) {
        log.warn(requestId, "Conditional update on Table segment '{}' failed as the key does not exist.", segment);
        invokeSafely(connection::send, new WireCommands.TableKeyDoesNotExist(requestId, segment, clientReplyStackTrace), failureHandler);
    } else if (u instanceof BadKeyVersionException) {
        log.warn(requestId, "Conditional update on Table segment '{}' failed due to bad key version.", segment);
        invokeSafely(connection::send, new WireCommands.TableKeyBadVersion(requestId, segment, clientReplyStackTrace), failureHandler);
    } else if (errorCodeExists(u)) {
        log.warn(requestId, "Operation on segment '{}' failed due to a {}.", segment, u.getClass());
        invokeSafely(connection::send, new WireCommands.ErrorMessage(requestId, segment, u.getMessage(), WireCommands.ErrorMessage.ErrorCode.valueOf(u.getClass())), failureHandler);
    } else {
        logError(requestId, segment, operation, u);
        // Closing connection should reinitialize things, and hopefully fix the problem
        connection.close();
        throw new IllegalStateException("Unknown exception.", u);
    }
    return null;
}
Also used : SCALE_POLICY_RATE(io.pravega.segmentstore.contracts.Attributes.SCALE_POLICY_RATE) Arrays(java.util.Arrays) TableSegmentConfig(io.pravega.segmentstore.contracts.tables.TableSegmentConfig) ErrorCode(io.pravega.shared.protocol.netty.WireCommands.ErrorMessage.ErrorCode) READ(io.pravega.auth.AuthHandler.Permissions.READ) StreamSegmentNotExistsException(io.pravega.segmentstore.contracts.StreamSegmentNotExistsException) SegmentIsTruncated(io.pravega.shared.protocol.netty.WireCommands.SegmentIsTruncated) CreateTableSegment(io.pravega.shared.protocol.netty.WireCommands.CreateTableSegment) CREATION_TIME(io.pravega.segmentstore.contracts.Attributes.CREATION_TIME) GetStreamSegmentInfo(io.pravega.shared.protocol.netty.WireCommands.GetStreamSegmentInfo) AuthTokenCheckFailed(io.pravega.shared.protocol.netty.WireCommands.AuthTokenCheckFailed) MergeSegments(io.pravega.shared.protocol.netty.WireCommands.MergeSegments) Duration(java.time.Duration) Map(java.util.Map) SegmentCreated(io.pravega.shared.protocol.netty.WireCommands.SegmentCreated) DeltaIteratorState(io.pravega.segmentstore.server.tables.DeltaIteratorState) StreamSegmentStore(io.pravega.segmentstore.contracts.StreamSegmentStore) Attributes(io.pravega.segmentstore.contracts.Attributes) TableKey(io.pravega.segmentstore.contracts.tables.TableKey) CancellationException(java.util.concurrent.CancellationException) NonNull(lombok.NonNull) ThreadSafe(javax.annotation.concurrent.ThreadSafe) ContainerNotFoundException(io.pravega.segmentstore.contracts.ContainerNotFoundException) GuardedBy(javax.annotation.concurrent.GuardedBy) CreateSegment(io.pravega.shared.protocol.netty.WireCommands.CreateSegment) SealSegment(io.pravega.shared.protocol.netty.WireCommands.SealSegment) SegmentSealed(io.pravega.shared.protocol.netty.WireCommands.SegmentSealed) EndOfStreamSegment(io.pravega.segmentstore.contracts.ReadResultEntryType.EndOfStreamSegment) Futures(io.pravega.common.concurrent.Futures) SegmentAttribute(io.pravega.shared.protocol.netty.WireCommands.SegmentAttribute) IllegalContainerStateException(io.pravega.segmentstore.server.IllegalContainerStateException) Exceptions(io.pravega.common.Exceptions) BadAttributeUpdateException(io.pravega.segmentstore.contracts.BadAttributeUpdateException) GetSegmentAttribute(io.pravega.shared.protocol.netty.WireCommands.GetSegmentAttribute) BadKeyVersionException(io.pravega.segmentstore.contracts.tables.BadKeyVersionException) ArrayList(java.util.ArrayList) READ_UPDATE(io.pravega.auth.AuthHandler.Permissions.READ_UPDATE) SegmentType(io.pravega.segmentstore.contracts.SegmentType) SegmentRead(io.pravega.shared.protocol.netty.WireCommands.SegmentRead) AccessLevel(lombok.AccessLevel) Future(io.pravega.segmentstore.contracts.ReadResultEntryType.Future) FailingRequestProcessor(io.pravega.shared.protocol.netty.FailingRequestProcessor) TokenException(io.pravega.auth.TokenException) StreamSegmentTruncatedException(io.pravega.segmentstore.contracts.StreamSegmentTruncatedException) KeyNotExistsException(io.pravega.segmentstore.contracts.tables.KeyNotExistsException) DeleteTableSegment(io.pravega.shared.protocol.netty.WireCommands.DeleteTableSegment) AttributeId(io.pravega.segmentstore.contracts.AttributeId) lombok.val(lombok.val) Throwables(com.google.common.base.Throwables) WireCommands(io.pravega.shared.protocol.netty.WireCommands) WrongHost(io.pravega.shared.protocol.netty.WireCommands.WrongHost) SegmentDeleted(io.pravega.shared.protocol.netty.WireCommands.SegmentDeleted) AttributeUpdateCollection(io.pravega.segmentstore.contracts.AttributeUpdateCollection) Truncated(io.pravega.segmentstore.contracts.ReadResultEntryType.Truncated) SegmentTruncated(io.pravega.shared.protocol.netty.WireCommands.SegmentTruncated) RequestProcessor(io.pravega.shared.protocol.netty.RequestProcessor) OperationUnsupported(io.pravega.shared.protocol.netty.WireCommands.OperationUnsupported) Preconditions(com.google.common.base.Preconditions) Callbacks.invokeSafely(io.pravega.common.function.Callbacks.invokeSafely) TableEntry(io.pravega.segmentstore.contracts.tables.TableEntry) EMPTY_BUFFER(io.netty.buffer.Unpooled.EMPTY_BUFFER) Cache(io.pravega.segmentstore.contracts.ReadResultEntryType.Cache) TokenExpiredException(io.pravega.auth.TokenExpiredException) TableStore(io.pravega.segmentstore.contracts.tables.TableStore) LoggerFactory(org.slf4j.LoggerFactory) Unpooled(io.netty.buffer.Unpooled) IteratorArgs(io.pravega.segmentstore.contracts.tables.IteratorArgs) SegmentProperties(io.pravega.segmentstore.contracts.SegmentProperties) AttributeUpdate(io.pravega.segmentstore.contracts.AttributeUpdate) StreamSegmentSealedException(io.pravega.segmentstore.contracts.StreamSegmentSealedException) TagLogger(io.pravega.common.tracing.TagLogger) UpdateSegmentAttribute(io.pravega.shared.protocol.netty.WireCommands.UpdateSegmentAttribute) AtomicInteger(java.util.concurrent.atomic.AtomicInteger) BufferView(io.pravega.common.util.BufferView) UpdateSegmentPolicy(io.pravega.shared.protocol.netty.WireCommands.UpdateSegmentPolicy) SegmentStatsRecorder(io.pravega.segmentstore.server.host.stat.SegmentStatsRecorder) ROLLOVER_SIZE(io.pravega.segmentstore.contracts.Attributes.ROLLOVER_SIZE) Collection(java.util.Collection) CompletionException(java.util.concurrent.CompletionException) Math.min(java.lang.Math.min) Collectors(java.util.stream.Collectors) List(java.util.List) StreamSegmentExistsException(io.pravega.segmentstore.contracts.StreamSegmentExistsException) PassingTokenVerifier(io.pravega.segmentstore.server.host.delegationtoken.PassingTokenVerifier) BadOffsetException(io.pravega.segmentstore.contracts.BadOffsetException) DelegationTokenVerifier(io.pravega.segmentstore.server.host.delegationtoken.DelegationTokenVerifier) Math.max(java.lang.Math.max) SegmentIsSealed(io.pravega.shared.protocol.netty.WireCommands.SegmentIsSealed) ReadResult(io.pravega.segmentstore.contracts.ReadResult) IntStream(java.util.stream.IntStream) Setter(lombok.Setter) DeleteSegment(io.pravega.shared.protocol.netty.WireCommands.DeleteSegment) SegmentPolicyUpdated(io.pravega.shared.protocol.netty.WireCommands.SegmentPolicyUpdated) Getter(lombok.Getter) NoSuchSegment(io.pravega.shared.protocol.netty.WireCommands.NoSuchSegment) AtomicBoolean(java.util.concurrent.atomic.AtomicBoolean) HashMap(java.util.HashMap) CompletableFuture(java.util.concurrent.CompletableFuture) Iterators(com.google.common.collect.Iterators) TableSegmentNotEmpty(io.pravega.shared.protocol.netty.WireCommands.TableSegmentNotEmpty) TableSegmentNotEmptyException(io.pravega.segmentstore.contracts.tables.TableSegmentNotEmptyException) ByteBuf(io.netty.buffer.ByteBuf) ReadResultEntry(io.pravega.segmentstore.contracts.ReadResultEntry) SCALE_POLICY_TYPE(io.pravega.segmentstore.contracts.Attributes.SCALE_POLICY_TYPE) TYPE_PLUS_LENGTH_SIZE(io.pravega.shared.protocol.netty.WireCommands.TYPE_PLUS_LENGTH_SIZE) StreamSegmentInfo(io.pravega.shared.protocol.netty.WireCommands.StreamSegmentInfo) TruncateSegment(io.pravega.shared.protocol.netty.WireCommands.TruncateSegment) ReadSegment(io.pravega.shared.protocol.netty.WireCommands.ReadSegment) SegmentAlreadyExists(io.pravega.shared.protocol.netty.WireCommands.SegmentAlreadyExists) ByteBufWrapper(io.pravega.shared.protocol.netty.ByteBufWrapper) LoggerHelpers(io.pravega.common.LoggerHelpers) MergeStreamSegmentResult(io.pravega.segmentstore.contracts.MergeStreamSegmentResult) StreamSegmentMergedException(io.pravega.segmentstore.contracts.StreamSegmentMergedException) Timer(io.pravega.common.Timer) TableSegmentStatsRecorder(io.pravega.segmentstore.server.host.stat.TableSegmentStatsRecorder) SegmentAttributeUpdated(io.pravega.shared.protocol.netty.WireCommands.SegmentAttributeUpdated) Consumer(java.util.function.Consumer) AbstractMap(java.util.AbstractMap) Collectors.toList(java.util.stream.Collectors.toList) VisibleForTesting(com.google.common.annotations.VisibleForTesting) AttributeUpdateType(io.pravega.segmentstore.contracts.AttributeUpdateType) Collections(java.util.Collections) BadKeyVersionException(io.pravega.segmentstore.contracts.tables.BadKeyVersionException) WrongHost(io.pravega.shared.protocol.netty.WireCommands.WrongHost) SegmentAlreadyExists(io.pravega.shared.protocol.netty.WireCommands.SegmentAlreadyExists) AuthTokenCheckFailed(io.pravega.shared.protocol.netty.WireCommands.AuthTokenCheckFailed) StreamSegmentSealedException(io.pravega.segmentstore.contracts.StreamSegmentSealedException) SegmentIsSealed(io.pravega.shared.protocol.netty.WireCommands.SegmentIsSealed) SegmentRead(io.pravega.shared.protocol.netty.WireCommands.SegmentRead) SegmentIsTruncated(io.pravega.shared.protocol.netty.WireCommands.SegmentIsTruncated) TokenException(io.pravega.auth.TokenException) BadOffsetException(io.pravega.segmentstore.contracts.BadOffsetException) TableSegmentNotEmpty(io.pravega.shared.protocol.netty.WireCommands.TableSegmentNotEmpty) WireCommands(io.pravega.shared.protocol.netty.WireCommands) ContainerNotFoundException(io.pravega.segmentstore.contracts.ContainerNotFoundException) OperationUnsupported(io.pravega.shared.protocol.netty.WireCommands.OperationUnsupported) StreamSegmentNotExistsException(io.pravega.segmentstore.contracts.StreamSegmentNotExistsException) StreamSegmentExistsException(io.pravega.segmentstore.contracts.StreamSegmentExistsException) TokenExpiredException(io.pravega.auth.TokenExpiredException) CancellationException(java.util.concurrent.CancellationException) KeyNotExistsException(io.pravega.segmentstore.contracts.tables.KeyNotExistsException) TableSegmentNotEmptyException(io.pravega.segmentstore.contracts.tables.TableSegmentNotEmptyException) NoSuchSegment(io.pravega.shared.protocol.netty.WireCommands.NoSuchSegment)

Example 8 with BadKeyVersionException

use of io.pravega.segmentstore.contracts.tables.BadKeyVersionException in project pravega by pravega.

the class FixedKeyLengthTableSegmentLayout method handleConditionalUpdateException.

private <T> CompletableFuture<T> handleConditionalUpdateException(CompletableFuture<T> update, SegmentProperties segmentInfo) {
    return update.exceptionally(ex -> {
        ex = Exceptions.unwrap(ex);
        if (ex instanceof BadAttributeUpdateException) {
            val bau = (BadAttributeUpdateException) ex;
            ex = bau.isPreviousValueMissing() ? new KeyNotExistsException(segmentInfo.getName(), bau.getAttributeId().toBuffer()) : new BadKeyVersionException(segmentInfo.getName(), Collections.emptyMap());
        }
        throw new CompletionException(ex);
    });
}
Also used : lombok.val(lombok.val) BadKeyVersionException(io.pravega.segmentstore.contracts.tables.BadKeyVersionException) BadAttributeUpdateException(io.pravega.segmentstore.contracts.BadAttributeUpdateException) KeyNotExistsException(io.pravega.segmentstore.contracts.tables.KeyNotExistsException) CompletionException(java.util.concurrent.CompletionException)

Example 9 with BadKeyVersionException

use of io.pravega.segmentstore.contracts.tables.BadKeyVersionException in project pravega by pravega.

the class ContainerKeyIndexTests method testConditionalUpdateFailure.

/**
 * Tests the ability of the {@link ContainerKeyIndex} to reject conditional updates if the condition does not match
 * the state of the Key (version mismatch or key not exists).
 */
@Test
public void testConditionalUpdateFailure() {
    @Cleanup val context = new TestContext();
    Supplier<CompletableFuture<Long>> noPersist = () -> Futures.failedFuture(new AssertionError("Not expecting persist to be invoked."));
    val keyData = generateUnversionedKeys(1, context).get(0).getKey();
    // Key not exists, but we conditioned on it existing.
    AssertExtensions.assertSuppliedFutureThrows("update() allowed conditional update on inexistent key conditioned on existing.", () -> context.index.update(context.segment, toUpdateBatch(TableKey.versioned(keyData, 0L)), noPersist, context.timer), ex -> ex instanceof KeyNotExistsException && ((KeyNotExistsException) ex).getKey().equals(keyData));
    // Create the key. We must actually write something to the segment here as this will be used in the subsequent
    // calls to validate the key.
    val s = new EntrySerializer();
    val toUpdate = TableEntry.versioned(keyData, new ByteArraySegment(new byte[100]), TableKey.NOT_EXISTS);
    val toWrite = s.serializeUpdate(Collections.singleton(toUpdate));
    context.index.update(context.segment, toUpdateBatch(toUpdate.getKey()), () -> context.segment.append(toWrite, null, TIMEOUT), context.timer).join();
    // Key exists, but we conditioned on it not existing.
    AssertExtensions.assertSuppliedFutureThrows("update() allowed conditional update on existent key conditioned on not existing.", () -> context.index.update(context.segment, toUpdateBatch(TableKey.versioned(keyData, TableKey.NOT_EXISTS)), noPersist, context.timer), ex -> ex instanceof BadKeyVersionException && keyMatches(((BadKeyVersionException) ex).getExpectedVersions(), keyData));
    // Key exists, but we conditioned on the wrong value.
    AssertExtensions.assertSuppliedFutureThrows("update() allowed conditional update on inexistent key conditioned on existing.", () -> context.index.update(context.segment, toUpdateBatch(TableKey.versioned(keyData, 123L)), noPersist, context.timer), ex -> ex instanceof BadKeyVersionException && keyMatches(((BadKeyVersionException) ex).getExpectedVersions(), keyData));
}
Also used : lombok.val(lombok.val) BadKeyVersionException(io.pravega.segmentstore.contracts.tables.BadKeyVersionException) CompletableFuture(java.util.concurrent.CompletableFuture) ByteArraySegment(io.pravega.common.util.ByteArraySegment) KeyNotExistsException(io.pravega.segmentstore.contracts.tables.KeyNotExistsException) Cleanup(lombok.Cleanup) Test(org.junit.Test)

Example 10 with BadKeyVersionException

use of io.pravega.segmentstore.contracts.tables.BadKeyVersionException in project pravega by pravega.

the class ContainerKeyIndexTests method testConditionalUpdateFailureReconciliation.

/**
 * Tests the ability of the {@link ContainerKeyIndex} to reconcile conditional updates if the condition does not match
 * the given Key's Bucket offset, but it matches the Key's offset (this is a corner scenario in a situation with multiple
 * Keys sharing the same bucket).
 */
@Test
public void testConditionalUpdateFailureReconciliation() {
    val hasher = KeyHashers.CONSTANT_HASHER;
    @Cleanup val context = new TestContext();
    Supplier<CompletableFuture<Long>> noPersist = () -> Futures.failedFuture(new AssertionError("Not expecting persist to be invoked."));
    val keys = generateUnversionedKeys(2, context);
    // First, write two keys with the same hash (and serialize them to the Segment).
    val versions = new HashMap<BufferView, Long>();
    for (val key : keys) {
        val s = new EntrySerializer();
        val toUpdate = TableEntry.unversioned(key.getKey(), new ByteArraySegment(new byte[100]));
        val toWrite = s.serializeUpdate(Collections.singleton(toUpdate));
        val r = context.index.update(context.segment, toUpdateBatch(hasher, Collections.singletonList(toUpdate.getKey())), () -> context.segment.append(toWrite, null, TIMEOUT), context.timer).join();
        versions.put(key.getKey(), r.get(0));
    }
    // We want to remove the first key.
    val keyToRemove = keys.get(0);
    val validVersion = versions.get(keyToRemove.getKey());
    val invalidVersion = validVersion + 1;
    // Verify that a bad version won't work.
    AssertExtensions.assertSuppliedFutureThrows("update() allowed conditional update with invalid version.", () -> context.index.update(context.segment, toUpdateBatch(hasher, Collections.singletonList(TableKey.versioned(keyToRemove.getKey(), invalidVersion))), noPersist, context.timer), ex -> ex instanceof BadKeyVersionException);
    // Verify that the key's version (which is different than the bucket's version), works.
    AtomicBoolean persisted = new AtomicBoolean();
    context.index.update(context.segment, toUpdateBatch(hasher, Collections.singletonList(TableKey.versioned(keyToRemove.getKey(), validVersion))), () -> {
        persisted.set(true);
        return CompletableFuture.completedFuture(context.segment.getInfo().getLength());
    }, context.timer).join();
    Assert.assertTrue("Expected persisted to have been invoked after reconciled conditional update.", persisted.get());
}
Also used : lombok.val(lombok.val) BadKeyVersionException(io.pravega.segmentstore.contracts.tables.BadKeyVersionException) AtomicBoolean(java.util.concurrent.atomic.AtomicBoolean) CompletableFuture(java.util.concurrent.CompletableFuture) ByteArraySegment(io.pravega.common.util.ByteArraySegment) HashMap(java.util.HashMap) Cleanup(lombok.Cleanup) Test(org.junit.Test)

Aggregations

BadKeyVersionException (io.pravega.segmentstore.contracts.tables.BadKeyVersionException)10 lombok.val (lombok.val)9 KeyNotExistsException (io.pravega.segmentstore.contracts.tables.KeyNotExistsException)6 HashMap (java.util.HashMap)6 CompletableFuture (java.util.concurrent.CompletableFuture)6 CompletionException (java.util.concurrent.CompletionException)6 VisibleForTesting (com.google.common.annotations.VisibleForTesting)3 Exceptions (io.pravega.common.Exceptions)3 Timer (io.pravega.common.Timer)3 Futures (io.pravega.common.concurrent.Futures)3 BufferView (io.pravega.common.util.BufferView)3 TableKey (io.pravega.segmentstore.contracts.tables.TableKey)3 IOException (java.io.IOException)3 ArrayList (java.util.ArrayList)3 List (java.util.List)3 ConcurrentHashMap (java.util.concurrent.ConcurrentHashMap)3 AtomicLong (java.util.concurrent.atomic.AtomicLong)3 SneakyThrows (lombok.SneakyThrows)3 Maps (com.google.common.collect.Maps)2 ObjectClosedException (io.pravega.common.ObjectClosedException)2