diff --git a/java/org/rocksdb/BackupInfo.java b/java/org/rocksdb/BackupInfo.java new file mode 100644 index 000000000..407445473 --- /dev/null +++ b/java/org/rocksdb/BackupInfo.java @@ -0,0 +1,67 @@ +// 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; + +/** + * Instances of this class describe a Backup made by + * {@link org.rocksdb.BackupableDB}. + */ +public class BackupInfo { + + /** + * Package private constructor used to create instances + * of BackupInfo by {@link org.rocksdb.BackupableDB} and + * {@link org.rocksdb.RestoreBackupableDB}. + * + * @param backupId id of backup + * @param timestamp timestamp of backup + * @param size size of backup + * @param numberFiles number of files related to this backup. + */ + BackupInfo(int backupId, long timestamp, long size, + int numberFiles) { + backupId_ = backupId; + timestamp_ = timestamp; + size_ = size; + numberFiles_ = numberFiles; + } + + /** + * + * @return the backup id. + */ + public int backupId() { + return backupId_; + } + + /** + * + * @return the timestamp of the backup. + */ + public long timestamp() { + return timestamp_; + } + + /** + * + * @return the size of the backup + */ + public long size() { + return size_; + } + + /** + * + * @return the number of files of this backup. + */ + public int numberFiles() { + return numberFiles_; + } + + private int backupId_; + private long timestamp_; + private long size_; + private int numberFiles_; +} diff --git a/java/org/rocksdb/BackupableDB.java b/java/org/rocksdb/BackupableDB.java index 24e1c2e33..0c1ef328f 100644 --- a/java/org/rocksdb/BackupableDB.java +++ b/java/org/rocksdb/BackupableDB.java @@ -5,6 +5,8 @@ package org.rocksdb; +import java.util.List; + /** * A subclass of RocksDB which supports backup-related operations. * @@ -43,8 +45,10 @@ public class BackupableDB extends RocksDB { * * @param flushBeforeBackup if true, then all data will be flushed * before creating backup. + * @throws org.rocksdb.RocksDBException */ - public void createNewBackup(boolean flushBeforeBackup) { + public void createNewBackup(boolean flushBeforeBackup) + throws RocksDBException { createNewBackup(nativeHandle_, flushBeforeBackup); } @@ -52,11 +56,32 @@ public class BackupableDB extends RocksDB { * Deletes old backups, keeping latest numBackupsToKeep alive. * * @param numBackupsToKeep Number of latest backups to keep. + * @throws org.rocksdb.RocksDBException */ - public void purgeOldBackups(int numBackupsToKeep) { + public void purgeOldBackups(int numBackupsToKeep) + throws RocksDBException { purgeOldBackups(nativeHandle_, numBackupsToKeep); } + /** + * Deletes a specific backup. + * + * @param backupId of backup to delete. + * @throws org.rocksdb.RocksDBException + */ + public void deleteBackup(long backupId) throws RocksDBException { + deleteBackup0(nativeHandle_, backupId); + } + + /** + * Returns a list of {@link BackupInfo} instances, which describe + * already made backups. + * + * @return List of {@link BackupInfo} instances. + */ + public List getBackupInfos() { + return getBackupInfo(nativeHandle_); + } /** * Close the BackupableDB instance and release resource. @@ -85,6 +110,11 @@ public class BackupableDB extends RocksDB { } protected native void open(long rocksDBHandle, long backupDBOptionsHandle); - protected native void createNewBackup(long handle, boolean flag); - protected native void purgeOldBackups(long handle, int numBackupsToKeep); + protected native void createNewBackup(long handle, boolean flag) + throws RocksDBException; + protected native void purgeOldBackups(long handle, int numBackupsToKeep) + throws RocksDBException; + private native void deleteBackup0(long nativeHandle, long backupId) + throws RocksDBException; + protected native List getBackupInfo(long handle); } diff --git a/java/org/rocksdb/RestoreBackupableDB.java b/java/org/rocksdb/RestoreBackupableDB.java index 5bc8dfbec..7b2296898 100644 --- a/java/org/rocksdb/RestoreBackupableDB.java +++ b/java/org/rocksdb/RestoreBackupableDB.java @@ -5,6 +5,8 @@ package org.rocksdb; +import java.util.List; + /** * This class is used to access information about backups and restore from them. * @@ -65,6 +67,7 @@ public class RestoreBackupableDB extends RocksObject { * Deletes old backups, keeping latest numBackupsToKeep alive. * * @param numBackupsToKeep of latest backups to keep + * @throws org.rocksdb.RocksDBException */ public void purgeOldBackups(int numBackupsToKeep) throws RocksDBException { purgeOldBackups0(nativeHandle_, numBackupsToKeep); @@ -74,11 +77,22 @@ public class RestoreBackupableDB extends RocksObject { * Deletes a specific backup. * * @param backupId of backup to delete. + * @throws org.rocksdb.RocksDBException */ public void deleteBackup(long backupId) throws RocksDBException { deleteBackup0(nativeHandle_, backupId); } + /** + * Returns a list of {@link BackupInfo} instances, which describe + * already made backups. + * + * @return List of {@link BackupInfo} instances. + */ + public List getBackupInfos() { + return getBackupInfo(nativeHandle_); + } + /** * Release the memory allocated for the current instance * in the c++ side. @@ -90,10 +104,15 @@ public class RestoreBackupableDB extends RocksObject { private native long newRestoreBackupableDB(long options); private native void restoreDBFromBackup0(long nativeHandle, long backupId, - String dbDir, String walDir, long restoreOptions) throws RocksDBException; + String dbDir, String walDir, long restoreOptions) + throws RocksDBException; private native void restoreDBFromLatestBackup0(long nativeHandle, - String dbDir, String walDir, long restoreOptions) throws RocksDBException; - private native void purgeOldBackups0(long nativeHandle, int numBackupsToKeep); - private native void deleteBackup0(long nativeHandle, long backupId); + String dbDir, String walDir, long restoreOptions) + throws RocksDBException; + private native void purgeOldBackups0(long nativeHandle, int numBackupsToKeep) + throws RocksDBException; + private native void deleteBackup0(long nativeHandle, long backupId) + throws RocksDBException; + protected native List getBackupInfo(long handle); private native void dispose(long nativeHandle); } diff --git a/java/org/rocksdb/test/BackupableDBTest.java b/java/org/rocksdb/test/BackupableDBTest.java index ee4509697..2115e9ca9 100644 --- a/java/org/rocksdb/test/BackupableDBTest.java +++ b/java/org/rocksdb/test/BackupableDBTest.java @@ -7,6 +7,8 @@ package org.rocksdb.test; import org.rocksdb.*; +import java.util.List; + public class BackupableDBTest { static final String db_path = "/tmp/rocksdbjni_backupable_db_test"; static final String backup_path = "/tmp/rocksdbjni_backupable_db_backup_test"; @@ -21,14 +23,34 @@ public class BackupableDBTest { BackupableDBOptions bopt = new BackupableDBOptions(backup_path, false, true, false, true, 0, 0); BackupableDB bdb = null; - + List backupInfos; + List restoreInfos; try { bdb = BackupableDB.open(opt, bopt, db_path); bdb.put("abc".getBytes(), "def".getBytes()); bdb.put("ghi".getBytes(), "jkl".getBytes()); + + backupInfos = bdb.getBackupInfos(); + assert(backupInfos.size() == 0); + bdb.createNewBackup(true); + backupInfos = bdb.getBackupInfos(); + assert(backupInfos.size() == 1); + + // Retrieving backup infos twice shall not + // lead to different results + List tmpBackupInfo = bdb.getBackupInfos(); + assert(tmpBackupInfo.get(0).backupId() == + backupInfos.get(0).backupId()); + assert(tmpBackupInfo.get(0).timestamp() == + backupInfos.get(0).timestamp()); + assert(tmpBackupInfo.get(0).size() == + backupInfos.get(0).size()); + assert(tmpBackupInfo.get(0).numberFiles() == + backupInfos.get(0).numberFiles()); + // delete record after backup bdb.remove("abc".getBytes()); byte[] value = bdb.get("abc".getBytes()); @@ -38,8 +60,26 @@ public class BackupableDBTest { // restore from backup RestoreOptions ropt = new RestoreOptions(false); RestoreBackupableDB rdb = new RestoreBackupableDB(bopt); + + // getting backup infos from restorable db should + // lead to the same infos as from backupable db + restoreInfos = rdb.getBackupInfos(); + assert(restoreInfos.size() == backupInfos.size()); + assert(restoreInfos.get(0).backupId() == + backupInfos.get(0).backupId()); + assert(restoreInfos.get(0).timestamp() == + backupInfos.get(0).timestamp()); + assert(restoreInfos.get(0).size() == + backupInfos.get(0).size()); + assert(restoreInfos.get(0).numberFiles() == + backupInfos.get(0).numberFiles()); + rdb.restoreDBFromLatestBackup(db_path, db_path, ropt); + // do nothing because there is only one backup + rdb.purgeOldBackups(1); + restoreInfos = rdb.getBackupInfos(); + assert(restoreInfos.size() == 1); rdb.dispose(); ropt.dispose(); @@ -48,6 +88,28 @@ public class BackupableDBTest { value = bdb.get("abc".getBytes()); assert(new String(value).equals("def")); + bdb.createNewBackup(false); + // after new backup there must be two backup infos + backupInfos = bdb.getBackupInfos(); + assert(backupInfos.size() == 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(); + assert(backupInfos.size() == 1); + bdb.createNewBackup(false); + bdb.createNewBackup(false); + bdb.createNewBackup(false); + backupInfos = bdb.getBackupInfos(); + assert(backupInfos.size() == 4); + // purge everything and keep two + bdb.purgeOldBackups(2); + // backup infos need to be two + backupInfos = bdb.getBackupInfos(); + assert(backupInfos.size() == 2); + assert(backupInfos.get(0).backupId() == 4); + assert(backupInfos.get(1).backupId() == 5); System.out.println("Backup and restore test passed"); } catch (RocksDBException e) { System.err.format("[ERROR]: %s%n", e); diff --git a/java/rocksjni/backupablejni.cc b/java/rocksjni/backupablejni.cc index 2aa1d0b1d..a5107af57 100644 --- a/java/rocksjni/backupablejni.cc +++ b/java/rocksjni/backupablejni.cc @@ -11,6 +11,7 @@ #include #include #include +#include #include "include/org_rocksdb_BackupableDB.h" #include "include/org_rocksdb_BackupableDBOptions.h" @@ -53,7 +54,7 @@ void Java_org_rocksdb_BackupableDB_createNewBackup( * Signature: (JI)V */ void Java_org_rocksdb_BackupableDB_purgeOldBackups( - JNIEnv* env, jobject jbdb, jlong jhandle, jboolean jnumBackupsToKeep) { + JNIEnv* env, jobject jbdb, jlong jhandle, jint jnumBackupsToKeep) { rocksdb::Status s = reinterpret_cast(jhandle)-> PurgeOldBackups(jnumBackupsToKeep); @@ -62,6 +63,35 @@ void Java_org_rocksdb_BackupableDB_purgeOldBackups( } } +/* + * Class: org_rocksdb_BackupableDB + * Method: deleteBackup0 + * Signature: (JJ)V + */ +void Java_org_rocksdb_BackupableDB_deleteBackup0(JNIEnv* env, + jobject jobj, jlong jhandle, jlong jbackup_id) { + auto rdb = reinterpret_cast(jhandle); + rocksdb::Status s = rdb->DeleteBackup(jbackup_id); + + if (!s.ok()) { + rocksdb::RocksDBExceptionJni::ThrowNew(env, s); + } +} + +/* + * Class: org_rocksdb_BackupableDB + * Method: getBackupInfo + * Signature: (J)Ljava/util/List; + */ +jobject Java_org_rocksdb_BackupableDB_getBackupInfo( + JNIEnv* env, jobject jbdb, jlong jhandle) { + std::vector backup_infos; + reinterpret_cast(jhandle)-> + GetBackupInfo(&backup_infos); + return rocksdb::BackupInfoListJni::getBackupInfo(env, + backup_infos); +} + /////////////////////////////////////////////////////////////////////////// // BackupDBOptions diff --git a/java/rocksjni/comparatorjnicallback.cc b/java/rocksjni/comparatorjnicallback.cc index 1be363541..fcae16c7c 100644 --- a/java/rocksjni/comparatorjnicallback.cc +++ b/java/rocksjni/comparatorjnicallback.cc @@ -15,7 +15,6 @@ BaseComparatorJniCallback::BaseComparatorJniCallback( const ComparatorJniCallbackOptions* copt) : mtx_compare(new port::Mutex(copt->use_adaptive_mutex)), mtx_findShortestSeparator(new port::Mutex(copt->use_adaptive_mutex)) { - // Note: Comparator methods may be accessed by multiple threads, // so we ref the jvm not the env const jint rs = env->GetJavaVM(&m_jvm); diff --git a/java/rocksjni/portal.h b/java/rocksjni/portal.h index 32452ae0b..5a56fe639 100644 --- a/java/rocksjni/portal.h +++ b/java/rocksjni/portal.h @@ -13,6 +13,7 @@ #include #include #include +#include #include "rocksdb/db.h" #include "rocksdb/filter_policy.h" @@ -591,6 +592,50 @@ class ListJni { } }; +class BackupInfoJni { + public: + // Get the java class id of org.rocksdb.BackupInfo. + static jclass getJClass(JNIEnv* env) { + jclass jclazz = env->FindClass("org/rocksdb/BackupInfo"); + assert(jclazz != nullptr); + return jclazz; + } + + static jobject construct0(JNIEnv* env, uint32_t backup_id, int64_t timestamp, + uint64_t size, uint32_t number_files) { + static jmethodID mid = env->GetMethodID(getJClass(env), "", + "(IJJI)V"); + assert(mid != nullptr); + return env->NewObject(getJClass(env), mid, + backup_id, timestamp, size, number_files); + } +}; + +class BackupInfoListJni { + public: + static jobject getBackupInfo(JNIEnv* env, + std::vector backup_infos) { + jclass jclazz = env->FindClass("java/util/ArrayList"); + jmethodID mid = rocksdb::ListJni::getArrayListConstructorMethodId( + env, jclazz); + jobject jbackup_info_handle_list = env->NewObject(jclazz, mid, + backup_infos.size()); + // insert in java list + for (std::vector::size_type i = 0; + i != backup_infos.size(); i++) { + rocksdb::BackupInfo backup_info = backup_infos[i]; + jobject obj = rocksdb::BackupInfoJni::construct0(env, + backup_info.backup_id, + backup_info.timestamp, + backup_info.size, + backup_info.number_files); + env->CallBooleanMethod(jbackup_info_handle_list, + rocksdb::ListJni::getListAddMethodId(env), obj); + } + return jbackup_info_handle_list; + } +}; + class JniUtil { public: /** diff --git a/java/rocksjni/restorejni.cc b/java/rocksjni/restorejni.cc index bd1734010..2e833d3be 100644 --- a/java/rocksjni/restorejni.cc +++ b/java/rocksjni/restorejni.cc @@ -131,6 +131,20 @@ void Java_org_rocksdb_RestoreBackupableDB_deleteBackup0(JNIEnv* env, } } +/* + * Class: org_rocksdb_RestoreBackupableDB + * Method: getBackupInfo + * Signature: (J)Ljava/util/List; + */ +jobject Java_org_rocksdb_RestoreBackupableDB_getBackupInfo( + JNIEnv* env, jobject jbdb, jlong jhandle) { + std::vector backup_infos; + reinterpret_cast(jhandle)-> + GetBackupInfo(&backup_infos); + return rocksdb::BackupInfoListJni::getBackupInfo(env, + backup_infos); +} + /* * Class: org_rocksdb_RestoreBackupableDB * Method: dispose