Expose the BackupEngine from the Java API
Summary: Merge pull request #665 by adamretter Exposes BackupEngine from C++ to the Java API. Previously only BackupableDB was available Test Plan: BackupEngineTest.java Reviewers: fyrz, igor, ankgup87, yhchiang Reviewed By: yhchiang Subscribers: dhruba Differential Revision: https://reviews.facebook.net/D42873
This commit is contained in:
parent
b0d12a135f
commit
ce21afd205
@ -1,5 +1,6 @@
|
||||
NATIVE_JAVA_CLASSES = org.rocksdb.AbstractComparator\
|
||||
org.rocksdb.AbstractSlice\
|
||||
org.rocksdb.BackupEngine\
|
||||
org.rocksdb.BackupableDB\
|
||||
org.rocksdb.BackupableDBOptions\
|
||||
org.rocksdb.BlockBasedTableConfig\
|
||||
@ -58,6 +59,7 @@ ROCKSDB_JAR = rocksdbjni-$(ROCKSDB_MAJOR).$(ROCKSDB_MINOR).$(ROCKSDB_PATCH)-osx.
|
||||
endif
|
||||
|
||||
JAVA_TESTS = org.rocksdb.BackupableDBOptionsTest\
|
||||
org.rocksdb.BackupEngineTest\
|
||||
org.rocksdb.BackupableDBTest\
|
||||
org.rocksdb.BlockBasedTableConfigTest\
|
||||
org.rocksdb.CheckPointTest\
|
||||
@ -74,11 +76,11 @@ JAVA_TESTS = org.rocksdb.BackupableDBOptionsTest\
|
||||
org.rocksdb.FlushTest\
|
||||
org.rocksdb.InfoLogLevelTest\
|
||||
org.rocksdb.KeyMayExistTest\
|
||||
org.rocksdb.LoggerTest\
|
||||
org.rocksdb.LoggerTest\
|
||||
org.rocksdb.MemTableTest\
|
||||
org.rocksdb.MergeTest\
|
||||
org.rocksdb.MixedOptionsTest\
|
||||
org.rocksdb.NativeLibraryLoaderTest\
|
||||
org.rocksdb.NativeLibraryLoaderTest\
|
||||
org.rocksdb.OptionsTest\
|
||||
org.rocksdb.PlainTableConfigTest\
|
||||
org.rocksdb.ReadOnlyTest\
|
||||
|
216
java/rocksjni/backupenginejni.cc
Normal file
216
java/rocksjni/backupenginejni.cc
Normal file
@ -0,0 +1,216 @@
|
||||
// Copyright (c) 2015, 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.
|
||||
//
|
||||
// This file implements the "bridge" between Java and C++ and enables
|
||||
// calling C++ rocksdb::BackupEngine methods from the Java side.
|
||||
|
||||
#include <jni.h>
|
||||
#include <vector>
|
||||
|
||||
#include "include/org_rocksdb_BackupEngine.h"
|
||||
#include "rocksdb/utilities/backupable_db.h"
|
||||
#include "rocksjni/portal.h"
|
||||
|
||||
/*
|
||||
* Class: org_rocksdb_BackupEngine
|
||||
* Method: open
|
||||
* Signature: (JJ)V
|
||||
*/
|
||||
void Java_org_rocksdb_BackupEngine_open(
|
||||
JNIEnv* env, jobject jbe, jlong env_handle,
|
||||
jlong backupable_db_options_handle) {
|
||||
auto* rocks_env = reinterpret_cast<rocksdb::Env*>(env_handle);
|
||||
auto* backupable_db_options =
|
||||
reinterpret_cast<rocksdb::BackupableDBOptions*>(
|
||||
backupable_db_options_handle);
|
||||
rocksdb::BackupEngine* backup_engine;
|
||||
auto status = rocksdb::BackupEngine::Open(rocks_env,
|
||||
*backupable_db_options, &backup_engine);
|
||||
|
||||
if (status.ok()) {
|
||||
rocksdb::BackupEngineJni::setHandle(env, jbe, backup_engine);
|
||||
return;
|
||||
}
|
||||
|
||||
rocksdb::RocksDBExceptionJni::ThrowNew(env, status);
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: org_rocksdb_BackupEngine
|
||||
* Method: createNewBackup
|
||||
* Signature: (JJZ)V
|
||||
*/
|
||||
void Java_org_rocksdb_BackupEngine_createNewBackup(
|
||||
JNIEnv* env, jobject jbe, jlong jbe_handle, jlong db_handle,
|
||||
jboolean jflush_before_backup) {
|
||||
auto* db = reinterpret_cast<rocksdb::DB*>(db_handle);
|
||||
auto* backup_engine = reinterpret_cast<rocksdb::BackupEngine*>(jbe_handle);
|
||||
auto status = backup_engine->CreateNewBackup(db,
|
||||
static_cast<bool>(jflush_before_backup));
|
||||
|
||||
if (status.ok()) {
|
||||
return;
|
||||
}
|
||||
|
||||
rocksdb::RocksDBExceptionJni::ThrowNew(env, status);
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: org_rocksdb_BackupEngine
|
||||
* Method: getBackupInfo
|
||||
* Signature: (J)Ljava/util/List;
|
||||
*/
|
||||
jobject Java_org_rocksdb_BackupEngine_getBackupInfo(
|
||||
JNIEnv* env, jobject jbe, jlong jbe_handle) {
|
||||
auto* backup_engine = reinterpret_cast<rocksdb::BackupEngine*>(jbe_handle);
|
||||
std::vector<rocksdb::BackupInfo> backup_infos;
|
||||
backup_engine->GetBackupInfo(&backup_infos);
|
||||
return rocksdb::BackupInfoListJni::getBackupInfo(env, backup_infos);
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: org_rocksdb_BackupEngine
|
||||
* Method: getCorruptedBackups
|
||||
* Signature: (J)[I
|
||||
*/
|
||||
jintArray Java_org_rocksdb_BackupEngine_getCorruptedBackups(
|
||||
JNIEnv* env, jobject jbe, jlong jbe_handle) {
|
||||
auto* backup_engine = reinterpret_cast<rocksdb::BackupEngine*>(jbe_handle);
|
||||
std::vector<rocksdb::BackupID> backup_ids;
|
||||
backup_engine->GetCorruptedBackups(&backup_ids);
|
||||
// store backupids in int array
|
||||
const std::vector<rocksdb::BackupID>::size_type
|
||||
kIdSize = backup_ids.size();
|
||||
int int_backup_ids[kIdSize];
|
||||
for (std::vector<rocksdb::BackupID>::size_type i = 0;
|
||||
i != kIdSize; i++) {
|
||||
int_backup_ids[i] = backup_ids[i];
|
||||
}
|
||||
// Store ints in java array
|
||||
jintArray ret_backup_ids;
|
||||
// Its ok to loose precision here (64->32)
|
||||
jsize ret_backup_ids_size = static_cast<jsize>(kIdSize);
|
||||
ret_backup_ids = env->NewIntArray(ret_backup_ids_size);
|
||||
env->SetIntArrayRegion(ret_backup_ids, 0, ret_backup_ids_size,
|
||||
int_backup_ids);
|
||||
return ret_backup_ids;
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: org_rocksdb_BackupEngine
|
||||
* Method: garbageCollect
|
||||
* Signature: (J)V
|
||||
*/
|
||||
void Java_org_rocksdb_BackupEngine_garbageCollect(
|
||||
JNIEnv* env, jobject jbe, jlong jbe_handle) {
|
||||
auto* backup_engine = reinterpret_cast<rocksdb::BackupEngine*>(jbe_handle);
|
||||
auto status = backup_engine->GarbageCollect();
|
||||
|
||||
if (status.ok()) {
|
||||
return;
|
||||
}
|
||||
|
||||
rocksdb::RocksDBExceptionJni::ThrowNew(env, status);
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: org_rocksdb_BackupEngine
|
||||
* Method: purgeOldBackups
|
||||
* Signature: (JI)V
|
||||
*/
|
||||
void Java_org_rocksdb_BackupEngine_purgeOldBackups(
|
||||
JNIEnv* env, jobject jbe, jlong jbe_handle, jint jnum_backups_to_keep) {
|
||||
auto* backup_engine = reinterpret_cast<rocksdb::BackupEngine*>(jbe_handle);
|
||||
auto status =
|
||||
backup_engine->
|
||||
PurgeOldBackups(static_cast<uint32_t>(jnum_backups_to_keep));
|
||||
|
||||
if (status.ok()) {
|
||||
return;
|
||||
}
|
||||
|
||||
rocksdb::RocksDBExceptionJni::ThrowNew(env, status);
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: org_rocksdb_BackupEngine
|
||||
* Method: deleteBackup
|
||||
* Signature: (JI)V
|
||||
*/
|
||||
void Java_org_rocksdb_BackupEngine_deleteBackup(
|
||||
JNIEnv* env, jobject jbe, jlong jbe_handle, jint jbackup_id) {
|
||||
auto* backup_engine = reinterpret_cast<rocksdb::BackupEngine*>(jbe_handle);
|
||||
auto status =
|
||||
backup_engine->DeleteBackup(static_cast<rocksdb::BackupID>(jbackup_id));
|
||||
|
||||
if (status.ok()) {
|
||||
return;
|
||||
}
|
||||
|
||||
rocksdb::RocksDBExceptionJni::ThrowNew(env, status);
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: org_rocksdb_BackupEngine
|
||||
* Method: restoreDbFromBackup
|
||||
* Signature: (JILjava/lang/String;Ljava/lang/String;J)V
|
||||
*/
|
||||
void Java_org_rocksdb_BackupEngine_restoreDbFromBackup(
|
||||
JNIEnv* env, jobject jbe, jlong jbe_handle, jint jbackup_id,
|
||||
jstring jdb_dir, jstring jwal_dir, jlong jrestore_options_handle) {
|
||||
auto* backup_engine = reinterpret_cast<rocksdb::BackupEngine*>(jbe_handle);
|
||||
const char* db_dir = env->GetStringUTFChars(jdb_dir, 0);
|
||||
const char* wal_dir = env->GetStringUTFChars(jwal_dir, 0);
|
||||
auto* restore_options =
|
||||
reinterpret_cast<rocksdb::RestoreOptions*>(jrestore_options_handle);
|
||||
auto status =
|
||||
backup_engine->RestoreDBFromBackup(
|
||||
static_cast<rocksdb::BackupID>(jbackup_id), db_dir, wal_dir,
|
||||
*restore_options);
|
||||
env->ReleaseStringUTFChars(jwal_dir, wal_dir);
|
||||
env->ReleaseStringUTFChars(jdb_dir, db_dir);
|
||||
|
||||
if (status.ok()) {
|
||||
return;
|
||||
}
|
||||
|
||||
rocksdb::RocksDBExceptionJni::ThrowNew(env, status);
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: org_rocksdb_BackupEngine
|
||||
* Method: restoreDbFromLatestBackup
|
||||
* Signature: (JLjava/lang/String;Ljava/lang/String;J)V
|
||||
*/
|
||||
void Java_org_rocksdb_BackupEngine_restoreDbFromLatestBackup(
|
||||
JNIEnv* env, jobject jbe, jlong jbe_handle, jstring jdb_dir,
|
||||
jstring jwal_dir, jlong jrestore_options_handle) {
|
||||
auto* backup_engine = reinterpret_cast<rocksdb::BackupEngine*>(jbe_handle);
|
||||
const char* db_dir = env->GetStringUTFChars(jdb_dir, 0);
|
||||
const char* wal_dir = env->GetStringUTFChars(jwal_dir, 0);
|
||||
auto* restore_options =
|
||||
reinterpret_cast<rocksdb::RestoreOptions*>(jrestore_options_handle);
|
||||
auto status =
|
||||
backup_engine->RestoreDBFromLatestBackup(db_dir, wal_dir,
|
||||
*restore_options);
|
||||
env->ReleaseStringUTFChars(jwal_dir, wal_dir);
|
||||
env->ReleaseStringUTFChars(jdb_dir, db_dir);
|
||||
|
||||
if (status.ok()) {
|
||||
return;
|
||||
}
|
||||
|
||||
rocksdb::RocksDBExceptionJni::ThrowNew(env, status);
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: org_rocksdb_BackupEngine
|
||||
* Method: disposeInternal
|
||||
* Signature: (J)V
|
||||
*/
|
||||
void Java_org_rocksdb_BackupEngine_disposeInternal(
|
||||
JNIEnv* env, jobject jbe, jlong jbe_handle) {
|
||||
delete reinterpret_cast<rocksdb::BackupEngine*>(jbe_handle);
|
||||
}
|
@ -296,6 +296,15 @@ class BackupableDBOptionsJni : public RocksDBNativeClass<
|
||||
}
|
||||
};
|
||||
|
||||
class BackupEngineJni : public RocksDBNativeClass<
|
||||
rocksdb::BackupEngine*, BackupEngineJni> {
|
||||
public:
|
||||
static jclass getJClass(JNIEnv* env) {
|
||||
return RocksDBNativeClass::getJClass(env,
|
||||
"org/rocksdb/BackupEngine");
|
||||
}
|
||||
};
|
||||
|
||||
// The portal class for org.rocksdb.RocksIterator
|
||||
class IteratorJni : public RocksDBNativeClass<
|
||||
rocksdb::Iterator*, IteratorJni> {
|
||||
|
222
java/src/main/java/org/rocksdb/BackupEngine.java
Normal file
222
java/src/main/java/org/rocksdb/BackupEngine.java
Normal file
@ -0,0 +1,222 @@
|
||||
// Copyright (c) 2015, 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;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* BackupEngine allows you to backup
|
||||
* and restore the database
|
||||
*
|
||||
* Be aware, that `new BackupEngine` takes time proportional to the amount
|
||||
* of backups. So if you have a slow filesystem to backup (like HDFS)
|
||||
* and you have a lot of backups then restoring can take some time.
|
||||
* That's why we recommend to limit the number of backups.
|
||||
* Also we recommend to keep BackupEngine alive and not to recreate it every
|
||||
* time you need to do a backup.
|
||||
*/
|
||||
public class BackupEngine extends RocksObject implements AutoCloseable {
|
||||
|
||||
protected BackupEngine() {
|
||||
super();
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens a new Backup Engine
|
||||
*
|
||||
* @param env The environment that the backup engine should operate within
|
||||
* @param options Any options for the backup engine
|
||||
*
|
||||
* @return A new BackupEngine instance
|
||||
*/
|
||||
public static BackupEngine open(final Env env,
|
||||
final BackupableDBOptions options) throws RocksDBException {
|
||||
final BackupEngine be = new BackupEngine();
|
||||
be.open(env.nativeHandle_, options.nativeHandle_);
|
||||
return be;
|
||||
}
|
||||
|
||||
/**
|
||||
* Captures the state of the database in the latest backup
|
||||
*
|
||||
* Just a convenience for {@link #createNewBackup(RocksDB, boolean)} with
|
||||
* the flushBeforeBackup parameter set to false
|
||||
*
|
||||
* @param db The database to backup
|
||||
*
|
||||
* Note - This method is not thread safe
|
||||
*/
|
||||
public void createNewBackup(final RocksDB db) throws RocksDBException {
|
||||
createNewBackup(db, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Captures the state of the database in the latest backup
|
||||
*
|
||||
* @param db The database to backup
|
||||
* @param flushBeforeBackup When true, the Backup Engine will first issue a
|
||||
* memtable flush and only then copy the DB files to
|
||||
* the backup directory. Doing so will prevent log
|
||||
* files from being copied to the backup directory
|
||||
* (since flush will delete them).
|
||||
* When false, the Backup Engine will not issue a
|
||||
* flush before starting the backup. In that case,
|
||||
* the backup will also include log files
|
||||
* corresponding to live memtables. The backup will
|
||||
* always be consistent with the current state of the
|
||||
* database regardless of the flushBeforeBackup
|
||||
* parameter.
|
||||
*
|
||||
* Note - This method is not thread safe
|
||||
*/
|
||||
public void createNewBackup(
|
||||
final RocksDB db, final boolean flushBeforeBackup)
|
||||
throws RocksDBException {
|
||||
assert (isInitialized());
|
||||
createNewBackup(nativeHandle_, db.nativeHandle_, flushBeforeBackup);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets information about the available
|
||||
* backups
|
||||
*
|
||||
* @return A list of information about each available backup
|
||||
*/
|
||||
public List<BackupInfo> getBackupInfo() {
|
||||
assert (isInitialized());
|
||||
return getBackupInfo(nativeHandle_);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Returns a list of corrupted backup ids. If there
|
||||
* is no corrupted backup the method will return an
|
||||
* empty list.</p>
|
||||
*
|
||||
* @return array of backup ids as int ids.
|
||||
*/
|
||||
public int[] getCorruptedBackups() {
|
||||
assert(isInitialized());
|
||||
return getCorruptedBackups(nativeHandle_);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>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.</p>
|
||||
*
|
||||
* @throws RocksDBException thrown if error happens in underlying
|
||||
* native library.
|
||||
*/
|
||||
public void garbageCollect() throws RocksDBException {
|
||||
assert(isInitialized());
|
||||
garbageCollect(nativeHandle_);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes old backups, keeping just the latest numBackupsToKeep
|
||||
*
|
||||
* @param numBackupsToKeep The latest n backups to keep
|
||||
*/
|
||||
public void purgeOldBackups(
|
||||
final int numBackupsToKeep) throws RocksDBException {
|
||||
assert (isInitialized());
|
||||
purgeOldBackups(nativeHandle_, numBackupsToKeep);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes a backup
|
||||
*
|
||||
* @param backupId The id of the backup to delete
|
||||
*/
|
||||
public void deleteBackup(final int backupId) throws RocksDBException {
|
||||
assert (isInitialized());
|
||||
deleteBackup(nativeHandle_, backupId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Restore the database from a backup
|
||||
*
|
||||
* IMPORTANT: if options.share_table_files == true and you restore the 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 The id of the backup to restore
|
||||
* @param dbDir The directory to restore the backup to, i.e. where your
|
||||
* database is
|
||||
* @param walDir The location of the log files for your database,
|
||||
* often the same as dbDir
|
||||
* @param restoreOptions Options for controlling the restore
|
||||
*/
|
||||
public void restoreDbFromBackup(
|
||||
final int backupId, final String dbDir, final String walDir,
|
||||
final RestoreOptions restoreOptions) throws RocksDBException {
|
||||
assert (isInitialized());
|
||||
restoreDbFromBackup(nativeHandle_, backupId, dbDir, walDir,
|
||||
restoreOptions.nativeHandle_);
|
||||
}
|
||||
|
||||
/**
|
||||
* Restore the database from the latest backup
|
||||
*
|
||||
* @param dbDir The directory to restore the backup to, i.e. where your database is
|
||||
* @param walDir The location of the log files for your database, often the same as dbDir
|
||||
* @param restoreOptions Options for controlling the restore
|
||||
*/
|
||||
public void restoreDbFromLatestBackup(
|
||||
final String dbDir, final String walDir,
|
||||
final RestoreOptions restoreOptions) throws RocksDBException {
|
||||
assert (isInitialized());
|
||||
restoreDbFromLatestBackup(nativeHandle_, dbDir, walDir,
|
||||
restoreOptions.nativeHandle_);
|
||||
}
|
||||
|
||||
/**
|
||||
* Close the Backup Engine
|
||||
*/
|
||||
@Override
|
||||
public void close() throws RocksDBException {
|
||||
dispose();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void disposeInternal() {
|
||||
assert (isInitialized());
|
||||
disposeInternal(nativeHandle_);
|
||||
}
|
||||
|
||||
private native void open(final long env, final long backupableDbOptions)
|
||||
throws RocksDBException;
|
||||
|
||||
private native void createNewBackup(final long handle, final long dbHandle,
|
||||
final boolean flushBeforeBackup) throws RocksDBException;
|
||||
|
||||
private native List<BackupInfo> getBackupInfo(final long handle);
|
||||
|
||||
private native int[] getCorruptedBackups(final long handle);
|
||||
|
||||
private native void garbageCollect(final long handle) throws RocksDBException;
|
||||
|
||||
private native void purgeOldBackups(final long handle,
|
||||
final int numBackupsToKeep) throws RocksDBException;
|
||||
|
||||
private native void deleteBackup(final long handle, final int backupId)
|
||||
throws RocksDBException;
|
||||
|
||||
private native void restoreDbFromBackup(final long handle, final int backupId,
|
||||
final String dbDir, final String walDir, final long restoreOptionsHandle)
|
||||
throws RocksDBException;
|
||||
|
||||
private native void restoreDbFromLatestBackup(final long handle,
|
||||
final String dbDir, final String walDir, final long restoreOptionsHandle)
|
||||
throws RocksDBException;
|
||||
|
||||
private native void disposeInternal(final long handle);
|
||||
}
|
305
java/src/test/java/org/rocksdb/BackupEngineTest.java
Normal file
305
java/src/test/java/org/rocksdb/BackupEngineTest.java
Normal file
@ -0,0 +1,305 @@
|
||||
// Copyright (c) 2015, 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;
|
||||
|
||||
import org.junit.ClassRule;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.TemporaryFolder;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
public class BackupEngineTest {
|
||||
|
||||
@ClassRule
|
||||
public static final RocksMemoryResource rocksMemoryResource =
|
||||
new RocksMemoryResource();
|
||||
|
||||
@Rule
|
||||
public TemporaryFolder dbFolder = new TemporaryFolder();
|
||||
|
||||
@Rule
|
||||
public TemporaryFolder backupFolder = new TemporaryFolder();
|
||||
|
||||
@Test
|
||||
public void backupDb() throws RocksDBException {
|
||||
Options opt = null;
|
||||
RocksDB db = null;
|
||||
try {
|
||||
opt = new Options().setCreateIfMissing(true);
|
||||
// Open empty database.
|
||||
db = RocksDB.open(opt,
|
||||
dbFolder.getRoot().getAbsolutePath());
|
||||
// Fill database with some test values
|
||||
prepareDatabase(db);
|
||||
// Create two backups
|
||||
BackupableDBOptions bopt = null;
|
||||
try {
|
||||
bopt = new BackupableDBOptions(
|
||||
backupFolder.getRoot().getAbsolutePath());
|
||||
try(final BackupEngine be = BackupEngine.open(opt.getEnv(), bopt)) {
|
||||
be.createNewBackup(db, false);
|
||||
be.createNewBackup(db, true);
|
||||
verifyNumberOfValidBackups(be, 2);
|
||||
}
|
||||
} finally {
|
||||
if(bopt != null) {
|
||||
bopt.dispose();
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
if (db != null) {
|
||||
db.close();
|
||||
}
|
||||
if (opt != null) {
|
||||
opt.dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void deleteBackup() throws RocksDBException {
|
||||
Options opt = null;
|
||||
RocksDB db = null;
|
||||
try {
|
||||
opt = new Options().setCreateIfMissing(true);
|
||||
// Open empty database.
|
||||
db = RocksDB.open(opt,
|
||||
dbFolder.getRoot().getAbsolutePath());
|
||||
// Fill database with some test values
|
||||
prepareDatabase(db);
|
||||
// Create two backups
|
||||
BackupableDBOptions bopt = null;
|
||||
try {
|
||||
bopt = new BackupableDBOptions(
|
||||
backupFolder.getRoot().getAbsolutePath());
|
||||
try(final BackupEngine be = BackupEngine.open(opt.getEnv(), bopt)) {
|
||||
be.createNewBackup(db, false);
|
||||
be.createNewBackup(db, true);
|
||||
final List<BackupInfo> backupInfo =
|
||||
verifyNumberOfValidBackups(be, 2);
|
||||
// Delete the first backup
|
||||
be.deleteBackup(backupInfo.get(0).backupId());
|
||||
final List<BackupInfo> newBackupInfo =
|
||||
verifyNumberOfValidBackups(be, 1);
|
||||
|
||||
// The second backup must remain.
|
||||
assertThat(newBackupInfo.get(0).backupId()).
|
||||
isEqualTo(backupInfo.get(1).backupId());
|
||||
}
|
||||
} finally {
|
||||
if(bopt != null) {
|
||||
bopt.dispose();
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
if (db != null) {
|
||||
db.close();
|
||||
}
|
||||
if (opt != null) {
|
||||
opt.dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void purgeOldBackups() throws RocksDBException {
|
||||
Options opt = null;
|
||||
RocksDB db = null;
|
||||
try {
|
||||
opt = new Options().setCreateIfMissing(true);
|
||||
// Open empty database.
|
||||
db = RocksDB.open(opt,
|
||||
dbFolder.getRoot().getAbsolutePath());
|
||||
// Fill database with some test values
|
||||
prepareDatabase(db);
|
||||
// Create four backups
|
||||
BackupableDBOptions bopt = null;
|
||||
try {
|
||||
bopt = new BackupableDBOptions(
|
||||
backupFolder.getRoot().getAbsolutePath());
|
||||
try(final BackupEngine be = BackupEngine.open(opt.getEnv(), bopt)) {
|
||||
be.createNewBackup(db, false);
|
||||
be.createNewBackup(db, true);
|
||||
be.createNewBackup(db, true);
|
||||
be.createNewBackup(db, true);
|
||||
final List<BackupInfo> backupInfo =
|
||||
verifyNumberOfValidBackups(be, 4);
|
||||
// Delete everything except the latest backup
|
||||
be.purgeOldBackups(1);
|
||||
final List<BackupInfo> newBackupInfo =
|
||||
verifyNumberOfValidBackups(be, 1);
|
||||
// The latest backup must remain.
|
||||
assertThat(newBackupInfo.get(0).backupId()).
|
||||
isEqualTo(backupInfo.get(3).backupId());
|
||||
}
|
||||
} finally {
|
||||
if(bopt != null) {
|
||||
bopt.dispose();
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
if (db != null) {
|
||||
db.close();
|
||||
}
|
||||
if (opt != null) {
|
||||
opt.dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void restoreLatestBackup()
|
||||
throws RocksDBException {
|
||||
Options opt = null;
|
||||
RocksDB db = null;
|
||||
try {
|
||||
opt = new Options().setCreateIfMissing(true);
|
||||
// Open empty database.
|
||||
db = RocksDB.open(opt,
|
||||
dbFolder.getRoot().getAbsolutePath());
|
||||
// Fill database with some test values
|
||||
prepareDatabase(db);
|
||||
BackupableDBOptions bopt = null;
|
||||
try {
|
||||
bopt = new BackupableDBOptions(
|
||||
backupFolder.getRoot().getAbsolutePath());
|
||||
try (final BackupEngine be = BackupEngine.open(opt.getEnv(), bopt)) {
|
||||
be.createNewBackup(db, true);
|
||||
verifyNumberOfValidBackups(be, 1);
|
||||
db.put("key1".getBytes(), "valueV2".getBytes());
|
||||
db.put("key2".getBytes(), "valueV2".getBytes());
|
||||
be.createNewBackup(db, true);
|
||||
verifyNumberOfValidBackups(be, 2);
|
||||
db.put("key1".getBytes(), "valueV3".getBytes());
|
||||
db.put("key2".getBytes(), "valueV3".getBytes());
|
||||
assertThat(new String(db.get("key1".getBytes()))).endsWith("V3");
|
||||
assertThat(new String(db.get("key2".getBytes()))).endsWith("V3");
|
||||
|
||||
db.close();
|
||||
|
||||
verifyNumberOfValidBackups(be, 2);
|
||||
// restore db from latest backup
|
||||
be.restoreDbFromLatestBackup(dbFolder.getRoot().getAbsolutePath(),
|
||||
dbFolder.getRoot().getAbsolutePath(),
|
||||
new RestoreOptions(false));
|
||||
// Open database again.
|
||||
db = RocksDB.open(opt,
|
||||
dbFolder.getRoot().getAbsolutePath());
|
||||
// Values must have suffix V2 because of restoring latest backup.
|
||||
assertThat(new String(db.get("key1".getBytes()))).endsWith("V2");
|
||||
assertThat(new String(db.get("key2".getBytes()))).endsWith("V2");
|
||||
}
|
||||
} finally {
|
||||
if(bopt != null) {
|
||||
bopt.dispose();
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
if (db != null) {
|
||||
db.close();
|
||||
}
|
||||
if (opt != null) {
|
||||
opt.dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void restoreFromBackup()
|
||||
throws RocksDBException {
|
||||
Options opt = null;
|
||||
RocksDB db = null;
|
||||
try {
|
||||
opt = new Options().setCreateIfMissing(true);
|
||||
// Open empty database.
|
||||
db = RocksDB.open(opt,
|
||||
dbFolder.getRoot().getAbsolutePath());
|
||||
// Fill database with some test values
|
||||
prepareDatabase(db);
|
||||
BackupableDBOptions bopt = null;
|
||||
try {
|
||||
bopt = new BackupableDBOptions(
|
||||
backupFolder.getRoot().getAbsolutePath());
|
||||
try (final BackupEngine be = BackupEngine.open(opt.getEnv(), bopt)) {
|
||||
be.createNewBackup(db, true);
|
||||
verifyNumberOfValidBackups(be, 1);
|
||||
db.put("key1".getBytes(), "valueV2".getBytes());
|
||||
db.put("key2".getBytes(), "valueV2".getBytes());
|
||||
be.createNewBackup(db, true);
|
||||
verifyNumberOfValidBackups(be, 2);
|
||||
db.put("key1".getBytes(), "valueV3".getBytes());
|
||||
db.put("key2".getBytes(), "valueV3".getBytes());
|
||||
assertThat(new String(db.get("key1".getBytes()))).endsWith("V3");
|
||||
assertThat(new String(db.get("key2".getBytes()))).endsWith("V3");
|
||||
|
||||
//close the database
|
||||
db.close();
|
||||
|
||||
//restore the backup
|
||||
List<BackupInfo> backupInfo = verifyNumberOfValidBackups(be, 2);
|
||||
// restore db from first backup
|
||||
be.restoreDbFromBackup(backupInfo.get(0).backupId(),
|
||||
dbFolder.getRoot().getAbsolutePath(),
|
||||
dbFolder.getRoot().getAbsolutePath(),
|
||||
new RestoreOptions(false));
|
||||
// Open database again.
|
||||
db = RocksDB.open(opt,
|
||||
dbFolder.getRoot().getAbsolutePath());
|
||||
// Values must have suffix V2 because of restoring latest backup.
|
||||
assertThat(new String(db.get("key1".getBytes()))).endsWith("V1");
|
||||
assertThat(new String(db.get("key2".getBytes()))).endsWith("V1");
|
||||
}
|
||||
} finally {
|
||||
if(bopt != null) {
|
||||
bopt.dispose();
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
if (db != null) {
|
||||
db.close();
|
||||
}
|
||||
if (opt != null) {
|
||||
opt.dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify backups.
|
||||
*
|
||||
* @param be {@link BackupEngine} instance.
|
||||
* @param expectedNumberOfBackups numerical value
|
||||
* @throws RocksDBException thrown if an error occurs within the native
|
||||
* part of the library.
|
||||
*/
|
||||
private List<BackupInfo> verifyNumberOfValidBackups(final BackupEngine be,
|
||||
final int expectedNumberOfBackups) throws RocksDBException {
|
||||
// Verify that backups exist
|
||||
assertThat(be.getCorruptedBackups().length).
|
||||
isEqualTo(0);
|
||||
be.garbageCollect();
|
||||
final List<BackupInfo> backupInfo = be.getBackupInfo();
|
||||
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(final RocksDB db)
|
||||
throws RocksDBException {
|
||||
db.put("key1".getBytes(), "valueV1".getBytes());
|
||||
db.put("key2".getBytes(), "valueV1".getBytes());
|
||||
}
|
||||
}
|
7
src.mk
7
src.mk
@ -32,7 +32,7 @@ LIB_SOURCES = \
|
||||
db/merge_helper.cc \
|
||||
db/merge_operator.cc \
|
||||
db/repair.cc \
|
||||
db/slice.cc \
|
||||
db/slice.cc \
|
||||
db/table_cache.cc \
|
||||
db/table_properties_collector.cc \
|
||||
db/transaction_log_impl.cc \
|
||||
@ -88,7 +88,7 @@ LIB_SOURCES = \
|
||||
util/env_hdfs.cc \
|
||||
util/env_posix.cc \
|
||||
util/file_util.cc \
|
||||
util/file_reader_writer.cc \
|
||||
util/file_reader_writer.cc \
|
||||
util/filter_policy.cc \
|
||||
util/hash.cc \
|
||||
util/hash_cuckoo_rep.cc \
|
||||
@ -166,7 +166,7 @@ TEST_BENCH_SOURCES = \
|
||||
db/db_iter_test.cc \
|
||||
db/db_test.cc \
|
||||
db/db_compaction_filter_test.cc \
|
||||
db/db_compaction_test.cc \
|
||||
db/db_compaction_test.cc \
|
||||
db/db_dynamic_level_test.cc \
|
||||
db/db_inplace_update_test.cc \
|
||||
db/db_log_iter_test.cc \
|
||||
@ -247,6 +247,7 @@ TEST_BENCH_SOURCES = \
|
||||
util/thread_local_test.cc
|
||||
|
||||
JNI_NATIVE_SOURCES = \
|
||||
java/rocksjni/backupenginejni.cc \
|
||||
java/rocksjni/backupablejni.cc \
|
||||
java/rocksjni/checkpoint.cc \
|
||||
java/rocksjni/columnfamilyhandle.cc \
|
||||
|
Loading…
x
Reference in New Issue
Block a user