use of io.pravega.segmentstore.storage.StorageNotPrimaryException in project pravega by pravega.
the class CreateOperation method call.
@Override
public SegmentProperties call() throws IOException, StorageNotPrimaryException {
// Create the segment using our own epoch.
String segmentName = getTarget();
val existingFiles = findAllRaw(segmentName);
if (existingFiles != null && existingFiles.length > 0) {
// Segment already exists; don't bother with anything else.
throw HDFSExceptionHelpers.segmentExistsException(segmentName);
}
// Create the first file in the segment.
Path fullPath = getFilePath(segmentName, 0, this.context.epoch);
long traceId = LoggerHelpers.traceEnter(log, "create", segmentName, fullPath);
atomicCreate(fullPath);
// Determine if someone also created it at the same time, but with a different epoch.
List<FileDescriptor> allFiles;
try {
allFiles = checkForFenceOut(segmentName, -1, new FileDescriptor(fullPath, 0, 0, this.context.epoch, false));
} catch (StorageNotPrimaryException ex) {
// We lost :(
this.context.fileSystem.delete(fullPath, true);
throw ex;
}
// wrote data to it while we were still checking).
for (FileDescriptor existingFile : allFiles) {
try {
if (existingFile.getEpoch() < this.context.epoch) {
if (existingFile.getLength() == 0) {
deleteFile(existingFile);
} else {
// Back off the change in case we found a non-empty file with lower epoch.
this.context.fileSystem.delete(fullPath, true);
throw HDFSExceptionHelpers.segmentExistsException(segmentName);
}
}
} catch (FileNotFoundException ex) {
log.warn("File {} was removed, unable include it in the post-fence check.", existingFile, ex);
}
}
LoggerHelpers.traceLeave(log, "create", traceId, segmentName);
return StreamSegmentInformation.builder().name(segmentName).build();
}
use of io.pravega.segmentstore.storage.StorageNotPrimaryException in project pravega by pravega.
the class FileSystemOperation method checkForFenceOut.
/**
* Verifies that the current segment has not been fenced out by another instance.
*
* @param segmentName The name of the segment.
* @param expectedFileCount The expected number of files in the file system. -1 means ignore.
* @param lastFile The last known file for this segment. This one's epoch will be compared against the files
* currently in the file system.
* @throws IOException If a general exception occurred.
* @throws StorageNotPrimaryException If this segment has been fenced out, using the arguments supplied above.
*/
List<FileDescriptor> checkForFenceOut(String segmentName, int expectedFileCount, FileDescriptor lastFile) throws IOException, StorageNotPrimaryException {
val systemFiles = findAll(segmentName, true);
if (expectedFileCount >= 0 && systemFiles.size() != expectedFileCount) {
// The files were changed externally (files removed or added). We cannot continue.
throw new StorageNotPrimaryException(segmentName, String.format("File count in FileSystem (%d) is different than the expected value (%d).", systemFiles.size(), expectedFileCount));
}
val lastSystemFile = systemFiles.get(systemFiles.size() - 1);
if (lastSystemFile.getEpoch() > lastFile.getEpoch()) {
// The last file's epoch in the file system is higher than ours. We have been fenced out.
throw new StorageNotPrimaryException(segmentName, String.format("Last file in FileSystem (%s) has a higher epoch than that of ours (%s).", lastSystemFile, lastFile));
}
return systemFiles;
}
use of io.pravega.segmentstore.storage.StorageNotPrimaryException in project pravega by pravega.
the class OpenWriteOperation method call.
@Override
public HDFSSegmentHandle call() throws IOException, StorageNotPrimaryException {
String segmentName = getTarget();
long traceId = LoggerHelpers.traceEnter(log, "openWrite", segmentName);
HDFSSegmentHandle result = null;
int attemptCount = 0;
while (result == null && attemptCount < MAX_OPEN_WRITE_RETRIES) {
// We care mostly about the last file in the sequence; we use this one to implement fencing.
val allFiles = findAll(segmentName, true);
val lastFile = allFiles.get(allFiles.size() - 1);
if (lastFile.getEpoch() > this.context.epoch) {
// so we cannot possibly reacquire it. This is regardless of whether the last file is read-only or not.
throw new StorageNotPrimaryException(segmentName, String.format("Found a file with a higher epoch (%d) than ours (%d): %s.", lastFile.getEpoch(), this.context.epoch, lastFile.getPath()));
}
if (lastFile.isReadOnly()) {
if (isSealed(lastFile)) {
// The last file is read-only and has the 'sealed' flag. This segment is sealed, as such, we cannot
// open it for writing, therefore open a read-only handle.
result = HDFSSegmentHandle.read(segmentName, allFiles);
} else if (lastFile.getEpoch() == this.context.epoch) {
// This means someone else must have just fenced us out.
throw new StorageNotPrimaryException(segmentName, String.format("Last file has our epoch (%d) but it is read-only: %s.", this.context.epoch, lastFile.getPath()));
} else {
// The last file is read-only and not sealed. This segment is fenced off and we can continue using it.
result = fenceOut(segmentName, lastFile.getLastOffset());
}
} else {
if (lastFile.getEpoch() == this.context.epoch) {
// The last file is not read-only and has the same epoch as us: We were the last owners of this segment;
// simply reuse the last file.
result = HDFSSegmentHandle.write(segmentName, allFiles);
} else {
// The last file has a lower epoch than us. Mark it as read-only, which should fence it off.
makeReadOnly(lastFile);
// Since the state of the last segment may have changed (new writes), we need to re-do the entire
// algorithm to pick up any new changes. This will also reduce the chances of collision with other
// competing instances of this container - eventually one of them will win based on the epoch.
// By not setting a result, we will force the containing loop to run one more time.
}
}
attemptCount++;
}
if (result == null) {
throw new StorageNotPrimaryException(segmentName, "Unable to acquire exclusive lock after the maximum number of attempts have been reached.");
}
LoggerHelpers.traceLeave(log, "openWrite", traceId, result);
return result;
}
use of io.pravega.segmentstore.storage.StorageNotPrimaryException in project pravega by pravega.
the class CreateOperationTests method testLowerEpochFencedOut.
/**
* Tests CreateOperation with fencing resolution for lower-epoch creation.
*/
@Test
public void testLowerEpochFencedOut() throws Exception {
@Cleanup val fs = new MockFileSystem();
val context1 = newContext(1, fs);
val context2 = newContext(2, fs);
// This should wipe out the file created by the first call.
new CreateOperation(SEGMENT_NAME, context2).call();
// This should fail because we attempt to create the segment using a lower epoch.
AssertExtensions.assertThrows("Lower epoch segment creation did not fail.", new CreateOperation(SEGMENT_NAME, context1)::call, ex -> ex instanceof StorageNotPrimaryException || ex instanceof FileAlreadyExistsException);
}
use of io.pravega.segmentstore.storage.StorageNotPrimaryException in project pravega by pravega.
the class CreateOperationTests method testConcurrentFencedOut.
/**
* Tests CreateOperation with fencing resolution for concurrent operations.
*/
@Test
public void testConcurrentFencedOut() throws Exception {
@Cleanup val fs = new MockFileSystem();
val context1 = newContext(1, fs);
val context2 = newContext(2, fs);
// Part 1: CreateOperation has higher epoch than competitor -> it should succeed.
Path fencedOutFile = context1.getFileName(SEGMENT_NAME, 0);
fs.setOnCreate(path -> fs.new CreateNewFileAction(fencedOutFile));
// This should wipe out the file created by the first call.
new CreateOperation(SEGMENT_NAME, context2).call();
checkFileExists(context2);
Assert.assertFalse("Fenced-out file was not deleted (lower-epoch test).", fs.exists(fencedOutFile));
// Part 2: CreateOperation has lower epoch than competitor -> it should back off and fail
fs.clear();
Path survivingFile = context2.getFileName(SEGMENT_NAME, 0);
fs.setOnCreate(path -> fs.new CreateNewFileAction(survivingFile));
// This should wipe out the file created by the first call.
AssertExtensions.assertThrows("Fenced-out operation did not fail.", new CreateOperation(SEGMENT_NAME, context1)::call, ex -> ex instanceof StorageNotPrimaryException);
checkFileExists(context2);
Assert.assertFalse("Fenced-out file was not deleted (higher-epoch test).", fs.exists(context1.getFileName(SEGMENT_NAME, 0)));
}
Aggregations