use of io.pravega.segmentstore.contracts.StreamSegmentNotExistsException in project pravega by pravega.
the class RollingStorage method concat.
@Override
public void concat(SegmentHandle targetHandle, long targetOffset, String sourceSegment) throws StreamSegmentException {
val target = asWritableHandle(targetHandle);
ensureOffset(target, targetOffset);
ensureNotDeleted(target);
ensureNotSealed(target);
long traceId = LoggerHelpers.traceEnter(log, "concat", target, targetOffset, sourceSegment);
// We can only use a Segment as a concat source if it is Sealed.
RollingSegmentHandle source = (RollingSegmentHandle) openWrite(sourceSegment);
Preconditions.checkState(source.isSealed(), "Cannot concat segment '%s' into '%s' because it is not sealed.", sourceSegment, target.getSegmentName());
if (source.length() == 0) {
// Source is empty; do not bother with concatenation.
log.debug("Concat source '{}' is empty. Deleting instead of concatenating.", source);
delete(source);
return;
}
// We can only use a Segment as a concat source if all of its SegmentChunks exist.
refreshChunkExistence(source);
Preconditions.checkState(source.chunks().stream().allMatch(SegmentChunk::exists), "Cannot use Segment '%s' as concat source because it is truncated.", source.getSegmentName());
if (shouldConcatNatively(source, target)) {
// The Source either does not have a Header or is made up of a single SegmentChunk that can fit entirely into
// the Target's Active SegmentChunk. Concat it directly without touching the header file; this helps prevent
// having a lot of very small SegmentChunks around if the application has a lot of small transactions.
log.debug("Concat '{}' into '{}' using native method.", source, target);
SegmentChunk lastTarget = target.lastChunk();
if (lastTarget == null || lastTarget.isSealed()) {
// Make sure the last SegmentChunk of the target is not sealed, otherwise we can't concat into it.
rollover(target);
}
SegmentChunk lastSource = source.lastChunk();
this.baseStorage.concat(target.getActiveChunkHandle(), target.lastChunk().getLength(), lastSource.getName());
target.lastChunk().increaseLength(lastSource.getLength());
if (source.getHeaderHandle() != null) {
try {
this.baseStorage.delete(source.getHeaderHandle());
} catch (StreamSegmentNotExistsException ex) {
// It's ok if it's not there anymore.
log.warn("Attempted to delete concat source Header '{}' but it doesn't exist.", source.getHeaderHandle().getSegmentName(), ex);
}
}
} else {
// Generate new SegmentChunk entries from the SegmentChunks of the Source Segment(but update their start offsets).
log.debug("Concat '{}' into '{}' using header merge method.", source, target);
if (target.getHeaderHandle() == null) {
// We need to concat into a Segment that does not have a Header (yet). Create one before continuing.
createHeader(target);
}
List<SegmentChunk> newSegmentChunks = rebase(source.chunks(), target.length());
sealActiveChunk(target);
serializeBeginConcat(target, source);
this.baseStorage.concat(target.getHeaderHandle(), target.getHeaderLength(), source.getHeaderHandle().getSegmentName());
target.increaseHeaderLength(source.getHeaderLength());
target.addChunks(newSegmentChunks);
// After we do a header merge, it's possible that the (new) last chunk may still have space to write to.
// Unseal it now so that future writes/concats will not unnecessarily create chunks. Note that this will not
// unseal the segment (even though it's unsealed) - that is determined by the Header file seal status.
unsealLastChunkIfNecessary(target);
}
LoggerHelpers.traceLeave(log, "concat", traceId, target, targetOffset, sourceSegment);
}
use of io.pravega.segmentstore.contracts.StreamSegmentNotExistsException in project pravega by pravega.
the class RollingStorage method openHandle.
private RollingSegmentHandle openHandle(String segmentName, boolean readOnly) throws StreamSegmentException {
// Load up the handle from Storage.
RollingSegmentHandle handle;
try {
// Attempt to open using Header.
val headerInfo = getHeaderInfo(segmentName);
val headerHandle = readOnly ? this.baseStorage.openRead(headerInfo.getName()) : this.baseStorage.openWrite(headerInfo.getName());
handle = readHeader(headerInfo, headerHandle);
} catch (StreamSegmentNotExistsException ex) {
// Header does not exist. Attempt to open Segment directly.
val segmentHandle = readOnly ? this.baseStorage.openRead(segmentName) : this.baseStorage.openWrite(segmentName);
handle = new RollingSegmentHandle(segmentHandle);
}
// Update each SegmentChunk's Length (based on offset difference) and mark them as Sealed.
SegmentChunk last = null;
for (SegmentChunk s : handle.chunks()) {
if (last != null) {
last.setLength(s.getStartOffset() - last.getStartOffset());
last.markSealed();
}
last = s;
}
// For the last one, we need to actually check the file and update its info.
if (last != null) {
val si = this.baseStorage.getStreamSegmentInfo(last.getName());
last.setLength(si.getLength());
if (si.isSealed()) {
last.markSealed();
if (handle.getHeaderHandle() == null) {
handle.markSealed();
}
}
}
return handle;
}
use of io.pravega.segmentstore.contracts.StreamSegmentNotExistsException in project pravega by pravega.
the class RollingStorage method deleteChunks.
private void deleteChunks(RollingSegmentHandle handle, Predicate<SegmentChunk> canDelete) throws StreamSegmentException {
for (SegmentChunk s : handle.chunks()) {
if (s.exists() && canDelete.test(s)) {
try {
val subHandle = this.baseStorage.openWrite(s.getName());
this.baseStorage.delete(subHandle);
s.markInexistent();
log.debug("Deleted SegmentChunk '{}' for '{}'.", s, handle);
} catch (StreamSegmentNotExistsException ex) {
// Ignore; It's OK if it doesn't exist; just make sure the handle is updated.
s.markInexistent();
}
}
}
}
use of io.pravega.segmentstore.contracts.StreamSegmentNotExistsException in project pravega by pravega.
the class StorageTestBase method testWrite.
/**
* Tests the write() method.
*
* @throws Exception if an unexpected error occurred.
*/
@Test
public void testWrite() throws Exception {
String segmentName = "foo_write";
int appendCount = 100;
try (Storage s = createStorage()) {
s.initialize(DEFAULT_EPOCH);
createSegment(segmentName, s);
// Invalid handle.
val readOnlyHandle = s.openRead(segmentName).join();
assertThrows("write() did not throw for read-only handle.", () -> s.write(readOnlyHandle, 0, new ByteArrayInputStream("h".getBytes()), 1, TIMEOUT), ex -> ex instanceof IllegalArgumentException);
assertThrows("write() did not throw for handle pointing to inexistent segment.", () -> s.write(createInexistentSegmentHandle(s, false), 0, new ByteArrayInputStream("h".getBytes()), 1, TIMEOUT), ex -> ex instanceof StreamSegmentNotExistsException);
val writeHandle = s.openWrite(segmentName).join();
long offset = 0;
for (int j = 0; j < appendCount; j++) {
byte[] writeData = String.format(APPEND_FORMAT, segmentName, j).getBytes();
// We intentionally add some garbage at the end of the dataStream to verify that write() takes into account
// the value of the "length" argument.
val dataStream = new SequenceInputStream(new ByteArrayInputStream(writeData), new ByteArrayInputStream(new byte[100]));
s.write(writeHandle, offset, dataStream, writeData.length, TIMEOUT).join();
offset += writeData.length;
}
// Check bad offset.
final long finalOffset = offset;
assertThrows("write() did not throw bad offset write (smaller).", () -> s.write(writeHandle, finalOffset - 1, new ByteArrayInputStream("h".getBytes()), 1, TIMEOUT), ex -> ex instanceof BadOffsetException);
assertThrows("write() did not throw bad offset write (larger).", () -> s.write(writeHandle, finalOffset + 1, new ByteArrayInputStream("h".getBytes()), 1, TIMEOUT), ex -> ex instanceof BadOffsetException);
// Check post-delete write.
s.delete(writeHandle, TIMEOUT).join();
assertThrows("write() did not throw for a deleted StreamSegment.", () -> s.write(writeHandle, 0, new ByteArrayInputStream(new byte[1]), 1, TIMEOUT), ex -> ex instanceof StreamSegmentNotExistsException);
}
}
use of io.pravega.segmentstore.contracts.StreamSegmentNotExistsException in project pravega by pravega.
the class RollingStorageTests method testCreateRecovery.
/**
* Tests the case when Create was interrupted after it created the Header file but before populating it.
*/
@Test
public void testCreateRecovery() throws Exception {
@Cleanup val baseStorage = new TestStorage();
@Cleanup val s = new RollingStorage(baseStorage, DEFAULT_ROLLING_POLICY);
s.initialize(1);
// Create an empty header file. This simulates a create() operation that failed mid-way.
baseStorage.create(StreamSegmentNameUtils.getHeaderSegmentName(SEGMENT_NAME));
Assert.assertFalse("Not expecting Segment to exist.", s.exists(SEGMENT_NAME));
AssertExtensions.assertThrows("Not expecting Segment to exist (getStreamSegmentInfo).", () -> s.getStreamSegmentInfo(SEGMENT_NAME), ex -> ex instanceof StreamSegmentNotExistsException);
AssertExtensions.assertThrows("Not expecting Segment to exist (openHandle).", () -> s.openRead(SEGMENT_NAME), ex -> ex instanceof StreamSegmentNotExistsException);
// Retry the operation and verify everything is in place.
s.create(SEGMENT_NAME);
val si = s.getStreamSegmentInfo(SEGMENT_NAME);
Assert.assertEquals("Expected the Segment to have been created.", 0, si.getLength());
}
Aggregations