diff --git a/oak-segment-azure/src/main/java/org/apache/jackrabbit/oak/segment/azure/AzureUtilities.java b/oak-segment-azure/src/main/java/org/apache/jackrabbit/oak/segment/azure/AzureUtilities.java index 716d588598..fe087f8d0c 100644 --- a/oak-segment-azure/src/main/java/org/apache/jackrabbit/oak/segment/azure/AzureUtilities.java +++ b/oak-segment-azure/src/main/java/org/apache/jackrabbit/oak/segment/azure/AzureUtilities.java @@ -36,9 +36,9 @@ 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 com.microsoft.azure.storage.blob.ListBlobItem; import org.apache.jackrabbit.oak.commons.Buffer; +import org.apache.jackrabbit.oak.segment.spi.FileNotFoundInArchiveException; import org.apache.jackrabbit.oak.segment.spi.RepositoryNotReachableException; import org.jetbrains.annotations.NotNull; import org.slf4j.Logger; @@ -76,10 +76,15 @@ public final class AzureUtilities { } public static void readBufferFully(CloudBlob blob, Buffer buffer) throws IOException { + try { blob.download(new ByteBufferOutputStream(buffer)); buffer.flip(); } catch (StorageException e) { + if (e.getHttpStatusCode() == 404) { + log.error("Blob not found in the remote repo: {}", blob.getName()); + throw new FileNotFoundInArchiveException("Blob not found in the remote repo: "+blob.getName(), e); + } throw new RepositoryNotReachableException(e); } } diff --git a/oak-segment-azure/src/test/java/org/apache/jackrabbit/oak/segment/azure/AzureArchiveManagerTest.java b/oak-segment-azure/src/test/java/org/apache/jackrabbit/oak/segment/azure/AzureArchiveManagerTest.java index 2e4f610ccd..1139053c37 100644 --- a/oak-segment-azure/src/test/java/org/apache/jackrabbit/oak/segment/azure/AzureArchiveManagerTest.java +++ b/oak-segment-azure/src/test/java/org/apache/jackrabbit/oak/segment/azure/AzureArchiveManagerTest.java @@ -21,15 +21,21 @@ import com.microsoft.azure.storage.blob.CloudBlob; import com.microsoft.azure.storage.blob.CloudBlobContainer; import com.microsoft.azure.storage.blob.ListBlobItem; import org.apache.jackrabbit.oak.api.CommitFailedException; +import org.apache.jackrabbit.oak.commons.Buffer; +import org.apache.jackrabbit.oak.segment.SegmentId; import org.apache.jackrabbit.oak.segment.SegmentNodeStore; import org.apache.jackrabbit.oak.segment.SegmentNodeStoreBuilders; +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.FileNotFoundInArchiveException; +import org.apache.jackrabbit.oak.segment.spi.RepositoryNotReachableException; import org.apache.jackrabbit.oak.segment.spi.monitor.FileStoreMonitorAdapter; import org.apache.jackrabbit.oak.segment.spi.monitor.IOMonitorAdapter; import org.apache.jackrabbit.oak.segment.spi.monitor.RemoteStoreMonitorAdapter; 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.apache.jackrabbit.oak.spi.commit.CommitInfo; import org.apache.jackrabbit.oak.spi.commit.EmptyHook; @@ -51,8 +57,10 @@ import java.util.UUID; import static com.google.common.collect.Lists.newArrayList; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; public class AzureArchiveManagerTest { @@ -257,4 +265,60 @@ public class AzureArchiveManagerTest { writer.flush(); Assert.assertTrue(manager.exists("data00000a.tar")); } + + @Test(expected = FileNotFoundInArchiveException.class) + public void testSegmentDeletedAfterCreatingReader() throws IOException, URISyntaxException, StorageException, InvalidFileStoreVersionException { + + AzurePersistence azurePersistence = new AzurePersistence(container.getDirectoryReference("oak")); + + SegmentArchiveManager manager = azurePersistence.createArchiveManager(false, false, new IOMonitorAdapter(), new FileStoreMonitorAdapter(), new RemoteStoreMonitorAdapter()); + SegmentArchiveWriter writer = manager.create("data00000a.tar"); + + Assert.assertFalse(manager.exists("data00000a.tar")); + UUID u = UUID.randomUUID(); + writer.writeSegment(u.getMostSignificantBits(), u.getLeastSignificantBits(), new byte[10], 0, 10, 0, 0, false); + writer.flush(); + writer.close(); + + SegmentArchiveReader reader = manager.open("data00000a.tar"); + Buffer segment = reader.readSegment(u.getMostSignificantBits(), u.getLeastSignificantBits()); + assertNotNull(segment); + + ListBlobItem segment0000 = container.listBlobs("oak/data00000a.tar/0000.").iterator().next(); + ((CloudBlob) segment0000).delete(); + + try { + // FileNotFoundInArchiveException should be thrown here + reader.readSegment(u.getMostSignificantBits(), u.getLeastSignificantBits()); + fail(); + } catch (RepositoryNotReachableException e) { + fail(); + } + } + + @Test(expected = SegmentNotFoundException.class) + public void testMissngSegmentDetectedInFileStore() throws IOException, StorageException, URISyntaxException, InvalidFileStoreVersionException { + + AzurePersistence azurePersistence = new AzurePersistence(container.getDirectoryReference("oak")); + FileStore fileStore = FileStoreBuilder.fileStoreBuilder(new File("target")).withCustomPersistence(azurePersistence).build(); + + SegmentArchiveManager manager = azurePersistence.createArchiveManager(false, false, new IOMonitorAdapter(), new FileStoreMonitorAdapter(), new RemoteStoreMonitorAdapter()); + SegmentArchiveWriter writer = manager.create("data00000a.tar"); + + //Assert.assertFalse(manager.exists("data00000a.tar")); + UUID u = UUID.randomUUID(); + writer.writeSegment(u.getMostSignificantBits(), u.getLeastSignificantBits(), new byte[10], 0, 10, 0, 0, false); + writer.flush(); + writer.close(); + + SegmentArchiveReader reader = manager.open("data00000a.tar"); + Buffer segment = reader.readSegment(u.getMostSignificantBits(), u.getLeastSignificantBits()); + assertNotNull(segment); + + ListBlobItem segment0000 = container.listBlobs("oak/data00000a.tar/0000.").iterator().next(); + ((CloudBlob) segment0000).delete(); + + // SegmentNotFoundException should be thrown here + fileStore.readSegment(new SegmentId(fileStore, u.getMostSignificantBits(), u.getLeastSignificantBits())); + } } diff --git a/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/SegmentNotFoundException.java b/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/SegmentNotFoundException.java index b001c615a7..6912103e4b 100644 --- a/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/SegmentNotFoundException.java +++ b/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/SegmentNotFoundException.java @@ -20,11 +20,12 @@ package org.apache.jackrabbit.oak.segment; import org.apache.jackrabbit.oak.api.IllegalRepositoryStateException; +import org.apache.jackrabbit.oak.segment.spi.FileNotFoundInArchiveException; /** * This exception is thrown when there the segment does not exist in the store */ -public class SegmentNotFoundException extends IllegalRepositoryStateException { +public class SegmentNotFoundException extends FileNotFoundInArchiveException { private final String segmentId; diff --git a/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/spi/FileNotFoundInArchiveException.java b/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/spi/FileNotFoundInArchiveException.java new file mode 100644 index 0000000000..63acd289fa --- /dev/null +++ b/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/spi/FileNotFoundInArchiveException.java @@ -0,0 +1,36 @@ +/* + * 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 requested file is not found in the archive (e.g. in the remote segment store). + * + */ +public class FileNotFoundInArchiveException extends IllegalRepositoryStateException { + + public FileNotFoundInArchiveException(String message, Throwable e) { + super(message, e); + } + + public FileNotFoundInArchiveException(String message) { + super(message); + } +} diff --git a/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/spi/package-info.java b/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/spi/package-info.java new file mode 100644 index 0000000000..3f53c4a222 --- /dev/null +++ b/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/spi/package-info.java @@ -0,0 +1,22 @@ +/* + * 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. + */ +@Internal(since = "1.0.0") +@Version("1.0.0") +package org.apache.jackrabbit.oak.segment.spi; + +import org.apache.jackrabbit.oak.commons.annotations.Internal; +import org.osgi.annotation.versioning.Version; \ No newline at end of file