use of io.pravega.segmentstore.contracts.tables.TableSegmentNotEmptyException in project pravega by pravega.
the class TableSegmentLayoutTestBase method testOffsetAcceptingMethods.
/**
* Tests operations that currently accept an offset argument, and whether they fail expectedly.
*/
@Test
public void testOffsetAcceptingMethods() {
@Cleanup val context = new TableContext(executorService());
createSegment(context, SEGMENT_NAME);
val key1 = createRandomKey(context);
val key2 = createRandomKey(context);
val value = new ByteArraySegment("value".getBytes());
val v1 = context.ext.put(SEGMENT_NAME, Collections.singletonList(TableEntry.notExists(key1, value)), TIMEOUT).join();
// key1 is present.
Assert.assertEquals(value, context.ext.get(SEGMENT_NAME, Collections.singletonList(key1), TIMEOUT).join().get(0).getValue());
if (supportsDeleteIfEmpty()) {
AssertExtensions.assertSuppliedFutureThrows("deleteSegment(mustBeEmpty==true) worked with non-empty segment #1.", () -> context.ext.deleteSegment(SEGMENT_NAME, true, TIMEOUT), ex -> ex instanceof TableSegmentNotEmptyException);
}
val length = context.segment().getInfo().getLength();
// The SegmentMock used does not process appends via the OperationProcessor so conditional appends are not processed accurately.
// Instead just make sure that this 'conditional' append still appends.
val v2 = context.ext.put(SEGMENT_NAME, Collections.singletonList(TableEntry.notExists(key2, value)), length, TIMEOUT).join();
AssertExtensions.assertGreaterThan("Invalid entry ordering.", v1.get(0), v2.get(0));
// Remove k1.
context.ext.remove(SEGMENT_NAME, Collections.singletonList(TableKey.versioned(key1, v1.get(0))), TIMEOUT).join();
// // Make sure its been removed.
val entries = context.ext.get(SEGMENT_NAME, Collections.singletonList(key1), TIMEOUT).join();
Assert.assertEquals(1, entries.size());
}
use of io.pravega.segmentstore.contracts.tables.TableSegmentNotEmptyException in project pravega by pravega.
the class TableSegmentLayoutTestBase method deleteSegment.
private void deleteSegment(Collection<BufferView> remainingKeys, boolean mustBeEmpty, ContainerTableExtension ext) throws Exception {
if (remainingKeys.size() > 0 && supportsDeleteIfEmpty()) {
AssertExtensions.assertSuppliedFutureThrows("deleteIfEmpty worked on a non-empty segment.", () -> ext.deleteSegment(SEGMENT_NAME, true, TIMEOUT), ex -> ex instanceof TableSegmentNotEmptyException);
}
val toRemove = remainingKeys.stream().map(TableKey::unversioned).collect(Collectors.toList());
ext.remove(SEGMENT_NAME, toRemove, TIMEOUT).get(TIMEOUT.toMillis(), TimeUnit.MILLISECONDS);
ext.deleteSegment(SEGMENT_NAME, mustBeEmpty, TIMEOUT).get(TIMEOUT.toMillis(), TimeUnit.MILLISECONDS);
}
use of io.pravega.segmentstore.contracts.tables.TableSegmentNotEmptyException 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;
}
use of io.pravega.segmentstore.contracts.tables.TableSegmentNotEmptyException in project pravega by pravega.
the class TableSegmentLayoutTestBase method testDeleteIfEmpty.
/**
* Tests the ability to delete a TableSegment, but only if it is empty.
*/
@Test
public void testDeleteIfEmpty() {
@Cleanup val context = new TableContext(executorService());
createSegment(context, SEGMENT_NAME);
val key1 = new ByteArraySegment("key1".getBytes());
val key2 = new ByteArraySegment("key2".getBytes());
val value = new ByteArraySegment("value".getBytes());
context.ext.put(SEGMENT_NAME, Collections.singletonList(TableEntry.notExists(key1, value)), TIMEOUT).join();
// key1 is present.
AssertExtensions.assertSuppliedFutureThrows("deleteSegment(mustBeEmpty==true) worked with non-empty segment #1.", () -> context.ext.deleteSegment(SEGMENT_NAME, true, TIMEOUT), ex -> ex instanceof TableSegmentNotEmptyException);
// Remove key1 and insert key2.
context.ext.remove(SEGMENT_NAME, Collections.singleton(TableKey.unversioned(key1)), TIMEOUT).join();
context.ext.put(SEGMENT_NAME, Collections.singletonList(TableEntry.notExists(key2, value)), TIMEOUT).join();
AssertExtensions.assertSuppliedFutureThrows("deleteSegment(mustBeEmpty==true) worked with non-empty segment #2.", () -> context.ext.deleteSegment(SEGMENT_NAME, true, TIMEOUT), ex -> ex instanceof TableSegmentNotEmptyException);
// Remove key2 and reinsert it.
context.ext.remove(SEGMENT_NAME, Collections.singleton(TableKey.unversioned(key2)), TIMEOUT).join();
context.ext.put(SEGMENT_NAME, Collections.singletonList(TableEntry.notExists(key2, value)), TIMEOUT).join();
AssertExtensions.assertSuppliedFutureThrows("deleteSegment(mustBeEmpty==true) worked with non-empty segment #3.", () -> context.ext.deleteSegment(SEGMENT_NAME, true, TIMEOUT), ex -> ex instanceof TableSegmentNotEmptyException);
// Remove key2.
context.ext.remove(SEGMENT_NAME, Collections.singleton(TableKey.unversioned(key2)), TIMEOUT).join();
context.ext.deleteSegment(SEGMENT_NAME, true, TIMEOUT).join();
Assert.assertNull("Segment not deleted", context.segment());
AssertExtensions.assertSuppliedFutureThrows("Segment not deleted.", () -> context.ext.deleteSegment(SEGMENT_NAME, true, TIMEOUT), ex -> ex instanceof StreamSegmentNotExistsException);
}
use of io.pravega.segmentstore.contracts.tables.TableSegmentNotEmptyException in project pravega by pravega.
the class ContainerKeyIndexTests method testExecuteIfEmpty.
/**
* Checks the functionality of the {@link ContainerKeyIndex#executeIfEmpty} method.
*/
@Test
public void testExecuteIfEmpty() throws Exception {
@Cleanup val context = new TestContext();
val unversionedKeys = generateUnversionedKeys(BATCH_SIZE, context);
// 1. Verify executeIfEmpty works on an empty segment. Also verifies it blocks a conditional update.
val toRun1 = new CompletableFuture<Void>();
val persist1 = new CompletableFuture<Long>();
val empty1 = context.index.executeIfEmpty(context.segment, () -> toRun1, context.timer);
val updateBatch = toUpdateBatch(unversionedKeys.stream().map(k -> TableKey.notExists(k.getKey())).collect(Collectors.toList()));
val update1 = empty1.thenCompose(v -> {
Assert.assertTrue("Conditional update not blocked by executeIfEmpty.", toRun1.isDone());
return context.index.update(context.segment, updateBatch, () -> persist1, context.timer);
});
Assert.assertFalse("Not expecting any task to be done yet.", empty1.isDone() || update1.isDone());
// Verify that conditional updates are properly blocked.
toRun1.complete(null);
empty1.get(SHORT_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
Assert.assertFalse("Not expecting first update to complete.", update1.isDone());
persist1.complete(1L);
update1.get(SHORT_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
// 2. After an update.
AssertExtensions.assertSuppliedFutureThrows("executeIfEmpty should not run if table is not empty.", () -> context.index.executeIfEmpty(context.segment, () -> Futures.failedFuture(new AssertionError("This should not run")), context.timer), ex -> ex instanceof TableSegmentNotEmptyException);
// 3. After a removal.
val removeKeys = new ArrayList<TableKey>();
val removeVersions = update1.join();
for (int i = 0; i < unversionedKeys.size(); i++) {
removeKeys.add(TableKey.versioned(unversionedKeys.get(i).getKey(), removeVersions.get(i)));
}
val removeBatch = toRemoveBatch(removeKeys);
val persist2 = new CompletableFuture<Long>();
val update2 = context.index.update(context.segment, removeBatch, () -> persist2, context.timer);
val empty2 = update2.thenCompose(v -> context.index.executeIfEmpty(context.segment, () -> {
Assert.assertTrue("executeIfEmpty should not execute prior to call to update()..", persist2.isDone());
return CompletableFuture.completedFuture(null);
}, context.timer));
// Verify that executeIfEmpty is blocked on a conditional update.
persist2.complete(2L);
update2.get(SHORT_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
empty2.get(SHORT_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
}
Aggregations