From fcc2dfd9f9fb07a4134d9ddb0961dc21707a678f Mon Sep 17 00:00:00 2001 From: fyrz Date: Fri, 21 Nov 2014 21:57:32 +0100 Subject: [PATCH] [RocksJava] Support for stored snapshots Summary: RocksDB supports two ways of saving snapshots. In memory and on disk. The later was added with this pull request to RocksJava. Test Plan: Reviewers: Subscribers: --- java/Makefile | 2 + java/org/rocksdb/Checkpoint.java | 65 ++++++++++++++ java/org/rocksdb/test/CheckPointTest.java | 101 ++++++++++++++++++++++ java/rocksjni/checkpoint.cc | 61 +++++++++++++ 4 files changed, 229 insertions(+) create mode 100644 java/org/rocksdb/Checkpoint.java create mode 100644 java/org/rocksdb/test/CheckPointTest.java create mode 100644 java/rocksjni/checkpoint.cc diff --git a/java/Makefile b/java/Makefile index 7a0edb3cf..1fbb40c85 100644 --- a/java/Makefile +++ b/java/Makefile @@ -4,6 +4,7 @@ NATIVE_JAVA_CLASSES = org.rocksdb.AbstractComparator\ org.rocksdb.BackupableDBOptions\ org.rocksdb.BlockBasedTableConfig\ org.rocksdb.BloomFilter\ + org.rocksdb.Checkpoint\ org.rocksdb.ColumnFamilyHandle\ org.rocksdb.ColumnFamilyOptions\ org.rocksdb.Comparator\ @@ -50,6 +51,7 @@ endif JAVA_TESTS = org.rocksdb.test.BackupableDBOptionsTest\ org.rocksdb.test.BackupableDBTest\ org.rocksdb.test.BlockBasedTableConfigTest\ + org.rocksdb.test.CheckpointTest\ org.rocksdb.test.ColumnFamilyOptionsTest\ org.rocksdb.test.ColumnFamilyTest\ org.rocksdb.test.ComparatorOptionsTest\ diff --git a/java/org/rocksdb/Checkpoint.java b/java/org/rocksdb/Checkpoint.java new file mode 100644 index 000000000..0830f2fb1 --- /dev/null +++ b/java/org/rocksdb/Checkpoint.java @@ -0,0 +1,65 @@ +// 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; + +/** + * Provides Checkpoint functionality. Checkpoints + * provide persistent snapshots of RocksDB databases. + */ +public class Checkpoint extends RocksObject { + + /** + * Creates a Checkpoint object to be used for creating open-able + * snapshots. + * + * @param db {@link RocksDB} instance. + * @return a Checkpoint instance. + */ + public static Checkpoint create(RocksDB db) { + if (db == null || !db.isInitialized()) { + throw new IllegalArgumentException( + "RocksDB instance needs to be initialized."); + } + Checkpoint checkpoint = new Checkpoint( + newCheckpoint(db.nativeHandle_)); + checkpoint.db_ = db; + return checkpoint; + } + + /** + *

Builds an open-able snapshot of RocksDB on the same disk, which + * accepts an output directory on the same disk, and under the directory + * (1) hard-linked SST files pointing to existing live SST files + * (2) a copied manifest files and other files

+ * + * @param checkpointPath path to the folder where the snapshot is going + * to be stored. + * @throws RocksDBException thrown if an error occurs within the native + * part of the library. + */ + public void createCheckpoint(String checkpointPath) + throws RocksDBException { + createCheckpoint(nativeHandle_, checkpointPath); + } + + @Override + protected void disposeInternal() { + disposeInternal(nativeHandle_); + } + + private Checkpoint(long handle) { + super(); + nativeHandle_ = handle; + } + + RocksDB db_; + + private static native long newCheckpoint(long dbHandle); + private native void disposeInternal(long handle); + + private native void createCheckpoint(long handle, String checkpointPath) + throws RocksDBException; +} diff --git a/java/org/rocksdb/test/CheckPointTest.java b/java/org/rocksdb/test/CheckPointTest.java new file mode 100644 index 000000000..63e996622 --- /dev/null +++ b/java/org/rocksdb/test/CheckPointTest.java @@ -0,0 +1,101 @@ +package org.rocksdb.test; + + +import org.junit.ClassRule; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; +import org.rocksdb.Checkpoint; +import org.rocksdb.Options; +import org.rocksdb.RocksDB; +import org.rocksdb.RocksDBException; + +import static org.assertj.core.api.Assertions.assertThat; + +public class CheckpointTest { + + @ClassRule + public static final RocksMemoryResource rocksMemoryResource = + new RocksMemoryResource(); + + @Rule + public TemporaryFolder dbFolder = new TemporaryFolder(); + + @Rule + public TemporaryFolder checkpointFolder = new TemporaryFolder(); + + @Test + public void checkPoint() throws RocksDBException { + RocksDB db = null; + Options options = null; + Checkpoint checkpoint = null; + try { + options = new Options(). + setCreateIfMissing(true); + db = RocksDB.open(options, + dbFolder.getRoot().getAbsolutePath()); + db.put("key".getBytes(), "value".getBytes()); + checkpoint = Checkpoint.create(db); + checkpoint.createCheckpoint(checkpointFolder. + getRoot().getAbsolutePath() + "/snapshot1"); + db.put("key2".getBytes(), "value2".getBytes()); + checkpoint.createCheckpoint(checkpointFolder. + getRoot().getAbsolutePath() + "/snapshot2"); + db.close(); + db = RocksDB.open(options, + checkpointFolder.getRoot().getAbsolutePath() + + "/snapshot1"); + assertThat(new String(db.get("key".getBytes()))). + isEqualTo("value"); + assertThat(db.get("key2".getBytes())).isNull(); + db.close(); + db = RocksDB.open(options, + checkpointFolder.getRoot().getAbsolutePath() + + "/snapshot2"); + assertThat(new String(db.get("key".getBytes()))). + isEqualTo("value"); + assertThat(new String(db.get("key2".getBytes()))). + isEqualTo("value2"); + } finally { + if (db != null) { + db.close(); + } + if (options != null) { + options.dispose(); + } + if (checkpoint != null) { + checkpoint.dispose(); + } + } + } + + @Test(expected = IllegalArgumentException.class) + public void failIfDbIsNull() { + Checkpoint.create(null); + } + + @Test(expected = IllegalArgumentException.class) + public void failIfDbNotInitialized() throws RocksDBException { + RocksDB db = RocksDB.open(dbFolder.getRoot().getAbsolutePath()); + db.dispose(); + Checkpoint.create(db); + } + + @Test(expected = RocksDBException.class) + public void failWithIllegalPath() throws RocksDBException { + RocksDB db = null; + Checkpoint checkpoint = null; + try { + db = RocksDB.open(dbFolder.getRoot().getAbsolutePath()); + checkpoint = Checkpoint.create(db); + checkpoint.createCheckpoint("/Z:///:\\C:\\TZ/-"); + } finally { + if (db != null) { + db.close(); + } + if (checkpoint != null) { + checkpoint.dispose(); + } + } + } +} diff --git a/java/rocksjni/checkpoint.cc b/java/rocksjni/checkpoint.cc new file mode 100644 index 000000000..841144d81 --- /dev/null +++ b/java/rocksjni/checkpoint.cc @@ -0,0 +1,61 @@ +// 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. +// +// This file implements the "bridge" between Java and C++ and enables +// calling c++ rocksdb::Checkpoint methods from Java side. + +#include +#include +#include +#include + +#include "include/org_rocksdb_Checkpoint.h" +#include "rocksjni/portal.h" +#include "rocksdb/db.h" +#include "rocksdb/utilities/checkpoint.h" +/* + * Class: org_rocksdb_Checkpoint + * Method: newCheckpoint + * Signature: (J)J + */ +jlong Java_org_rocksdb_Checkpoint_newCheckpoint(JNIEnv* env, + jclass jclazz, jlong jdb_handle) { + auto db = reinterpret_cast(jdb_handle); + rocksdb::Checkpoint* checkpoint; + rocksdb::Checkpoint::Create(db, &checkpoint); + return reinterpret_cast(checkpoint); +} + +/* + * Class: org_rocksdb_Checkpoint + * Method: dispose + * Signature: (J)V + */ +void Java_org_rocksdb_Checkpoint_disposeInternal(JNIEnv* env, jobject jobj, + jlong jhandle) { + auto checkpoint = reinterpret_cast(jhandle); + assert(checkpoint); + delete checkpoint; +} + +/* + * Class: org_rocksdb_Checkpoint + * Method: createCheckpoint + * Signature: (JLjava/lang/String;)V + */ +void Java_org_rocksdb_Checkpoint_createCheckpoint( + JNIEnv* env, jobject jobj, jlong jcheckpoint_handle, + jstring jcheckpoint_path) { + auto checkpoint = reinterpret_cast( + jcheckpoint_handle); + const char* checkpoint_path = env->GetStringUTFChars( + jcheckpoint_path, 0); + rocksdb::Status s = checkpoint->CreateCheckpoint( + checkpoint_path); + env->ReleaseStringUTFChars(jcheckpoint_path, checkpoint_path); + if (!s.ok()) { + rocksdb::RocksDBExceptionJni::ThrowNew(env, s); + } +} \ No newline at end of file