use of io.pravega.segmentstore.contracts.ReadResult in project pravega by pravega.
the class ContainerReadIndexTests method testStorageFailedReads.
/**
* Tests the ability to handle Storage read failures.
*/
@Test
public void testStorageFailedReads() {
// Create all segments (Storage and Metadata).
@Cleanup TestContext context = new TestContext();
ArrayList<Long> segmentIds = createSegments(context);
createSegmentsInStorage(context);
// Read beyond Storage actual offset (metadata is corrupt)
long testSegmentId = segmentIds.get(0);
UpdateableSegmentMetadata sm = context.metadata.getStreamSegmentMetadata(testSegmentId);
sm.setStorageLength(1024 * 1024);
sm.setLength(1024 * 1024);
AssertExtensions.assertThrows("Unexpected exception when attempting to read beyond the Segment length in Storage.", () -> {
@Cleanup ReadResult readResult = context.readIndex.read(testSegmentId, 0, 100, TIMEOUT);
Assert.assertTrue("Unexpected value from hasNext() when there should be at least one ReadResultEntry.", readResult.hasNext());
ReadResultEntry entry = readResult.next();
Assert.assertEquals("Unexpected ReadResultEntryType.", ReadResultEntryType.Storage, entry.getType());
entry.requestContent(TIMEOUT);
entry.getContent().get(TIMEOUT.toMillis(), TimeUnit.MILLISECONDS);
}, ex -> ex instanceof ArrayIndexOutOfBoundsException);
// Segment not exists (exists in metadata, but not in Storage)
val handle = context.storage.openWrite(sm.getName()).join();
context.storage.delete(handle, TIMEOUT).join();
AssertExtensions.assertThrows("Unexpected exception when attempting to from a segment that exists in Metadata, but not in Storage.", () -> {
@Cleanup ReadResult readResult = context.readIndex.read(testSegmentId, 0, 100, TIMEOUT);
Assert.assertTrue("Unexpected value from hasNext() when there should be at least one ReadResultEntry.", readResult.hasNext());
ReadResultEntry entry = readResult.next();
Assert.assertEquals("Unexpected ReadResultEntryType.", ReadResultEntryType.Storage, entry.getType());
entry.requestContent(TIMEOUT);
entry.getContent().get(TIMEOUT.toMillis(), TimeUnit.MILLISECONDS);
}, ex -> ex instanceof StreamSegmentNotExistsException);
}
use of io.pravega.segmentstore.contracts.ReadResult in project pravega by pravega.
the class PravegaRequestProcessor method readSegment.
// endregion
// region RequestProcessor Implementation
@Override
public void readSegment(ReadSegment readSegment) {
Timer timer = new Timer();
final String segment = readSegment.getSegment();
if (!verifyToken(segment, readSegment.getOffset(), readSegment.getDelegationToken(), READ, "Read Segment")) {
return;
}
final int readSize = min(MAX_READ_SIZE, max(TYPE_PLUS_LENGTH_SIZE, readSegment.getSuggestedLength()));
long trace = LoggerHelpers.traceEnter(log, "readSegment", readSegment);
segmentStore.read(segment, readSegment.getOffset(), readSize, TIMEOUT).thenAccept(readResult -> {
LoggerHelpers.traceLeave(log, "readSegment", trace, readResult);
handleReadResult(readSegment, readResult);
DYNAMIC_LOGGER.incCounterValue(nameFromSegment(SEGMENT_READ_BYTES, segment), readResult.getConsumedLength());
readStreamSegment.reportSuccessEvent(timer.getElapsed());
}).exceptionally(ex -> handleException(readSegment.getOffset(), segment, "Read segment", ex));
}
use of io.pravega.segmentstore.contracts.ReadResult in project pravega by pravega.
the class PravegaRequestProcessor method handleReadResult.
/**
* Handles a readResult.
* If there are cached entries that can be returned without blocking only these are returned.
* Otherwise the call will request the data and setup a callback to return the data when it is available.
* If no data is available but it was detected that the Segment had been truncated beyond the current offset,
* an appropriate message is sent back over the connection.
*/
private void handleReadResult(ReadSegment request, ReadResult result) {
String segment = request.getSegment();
ArrayList<ReadResultEntryContents> cachedEntries = new ArrayList<>();
ReadResultEntry nonCachedEntry = collectCachedEntries(request.getOffset(), result, cachedEntries);
boolean truncated = nonCachedEntry != null && nonCachedEntry.getType() == Truncated;
boolean endOfSegment = nonCachedEntry != null && nonCachedEntry.getType() == EndOfStreamSegment;
boolean atTail = nonCachedEntry != null && nonCachedEntry.getType() == Future;
if (!cachedEntries.isEmpty() || endOfSegment) {
// We managed to collect some data. Send it.
ByteBuffer data = copyData(cachedEntries);
SegmentRead reply = new SegmentRead(segment, request.getOffset(), atTail, endOfSegment, data);
connection.send(reply);
} else if (truncated) {
// We didn't collect any data, instead we determined that the current read offset was truncated.
// Determine the current Start Offset and send that back.
segmentStore.getStreamSegmentInfo(segment, false, TIMEOUT).thenAccept(info -> connection.send(new SegmentIsTruncated(nonCachedEntry.getStreamSegmentOffset(), segment, info.getStartOffset()))).exceptionally(e -> handleException(nonCachedEntry.getStreamSegmentOffset(), segment, "Read segment", e));
} else {
Preconditions.checkState(nonCachedEntry != null, "No ReadResultEntries returned from read!?");
nonCachedEntry.requestContent(TIMEOUT);
nonCachedEntry.getContent().thenAccept(contents -> {
ByteBuffer data = copyData(Collections.singletonList(contents));
connection.send(new SegmentRead(segment, nonCachedEntry.getStreamSegmentOffset(), false, endOfSegment, data));
}).exceptionally(e -> {
if (Exceptions.unwrap(e) instanceof StreamSegmentTruncatedException) {
// The Segment may have been truncated in Storage after we got this entry but before we managed
// to make a read. In that case, send the appropriate error back.
connection.send(new SegmentIsTruncated(nonCachedEntry.getStreamSegmentOffset(), segment, nonCachedEntry.getStreamSegmentOffset()));
} else {
handleException(nonCachedEntry.getStreamSegmentOffset(), segment, "Read segment", e);
}
return null;
}).exceptionally(e -> handleException(nonCachedEntry.getStreamSegmentOffset(), segment, "Read segment", e));
}
}
Aggregations