diff --git a/java/Makefile b/java/Makefile
index ea6b274f6..d490da4e5 100644
--- a/java/Makefile
+++ b/java/Makefile
@@ -46,6 +46,7 @@ test: java
java -ea -Djava.library.path=.:../ -cp "$(ROCKSDB_JAR):.:./*" org.rocksdb.test.OptionsTest
java -ea -Djava.library.path=.:../ -cp "$(ROCKSDB_JAR):.:./*" org.rocksdb.test.ReadOnlyTest
java -ea -Djava.library.path=.:../ -cp "$(ROCKSDB_JAR):.:./*" org.rocksdb.test.ReadOptionsTest
+ java -ea -Djava.library.path=.:../ -cp "$(ROCKSDB_JAR):.:./*" org.rocksdb.test.SnapshotTest
java -ea -Djava.library.path=.:../ -cp "$(ROCKSDB_JAR):.:./*" org.rocksdb.test.StatisticsCollectorTest
java -ea -Djava.library.path=.:../ -cp "$(ROCKSDB_JAR):.:./*" org.rocksdb.test.ComparatorOptionsTest
java -ea -Djava.library.path=.:../ -cp "$(ROCKSDB_JAR):.:./*" org.rocksdb.test.ComparatorTest
diff --git a/java/org/rocksdb/ReadOptions.java b/java/org/rocksdb/ReadOptions.java
index 3590a1a87..aa6977e98 100644
--- a/java/org/rocksdb/ReadOptions.java
+++ b/java/org/rocksdb/ReadOptions.java
@@ -80,6 +80,43 @@ public class ReadOptions extends RocksObject {
private native void setFillCache(
long handle, boolean fillCache);
+ /**
+ *
If "snapshot" is non-nullptr, read as of the supplied snapshot
+ * (which must belong to the DB that is being read and which must
+ * not have been released). If "snapshot" is nullptr, use an implicit
+ * snapshot of the state at the beginning of this read operation.
+ * Default: null
+ *
+ * @param snapshot {@link Snapshot} instance
+ * @return the reference to the current ReadOptions.
+ */
+ public ReadOptions setSnapshot(Snapshot snapshot) {
+ assert(isInitialized());
+ if (snapshot != null) {
+ setSnapshot(nativeHandle_, snapshot.nativeHandle_);
+ } else {
+ setSnapshot(nativeHandle_, 0l);
+ }
+ return this;
+ }
+ private native void setSnapshot(long handle, long snapshotHandle);
+
+ /**
+ * Returns the currently assigned Snapshot instance.
+ *
+ * @return the Snapshot assigned to this instance. If no Snapshot
+ * is assigned null.
+ */
+ public Snapshot snapshot() {
+ assert(isInitialized());
+ long snapshotHandle = snapshot(nativeHandle_);
+ if (snapshotHandle != 0) {
+ return new Snapshot(snapshotHandle);
+ }
+ return null;
+ }
+ private native long snapshot(long handle);
+
/**
* Specify to create a tailing iterator -- a special iterator that has a
* view of the complete database (i.e. it can also be used to read newly
diff --git a/java/org/rocksdb/RocksDB.java b/java/org/rocksdb/RocksDB.java
index d10c235dc..676f636d4 100644
--- a/java/org/rocksdb/RocksDB.java
+++ b/java/org/rocksdb/RocksDB.java
@@ -891,6 +891,38 @@ public class RocksDB extends RocksObject {
return new RocksIterator(iterator0(nativeHandle_));
}
+
+ /**
+ * Return a handle to the current DB state. Iterators created with
+ * this handle will all observe a stable snapshot of the current DB
+ * state. The caller must call ReleaseSnapshot(result) when the
+ * snapshot is no longer needed.
+ *
+ * nullptr will be returned if the DB fails to take a snapshot or does
+ * not support snapshot.
+ *
+ * @return Snapshot
+ */
+ public Snapshot getSnapshot() {
+ long snapshotHandle = getSnapshot(nativeHandle_);
+ if (snapshotHandle != 0) {
+ return new Snapshot(snapshotHandle);
+ }
+ return null;
+ }
+
+ /**
+ * Release a previously acquired snapshot. The caller must not
+ * use "snapshot" after this call.
+ *
+ * @param snapshot {@link Snapshot} instance
+ */
+ public void releaseSnapshot(final Snapshot snapshot) {
+ if (snapshot != null) {
+ releaseSnapshot(nativeHandle_, snapshot.nativeHandle_);
+ }
+ }
+
/**
* Return a heap-allocated iterator over the contents of the database.
* The result of newIterator() is initially invalid (caller must
@@ -1052,6 +1084,9 @@ public class RocksDB extends RocksObject {
protected native long iterator0(long handle, long cfHandle);
protected native long[] iterators(long handle,
List columnFamilyNames) throws RocksDBException;
+ protected native long getSnapshot(long nativeHandle);
+ protected native void releaseSnapshot(
+ long nativeHandle, long snapshotHandle);
private native void disposeInternal(long handle);
private native long createColumnFamily(long handle, String name) throws RocksDBException;
diff --git a/java/org/rocksdb/Snapshot.java b/java/org/rocksdb/Snapshot.java
new file mode 100644
index 000000000..5817a8b44
--- /dev/null
+++ b/java/org/rocksdb/Snapshot.java
@@ -0,0 +1,24 @@
+// 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;
+
+/**
+ * Snapshot of database
+ */
+public class Snapshot extends RocksObject {
+ Snapshot(long nativeHandle) {
+ super();
+ nativeHandle_ = nativeHandle;
+ }
+
+ /**
+ * Dont release C++ Snapshot pointer. The pointer
+ * to the snapshot is released by the database
+ * instance.
+ */
+ @Override protected void disposeInternal() {
+ }
+}
diff --git a/java/org/rocksdb/test/SnapshotTest.java b/java/org/rocksdb/test/SnapshotTest.java
new file mode 100644
index 000000000..67d0a83ef
--- /dev/null
+++ b/java/org/rocksdb/test/SnapshotTest.java
@@ -0,0 +1,87 @@
+// 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 java.util.ArrayList;
+import java.util.List;
+
+import org.rocksdb.ColumnFamilyHandle;
+import org.rocksdb.Options;
+import org.rocksdb.ReadOptions;
+import org.rocksdb.RocksDB;
+import org.rocksdb.RocksDBException;
+import org.rocksdb.Snapshot;
+import org.rocksdb.WriteBatch;
+import org.rocksdb.WriteOptions;
+
+
+public class SnapshotTest
+{
+ static final String DB_PATH = "/tmp/rocksdbjni_snapshot_test";
+ static {
+ RocksDB.loadLibrary();
+ }
+
+ public static void main(String[] args){
+ RocksDB db = null;
+ Options options = new Options();
+ options.setCreateIfMissing(true);
+ try {
+ db = RocksDB.open(options, DB_PATH);
+ db.put("key".getBytes(), "value".getBytes());
+ // Get new Snapshot of database
+ Snapshot snapshot = db.getSnapshot();
+ ReadOptions readOptions = new ReadOptions();
+ // set snapshot in ReadOptions
+ readOptions.setSnapshot(snapshot);
+ // retrieve key value pair
+ assert(new String(db.get("key".getBytes()))
+ .equals("value"));
+ // retrieve key value pair created before
+ // the snapshot was made
+ assert(new String(db.get(readOptions,
+ "key".getBytes())).equals("value"));
+ // add new key/value pair
+ db.put("newkey".getBytes(), "newvalue".getBytes());
+ // using no snapshot the latest db entries
+ // will be taken into account
+ assert(new String(db.get("newkey".getBytes()))
+ .equals("newvalue"));
+ // snapshopot was created before newkey
+ assert(db.get(readOptions, "newkey".getBytes())
+ == null);
+ // Retrieve snapshot from read options
+ Snapshot sameSnapshot = readOptions.snapshot();
+ readOptions.setSnapshot(sameSnapshot);
+ // results must be the same with new Snapshot
+ // instance using the same native pointer
+ assert(new String(db.get(readOptions,
+ "key".getBytes())).equals("value"));
+ // update key value pair to newvalue
+ db.put("key".getBytes(), "newvalue".getBytes());
+ // read with previously created snapshot will
+ // read previous version of key value pair
+ assert(new String(db.get(readOptions,
+ "key".getBytes())).equals("value"));
+ // read for newkey using the snapshot must be
+ // null
+ assert(db.get(readOptions, "newkey".getBytes())
+ == null);
+ // setting null to snapshot in ReadOptions leads
+ // to no Snapshot being used.
+ readOptions.setSnapshot(null);
+ assert(new String(db.get(readOptions,
+ "newkey".getBytes())).equals("newvalue"));
+ // release Snapshot
+ db.releaseSnapshot(snapshot);
+ // Close database
+ db.close();
+ }catch (RocksDBException e){
+ e.printStackTrace();
+ assert(false);
+ }
+ System.out.println("Passed SnapshotTest");
+ }
+}
diff --git a/java/rocksjni/options.cc b/java/rocksjni/options.cc
index ceb4ce031..3576a8c1e 100644
--- a/java/rocksjni/options.cc
+++ b/java/rocksjni/options.cc
@@ -1774,8 +1774,6 @@ void Java_org_rocksdb_ReadOptions_setTailing(
static_cast(jtailing);
}
-/////////////////////////////////////////////////////////////////////
-// rocksdb::ComparatorOptions
/*
* Class: org_rocksdb_ComparatorOptions
* Method: newComparatorOptions
@@ -1819,3 +1817,26 @@ void Java_org_rocksdb_ComparatorOptions_disposeInternal(
delete reinterpret_cast(jhandle);
rocksdb::ComparatorOptionsJni::setHandle(env, jobj, nullptr);
}
+
+/*
+ * Class: org_rocksdb_ReadOptions
+ * Method: setSnapshot
+ * Signature: (JJ)V
+ */
+void Java_org_rocksdb_ReadOptions_setSnapshot(
+ JNIEnv* env, jobject jobj, jlong jhandle, jlong jsnapshot) {
+ reinterpret_cast(jhandle)->snapshot =
+ reinterpret_cast(jsnapshot);
+}
+
+/*
+ * Class: org_rocksdb_ReadOptions
+ * Method: snapshot
+ * Signature: (J)J
+ */
+jlong Java_org_rocksdb_ReadOptions_snapshot(
+ JNIEnv* env, jobject jobj, jlong jhandle) {
+ auto& snapshot =
+ reinterpret_cast(jhandle)->snapshot;
+ return reinterpret_cast(snapshot);
+}
diff --git a/java/rocksjni/rocksjni.cc b/java/rocksjni/rocksjni.cc
index fa9a66a7d..5ba797d7d 100644
--- a/java/rocksjni/rocksjni.cc
+++ b/java/rocksjni/rocksjni.cc
@@ -1031,6 +1031,28 @@ void Java_org_rocksdb_RocksDB_dropColumnFamily(
}
}
+/*
+ * Method: getSnapshot
+ * Signature: (J)J
+ */
+jlong Java_org_rocksdb_RocksDB_getSnapshot(
+ JNIEnv* env, jobject jdb, jlong db_handle) {
+ auto db = reinterpret_cast(db_handle);
+ const rocksdb::Snapshot* snapshot = db->GetSnapshot();
+ return reinterpret_cast(snapshot);
+}
+
+/*
+ * Method: releaseSnapshot
+ * Signature: (JJ)V
+ */
+void Java_org_rocksdb_RocksDB_releaseSnapshot(
+ JNIEnv* env, jobject jdb, jlong db_handle, jlong snapshot_handle) {
+ auto db = reinterpret_cast(db_handle);
+ auto snapshot = reinterpret_cast(snapshot_handle);
+ db->ReleaseSnapshot(snapshot);
+}
+
/*
* Class: org_rocksdb_RocksDB
* Method: getProperty0