From 62ae677f748d90f8117bd063938a4b77f6ad0705 Mon Sep 17 00:00:00 2001 From: Keuin Date: Sat, 20 Jan 2024 00:58:31 +0800 Subject: [PATCH] feature: native copy-on-write support --- .../IncrementalBackupStorageManager.java | 20 +++++++-- .../ConfiguredIncrementalBackupMethod.java | 3 +- .../kbackupfabric/util/cow/FileCopier.java | 9 ++++ .../kbackupfabric/util/cow/FileCowCopier.java | 41 +++++++++++++++++++ .../util/cow/FileEagerCopier.java | 17 ++++++++ 5 files changed, 86 insertions(+), 4 deletions(-) create mode 100644 src/main/java/com/keuin/kbackupfabric/util/cow/FileCopier.java create mode 100644 src/main/java/com/keuin/kbackupfabric/util/cow/FileCowCopier.java create mode 100644 src/main/java/com/keuin/kbackupfabric/util/cow/FileEagerCopier.java diff --git a/src/main/java/com/keuin/kbackupfabric/backup/incremental/manager/IncrementalBackupStorageManager.java b/src/main/java/com/keuin/kbackupfabric/backup/incremental/manager/IncrementalBackupStorageManager.java index 7870620..a680db7 100644 --- a/src/main/java/com/keuin/kbackupfabric/backup/incremental/manager/IncrementalBackupStorageManager.java +++ b/src/main/java/com/keuin/kbackupfabric/backup/incremental/manager/IncrementalBackupStorageManager.java @@ -6,13 +6,15 @@ import com.keuin.kbackupfabric.backup.incremental.identifier.ObjectIdentifier; import com.keuin.kbackupfabric.util.FilesystemUtil; import com.keuin.kbackupfabric.util.PrintUtil; +import com.keuin.kbackupfabric.util.cow.FileCopier; +import com.keuin.kbackupfabric.util.cow.FileCowCopier; +import com.keuin.kbackupfabric.util.cow.FileEagerCopier; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; -import java.nio.file.Files; import java.nio.file.Path; import java.util.*; import java.util.logging.Logger; @@ -27,9 +29,21 @@ public class IncrementalBackupStorageManager { private final Logger logger = Logger.getLogger(IncrementalBackupStorageManager.class.getName()); private final Path backupStorageBase; private final Logger LOGGER = Logger.getLogger(IncrementalBackupStorageManager.class.getName()); + private FileCopier copier = null; public IncrementalBackupStorageManager(Path backupStorageBase) { this.backupStorageBase = backupStorageBase; + try { + this.copier = FileCowCopier.getInstance(); + } catch (Exception | UnsatisfiedLinkError ex) { + PrintUtil.error("Failed to initialize kbackup-cow: " + ex + ex.getMessage()); + this.copier = new FileEagerCopier(); + } + if (this.copier.isCow()) { + PrintUtil.info("Copy-on-write is enabled"); + } else { + PrintUtil.info("Copy-on-write is disabled"); + } } /** @@ -85,7 +99,7 @@ IncCopyResult addObjectCollection(ObjectCollection2 collection, File collectionB if (!contains(entry.getValue())) { // element does not exist. copy. logger.fine("Copy new file `" + copySourceFile.getName() + "`."); - Files.copy(copySourceFile.toPath(), copyDestination.toPath()); + copier.copy(copyDestination.getAbsolutePath(), copySourceFile.getAbsolutePath()); copyCount = copyCount.addWith(new IncCopyResult(1, 1, fileBytes, fileBytes)); } else { // element exists (file reused). Just update the stat info @@ -198,7 +212,7 @@ public int restoreObjectCollection(ObjectCollection2 collection, File collection } } - Files.copy(copySource.toPath(), copyTarget.toPath()); + copier.copy(copyTarget.getAbsolutePath(), copySource.getAbsolutePath()); ++copyCount; } diff --git a/src/main/java/com/keuin/kbackupfabric/operation/backup/method/ConfiguredIncrementalBackupMethod.java b/src/main/java/com/keuin/kbackupfabric/operation/backup/method/ConfiguredIncrementalBackupMethod.java index cfc9b81..3e85f13 100644 --- a/src/main/java/com/keuin/kbackupfabric/operation/backup/method/ConfiguredIncrementalBackupMethod.java +++ b/src/main/java/com/keuin/kbackupfabric/operation/backup/method/ConfiguredIncrementalBackupMethod.java @@ -46,7 +46,7 @@ public ConfiguredIncrementalBackupMethod(String backupIndexFileName, String leve @Override public IncrementalBackupFeedback backup() { final int hashFactoryThreads = ThreadingUtil.getRecommendedThreadCount(); // how many threads do we use to generate the hash tree - LOGGER.info("Threads: " + hashFactoryThreads); + PrintUtil.info("Threads: " + hashFactoryThreads); // needed in abort progress File levelPathFile = new File(levelPath); @@ -99,6 +99,7 @@ public IncrementalBackupFeedback backup() { PrintUtil.info("Incremental backup finished."); feedback = new IncrementalBackupFeedback(true, copyResult); } catch (IOException e) { + PrintUtil.error("Incremental backup failed: " + e + e.getMessage()); feedback = new IncrementalBackupFeedback(e); } diff --git a/src/main/java/com/keuin/kbackupfabric/util/cow/FileCopier.java b/src/main/java/com/keuin/kbackupfabric/util/cow/FileCopier.java new file mode 100644 index 0000000..eb21af7 --- /dev/null +++ b/src/main/java/com/keuin/kbackupfabric/util/cow/FileCopier.java @@ -0,0 +1,9 @@ +package com.keuin.kbackupfabric.util.cow; + +import java.io.IOException; + +public interface FileCopier { + void copy(String dst, String src) throws IOException; + + boolean isCow(); +} diff --git a/src/main/java/com/keuin/kbackupfabric/util/cow/FileCowCopier.java b/src/main/java/com/keuin/kbackupfabric/util/cow/FileCowCopier.java new file mode 100644 index 0000000..e52a893 --- /dev/null +++ b/src/main/java/com/keuin/kbackupfabric/util/cow/FileCowCopier.java @@ -0,0 +1,41 @@ +package com.keuin.kbackupfabric.util.cow; + +import com.keuin.kbackupfabric.util.PrintUtil; + +import java.io.IOException; +import java.util.concurrent.atomic.AtomicBoolean; + +public final class FileCowCopier { + private static final AtomicBoolean initialized = new AtomicBoolean(false); + + static { + try { + System.loadLibrary("kbackup_cow"); + } catch (SecurityException | UnsatisfiedLinkError ignored) { + } + } + + public static native void init(); + + public static native void copy(String dst, String src) throws IOException; + + public static native String getVersion(); + + public static FileCopier getInstance() { + if (initialized.compareAndSet(false, true)) { + FileCowCopier.init(); + PrintUtil.info("kbackup-cow version: " + FileCowCopier.getVersion()); + } + return new FileCopier() { + @Override + public void copy(String dst, String src) throws IOException { + FileCowCopier.copy(dst, src); + } + + @Override + public boolean isCow() { + return true; + } + }; + } +} diff --git a/src/main/java/com/keuin/kbackupfabric/util/cow/FileEagerCopier.java b/src/main/java/com/keuin/kbackupfabric/util/cow/FileEagerCopier.java new file mode 100644 index 0000000..0f12374 --- /dev/null +++ b/src/main/java/com/keuin/kbackupfabric/util/cow/FileEagerCopier.java @@ -0,0 +1,17 @@ +package com.keuin.kbackupfabric.util.cow; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Paths; + +public class FileEagerCopier implements FileCopier { + @Override + public void copy(String dst, String src) throws IOException { + Files.copy(Paths.get(src), Paths.get(dst)); + } + + @Override + public boolean isCow() { + return false; + } +}