diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/filename/FileTreeUtils.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/filename/FileTreeUtils.java index 7d1dd7d7f05d3..e6406cbb560d4 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/filename/FileTreeUtils.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/filename/FileTreeUtils.java @@ -19,6 +19,7 @@ import java.io.File; import java.io.IOException; +import java.nio.file.NoSuchFileException; import java.util.HashSet; import java.util.Map; import java.util.Set; @@ -63,10 +64,15 @@ public static void createCacheStorages(NodeFileTree ft, IgniteLogger log) throws public static void removeTmpSnapshotFiles(SnapshotFileTree sft, boolean err, IgniteLogger log) { NodeFileTree tmpFt = sft.tempFileTree(); - removeTmpDir(tmpFt.root(), err, log); + U.delete(tmpFt.nodeStorage()); - for (File tmpDrStorage : tmpFt.extraStorages().values()) - removeTmpDir(tmpDrStorage.getParentFile(), err, log); + for (File tmpDrStorage : tmpFt.extraStorages().values()) { + U.delete(tmpDrStorage); + + deleteSnapshotTempRoot(tmpDrStorage.getParentFile(), err, log); + } + + deleteSnapshotTempRoot(tmpFt.root(), err, log); } /** @@ -104,20 +110,20 @@ public static Set nodeStorages(DataStorageConfiguration dsCfg) throws Ig } /** - * @param dir Directory to remove - * @param err If {@code true} then operation ends with error. + * @param root Snapshot temporary root. + * @param err {@code True} if snapshot processing finished with an error. * @param log Logger. */ - private static void removeTmpDir(File dir, boolean err, IgniteLogger log) { - U.delete(dir); - - // Delete snapshot directory if no other files exists. + private static void deleteSnapshotTempRoot(File root, boolean err, IgniteLogger log) { try { - if (U.fileCount(dir.toPath()) == 0 || err) - U.delete(dir.toPath()); + if (err || U.fileCount(root.toPath()) == 0) + U.delete(root.toPath()); + } + catch (NoSuchFileException ignored) { + // Snapshot temporary root has already been removed. } catch (IOException e) { - log.error("Snapshot directory doesn't exist [snpName=" + dir.getName() + ", dir=" + dir.getParentFile() + ']'); + log.error("Failed to clean up snapshot temporary root [dir=" + root + ']', e); } } diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/snapshot/IgniteSnapshotManagerSelfTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/snapshot/IgniteSnapshotManagerSelfTest.java index f3edd6df687da..265942fd7fc48 100644 --- a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/snapshot/IgniteSnapshotManagerSelfTest.java +++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/snapshot/IgniteSnapshotManagerSelfTest.java @@ -59,6 +59,8 @@ import org.apache.ignite.internal.processors.cache.persistence.file.FilePageStoreManager; import org.apache.ignite.internal.processors.cache.persistence.file.FileVersionCheckingFactory; import org.apache.ignite.internal.processors.cache.persistence.file.RandomAccessFileIOFactory; +import org.apache.ignite.internal.processors.cache.persistence.filename.FileTreeUtils; +import org.apache.ignite.internal.processors.cache.persistence.filename.NodeFileTree; import org.apache.ignite.internal.processors.cache.persistence.filename.SnapshotFileTree; import org.apache.ignite.internal.processors.cache.persistence.partstate.GroupPartitionId; import org.apache.ignite.internal.util.GridCloseableIteratorAdapter; @@ -647,6 +649,70 @@ public void testFullSnapshotCreationLog() throws Exception { assertFalse(noMatchParams.check()); } + /** + * Tests that snapshot temporary root is not removed on successful cleanup if it still contains files after + * node-specific storage has been removed. + */ + @Test + public void testSnapshotTmpRootIsNotRemovedIfNotEmptyOnSuccess() throws Exception { + IgniteEx ignite = startGridWithCache(dfltCacheCfg, CACHE_KEYS_RANGE); + + SnapshotFileTree sft = snapshotFileTree(ignite, SNAPSHOT_NAME); + + NodeFileTree tmpFt = sft.tempFileTree(); + + File root = tmpFt.root(); + File nodeStorage = tmpFt.nodeStorage(); + + assertTrue(nodeStorage.mkdirs()); + + File extraFile = new File(root, "unexpected-tmp-file"); + + assertTrue(extraFile.createNewFile()); + + try { + FileTreeUtils.removeTmpSnapshotFiles(sft, false, log); + + assertFalse("Node-specific temporary storage must be removed: " + nodeStorage, nodeStorage.exists()); + + assertTrue("Snapshot temporary root must not be removed if it is not empty: " + root, root.exists()); + + assertTrue("Unexpected temporary file must not be removed on successful cleanup: " + extraFile, + extraFile.exists()); + } + finally { + U.delete(root); + } + } + + /** + * Tests that snapshot temporary root is removed on cleanup after an error even if it still contains files after + * node-specific storage has been removed. + */ + @Test + public void testSnapshotTmpRootIsRemovedIfNotEmptyOnError() throws Exception { + IgniteEx ignite = startGridWithCache(dfltCacheCfg, CACHE_KEYS_RANGE); + + SnapshotFileTree sft = snapshotFileTree(ignite, SNAPSHOT_NAME); + + NodeFileTree tmpFt = sft.tempFileTree(); + + File root = tmpFt.root(); + File nodeStorage = tmpFt.nodeStorage(); + + assertTrue(nodeStorage.mkdirs()); + + File extraFile = new File(root, "unexpected-tmp-file"); + + assertTrue(extraFile.createNewFile()); + + FileTreeUtils.removeTmpSnapshotFiles(sft, true, log); + + assertFalse("Node-specific temporary storage must be removed: " + nodeStorage, nodeStorage.exists()); + + assertFalse("Snapshot temporary root must be removed on error: " + root, root.exists()); + } + /** * @param ignite Ignite instance to set factory. * @param factory New factory to use.