Allow storing metadata with backups for Java API (#4111)
Summary: Exposes BackupEngine::CreateNewBackupWithMetadata and BackupInfo metadata to the Java API. Full disclaimer, I'm not familiar with JNI stuff, so I might have forgotten something (hopefully no memory leaks!). I also tried to find contributing guidelines but didn't see any, but I hope the PR style is consistent with the rest of the code base. Pull Request resolved: https://github.com/facebook/rocksdb/pull/4111 Differential Revision: D8811180 Pulled By: ajkr fbshipit-source-id: e38b3e396c7574328c2a1a0e55acc8d092b6a569
This commit is contained in:
parent
1c912196de
commit
cfee7fb51a
@ -56,6 +56,35 @@ void Java_org_rocksdb_BackupEngine_createNewBackup(
|
|||||||
rocksdb::RocksDBExceptionJni::ThrowNew(env, status);
|
rocksdb::RocksDBExceptionJni::ThrowNew(env, status);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Class: org_rocksdb_BackupEngine
|
||||||
|
* Method: createNewBackupWithMetadata
|
||||||
|
* Signature: (JJLjava/lang/String;Z)V
|
||||||
|
*/
|
||||||
|
void Java_org_rocksdb_BackupEngine_createNewBackupWithMetadata(
|
||||||
|
JNIEnv* env, jobject /*jbe*/, jlong jbe_handle, jlong db_handle,
|
||||||
|
jstring japp_metadata, jboolean jflush_before_backup) {
|
||||||
|
auto* db = reinterpret_cast<rocksdb::DB*>(db_handle);
|
||||||
|
auto* backup_engine = reinterpret_cast<rocksdb::BackupEngine*>(jbe_handle);
|
||||||
|
|
||||||
|
jboolean has_exception = JNI_FALSE;
|
||||||
|
std::string app_metadata = rocksdb::JniUtil::copyStdString(
|
||||||
|
env, japp_metadata, &has_exception);
|
||||||
|
if (has_exception == JNI_TRUE) {
|
||||||
|
rocksdb::RocksDBExceptionJni::ThrowNew(env, "Could not copy jstring to std::string");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto status = backup_engine->CreateNewBackupWithMetadata(
|
||||||
|
db, app_metadata, static_cast<bool>(jflush_before_backup));
|
||||||
|
|
||||||
|
if (status.ok()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
rocksdb::RocksDBExceptionJni::ThrowNew(env, status);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Class: org_rocksdb_BackupEngine
|
* Class: org_rocksdb_BackupEngine
|
||||||
* Method: getBackupInfo
|
* Method: getBackupInfo
|
||||||
|
@ -2364,27 +2364,38 @@ class BackupInfoJni : public JavaClass {
|
|||||||
* @param timestamp timestamp of the backup
|
* @param timestamp timestamp of the backup
|
||||||
* @param size size of the backup
|
* @param size size of the backup
|
||||||
* @param number_files number of files related to the backup
|
* @param number_files number of files related to the backup
|
||||||
|
* @param app_metadata application specific metadata
|
||||||
*
|
*
|
||||||
* @return A reference to a Java BackupInfo object, or a nullptr if an
|
* @return A reference to a Java BackupInfo object, or a nullptr if an
|
||||||
* exception occurs
|
* exception occurs
|
||||||
*/
|
*/
|
||||||
static jobject construct0(JNIEnv* env, uint32_t backup_id, int64_t timestamp,
|
static jobject construct0(JNIEnv* env, uint32_t backup_id, int64_t timestamp,
|
||||||
uint64_t size, uint32_t number_files) {
|
uint64_t size, uint32_t number_files, const std::string& app_metadata) {
|
||||||
jclass jclazz = getJClass(env);
|
jclass jclazz = getJClass(env);
|
||||||
if(jclazz == nullptr) {
|
if(jclazz == nullptr) {
|
||||||
// exception occurred accessing class
|
// exception occurred accessing class
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
static jmethodID mid = env->GetMethodID(jclazz, "<init>", "(IJJI)V");
|
static jmethodID mid = env->GetMethodID(jclazz, "<init>", "(IJJILjava/lang/String;)V");
|
||||||
if(mid == nullptr) {
|
if(mid == nullptr) {
|
||||||
// exception occurred accessing method
|
// exception occurred accessing method
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
jstring japp_metadata = nullptr;
|
||||||
|
if (app_metadata != nullptr) {
|
||||||
|
japp_metadata = env->NewStringUTF(app_metadata.c_str());
|
||||||
|
if (japp_metadata == nullptr) {
|
||||||
|
// exception occurred creating java string
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
jobject jbackup_info =
|
jobject jbackup_info =
|
||||||
env->NewObject(jclazz, mid, backup_id, timestamp, size, number_files);
|
env->NewObject(jclazz, mid, backup_id, timestamp, size, number_files, japp_metadata);
|
||||||
if(env->ExceptionCheck()) {
|
if(env->ExceptionCheck()) {
|
||||||
|
env->DeleteLocalRef(japp_metadata);
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2441,7 +2452,8 @@ class BackupInfoListJni {
|
|||||||
backup_info.backup_id,
|
backup_info.backup_id,
|
||||||
backup_info.timestamp,
|
backup_info.timestamp,
|
||||||
backup_info.size,
|
backup_info.size,
|
||||||
backup_info.number_files);
|
backup_info.number_files,
|
||||||
|
backup_info.app_metadata);
|
||||||
if(env->ExceptionCheck()) {
|
if(env->ExceptionCheck()) {
|
||||||
// exception occurred constructing object
|
// exception occurred constructing object
|
||||||
if(obj != nullptr) {
|
if(obj != nullptr) {
|
||||||
|
@ -81,6 +81,36 @@ public class BackupEngine extends RocksObject implements AutoCloseable {
|
|||||||
createNewBackup(nativeHandle_, db.nativeHandle_, flushBeforeBackup);
|
createNewBackup(nativeHandle_, db.nativeHandle_, flushBeforeBackup);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Captures the state of the database in the latest backup along with
|
||||||
|
* application specific metadata.
|
||||||
|
*
|
||||||
|
* @param db The database to backup
|
||||||
|
* @param metadata Application metadata
|
||||||
|
* @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
|
||||||
|
*
|
||||||
|
* @throws RocksDBException thrown if a new backup could not be created
|
||||||
|
*/
|
||||||
|
public void createNewBackupWithMetadata(
|
||||||
|
final RocksDB db, final String metadata, final boolean flushBeforeBackup)
|
||||||
|
throws RocksDBException {
|
||||||
|
assert (isOwningHandle());
|
||||||
|
createNewBackupWithMetadata(nativeHandle_, db.nativeHandle_, metadata, flushBeforeBackup);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets information about the available
|
* Gets information about the available
|
||||||
* backups
|
* backups
|
||||||
@ -197,6 +227,9 @@ public class BackupEngine extends RocksObject implements AutoCloseable {
|
|||||||
private native void createNewBackup(final long handle, final long dbHandle,
|
private native void createNewBackup(final long handle, final long dbHandle,
|
||||||
final boolean flushBeforeBackup) throws RocksDBException;
|
final boolean flushBeforeBackup) throws RocksDBException;
|
||||||
|
|
||||||
|
private native void createNewBackupWithMetadata(final long handle, final long dbHandle,
|
||||||
|
final String metadata, final boolean flushBeforeBackup) throws RocksDBException;
|
||||||
|
|
||||||
private native List<BackupInfo> getBackupInfo(final long handle);
|
private native List<BackupInfo> getBackupInfo(final long handle);
|
||||||
|
|
||||||
private native int[] getCorruptedBackups(final long handle);
|
private native int[] getCorruptedBackups(final long handle);
|
||||||
|
@ -20,11 +20,12 @@ public class BackupInfo {
|
|||||||
* @param numberFiles number of files related to this backup.
|
* @param numberFiles number of files related to this backup.
|
||||||
*/
|
*/
|
||||||
BackupInfo(final int backupId, final long timestamp, final long size,
|
BackupInfo(final int backupId, final long timestamp, final long size,
|
||||||
final int numberFiles) {
|
final int numberFiles, final String app_metadata) {
|
||||||
backupId_ = backupId;
|
backupId_ = backupId;
|
||||||
timestamp_ = timestamp;
|
timestamp_ = timestamp;
|
||||||
size_ = size;
|
size_ = size;
|
||||||
numberFiles_ = numberFiles;
|
numberFiles_ = numberFiles;
|
||||||
|
app_metadata_ = app_metadata;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -59,8 +60,17 @@ public class BackupInfo {
|
|||||||
return numberFiles_;
|
return numberFiles_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @return the associated application metadata, or null
|
||||||
|
*/
|
||||||
|
public String appMetadata() {
|
||||||
|
return app_metadata_;
|
||||||
|
}
|
||||||
|
|
||||||
private int backupId_;
|
private int backupId_;
|
||||||
private long timestamp_;
|
private long timestamp_;
|
||||||
private long size_;
|
private long size_;
|
||||||
private int numberFiles_;
|
private int numberFiles_;
|
||||||
|
private String app_metadata_;
|
||||||
}
|
}
|
||||||
|
@ -11,6 +11,7 @@ import org.junit.Test;
|
|||||||
import org.junit.rules.TemporaryFolder;
|
import org.junit.rules.TemporaryFolder;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.concurrent.ThreadLocalRandom;
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
@ -205,6 +206,29 @@ public class BackupEngineTest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void backupDbWithMetadata()
|
||||||
|
throws RocksDBException {
|
||||||
|
// Open empty database.
|
||||||
|
try(final Options opt = new Options().setCreateIfMissing(true);
|
||||||
|
final RocksDB db = RocksDB.open(opt,
|
||||||
|
dbFolder.getRoot().getAbsolutePath())) {
|
||||||
|
|
||||||
|
// Fill database with some test values
|
||||||
|
prepareDatabase(db);
|
||||||
|
|
||||||
|
// Create two backups
|
||||||
|
try(final BackupableDBOptions bopt = new BackupableDBOptions(
|
||||||
|
backupFolder.getRoot().getAbsolutePath());
|
||||||
|
final BackupEngine be = BackupEngine.open(opt.getEnv(), bopt)) {
|
||||||
|
final String metadata = String.valueOf(ThreadLocalRandom.current().nextInt());
|
||||||
|
be.createNewBackupWithMetadata(db, metadata, true);
|
||||||
|
final List<BackupInfo> backupInfoList = verifyNumberOfValidBackups(be, 1);
|
||||||
|
assertThat(backupInfoList.get(0).appMetadata()).isEqualTo(metadata);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Verify backups.
|
* Verify backups.
|
||||||
*
|
*
|
||||||
|
Loading…
Reference in New Issue
Block a user