diff --git a/java/Makefile b/java/Makefile index e8dc5cb471e087616fd4dad5c2d6395f02051880..0a4e2ba1607fb8e62c2a4f6817c34d57163e25ff 100644 --- a/java/Makefile +++ b/java/Makefile @@ -47,7 +47,8 @@ ifeq ($(PLATFORM), OS_MACOSX) ROCKSDB_JAR = rocksdbjni-$(ROCKSDB_MAJOR).$(ROCKSDB_MINOR).$(ROCKSDB_PATCH)-osx.jar endif -JAVA_TESTS = org.rocksdb.test.BackupableDBTest\ +JAVA_TESTS = org.rocksdb.test.BackupableDBOptionsTest\ + org.rocksdb.test.BackupableDBTest\ org.rocksdb.test.BlockBasedTableConfigTest\ org.rocksdb.test.ColumnFamilyOptionsTest\ org.rocksdb.test.ColumnFamilyTest\ diff --git a/java/org/rocksdb/BackupableDB.java b/java/org/rocksdb/BackupableDB.java index 2644fec8fdefaa7271d23e5f2415a770a7d993f8..a743d861e4e6bb75ad69cb859ab33ce0dabb7b7b 100644 --- a/java/org/rocksdb/BackupableDB.java +++ b/java/org/rocksdb/BackupableDB.java @@ -8,21 +8,23 @@ package org.rocksdb; import java.util.List; /** - * A subclass of RocksDB which supports backup-related operations. + *

A subclass of RocksDB which supports + * backup-related operations.

* * @see org.rocksdb.BackupableDBOptions */ public class BackupableDB extends RocksDB { /** - * Open a {@code BackupableDB} under the specified path. + *

Open a {@code BackupableDB} under the specified path. * Note that the backup path should be set properly in the - * input BackupableDBOptions. + * input BackupableDBOptions.

* * @param opt {@link org.rocksdb.Options} to set for the database. * @param bopt {@link org.rocksdb.BackupableDBOptions} to use. * @param db_path Path to store data to. The path for storing the backup should be * specified in the {@link org.rocksdb.BackupableDBOptions}. - * @return BackupableDB reference to the opened database. + * + * @return {@link BackupableDB} reference to the opened database. * * @throws RocksDBException thrown if error happens in underlying * native library. @@ -43,8 +45,8 @@ public class BackupableDB extends RocksDB { } /** - * Captures the state of the database in the latest backup. - * Note that this function is not thread-safe. + *

Captures the state of the database in the latest backup. + * Note that this function is not thread-safe.

* * @param flushBeforeBackup if true, then all data will be flushed * before creating backup. @@ -54,11 +56,12 @@ public class BackupableDB extends RocksDB { */ public void createNewBackup(boolean flushBeforeBackup) throws RocksDBException { + assert(isInitialized()); createNewBackup(nativeHandle_, flushBeforeBackup); } /** - * Deletes old backups, keeping latest numBackupsToKeep alive. + *

Deletes old backups, keeping latest numBackupsToKeep alive.

* * @param numBackupsToKeep Number of latest backups to keep. * @@ -67,11 +70,12 @@ public class BackupableDB extends RocksDB { */ public void purgeOldBackups(int numBackupsToKeep) throws RocksDBException { + assert(isInitialized()); purgeOldBackups(nativeHandle_, numBackupsToKeep); } /** - * Deletes a specific backup. + *

Deletes a specific backup.

* * @param backupId of backup to delete. * @@ -79,25 +83,54 @@ public class BackupableDB extends RocksDB { * native library. */ public void deleteBackup(int backupId) throws RocksDBException { + assert(isInitialized()); deleteBackup0(nativeHandle_, backupId); } /** - * Returns a list of {@link BackupInfo} instances, which describe - * already made backups. + *

Returns a list of {@link BackupInfo} instances, which describe + * already made backups.

* * @return List of {@link BackupInfo} instances. */ public List getBackupInfos() { + assert(isInitialized()); return getBackupInfo(nativeHandle_); } /** - * Close the BackupableDB instance and release resource. + *

Returns a list of corrupted backup ids. If there + * is no corrupted backup the method will return an + * empty list.

+ * + * @return array of backup ids as int ids. + */ + public int[] getCorruptedBackups() { + assert(isInitialized()); + return getCorruptedBackups(nativeHandle_); + } + + /** + *

Will delete all the files we don't need anymore. It will + * do the full scan of the files/ directory and delete all the + * files that are not referenced.

+ * + * @throws RocksDBException thrown if error happens in underlying + * native library. + */ + public void garbageCollect() throws RocksDBException { + assert(isInitialized()); + garbageCollect(nativeHandle_); + } + + /** + *

Close the BackupableDB instance and release resource.

* - * Internally, BackupableDB owns the {@code rocksdb::DB} pointer to its associated - * {@link org.rocksdb.RocksDB}. The release of that RocksDB pointer is handled in the destructor - * of the c++ {@code rocksdb::BackupableDB} and should be transparent to Java developers. + *

Internally, {@link BackupableDB} owns the {@code rocksdb::DB} + * pointer to its associated {@link org.rocksdb.RocksDB}. + * The release of that RocksDB pointer is handled in the destructor + * of the c++ {@code rocksdb::BackupableDB} and should be transparent + * to Java developers.

*/ @Override public synchronized void close() { if (isInitialized()) { @@ -106,8 +139,9 @@ public class BackupableDB extends RocksDB { } /** - * A protected construction that will be used in the static factory - * method {@link #open(Options, BackupableDBOptions, String)}. + *

A protected construction that will be used in the static + * factory method {@link #open(Options, BackupableDBOptions, String)}. + *

*/ protected BackupableDB() { super(); @@ -126,4 +160,7 @@ public class BackupableDB extends RocksDB { private native void deleteBackup0(long nativeHandle, int backupId) throws RocksDBException; protected native List getBackupInfo(long handle); + private native int[] getCorruptedBackups(long handle); + private native void garbageCollect(long handle) + throws RocksDBException; } diff --git a/java/org/rocksdb/BackupableDBOptions.java b/java/org/rocksdb/BackupableDBOptions.java index 07751a64dbfa23880a4807b98bbac981b34aeb68..ab532f282ed5e62518fe20528c98919e7ec2f040 100644 --- a/java/org/rocksdb/BackupableDBOptions.java +++ b/java/org/rocksdb/BackupableDBOptions.java @@ -5,63 +5,242 @@ package org.rocksdb; +import java.io.File; +import java.nio.file.Path; + /** - * BackupableDBOptions to control the behavior of a backupable database. + *

BackupableDBOptions to control the behavior of a backupable database. * It will be used during the creation of a {@link org.rocksdb.BackupableDB}. - * - * Note that dispose() must be called before an Options instance - * become out-of-scope to release the allocated memory in c++. + *

+ *

Note that dispose() must be called before an Options instance + * become out-of-scope to release the allocated memory in c++.

* * @see org.rocksdb.BackupableDB */ public class BackupableDBOptions extends RocksObject { /** - * BackupableDBOptions constructor + *

BackupableDBOptions constructor.

* * @param path Where to keep the backup files. Has to be different than db name. - * Best to set this to {@code db name_ + "/backups"} + * Best to set this to {@code db name_ + "/backups"} + * @throws java.lang.IllegalArgumentException if illegal path is used. + */ + public BackupableDBOptions(String path) { + super(); + File backupPath = path == null ? null : new File(path); + if (backupPath == null || !backupPath.isDirectory() || !backupPath.canWrite()) { + throw new IllegalArgumentException("Illegal path provided."); + } + newBackupableDBOptions(path); + } + + /** + *

Returns the path to the BackupableDB directory.

+ * + * @return the path to the BackupableDB directory. + */ + public String backupDir() { + assert(isInitialized()); + return backupDir(nativeHandle_); + } + + /** + *

Share table files between backups.

+ * * @param shareTableFiles If {@code share_table_files == true}, backup will assume * that table files with same name have the same contents. This enables incremental * backups and avoids unnecessary data copies. If {@code share_table_files == false}, * each backup will be on its own and will not share any data with other backups. - * Default: true + * + *

Default: true

+ * + * @return instance of current BackupableDBOptions. + */ + public BackupableDBOptions setShareTableFiles(boolean shareTableFiles) { + assert(isInitialized()); + setShareTableFiles(nativeHandle_, shareTableFiles); + return this; + } + + /** + *

Share table files between backups.

+ * + * @return boolean value indicating if SST files will be shared between + * backups. + */ + public boolean shareTableFiles() { + assert(isInitialized()); + return shareTableFiles(nativeHandle_); + } + + /** + *

Set synchronous backups.

+ * * @param sync If {@code sync == true}, we can guarantee you'll get consistent backup * even on a machine crash/reboot. Backup process is slower with sync enabled. * If {@code sync == false}, we don't guarantee anything on machine reboot. * However,chances are some of the backups are consistent. - * Default: true + * + *

Default: true

+ * + * @return instance of current BackupableDBOptions. + */ + public BackupableDBOptions setSync(boolean sync) { + assert(isInitialized()); + setSync(nativeHandle_, sync); + return this; + } + + /** + *

Are synchronous backups activated.

+ * + * @return boolean value if synchronous backups are configured. + */ + public boolean sync() { + assert(isInitialized()); + return sync(nativeHandle_); + } + + /** + *

Set if old data will be destroyed.

+ * * @param destroyOldData If true, it will delete whatever backups there are already. - * Default: false + * + *

Default: false

+ * + * @return instance of current BackupableDBOptions. + */ + public BackupableDBOptions setDestroyOldData(boolean destroyOldData) { + assert(isInitialized()); + setDestroyOldData(nativeHandle_, destroyOldData); + return this; + } + + /** + *

Returns if old data will be destroyed will performing new backups.

+ * + * @return boolean value indicating if old data will be destroyed. + */ + public boolean destroyOldData() { + assert(isInitialized()); + return destroyOldData(nativeHandle_); + } + + /** + *

Set if log files shall be persisted.

+ * * @param backupLogFiles If false, we won't backup log files. This option can be * useful for backing up in-memory databases where log file are persisted,but table * files are in memory. - * Default: true - * @param backupRateLimit Max bytes that can be transferred in a second during backup. - * If 0 or negative, then go as fast as you can. Default: 0 - * @param restoreRateLimit Max bytes that can be transferred in a second during restore. - * If 0 or negative, then go as fast as you can. Default: 0 + * + *

Default: true

+ * + * @return instance of current BackupableDBOptions. */ - public BackupableDBOptions(String path, boolean shareTableFiles, boolean sync, - boolean destroyOldData, boolean backupLogFiles, long backupRateLimit, - long restoreRateLimit) { - super(); + public BackupableDBOptions setBackupLogFiles(boolean backupLogFiles) { + assert(isInitialized()); + setBackupLogFiles(nativeHandle_, backupLogFiles); + return this; + } + + /** + *

Return information if log files shall be persisted.

+ * + * @return boolean value indicating if log files will be persisted. + */ + public boolean backupLogFiles() { + assert(isInitialized()); + return backupLogFiles(nativeHandle_); + } + /** + *

Set backup rate limit.

+ * + * @param backupRateLimit Max bytes that can be transferred in a second during backup. + * If 0 or negative, then go as fast as you can. + * + *

Default: 0

+ * + * @return instance of current BackupableDBOptions. + */ + public BackupableDBOptions setBackupRateLimit(long backupRateLimit) { + assert(isInitialized()); backupRateLimit = (backupRateLimit <= 0) ? 0 : backupRateLimit; + setBackupRateLimit(nativeHandle_, backupRateLimit); + return this; + } + + /** + *

Return backup rate limit which described the max bytes that can be transferred in a + * second during backup.

+ * + * @return numerical value describing the backup transfer limit in bytes per second. + */ + public long backupRateLimit() { + assert(isInitialized()); + return backupRateLimit(nativeHandle_); + } + + /** + *

Set restore rate limit.

+ * + * @param restoreRateLimit Max bytes that can be transferred in a second during restore. + * If 0 or negative, then go as fast as you can. + * + *

Default: 0

+ * + * @return instance of current BackupableDBOptions. + */ + public BackupableDBOptions setRestoreRateLimit(long restoreRateLimit) { + assert(isInitialized()); restoreRateLimit = (restoreRateLimit <= 0) ? 0 : restoreRateLimit; + setRestoreRateLimit(nativeHandle_, restoreRateLimit); + return this; + } + + /** + *

Return restore rate limit which described the max bytes that can be transferred in a + * second during restore.

+ * + * @return numerical value describing the restore transfer limit in bytes per second. + */ + public long restoreRateLimit() { + assert(isInitialized()); + return restoreRateLimit(nativeHandle_); + } - newBackupableDBOptions(path, shareTableFiles, sync, destroyOldData, - backupLogFiles, backupRateLimit, restoreRateLimit); + /** + *

Only used if share_table_files is set to true. If true, will consider that + * backups can come from different databases, hence a sst is not uniquely + * identified by its name, but by the triple (file name, crc32, file length)

+ * + * @param shareFilesWithChecksum boolean value indicating if SST files are stored + * using the triple (file name, crc32, file length) and not its name. + * + *

Note: this is an experimental option, and you'll need to set it manually + * turn it on only if you know what you're doing*

+ * + *

Default: false

+ * + * @return instance of current BackupableDBOptions. + */ + public BackupableDBOptions setShareFilesWithChecksum( + boolean shareFilesWithChecksum) { + assert(isInitialized()); + setShareFilesWithChecksum(nativeHandle_, shareFilesWithChecksum); + return this; } /** - * Returns the path to the BackupableDB directory. + *

Return of share files with checksum is active.

* - * @return the path to the BackupableDB directory. + * @return boolean value indicating if share files with checksum + * is active. */ - public String backupDir() { + public boolean shareFilesWithChecksum() { assert(isInitialized()); - return backupDir(nativeHandle_); + return shareFilesWithChecksum(nativeHandle_); } /** @@ -69,13 +248,24 @@ public class BackupableDBOptions extends RocksObject { * in the c++ side. */ @Override protected void disposeInternal() { - assert(isInitialized()); disposeInternal(nativeHandle_); } - private native void newBackupableDBOptions(String path, - boolean shareTableFiles, boolean sync, boolean destroyOldData, - boolean backupLogFiles, long backupRateLimit, long restoreRateLimit); + private native void newBackupableDBOptions(String path); private native String backupDir(long handle); + private native void setShareTableFiles(long handle, boolean flag); + private native boolean shareTableFiles(long handle); + private native void setSync(long handle, boolean flag); + private native boolean sync(long handle); + private native void setDestroyOldData(long handle, boolean flag); + private native boolean destroyOldData(long handle); + private native void setBackupLogFiles(long handle, boolean flag); + private native boolean backupLogFiles(long handle); + private native void setBackupRateLimit(long handle, long rateLimit); + private native long backupRateLimit(long handle); + private native void setRestoreRateLimit(long handle, long rateLimit); + private native long restoreRateLimit(long handle); + private native void setShareFilesWithChecksum(long handle, boolean flag); + private native boolean shareFilesWithChecksum(long handle); private native void disposeInternal(long handle); } diff --git a/java/org/rocksdb/RestoreBackupableDB.java b/java/org/rocksdb/RestoreBackupableDB.java index ffbc2e0112f24a676838b5dab9c6abeee81eebe0..e29628815891d654489885c3e4dab88069953b68 100644 --- a/java/org/rocksdb/RestoreBackupableDB.java +++ b/java/org/rocksdb/RestoreBackupableDB.java @@ -8,15 +8,17 @@ package org.rocksdb; import java.util.List; /** - * This class is used to access information about backups and restore from them. + *

This class is used to access information about backups and + * restore from them.

* - * Note that dispose() must be called before this instance become out-of-scope - * to release the allocated memory in c++. + *

Note: {@code dispose()} must be called before this instance + * become out-of-scope to release the allocated + * memory in c++.

* */ public class RestoreBackupableDB extends RocksObject { /** - * Constructor + *

Construct new estoreBackupableDB instance.

* * @param options {@link org.rocksdb.BackupableDBOptions} instance */ @@ -26,16 +28,18 @@ public class RestoreBackupableDB extends RocksObject { } /** - * Restore from backup with backup_id - * IMPORTANT -- if options_.share_table_files == true and you restore DB - * from some backup that is not the latest, and you start creating new - * backups from the new DB, they will probably fail. + *

Restore from backup with backup_id.

* - * Example: Let's say you have backups 1, 2, 3, 4, 5 and you restore 3. - * If you add new data to the DB and try creating a new backup now, the - * database will diverge from backups 4 and 5 and the new backup will fail. - * If you want to create new backup, you will first have to delete backups 4 - * and 5. + *

Important: If options_.share_table_files == true + * and you restore DB from some backup that is not the latest, and you + * start creating new backups from the new DB, they will probably + * fail.

+ * + *

Example: Let's say you have backups 1, 2, 3, 4, 5 + * and you restore 3. If you add new data to the DB and try creating a new + * backup now, the database will diverge from backups 4 and 5 and the new + * backup will fail. If you want to create new backup, you will first have + * to delete backups 4 and 5.

* * @param backupId id pointing to backup * @param dbDir database directory to restore to @@ -47,12 +51,13 @@ public class RestoreBackupableDB extends RocksObject { */ public void restoreDBFromBackup(long backupId, String dbDir, String walDir, RestoreOptions restoreOptions) throws RocksDBException { + assert(isInitialized()); restoreDBFromBackup0(nativeHandle_, backupId, dbDir, walDir, restoreOptions.nativeHandle_); } /** - * Restore from the latest backup. + *

Restore from the latest backup.

* * @param dbDir database directory to restore to * @param walDir directory where wal files are located @@ -63,12 +68,13 @@ public class RestoreBackupableDB extends RocksObject { */ public void restoreDBFromLatestBackup(String dbDir, String walDir, RestoreOptions restoreOptions) throws RocksDBException { + assert(isInitialized()); restoreDBFromLatestBackup0(nativeHandle_, dbDir, walDir, restoreOptions.nativeHandle_); } /** - * Deletes old backups, keeping latest numBackupsToKeep alive. + *

Deletes old backups, keeping latest numBackupsToKeep alive.

* * @param numBackupsToKeep of latest backups to keep * @@ -76,11 +82,12 @@ public class RestoreBackupableDB extends RocksObject { * native library. */ public void purgeOldBackups(int numBackupsToKeep) throws RocksDBException { + assert(isInitialized()); purgeOldBackups0(nativeHandle_, numBackupsToKeep); } /** - * Deletes a specific backup. + *

Deletes a specific backup.

* * @param backupId of backup to delete. * @@ -88,25 +95,51 @@ public class RestoreBackupableDB extends RocksObject { * native library. */ public void deleteBackup(int backupId) throws RocksDBException { + assert(isInitialized()); deleteBackup0(nativeHandle_, backupId); } /** - * Returns a list of {@link BackupInfo} instances, which describe - * already made backups. + *

Returns a list of {@link BackupInfo} instances, which describe + * already made backups.

* * @return List of {@link BackupInfo} instances. */ public List getBackupInfos() { + assert(isInitialized()); return getBackupInfo(nativeHandle_); } /** - * Release the memory allocated for the current instance - * in the c++ side. + *

Returns a list of corrupted backup ids. If there + * is no corrupted backup the method will return an + * empty list.

+ * + * @return array of backup ids as int ids. */ - @Override public synchronized void disposeInternal() { + public int[] getCorruptedBackups() { + assert(isInitialized()); + return getCorruptedBackups(nativeHandle_); + } + + /** + *

Will delete all the files we don't need anymore. It will + * do the full scan of the files/ directory and delete all the + * files that are not referenced.

+ * + * @throws RocksDBException thrown if error happens in underlying + * native library. + */ + public void garbageCollect() throws RocksDBException { assert(isInitialized()); + garbageCollect(nativeHandle_); + } + + /** + *

Release the memory allocated for the current instance + * in the c++ side.

+ */ + @Override public synchronized void disposeInternal() { dispose(nativeHandle_); } @@ -121,6 +154,9 @@ public class RestoreBackupableDB extends RocksObject { throws RocksDBException; private native void deleteBackup0(long nativeHandle, int backupId) throws RocksDBException; - protected native List getBackupInfo(long handle); + private native List getBackupInfo(long handle); + private native int[] getCorruptedBackups(long handle); + private native void garbageCollect(long handle) + throws RocksDBException; private native void dispose(long nativeHandle); } diff --git a/java/org/rocksdb/test/BackupableDBOptionsTest.java b/java/org/rocksdb/test/BackupableDBOptionsTest.java new file mode 100644 index 0000000000000000000000000000000000000000..b7bdc00115dcb8413314926adeb475a6da7c4a88 --- /dev/null +++ b/java/org/rocksdb/test/BackupableDBOptionsTest.java @@ -0,0 +1,284 @@ +// Copyright (c) 2014, Facebook, Inc. All rights reserved. +// This source code is licensed under the BSD-style license found in the +// LICENSE file in the root directory of this source tree. An additional grant +// of patent rights can be found in the PATENTS file in the same directory. + +package org.rocksdb.test; + +import org.junit.ClassRule; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.rocksdb.BackupableDBOptions; + +import java.util.Random; + +import static org.assertj.core.api.Assertions.assertThat; + +public class BackupableDBOptionsTest { + + private final static String ARBITRARY_PATH = "/tmp"; + + @ClassRule + public static final RocksMemoryResource rocksMemoryResource = + new RocksMemoryResource(); + + @Rule + public ExpectedException exception = ExpectedException.none(); + + public static final Random rand = PlatformRandomHelper. + getPlatformSpecificRandomFactory(); + + @Test + public void backupDir() { + BackupableDBOptions backupableDBOptions = null; + try { + backupableDBOptions = new BackupableDBOptions(ARBITRARY_PATH); + assertThat(backupableDBOptions.backupDir()). + isEqualTo(ARBITRARY_PATH); + } finally { + if (backupableDBOptions != null) { + backupableDBOptions.dispose(); + } + } + } + + @Test + public void shareTableFiles() { + BackupableDBOptions backupableDBOptions = null; + try { + backupableDBOptions = new BackupableDBOptions(ARBITRARY_PATH); + boolean value = rand.nextBoolean(); + backupableDBOptions.setShareTableFiles(value); + assertThat(backupableDBOptions.shareTableFiles()). + isEqualTo(value); + } finally { + if (backupableDBOptions != null) { + backupableDBOptions.dispose(); + } + } + } + + @Test + public void sync() { + BackupableDBOptions backupableDBOptions = null; + try { + backupableDBOptions = new BackupableDBOptions(ARBITRARY_PATH); + boolean value = rand.nextBoolean(); + backupableDBOptions.setSync(value); + assertThat(backupableDBOptions.sync()).isEqualTo(value); + } finally { + if (backupableDBOptions != null) { + backupableDBOptions.dispose(); + } + } + } + + @Test + public void destroyOldData() { + BackupableDBOptions backupableDBOptions = null; + try { + backupableDBOptions = new BackupableDBOptions(ARBITRARY_PATH); + boolean value = rand.nextBoolean(); + backupableDBOptions.setDestroyOldData(value); + assertThat(backupableDBOptions.destroyOldData()). + isEqualTo(value); + } finally { + if (backupableDBOptions != null) { + backupableDBOptions.dispose(); + } + } + } + + @Test + public void backupLogFiles() { + BackupableDBOptions backupableDBOptions = null; + try { + backupableDBOptions = new BackupableDBOptions(ARBITRARY_PATH); + boolean value = rand.nextBoolean(); + backupableDBOptions.setBackupLogFiles(value); + assertThat(backupableDBOptions.backupLogFiles()). + isEqualTo(value); + } finally { + if (backupableDBOptions != null) { + backupableDBOptions.dispose(); + } + } + } + + @Test + public void backupRateLimit() { + BackupableDBOptions backupableDBOptions = null; + try { + backupableDBOptions = new BackupableDBOptions(ARBITRARY_PATH); + long value = Math.abs(rand.nextLong()); + backupableDBOptions.setBackupRateLimit(value); + assertThat(backupableDBOptions.backupRateLimit()). + isEqualTo(value); + // negative will be mapped to 0 + backupableDBOptions.setBackupRateLimit(-1); + assertThat(backupableDBOptions.backupRateLimit()). + isEqualTo(0); + } finally { + if (backupableDBOptions != null) { + backupableDBOptions.dispose(); + } + } + } + + @Test + public void restoreRateLimit() { + BackupableDBOptions backupableDBOptions = null; + try { + backupableDBOptions = new BackupableDBOptions(ARBITRARY_PATH); + long value = Math.abs(rand.nextLong()); + backupableDBOptions.setRestoreRateLimit(value); + assertThat(backupableDBOptions.restoreRateLimit()). + isEqualTo(value); + // negative will be mapped to 0 + backupableDBOptions.setRestoreRateLimit(-1); + assertThat(backupableDBOptions.restoreRateLimit()). + isEqualTo(0); + } finally { + if (backupableDBOptions != null) { + backupableDBOptions.dispose(); + } + } + } + + @Test + public void shareFilesWithChecksum() { + BackupableDBOptions backupableDBOptions = null; + try { + backupableDBOptions = new BackupableDBOptions(ARBITRARY_PATH); + boolean value = rand.nextBoolean(); + backupableDBOptions.setShareFilesWithChecksum(value); + assertThat(backupableDBOptions.shareFilesWithChecksum()). + isEqualTo(value); + } finally { + if (backupableDBOptions != null) { + backupableDBOptions.dispose(); + } + } + } + + @Test + public void failBackupDirIsNull() { + exception.expect(IllegalArgumentException.class); + new BackupableDBOptions(null); + } + + @Test + public void failBackupDirIfDisposed(){ + BackupableDBOptions options = setupUninitializedBackupableDBOptions( + exception); + options.backupDir(); + } + + @Test + public void failSetShareTableFilesIfDisposed(){ + BackupableDBOptions options = setupUninitializedBackupableDBOptions( + exception); + options.setShareTableFiles(true); + } + + @Test + public void failShareTableFilesIfDisposed(){ + BackupableDBOptions options = setupUninitializedBackupableDBOptions( + exception); + options.shareTableFiles(); + } + + @Test + public void failSetSyncIfDisposed(){ + BackupableDBOptions options = setupUninitializedBackupableDBOptions( + exception); + options.setSync(true); + } + + @Test + public void failSyncIfDisposed(){ + BackupableDBOptions options = setupUninitializedBackupableDBOptions( + exception); + options.sync(); + } + + @Test + public void failSetDestroyOldDataIfDisposed(){ + BackupableDBOptions options = setupUninitializedBackupableDBOptions( + exception); + options.setDestroyOldData(true); + } + + @Test + public void failDestroyOldDataIfDisposed(){ + BackupableDBOptions options = setupUninitializedBackupableDBOptions( + exception); + options.destroyOldData(); + } + + @Test + public void failSetBackupLogFilesIfDisposed(){ + BackupableDBOptions options = setupUninitializedBackupableDBOptions( + exception); + options.setBackupLogFiles(true); + } + + @Test + public void failBackupLogFilesIfDisposed(){ + BackupableDBOptions options = setupUninitializedBackupableDBOptions( + exception); + options.backupLogFiles(); + } + + @Test + public void failSetBackupRateLimitIfDisposed(){ + BackupableDBOptions options = setupUninitializedBackupableDBOptions( + exception); + options.setBackupRateLimit(1); + } + + @Test + public void failBackupRateLimitIfDisposed(){ + BackupableDBOptions options = setupUninitializedBackupableDBOptions( + exception); + options.backupRateLimit(); + } + + @Test + public void failSetRestoreRateLimitIfDisposed(){ + BackupableDBOptions options = setupUninitializedBackupableDBOptions( + exception); + options.setRestoreRateLimit(1); + } + + @Test + public void failRestoreRateLimitIfDisposed(){ + BackupableDBOptions options = setupUninitializedBackupableDBOptions( + exception); + options.restoreRateLimit(); + } + + @Test + public void failSetShareFilesWithChecksumIfDisposed(){ + BackupableDBOptions options = setupUninitializedBackupableDBOptions( + exception); + options.setShareFilesWithChecksum(true); + } + + @Test + public void failShareFilesWithChecksumIfDisposed(){ + BackupableDBOptions options = setupUninitializedBackupableDBOptions( + exception); + options.shareFilesWithChecksum(); + } + + private BackupableDBOptions setupUninitializedBackupableDBOptions( + ExpectedException exception) { + BackupableDBOptions backupableDBOptions = + new BackupableDBOptions(ARBITRARY_PATH); + backupableDBOptions.dispose(); + exception.expect(AssertionError.class); + return backupableDBOptions; + } +} diff --git a/java/org/rocksdb/test/BackupableDBTest.java b/java/org/rocksdb/test/BackupableDBTest.java index 55a707687b12277f6573281a36ef99bbe430c538..3da519418b44992670b5b2840a564a4585669c7a 100644 --- a/java/org/rocksdb/test/BackupableDBTest.java +++ b/java/org/rocksdb/test/BackupableDBTest.java @@ -28,138 +28,387 @@ public class BackupableDBTest { public TemporaryFolder backupFolder = new TemporaryFolder(); @Test - public void backupableDb() throws RocksDBException { + public void backupDb() throws RocksDBException { Options opt = null; BackupableDBOptions bopt = null; BackupableDB bdb = null; - RestoreOptions ropt = null; - RestoreBackupableDB rdb = null; try { - opt = new Options(); - opt.setCreateIfMissing(true); + opt = new Options().setCreateIfMissing(true); + bopt = new BackupableDBOptions( + backupFolder.getRoot().getAbsolutePath()); + assertThat(bopt.backupDir()).isEqualTo( + backupFolder.getRoot().getAbsolutePath()); + // Open empty database. + bdb = BackupableDB.open(opt, bopt, + dbFolder.getRoot().getAbsolutePath()); + // Fill database with some test values + prepareDatabase(bdb); + // Create two backups + bdb.createNewBackup(false); + bdb.createNewBackup(true); + verifyNumberOfValidBackups(bdb, 2); + } finally { + if (bdb != null) { + bdb.close(); + } + if (bopt != null) { + bopt.dispose(); + } + if (opt != null) { + opt.dispose(); + } + } + } + @Test + public void deleteBackup() throws RocksDBException { + Options opt = null; + BackupableDBOptions bopt = null; + BackupableDB bdb = null; + try { + opt = new Options().setCreateIfMissing(true); bopt = new BackupableDBOptions( - backupFolder.getRoot().getAbsolutePath(), false, - true, false, true, 0, 0); + backupFolder.getRoot().getAbsolutePath()); assertThat(bopt.backupDir()).isEqualTo( backupFolder.getRoot().getAbsolutePath()); + // Open empty database. + bdb = BackupableDB.open(opt, bopt, + dbFolder.getRoot().getAbsolutePath()); + // Fill database with some test values + prepareDatabase(bdb); + // Create two backups + bdb.createNewBackup(false); + bdb.createNewBackup(true); + List backupInfo = + verifyNumberOfValidBackups(bdb, 2); + // Delete the first backup + bdb.deleteBackup(backupInfo.get(0).backupId()); + List newBackupInfo = + verifyNumberOfValidBackups(bdb, 1); + // The second backup must remain. + assertThat(newBackupInfo.get(0).backupId()). + isEqualTo(backupInfo.get(1).backupId()); + } finally { + if (bdb != null) { + bdb.close(); + } + if (bopt != null) { + bopt.dispose(); + } + if (opt != null) { + opt.dispose(); + } + } + } - List backupInfos; - List restoreInfos; + @Test + public void deleteBackupWithRestoreBackupableDB() + throws RocksDBException { + Options opt = null; + BackupableDBOptions bopt = null; + BackupableDB bdb = null; + RestoreBackupableDB rdb = null; + try { + opt = new Options().setCreateIfMissing(true); + bopt = new BackupableDBOptions( + backupFolder.getRoot().getAbsolutePath()); + assertThat(bopt.backupDir()).isEqualTo( + backupFolder.getRoot().getAbsolutePath()); + // Open empty database. + bdb = BackupableDB.open(opt, bopt, + dbFolder.getRoot().getAbsolutePath()); + // Fill database with some test values + prepareDatabase(bdb); + // Create two backups + bdb.createNewBackup(false); + bdb.createNewBackup(true); + List backupInfo = + verifyNumberOfValidBackups(bdb, 2); + // init RestoreBackupableDB + rdb = new RestoreBackupableDB(bopt); + // Delete the first backup + rdb.deleteBackup(backupInfo.get(0).backupId()); + // Fetch backup info using RestoreBackupableDB + List newBackupInfo = verifyNumberOfValidBackups(rdb, 1); + // The second backup must remain. + assertThat(newBackupInfo.get(0).backupId()). + isEqualTo(backupInfo.get(1).backupId()); + } finally { + if (bdb != null) { + bdb.close(); + } + if (rdb != null) { + rdb.dispose(); + } + if (bopt != null) { + bopt.dispose(); + } + if (opt != null) { + opt.dispose(); + } + } + } + @Test + public void purgeOldBackups() throws RocksDBException { + Options opt = null; + BackupableDBOptions bopt = null; + BackupableDB bdb = null; + try { + opt = new Options().setCreateIfMissing(true); + bopt = new BackupableDBOptions( + backupFolder.getRoot().getAbsolutePath()); + assertThat(bopt.backupDir()).isEqualTo( + backupFolder.getRoot().getAbsolutePath()); + // Open empty database. bdb = BackupableDB.open(opt, bopt, dbFolder.getRoot().getAbsolutePath()); - bdb.put("abc".getBytes(), "def".getBytes()); - bdb.put("ghi".getBytes(), "jkl".getBytes()); + // Fill database with some test values + prepareDatabase(bdb); + // Create two backups + bdb.createNewBackup(false); + bdb.createNewBackup(true); + bdb.createNewBackup(true); + bdb.createNewBackup(true); + List backupInfo = + verifyNumberOfValidBackups(bdb, 4); + // Delete everything except the latest backup + bdb.purgeOldBackups(1); + List newBackupInfo = + verifyNumberOfValidBackups(bdb, 1); + // The latest backup must remain. + assertThat(newBackupInfo.get(0).backupId()). + isEqualTo(backupInfo.get(3).backupId()); + } finally { + if (bdb != null) { + bdb.close(); + } + if (bopt != null) { + bopt.dispose(); + } + if (opt != null) { + opt.dispose(); + } + } + } - backupInfos = bdb.getBackupInfos(); - assertThat(backupInfos.size()). - isEqualTo(0); + @Test + public void purgeOldBackupsWithRestoreBackupableDb() + throws RocksDBException { + Options opt = null; + BackupableDBOptions bopt = null; + BackupableDB bdb = null; + RestoreBackupableDB rdb = null; + try { + opt = new Options().setCreateIfMissing(true); + bopt = new BackupableDBOptions( + backupFolder.getRoot().getAbsolutePath()); + assertThat(bopt.backupDir()).isEqualTo( + backupFolder.getRoot().getAbsolutePath()); + // Open empty database. + bdb = BackupableDB.open(opt, bopt, + dbFolder.getRoot().getAbsolutePath()); + // Fill database with some test values + prepareDatabase(bdb); + // Create two backups + bdb.createNewBackup(false); + bdb.createNewBackup(true); + bdb.createNewBackup(true); + bdb.createNewBackup(true); + verifyNumberOfValidBackups(bdb, 4); + // init RestoreBackupableDB + rdb = new RestoreBackupableDB(bopt); + // the same number of backups must + // exist using RestoreBackupableDB. + verifyNumberOfValidBackups(rdb, 4); + rdb.purgeOldBackups(1); + verifyNumberOfValidBackups(rdb, 1); + } finally { + if (bdb != null) { + bdb.close(); + } + if (rdb != null) { + rdb.dispose(); + } + if (bopt != null) { + bopt.dispose(); + } + if (opt != null) { + opt.dispose(); + } + } + } + @Test + public void restoreLatestBackup() + throws RocksDBException { + Options opt = null; + BackupableDBOptions bopt = null; + BackupableDB bdb = null; + RestoreBackupableDB rdb = null; + try { + opt = new Options().setCreateIfMissing(true); + bopt = new BackupableDBOptions( + backupFolder.getRoot().getAbsolutePath()); + assertThat(bopt.backupDir()).isEqualTo( + backupFolder.getRoot().getAbsolutePath()); + // Open empty database. + bdb = BackupableDB.open(opt, bopt, + dbFolder.getRoot().getAbsolutePath()); + // Fill database with some test values + prepareDatabase(bdb); + bdb.createNewBackup(true); + verifyNumberOfValidBackups(bdb, 1); + bdb.put("key1".getBytes(), "valueV2".getBytes()); + bdb.put("key2".getBytes(), "valueV2".getBytes()); bdb.createNewBackup(true); - backupInfos = bdb.getBackupInfos(); - assertThat(backupInfos.size()). - isEqualTo(1); - - // Retrieving backup infos twice shall not - // lead to different results - List tmpBackupInfo = bdb.getBackupInfos(); - assertThat(tmpBackupInfo.get(0).backupId()). - isEqualTo(backupInfos.get(0).backupId()); - assertThat(tmpBackupInfo.get(0).timestamp()). - isEqualTo(backupInfos.get(0).timestamp()); - assertThat(tmpBackupInfo.get(0).size()). - isEqualTo(backupInfos.get(0).size()); - assertThat(tmpBackupInfo.get(0).numberFiles()). - isEqualTo(backupInfos.get(0).numberFiles()); - - // delete record after backup - bdb.remove("abc".getBytes()); - byte[] value = bdb.get("abc".getBytes()); - assertThat(value).isNull(); + verifyNumberOfValidBackups(bdb, 2); + bdb.put("key1".getBytes(), "valueV3".getBytes()); + bdb.put("key2".getBytes(), "valueV3".getBytes()); + assertThat(new String(bdb.get("key1".getBytes()))).endsWith("V3"); + assertThat(new String(bdb.get("key2".getBytes()))).endsWith("V3"); bdb.close(); - // restore from backup - ropt = new RestoreOptions(false); + // init RestoreBackupableDB rdb = new RestoreBackupableDB(bopt); - - // getting backup infos from restorable db should - // lead to the same infos as from backupable db - restoreInfos = rdb.getBackupInfos(); - assertThat(restoreInfos.size()). - isEqualTo(backupInfos.size()); - assertThat(restoreInfos.get(0).backupId()). - isEqualTo(backupInfos.get(0).backupId()); - assertThat(restoreInfos.get(0).timestamp()). - isEqualTo(backupInfos.get(0).timestamp()); - assertThat(restoreInfos.get(0).size()). - isEqualTo(backupInfos.get(0).size()); - assertThat(restoreInfos.get(0).numberFiles()). - isEqualTo(backupInfos.get(0).numberFiles()); - - rdb.restoreDBFromLatestBackup( + verifyNumberOfValidBackups(rdb, 2); + // restore db from latest backup + rdb.restoreDBFromLatestBackup(dbFolder.getRoot().getAbsolutePath(), dbFolder.getRoot().getAbsolutePath(), - dbFolder.getRoot().getAbsolutePath(), - ropt); - // do nothing because there is only one backup - rdb.purgeOldBackups(1); - restoreInfos = rdb.getBackupInfos(); - assertThat(restoreInfos.size()). - isEqualTo(1); - rdb.dispose(); - ropt.dispose(); - - // verify that backed up data contains deleted record + new RestoreOptions(false)); + // Open database again. bdb = BackupableDB.open(opt, bopt, dbFolder.getRoot().getAbsolutePath()); - value = bdb.get("abc".getBytes()); - assertThat(new String(value)). - isEqualTo("def"); - - bdb.createNewBackup(false); - // after new backup there must be two backup infos - backupInfos = bdb.getBackupInfos(); - assertThat(backupInfos.size()). - isEqualTo(2); - // deleting the backup must be possible using the - // id provided by backup infos - bdb.deleteBackup(backupInfos.get(1).backupId()); - // after deletion there should only be one info - backupInfos = bdb.getBackupInfos(); - assertThat(backupInfos.size()). - isEqualTo(1); - bdb.createNewBackup(false); - bdb.createNewBackup(false); - bdb.createNewBackup(false); - backupInfos = bdb.getBackupInfos(); - assertThat(backupInfos.size()). - isEqualTo(4); - // purge everything and keep two - bdb.purgeOldBackups(2); - // backup infos need to be two - backupInfos = bdb.getBackupInfos(); - assertThat(backupInfos.size()). - isEqualTo(2); - assertThat(backupInfos.get(0).backupId()). - isEqualTo(4); - assertThat(backupInfos.get(1).backupId()). - isEqualTo(5); + // Values must have suffix V2 because of restoring latest backup. + assertThat(new String(bdb.get("key1".getBytes()))).endsWith("V2"); + assertThat(new String(bdb.get("key2".getBytes()))).endsWith("V2"); } finally { - if (opt != null) { - opt.dispose(); + if (bdb != null) { + bdb.close(); + } + if (rdb != null) { + rdb.dispose(); } if (bopt != null) { bopt.dispose(); } + if (opt != null) { + opt.dispose(); + } + } + } + + @Test + public void restoreFromBackup() + throws RocksDBException { + Options opt = null; + BackupableDBOptions bopt = null; + BackupableDB bdb = null; + RestoreBackupableDB rdb = null; + try { + opt = new Options().setCreateIfMissing(true); + bopt = new BackupableDBOptions( + backupFolder.getRoot().getAbsolutePath()); + assertThat(bopt.backupDir()).isEqualTo( + backupFolder.getRoot().getAbsolutePath()); + // Open empty database. + bdb = BackupableDB.open(opt, bopt, + dbFolder.getRoot().getAbsolutePath()); + // Fill database with some test values + prepareDatabase(bdb); + bdb.createNewBackup(true); + verifyNumberOfValidBackups(bdb, 1); + bdb.put("key1".getBytes(), "valueV2".getBytes()); + bdb.put("key2".getBytes(), "valueV2".getBytes()); + bdb.createNewBackup(true); + verifyNumberOfValidBackups(bdb, 2); + bdb.put("key1".getBytes(), "valueV3".getBytes()); + bdb.put("key2".getBytes(), "valueV3".getBytes()); + assertThat(new String(bdb.get("key1".getBytes()))).endsWith("V3"); + assertThat(new String(bdb.get("key2".getBytes()))).endsWith("V3"); + bdb.close(); + + // init RestoreBackupableDB + rdb = new RestoreBackupableDB(bopt); + List backupInfo = verifyNumberOfValidBackups(rdb, 2); + // restore db from first backup + rdb.restoreDBFromBackup(backupInfo.get(0).backupId(), + dbFolder.getRoot().getAbsolutePath(), + dbFolder.getRoot().getAbsolutePath(), + new RestoreOptions(false)); + // Open database again. + bdb = BackupableDB.open(opt, bopt, + dbFolder.getRoot().getAbsolutePath()); + // Values must have suffix V2 because of restoring latest backup. + assertThat(new String(bdb.get("key1".getBytes()))).endsWith("V1"); + assertThat(new String(bdb.get("key2".getBytes()))).endsWith("V1"); + } finally { if (bdb != null) { bdb.close(); } - if (ropt != null) { - ropt.dispose(); - } if (rdb != null) { rdb.dispose(); } + if (bopt != null) { + bopt.dispose(); + } + if (opt != null) { + opt.dispose(); + } } } + + /** + * Verify backups. + * + * @param bdb {@link BackupableDB} instance. + * @param expectedNumberOfBackups numerical value + * @throws RocksDBException thrown if an error occurs within the native + * part of the library. + */ + private List verifyNumberOfValidBackups(BackupableDB bdb, + int expectedNumberOfBackups) throws RocksDBException { + // Verify that backups exist + assertThat(bdb.getCorruptedBackups().length). + isEqualTo(0); + bdb.garbageCollect(); + List backupInfo = bdb.getBackupInfos(); + assertThat(backupInfo.size()). + isEqualTo(expectedNumberOfBackups); + return backupInfo; + } + + /** + * Verify backups. + * + * @param rdb {@link RestoreBackupableDB} instance. + * @param expectedNumberOfBackups numerical value + * @throws RocksDBException thrown if an error occurs within the native + * part of the library. + */ + private List verifyNumberOfValidBackups( + RestoreBackupableDB rdb, int expectedNumberOfBackups) + throws RocksDBException { + // Verify that backups exist + assertThat(rdb.getCorruptedBackups().length). + isEqualTo(0); + rdb.garbageCollect(); + List backupInfo = rdb.getBackupInfos(); + assertThat(backupInfo.size()). + isEqualTo(expectedNumberOfBackups); + return backupInfo; + } + + /** + * Fill database with some test values. + * + * @param db {@link RocksDB} instance. + * @throws RocksDBException thrown if an error occurs within the native + * part of the library. + */ + private void prepareDatabase(RocksDB db) + throws RocksDBException { + db.put("key1".getBytes(), "valueV1".getBytes()); + db.put("key2".getBytes(), "valueV1".getBytes()); + } } diff --git a/java/rocksjni/backupablejni.cc b/java/rocksjni/backupablejni.cc index 609cbd73ebba5b183e6e1e0412222d6e4acf2ff8..83c641370c6a4ab4ade2fd2f4206ca12ebc41361 100644 --- a/java/rocksjni/backupablejni.cc +++ b/java/rocksjni/backupablejni.cc @@ -92,6 +92,45 @@ jobject Java_org_rocksdb_BackupableDB_getBackupInfo( backup_infos); } +/* + * Class: org_rocksdb_BackupableDB + * Method: getCorruptedBackups + * Signature: (J)[I; + */ +jintArray Java_org_rocksdb_BackupableDB_getCorruptedBackups( + JNIEnv* env, jobject jbdb, jlong jhandle) { + std::vector backup_ids; + reinterpret_cast(jhandle)-> + GetCorruptedBackups(&backup_ids); + // store backupids in int array + const int kIdSize = backup_ids.size(); + int int_backup_ids[kIdSize]; + for (std::vector::size_type i = 0; + i != backup_ids.size(); i++) { + int_backup_ids[i] = backup_ids[i]; + } + // Store ints in java array + jintArray ret_backup_ids; + ret_backup_ids = env->NewIntArray(kIdSize); + env->SetIntArrayRegion(ret_backup_ids, 0, kIdSize, int_backup_ids); + return ret_backup_ids; +} + +/* + * Class: org_rocksdb_BackupableDB + * Method: garbageCollect + * Signature: (J)V + */ +void Java_org_rocksdb_BackupableDB_garbageCollect(JNIEnv* env, + jobject jobj, jlong jhandle) { + auto db = reinterpret_cast(jhandle); + rocksdb::Status s = db->GarbageCollect(); + + if (!s.ok()) { + rocksdb::RocksDBExceptionJni::ThrowNew(env, s); + } +} + /////////////////////////////////////////////////////////////////////////// // BackupDBOptions @@ -101,20 +140,10 @@ jobject Java_org_rocksdb_BackupableDB_getBackupInfo( * Signature: (Ljava/lang/String;)V */ void Java_org_rocksdb_BackupableDBOptions_newBackupableDBOptions( - JNIEnv* env, jobject jobj, jstring jpath, jboolean jshare_table_files, - jboolean jsync, jboolean jdestroy_old_data, jboolean jbackup_log_files, - jlong jbackup_rate_limit, jlong jrestore_rate_limit) { - jbackup_rate_limit = (jbackup_rate_limit <= 0) ? 0 : jbackup_rate_limit; - jrestore_rate_limit = (jrestore_rate_limit <= 0) ? 0 : jrestore_rate_limit; - + JNIEnv* env, jobject jobj, jstring jpath) { const char* cpath = env->GetStringUTFChars(jpath, 0); - - auto bopt = new rocksdb::BackupableDBOptions(cpath, nullptr, - jshare_table_files, nullptr, jsync, jdestroy_old_data, jbackup_log_files, - jbackup_rate_limit, jrestore_rate_limit); - + auto bopt = new rocksdb::BackupableDBOptions(cpath); env->ReleaseStringUTFChars(jpath, cpath); - rocksdb::BackupableDBOptionsJni::setHandle(env, jobj, bopt); } @@ -129,6 +158,160 @@ jstring Java_org_rocksdb_BackupableDBOptions_backupDir( return env->NewStringUTF(bopt->backup_dir.c_str()); } +/* + * Class: org_rocksdb_BackupableDBOptions + * Method: setShareTableFiles + * Signature: (JZ)V + */ +void Java_org_rocksdb_BackupableDBOptions_setShareTableFiles( + JNIEnv* env, jobject jobj, jlong jhandle, jboolean flag) { + auto bopt = reinterpret_cast(jhandle); + bopt->share_table_files = flag; +} + +/* + * Class: org_rocksdb_BackupableDBOptions + * Method: shareTableFiles + * Signature: (J)Z + */ +jboolean Java_org_rocksdb_BackupableDBOptions_shareTableFiles( + JNIEnv* env, jobject jobj, jlong jhandle) { + auto bopt = reinterpret_cast(jhandle); + return bopt->share_table_files; +} + +/* + * Class: org_rocksdb_BackupableDBOptions + * Method: setSync + * Signature: (JZ)V + */ +void Java_org_rocksdb_BackupableDBOptions_setSync( + JNIEnv* env, jobject jobj, jlong jhandle, jboolean flag) { + auto bopt = reinterpret_cast(jhandle); + bopt->sync = flag; +} + +/* + * Class: org_rocksdb_BackupableDBOptions + * Method: sync + * Signature: (J)Z + */ +jboolean Java_org_rocksdb_BackupableDBOptions_sync( + JNIEnv* env, jobject jobj, jlong jhandle) { + auto bopt = reinterpret_cast(jhandle); + return bopt->sync; +} + +/* + * Class: org_rocksdb_BackupableDBOptions + * Method: setDestroyOldData + * Signature: (JZ)V + */ +void Java_org_rocksdb_BackupableDBOptions_setDestroyOldData( + JNIEnv* env, jobject jobj, jlong jhandle, jboolean flag) { + auto bopt = reinterpret_cast(jhandle); + bopt->destroy_old_data = flag; +} + +/* + * Class: org_rocksdb_BackupableDBOptions + * Method: destroyOldData + * Signature: (J)Z + */ +jboolean Java_org_rocksdb_BackupableDBOptions_destroyOldData( + JNIEnv* env, jobject jobj, jlong jhandle) { + auto bopt = reinterpret_cast(jhandle); + return bopt->destroy_old_data; +} + +/* + * Class: org_rocksdb_BackupableDBOptions + * Method: setBackupLogFiles + * Signature: (JZ)V + */ +void Java_org_rocksdb_BackupableDBOptions_setBackupLogFiles( + JNIEnv* env, jobject jobj, jlong jhandle, jboolean flag) { + auto bopt = reinterpret_cast(jhandle); + bopt->backup_log_files = flag; +} + +/* + * Class: org_rocksdb_BackupableDBOptions + * Method: backupLogFiles + * Signature: (J)Z + */ +jboolean Java_org_rocksdb_BackupableDBOptions_backupLogFiles( + JNIEnv* env, jobject jobj, jlong jhandle) { + auto bopt = reinterpret_cast(jhandle); + return bopt->backup_log_files; +} + +/* + * Class: org_rocksdb_BackupableDBOptions + * Method: setBackupRateLimit + * Signature: (JJ)V + */ +void Java_org_rocksdb_BackupableDBOptions_setBackupRateLimit( + JNIEnv* env, jobject jobj, jlong jhandle, jlong jbackup_rate_limit) { + auto bopt = reinterpret_cast(jhandle); + bopt->backup_rate_limit = jbackup_rate_limit; +} + +/* + * Class: org_rocksdb_BackupableDBOptions + * Method: backupRateLimit + * Signature: (J)J + */ +jlong Java_org_rocksdb_BackupableDBOptions_backupRateLimit( + JNIEnv* env, jobject jobj, jlong jhandle) { + auto bopt = reinterpret_cast(jhandle); + return bopt->backup_rate_limit; +} + +/* + * Class: org_rocksdb_BackupableDBOptions + * Method: setRestoreRateLimit + * Signature: (JJ)V + */ +void Java_org_rocksdb_BackupableDBOptions_setRestoreRateLimit( + JNIEnv* env, jobject jobj, jlong jhandle, jlong jrestore_rate_limit) { + auto bopt = reinterpret_cast(jhandle); + bopt->restore_rate_limit = jrestore_rate_limit; +} + +/* + * Class: org_rocksdb_BackupableDBOptions + * Method: restoreRateLimit + * Signature: (J)J + */ +jlong Java_org_rocksdb_BackupableDBOptions_restoreRateLimit( + JNIEnv* env, jobject jobj, jlong jhandle) { + auto bopt = reinterpret_cast(jhandle); + return bopt->restore_rate_limit; +} + +/* + * Class: org_rocksdb_BackupableDBOptions + * Method: setShareFilesWithChecksum + * Signature: (JZ)V + */ +void Java_org_rocksdb_BackupableDBOptions_setShareFilesWithChecksum( + JNIEnv* env, jobject jobj, jlong jhandle, jboolean flag) { + auto bopt = reinterpret_cast(jhandle); + bopt->share_files_with_checksum = flag; +} + +/* + * Class: org_rocksdb_BackupableDBOptions + * Method: shareFilesWithChecksum + * Signature: (J)Z + */ +jboolean Java_org_rocksdb_BackupableDBOptions_shareFilesWithChecksum( + JNIEnv* env, jobject jobj, jlong jhandle) { + auto bopt = reinterpret_cast(jhandle); + return bopt->share_files_with_checksum; +} + /* * Class: org_rocksdb_BackupableDBOptions * Method: disposeInternal @@ -139,6 +322,5 @@ void Java_org_rocksdb_BackupableDBOptions_disposeInternal( auto bopt = reinterpret_cast(jhandle); assert(bopt); delete bopt; - rocksdb::BackupableDBOptionsJni::setHandle(env, jopt, nullptr); } diff --git a/java/rocksjni/restorejni.cc b/java/rocksjni/restorejni.cc index 4fe813d09d34e72de29ad00fff60452e97395b69..ad8749758c2003e0d85aa730773dd8eac84f454c 100644 --- a/java/rocksjni/restorejni.cc +++ b/java/rocksjni/restorejni.cc @@ -145,6 +145,46 @@ jobject Java_org_rocksdb_RestoreBackupableDB_getBackupInfo( backup_infos); } +/* + * Class: org_rocksdb_RestoreBackupableDB + * Method: getCorruptedBackups + * Signature: (J)[I; + */ +jintArray Java_org_rocksdb_RestoreBackupableDB_getCorruptedBackups( + JNIEnv* env, jobject jbdb, jlong jhandle) { + std::vector backup_ids; + reinterpret_cast(jhandle)-> + GetCorruptedBackups(&backup_ids); + // store backupids in int array + const int kIdSize = backup_ids.size(); + int int_backup_ids[kIdSize]; + for (std::vector::size_type i = 0; + i != backup_ids.size(); i++) { + int_backup_ids[i] = backup_ids[i]; + } + // Store ints in java array + jintArray ret_backup_ids; + ret_backup_ids = env->NewIntArray(kIdSize); + env->SetIntArrayRegion(ret_backup_ids, 0, kIdSize, int_backup_ids); + return ret_backup_ids; +} + +/* + * Class: org_rocksdb_RestoreBackupableDB + * Method: garbageCollect + * Signature: (J)V + */ +void Java_org_rocksdb_RestoreBackupableDB_garbageCollect( + JNIEnv* env, jobject jobj, jlong jhandle) { + auto db = reinterpret_cast( + jhandle); + rocksdb::Status s = db->GarbageCollect(); + + if (!s.ok()) { + rocksdb::RocksDBExceptionJni::ThrowNew(env, s); + } +} + /* * Class: org_rocksdb_RestoreBackupableDB * Method: dispose