use of io.pravega.segmentstore.contracts.StreamSegmentSealedException in project pravega by pravega.
the class FileSystemStorage method doWrite.
private Void doWrite(SegmentHandle handle, long offset, InputStream data, int length) throws Exception {
long traceId = LoggerHelpers.traceEnter(log, "write", handle.getSegmentName(), offset, length);
Timer timer = new Timer();
if (handle.isReadOnly()) {
throw new IllegalArgumentException("Write called on a readonly handle of segment " + handle.getSegmentName());
}
Path path = Paths.get(config.getRoot(), handle.getSegmentName());
// This means that writes to readonly files also succeed. We need to explicitly check permissions in this case.
if (!isWritableFile(path)) {
throw new StreamSegmentSealedException(handle.getSegmentName());
}
long fileSize = path.toFile().length();
if (fileSize < offset) {
throw new BadOffsetException(handle.getSegmentName(), fileSize, offset);
} else {
long totalBytesWritten = 0;
try (FileChannel channel = FileChannel.open(path, StandardOpenOption.WRITE)) {
// Wrap the input data into a ReadableByteChannel, but do not close it. Doing so will result in closing
// the underlying InputStream, which is not desirable if it is to be reused.
ReadableByteChannel sourceChannel = Channels.newChannel(data);
while (length != 0) {
long bytesWritten = channel.transferFrom(sourceChannel, offset, length);
assert bytesWritten > 0 : "Unable to make any progress transferring data.";
offset += bytesWritten;
totalBytesWritten += bytesWritten;
length -= bytesWritten;
}
}
FileSystemMetrics.WRITE_LATENCY.reportSuccessEvent(timer.getElapsed());
FileSystemMetrics.WRITE_BYTES.add(totalBytesWritten);
LoggerHelpers.traceLeave(log, "write", traceId);
return null;
}
}
use of io.pravega.segmentstore.contracts.StreamSegmentSealedException in project pravega by pravega.
the class ContainerMetadataUpdateTransactionTests method testPreProcessStreamSegmentAppend.
// region StreamSegmentAppendOperation
/**
* Tests the preProcess method with StreamSegmentAppend operations.
* Scenarios:
* * Recovery Mode
* * Non-recovery mode
* * StreamSegment is Merged (both in-transaction and in-metadata)
* * StreamSegment is Sealed (both in-transaction and in-metadata)
*/
@Test
public void testPreProcessStreamSegmentAppend() throws Exception {
val metadata = createMetadata();
StreamSegmentAppendOperation appendOp = createAppendNoOffset();
// When everything is OK (in recovery mode) - nothing should change.
metadata.enterRecoveryMode();
val txn1 = createUpdateTransaction(metadata);
txn1.preProcessOperation(appendOp);
AssertExtensions.assertLessThan("Unexpected StreamSegmentOffset after call to preProcess in recovery mode.", 0, appendOp.getStreamSegmentOffset());
checkNoSequenceNumberAssigned(appendOp, "call to preProcess in recovery mode");
Assert.assertEquals("preProcess(Append) seems to have changed the Updater internal state in recovery mode.", SEGMENT_LENGTH, txn1.getStreamSegmentMetadata(SEGMENT_ID).getLength());
Assert.assertEquals("preProcess(Append) seems to have changed the metadata in recovery mode.", SEGMENT_LENGTH, metadata.getStreamSegmentMetadata(SEGMENT_ID).getLength());
// When everything is OK (no recovery mode).
metadata.exitRecoveryMode();
val txn2 = createUpdateTransaction(metadata);
txn2.preProcessOperation(appendOp);
Assert.assertEquals("Unexpected StreamSegmentOffset after call to preProcess in non-recovery mode.", SEGMENT_LENGTH, appendOp.getStreamSegmentOffset());
checkNoSequenceNumberAssigned(appendOp, "call to preProcess in non-recovery mode");
Assert.assertEquals("preProcess(Append) seems to have changed the Updater internal state.", SEGMENT_LENGTH, txn2.getStreamSegmentMetadata(SEGMENT_ID).getLength());
Assert.assertEquals("preProcess(Append) seems to have changed the metadata.", SEGMENT_LENGTH, metadata.getStreamSegmentMetadata(SEGMENT_ID).getLength());
// When StreamSegment is merged (via transaction).
StreamSegmentAppendOperation transactionAppendOp = new StreamSegmentAppendOperation(SEALED_TRANSACTION_ID, DEFAULT_APPEND_DATA, null);
MergeTransactionOperation mergeOp = createMerge();
txn2.preProcessOperation(mergeOp);
txn2.acceptOperation(mergeOp);
Assert.assertFalse("Transaction should not be merged in metadata (yet).", metadata.getStreamSegmentMetadata(SEALED_TRANSACTION_ID).isMerged());
AssertExtensions.assertThrows("Unexpected behavior for preProcess(Append) when Segment is merged (in transaction).", () -> txn2.preProcessOperation(transactionAppendOp), ex -> ex instanceof StreamSegmentMergedException);
// When StreamSegment is merged (via metadata).
txn2.commit(metadata);
Assert.assertTrue("Transaction should have been merged in metadata.", metadata.getStreamSegmentMetadata(SEALED_TRANSACTION_ID).isMerged());
AssertExtensions.assertThrows("Unexpected behavior for preProcess(Append) when Segment is merged (in metadata).", () -> txn2.preProcessOperation(transactionAppendOp), ex -> ex instanceof StreamSegmentMergedException);
// When StreamSegment is sealed (via transaction).
StreamSegmentSealOperation sealOp = createSeal();
txn2.preProcessOperation(sealOp);
txn2.acceptOperation(sealOp);
Assert.assertFalse("StreamSegment should not be sealed in metadata (yet).", metadata.getStreamSegmentMetadata(SEGMENT_ID).isSealed());
AssertExtensions.assertThrows("Unexpected behavior for preProcess(Append) when Segment is sealed (in transaction).", () -> txn2.preProcessOperation(createAppendNoOffset()), ex -> ex instanceof StreamSegmentSealedException);
// When StreamSegment is sealed (via metadata).
txn2.commit(metadata);
Assert.assertTrue("StreamSegment should have been sealed in metadata.", metadata.getStreamSegmentMetadata(SEGMENT_ID).isSealed());
AssertExtensions.assertThrows("Unexpected behavior for preProcess(Append) when Segment is sealed (in metadata).", () -> txn2.preProcessOperation(createAppendNoOffset()), ex -> ex instanceof StreamSegmentSealedException);
}
use of io.pravega.segmentstore.contracts.StreamSegmentSealedException in project pravega by pravega.
the class ContainerMetadataUpdateTransactionTests method testPreProcessStreamSegmentSeal.
// endregion
// region StreamSegmentSealOperation
/**
* Tests the preProcess method with StreamSegmentSeal operations.
* Scenarios:
* * Recovery Mode
* * Non-recovery mode
* * StreamSegment is Merged (both in-transaction and in-metadata)
* * StreamSegment is Sealed (both in-transaction and in-metadata)
*/
@Test
public void testPreProcessStreamSegmentSeal() throws Exception {
UpdateableContainerMetadata metadata = createMetadata();
StreamSegmentSealOperation sealOp = createSeal();
// When everything is OK (in recovery mode) - nothing should change.
metadata.enterRecoveryMode();
val txn1 = createUpdateTransaction(metadata);
txn1.preProcessOperation(sealOp);
AssertExtensions.assertLessThan("Unexpected StreamSegmentLength after call to preProcess in recovery mode.", 0, sealOp.getStreamSegmentOffset());
checkNoSequenceNumberAssigned(sealOp, "call to preProcess in recovery mode");
Assert.assertFalse("preProcess(Seal) seems to have changed the Updater internal state in recovery mode.", txn1.getStreamSegmentMetadata(SEGMENT_ID).isSealed());
Assert.assertFalse("preProcess(Seal) seems to have changed the metadata in recovery mode.", metadata.getStreamSegmentMetadata(SEGMENT_ID).isSealed());
// When everything is OK (no recovery mode).
metadata.exitRecoveryMode();
val txn2 = createUpdateTransaction(metadata);
txn2.preProcessOperation(sealOp);
Assert.assertEquals("Unexpected StreamSegmentLength after call to preProcess in non-recovery mode.", SEGMENT_LENGTH, sealOp.getStreamSegmentOffset());
checkNoSequenceNumberAssigned(sealOp, "call to preProcess in non-recovery mode");
Assert.assertFalse("preProcess(Seal) seems to have changed the Updater internal state.", txn2.getStreamSegmentMetadata(SEGMENT_ID).isSealed());
Assert.assertFalse("preProcess(Seal) seems to have changed the metadata.", metadata.getStreamSegmentMetadata(SEGMENT_ID).isSealed());
// When StreamSegment is merged (via transaction).
StreamSegmentSealOperation transactionSealOp = new StreamSegmentSealOperation(SEALED_TRANSACTION_ID);
MergeTransactionOperation mergeOp = createMerge();
txn2.preProcessOperation(mergeOp);
txn2.acceptOperation(mergeOp);
Assert.assertFalse("Transaction should not be merged in metadata (yet).", metadata.getStreamSegmentMetadata(SEALED_TRANSACTION_ID).isMerged());
AssertExtensions.assertThrows("Unexpected behavior for preProcess(Seal) when Segment is merged (in transaction).", () -> txn2.preProcessOperation(transactionSealOp), ex -> ex instanceof StreamSegmentMergedException);
// When StreamSegment is merged (via metadata).
txn2.commit(metadata);
Assert.assertTrue("Transaction should have been merged in metadata.", metadata.getStreamSegmentMetadata(SEALED_TRANSACTION_ID).isMerged());
AssertExtensions.assertThrows("Unexpected behavior for preProcess(Seal) when Segment is merged (in metadata).", () -> txn2.preProcessOperation(transactionSealOp), ex -> ex instanceof StreamSegmentMergedException);
// When StreamSegment is sealed (via transaction).
txn2.acceptOperation(sealOp);
Assert.assertFalse("StreamSegment should not be sealed in metadata (yet).", metadata.getStreamSegmentMetadata(SEGMENT_ID).isSealed());
AssertExtensions.assertThrows("Unexpected behavior for preProcess(Seal) when Segment is sealed (in transaction).", () -> txn2.preProcessOperation(createSeal()), ex -> ex instanceof StreamSegmentSealedException);
// When StreamSegment is sealed (via metadata).
txn2.commit(metadata);
Assert.assertTrue("StreamSegment should have been sealed in metadata.", metadata.getStreamSegmentMetadata(SEGMENT_ID).isSealed());
AssertExtensions.assertThrows("Unexpected behavior for preProcess(Seal) when Segment is sealed (in metadata).", () -> txn2.preProcessOperation(createSeal()), ex -> ex instanceof StreamSegmentSealedException);
}
use of io.pravega.segmentstore.contracts.StreamSegmentSealedException in project pravega by pravega.
the class AppendProcessor method handleException.
private void handleException(UUID writerId, long requestId, String segment, String doingWhat, Throwable u) {
if (u == null) {
IllegalStateException exception = new IllegalStateException("No exception to handle.");
log.error("Append processor: Error {} on segment = '{}'", doingWhat, segment, exception);
throw exception;
}
u = Exceptions.unwrap(u);
if (u instanceof StreamSegmentExistsException) {
log.warn("Segment '{}' already exists and {} cannot perform operation '{}'.", segment, writerId, doingWhat);
connection.send(new SegmentAlreadyExists(requestId, segment));
} else if (u instanceof StreamSegmentNotExistsException) {
log.warn("Segment '{}' does not exist and {} cannot perform operation '{}'.", segment, writerId, doingWhat);
connection.send(new NoSuchSegment(requestId, segment));
} else if (u instanceof StreamSegmentSealedException) {
log.info("Segment '{}' is sealed and {} cannot perform operation '{}'.", segment, writerId, doingWhat);
connection.send(new SegmentIsSealed(requestId, segment));
} else if (u instanceof ContainerNotFoundException) {
int containerId = ((ContainerNotFoundException) u).getContainerId();
log.warn("Wrong host. Segment '{}' (Container {}) is not owned and {} cannot perform operation '{}'.", segment, containerId, writerId, doingWhat);
connection.send(new WrongHost(requestId, segment, ""));
} else if (u instanceof BadAttributeUpdateException) {
log.warn("Bad attribute update by {} on segment {}.", writerId, segment, u);
connection.send(new InvalidEventNumber(writerId, requestId));
connection.close();
} else if (u instanceof TooManyAttributesException) {
log.warn("Attribute limit would be exceeded by {} on segment {}.", writerId, segment, u);
connection.send(new InvalidEventNumber(writerId, requestId));
connection.close();
} else if (u instanceof AuthenticationException) {
log.warn("Token check failed while being written by {} on segment {}.", writerId, segment, u);
connection.send(new WireCommands.AuthTokenCheckFailed(requestId));
connection.close();
} else if (u instanceof UnsupportedOperationException) {
log.warn("Unsupported Operation '{}'.", doingWhat, u);
connection.send(new OperationUnsupported(requestId, doingWhat));
} else {
log.error("Error (Segment = '{}', Operation = 'append')", segment, u);
// Closing connection should reinitialize things, and hopefully fix the problem
connection.close();
}
}
use of io.pravega.segmentstore.contracts.StreamSegmentSealedException in project pravega by pravega.
the class StreamSegmentReadIndex method triggerFutureReads.
// endregion
// region Reading
/**
* Triggers all future reads that have a starting offset before the given value.
*
* @throws IllegalStateException If the read index is in recovery mode.
*/
void triggerFutureReads() {
Exceptions.checkNotClosed(this.closed, this);
Preconditions.checkState(!this.recoveryMode, "StreamSegmentReadIndex is in Recovery Mode.");
// Get all eligible Future Reads which wait for data prior to the end offset.
// Since we are not actually using this entry's data, there is no need to 'touch' it.
ReadIndexEntry lastEntry;
synchronized (this.lock) {
lastEntry = this.indexEntries.getLast();
}
if (lastEntry == null) {
// Nothing to do.
return;
}
Collection<FutureReadResultEntry> futureReads;
boolean sealed = this.metadata.isSealed();
if (sealed) {
// Get everything, even if some Future Reads are in the future - those will eventually return EndOfSegment.
futureReads = this.futureReads.pollAll();
} else {
// Get only those up to the last offset of the last append.
futureReads = this.futureReads.poll(lastEntry.getLastStreamSegmentOffset());
}
log.debug("{}: triggerFutureReads (Count = {}, Offset = {}, Sealed = {}).", this.traceObjectId, futureReads.size(), lastEntry.getLastStreamSegmentOffset(), sealed);
for (FutureReadResultEntry r : futureReads) {
ReadResultEntry entry = getSingleReadResultEntry(r.getStreamSegmentOffset(), r.getRequestedReadLength());
assert entry != null : "Serving a StorageReadResultEntry with a null result";
assert !(entry instanceof FutureReadResultEntry) : "Serving a FutureReadResultEntry with another FutureReadResultEntry.";
log.trace("{}: triggerFutureReads (Offset = {}, Type = {}).", this.traceObjectId, r.getStreamSegmentOffset(), entry.getType());
if (entry.getType() == ReadResultEntryType.EndOfStreamSegment) {
// We have attempted to read beyond the end of the stream. Fail the read request with the appropriate message.
r.fail(new StreamSegmentSealedException(String.format("StreamSegment has been sealed at offset %d. There can be no more reads beyond this offset.", this.metadata.getLength())));
} else {
if (!entry.getContent().isDone()) {
// Normally, all Future Reads are served from Cache, since they reflect data that has just been appended.
// However, it's possible that after recovery, we get a read for some data that we do not have in the
// cache (but it's not a tail read) - this data exists in Storage but our StorageLength has not yet been
// updated. As such, the only solution we have is to return a FutureRead which will be satisfied when
// the Writer updates the StorageLength (and trigger future reads). In that scenario, entry we get
// will likely not be auto-fetched, so we need to request the content.
entry.requestContent(this.config.getStorageReadDefaultTimeout());
}
CompletableFuture<ReadResultEntryContents> entryContent = entry.getContent();
entryContent.thenAccept(r::complete);
Futures.exceptionListener(entryContent, r::fail);
}
}
}
Aggregations