Search in sources :

Example 1 with StorageNotPrimaryException

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();
}
Also used : lombok.val(lombok.val) Path(org.apache.hadoop.fs.Path) FileNotFoundException(java.io.FileNotFoundException) StorageNotPrimaryException(io.pravega.segmentstore.storage.StorageNotPrimaryException)

Example 2 with StorageNotPrimaryException

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;
}
Also used : lombok.val(lombok.val) StorageNotPrimaryException(io.pravega.segmentstore.storage.StorageNotPrimaryException)

Example 3 with StorageNotPrimaryException

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;
}
Also used : lombok.val(lombok.val) StorageNotPrimaryException(io.pravega.segmentstore.storage.StorageNotPrimaryException)

Example 4 with StorageNotPrimaryException

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);
}
Also used : lombok.val(lombok.val) FileAlreadyExistsException(org.apache.hadoop.fs.FileAlreadyExistsException) Cleanup(lombok.Cleanup) StorageNotPrimaryException(io.pravega.segmentstore.storage.StorageNotPrimaryException) Test(org.junit.Test)

Example 5 with StorageNotPrimaryException

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)));
}
Also used : lombok.val(lombok.val) Path(org.apache.hadoop.fs.Path) Cleanup(lombok.Cleanup) StorageNotPrimaryException(io.pravega.segmentstore.storage.StorageNotPrimaryException) Test(org.junit.Test)

Aggregations

StorageNotPrimaryException (io.pravega.segmentstore.storage.StorageNotPrimaryException)29 lombok.val (lombok.val)22 Cleanup (lombok.Cleanup)15 Test (org.junit.Test)15 ByteArrayInputStream (java.io.ByteArrayInputStream)10 IOException (java.io.IOException)6 Path (org.apache.hadoop.fs.Path)6 FileStatus (org.apache.hadoop.fs.FileStatus)5 BadOffsetException (io.pravega.segmentstore.contracts.BadOffsetException)4 SegmentHandle (io.pravega.segmentstore.storage.SegmentHandle)4 StreamSegmentSealedException (io.pravega.segmentstore.contracts.StreamSegmentSealedException)3 StorageMetadataWritesFencedOutException (io.pravega.segmentstore.storage.metadata.StorageMetadataWritesFencedOutException)3 CompletionException (java.util.concurrent.CompletionException)3 StorageFullException (io.pravega.segmentstore.storage.StorageFullException)2 FileNotFoundException (java.io.FileNotFoundException)2 ArrayList (java.util.ArrayList)2 FSDataOutputStream (org.apache.hadoop.fs.FSDataOutputStream)2 Exceptions (io.pravega.common.Exceptions)1 Timer (io.pravega.common.Timer)1 Futures (io.pravega.common.concurrent.Futures)1