diff --git oak-segment-azure/src/main/java/org/apache/jackrabbit/oak/segment/azure/AzureArchiveManager.java oak-segment-azure/src/main/java/org/apache/jackrabbit/oak/segment/azure/AzureArchiveManager.java index ed7b00939e..a50d12bf41 100644 --- oak-segment-azure/src/main/java/org/apache/jackrabbit/oak/segment/azure/AzureArchiveManager.java +++ oak-segment-azure/src/main/java/org/apache/jackrabbit/oak/segment/azure/AzureArchiveManager.java @@ -53,11 +53,11 @@ public class AzureArchiveManager implements SegmentArchiveManager { private static final Logger log = LoggerFactory.getLogger(AzureSegmentArchiveReader.class); - private final CloudBlobDirectory cloudBlobDirectory; + protected final CloudBlobDirectory cloudBlobDirectory; - private final IOMonitor ioMonitor; + protected final IOMonitor ioMonitor; - private final FileStoreMonitor monitor; + protected final FileStoreMonitor monitor; public AzureArchiveManager(CloudBlobDirectory cloudBlobDirectory, IOMonitor ioMonitor, FileStoreMonitor fileStoreMonitor) { this.cloudBlobDirectory = cloudBlobDirectory; @@ -207,7 +207,7 @@ public class AzureArchiveManager implements SegmentArchiveManager { } - private CloudBlobDirectory getDirectory(String archiveName) throws IOException { + protected CloudBlobDirectory getDirectory(String archiveName) throws IOException { try { return cloudBlobDirectory.getDirectoryReference(archiveName); } catch (URISyntaxException e) { diff --git oak-segment-azure/src/main/java/org/apache/jackrabbit/oak/segment/azure/AzurePersistence.java oak-segment-azure/src/main/java/org/apache/jackrabbit/oak/segment/azure/AzurePersistence.java index 1aaedf670d..ef82a87ccf 100644 --- oak-segment-azure/src/main/java/org/apache/jackrabbit/oak/segment/azure/AzurePersistence.java +++ oak-segment-azure/src/main/java/org/apache/jackrabbit/oak/segment/azure/AzurePersistence.java @@ -50,7 +50,7 @@ public class AzurePersistence implements SegmentNodeStorePersistence { private static final Logger log = LoggerFactory.getLogger(AzurePersistence.class); - private final CloudBlobDirectory segmentstoreDirectory; + protected final CloudBlobDirectory segmentstoreDirectory; public AzurePersistence(CloudBlobDirectory segmentStoreDirectory) { this.segmentstoreDirectory = segmentStoreDirectory; diff --git oak-segment-azure/src/main/java/org/apache/jackrabbit/oak/segment/azure/AzureUtilities.java oak-segment-azure/src/main/java/org/apache/jackrabbit/oak/segment/azure/AzureUtilities.java index a40d3ea3bc..565ef95975 100644 --- oak-segment-azure/src/main/java/org/apache/jackrabbit/oak/segment/azure/AzureUtilities.java +++ oak-segment-azure/src/main/java/org/apache/jackrabbit/oak/segment/azure/AzureUtilities.java @@ -35,6 +35,8 @@ import com.microsoft.azure.storage.blob.BlobListingDetails; import com.microsoft.azure.storage.blob.CloudBlob; import com.microsoft.azure.storage.blob.CloudBlobContainer; import com.microsoft.azure.storage.blob.CloudBlobDirectory; + +import org.apache.jackrabbit.oak.segment.spi.RepositoryNotReachableException; import org.apache.jackrabbit.oak.segment.spi.persistence.Buffer; import org.jetbrains.annotations.NotNull; import org.slf4j.Logger; @@ -80,7 +82,7 @@ public final class AzureUtilities { blob.download(new ByteBufferOutputStream(buffer)); buffer.flip(); } catch (StorageException e) { - throw new IOException(e); + throw new RepositoryNotReachableException(e); } } diff --git oak-segment-azure/src/test/java/org/apache/jackrabbit/oak/segment/azure/AzureReadSegmentTest.java oak-segment-azure/src/test/java/org/apache/jackrabbit/oak/segment/azure/AzureReadSegmentTest.java new file mode 100644 index 0000000000..c46eeeb256 --- /dev/null +++ oak-segment-azure/src/test/java/org/apache/jackrabbit/oak/segment/azure/AzureReadSegmentTest.java @@ -0,0 +1,122 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.jackrabbit.oak.segment.azure; + +import com.microsoft.azure.storage.StorageException; +import com.microsoft.azure.storage.blob.CloudBlobContainer; +import com.microsoft.azure.storage.blob.CloudBlobDirectory; + +import org.apache.jackrabbit.oak.segment.SegmentId; +import org.apache.jackrabbit.oak.segment.SegmentNotFoundException; +import org.apache.jackrabbit.oak.segment.file.FileStore; +import org.apache.jackrabbit.oak.segment.file.FileStoreBuilder; +import org.apache.jackrabbit.oak.segment.file.InvalidFileStoreVersionException; +import org.apache.jackrabbit.oak.segment.spi.RepositoryNotReachableException; +import org.apache.jackrabbit.oak.segment.spi.monitor.FileStoreMonitor; +import org.apache.jackrabbit.oak.segment.spi.monitor.IOMonitor; +import org.apache.jackrabbit.oak.segment.spi.monitor.RemoteStoreMonitor; +import org.apache.jackrabbit.oak.segment.spi.persistence.Buffer; +import org.apache.jackrabbit.oak.segment.spi.persistence.SegmentArchiveManager; +import org.apache.jackrabbit.oak.segment.spi.persistence.SegmentArchiveReader; +import org.apache.jackrabbit.oak.segment.spi.persistence.SegmentArchiveWriter; +import org.junit.Before; +import org.junit.ClassRule; +import org.junit.Test; + +import java.io.File; +import java.io.IOException; +import java.net.URISyntaxException; +import java.security.InvalidKeyException; + +public class AzureReadSegmentTest { + + @ClassRule + public static AzuriteDockerRule azurite = new AzuriteDockerRule(); + + private CloudBlobContainer container; + + @Before + public void setup() throws StorageException, InvalidKeyException, URISyntaxException { + container = azurite.getContainer("oak-test"); + } + + @Test(expected = SegmentNotFoundException.class) + public void testReadNonExistentSegmentRepositoryReachable() throws URISyntaxException, IOException, InvalidFileStoreVersionException, StorageException { + AzurePersistence p = new AzurePersistence(container.getDirectoryReference("oak")); + FileStore fs = FileStoreBuilder.fileStoreBuilder(new File("target")).withCustomPersistence(p).build(); + SegmentId id = new SegmentId(fs, 0, 0); + + try { + fs.readSegment(id); + } finally { + fs.close(); + } + } + + @Test(expected = RepositoryNotReachableException.class) + public void testReadExistentSegmentRepositoryNotReachable() throws URISyntaxException, IOException, InvalidFileStoreVersionException, StorageException { + AzurePersistence p = new ReadFailingAzurePersistence(container.getDirectoryReference("oak")); + FileStore fs = FileStoreBuilder.fileStoreBuilder(new File("target")).withCustomPersistence(p).build(); + + SegmentId id = new SegmentId(fs, 0, 0); + byte[] buffer = new byte[2]; + + try { + fs.writeSegment(id, buffer, 0, 2); + fs.readSegment(id); + } finally { + fs.close(); + } + } + + static class ReadFailingAzurePersistence extends AzurePersistence { + public ReadFailingAzurePersistence(CloudBlobDirectory segmentStoreDirectory) { + super(segmentStoreDirectory); + } + + @Override + public SegmentArchiveManager createArchiveManager(boolean mmap, boolean offHeapAccess, IOMonitor ioMonitor, + FileStoreMonitor fileStoreMonitor, RemoteStoreMonitor remoteStoreMonitor) { + return new AzureArchiveManager(segmentstoreDirectory, ioMonitor, fileStoreMonitor) { + @Override + public SegmentArchiveReader open(String archiveName) throws IOException { + CloudBlobDirectory archiveDirectory = getDirectory(archiveName); + return new AzureSegmentArchiveReader(archiveDirectory, ioMonitor) { + @Override + public Buffer readSegment(long msb, long lsb) throws IOException { + throw new RepositoryNotReachableException( + new RuntimeException("Cannot access Azure storage")); + } + }; + } + + @Override + public SegmentArchiveWriter create(String archiveName) throws IOException { + CloudBlobDirectory archiveDirectory = getDirectory(archiveName); + return new AzureSegmentArchiveWriter(archiveDirectory, ioMonitor, fileStoreMonitor) { + @Override + public Buffer readSegment(long msb, long lsb) throws IOException { + throw new RepositoryNotReachableException( + new RuntimeException("Cannot access Azure storage")); } + }; + } + }; + } + } +} diff --git oak-segment-tar/pom.xml oak-segment-tar/pom.xml index a77ecb8a6b..93f0d73563 100644 --- oak-segment-tar/pom.xml +++ oak-segment-tar/pom.xml @@ -45,6 +45,7 @@ + org.apache.jackrabbit.oak.segment.spi, org.apache.jackrabbit.oak.segment.spi.monitor, org.apache.jackrabbit.oak.segment.spi.persistence diff --git oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/file/FileStore.java oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/file/FileStore.java index 0a513db60e..a24f691d73 100644 --- oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/file/FileStore.java +++ oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/file/FileStore.java @@ -52,6 +52,7 @@ import org.apache.jackrabbit.oak.segment.file.tar.GCGeneration; import org.apache.jackrabbit.oak.segment.file.tar.TarFiles; import org.apache.jackrabbit.oak.segment.spi.persistence.RepositoryLock; import org.apache.jackrabbit.oak.segment.spi.persistence.SegmentNodeStorePersistence; +import org.apache.jackrabbit.oak.segment.spi.RepositoryNotReachableException; import org.apache.jackrabbit.oak.segment.spi.persistence.Buffer; import org.apache.jackrabbit.oak.spi.state.NodeBuilder; import org.apache.jackrabbit.oak.stats.CounterStats; @@ -502,6 +503,12 @@ public class FileStore extends AbstractFileStore { try (ShutDownCloser ignored = shutDown.keepAlive()) { return segmentCache.getSegment(id, () -> readSegmentUncached(tarFiles, id)); } catch (ExecutionException | UncheckedExecutionException e) { + if (e.getCause() instanceof RepositoryNotReachableException) { + RepositoryNotReachableException re = (RepositoryNotReachableException) e.getCause(); + log.warn("Unable to access repository", re); + throw re; + } + SegmentNotFoundException snfe = asSegmentNotFoundException(e, id); snfeListener.notify(id, snfe); stats.notify(id, snfe); diff --git oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/spi/RepositoryNotReachableException.java oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/spi/RepositoryNotReachableException.java new file mode 100644 index 0000000000..e37679010d --- /dev/null +++ oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/spi/RepositoryNotReachableException.java @@ -0,0 +1,32 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.jackrabbit.oak.segment.spi; + +import org.apache.jackrabbit.oak.api.IllegalRepositoryStateException; + +/** + * This exception is thrown when the store cannot be accessed (e.g. remote segment store) + */ +public class RepositoryNotReachableException extends IllegalRepositoryStateException { + + public RepositoryNotReachableException(Throwable e) { + super(e); + } + +}