From bef7821f07025c65f1bb98afeb8034c2272d4dc0 Mon Sep 17 00:00:00 2001 From: fyrz Date: Fri, 16 Jan 2015 23:35:21 +0100 Subject: [PATCH] [RocksJava] ReadOptions support in Iterators The methods: - newIterator - iterators support now also ReadOptions. That allows a user of the Java API to retrieve RocksIterator instances on a snapshot. --- java/org/rocksdb/RocksDB.java | 97 +++++++++++++++--- java/org/rocksdb/test/SnapshotTest.java | 129 +++++++++++++++++++++++- java/rocksjni/rocksjni.cc | 79 +++++++++++---- 3 files changed, 268 insertions(+), 37 deletions(-) diff --git a/java/org/rocksdb/RocksDB.java b/java/org/rocksdb/RocksDB.java index 0898825322..ac02860e81 100644 --- a/java/org/rocksdb/RocksDB.java +++ b/java/org/rocksdb/RocksDB.java @@ -1107,21 +1107,40 @@ public class RocksDB extends RocksObject { } /** - * Return a heap-allocated iterator over the contents of the database. - * The result of newIterator() is initially invalid (caller must - * call one of the Seek methods on the iterator before using it). + *

Return a heap-allocated iterator over the contents of the + * database. The result of newIterator() is initially invalid + * (caller must call one of the Seek methods on the iterator + * before using it).

* - * Caller should close the iterator when it is no longer needed. + *

Caller should close the iterator when it is no longer needed. * The returned iterator should be closed before this db is closed. + *

* * @return instance of iterator object. */ public RocksIterator newIterator() { - return new RocksIterator(this, iterator0(nativeHandle_)); + return new RocksIterator(this, iterator(nativeHandle_)); } - /** + *

Return a heap-allocated iterator over the contents of the + * database. The result of newIterator() is initially invalid + * (caller must call one of the Seek methods on the iterator + * before using it).

+ * + *

Caller should close the iterator when it is no longer needed. + * The returned iterator should be closed before this db is closed. + *

+ * + * @param readOptions {@link ReadOptions} instance. + * @return instance of iterator object. + */ + public RocksIterator newIterator(ReadOptions readOptions) { + return new RocksIterator(this, iterator(nativeHandle_, + readOptions.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 @@ -1153,22 +1172,45 @@ public class RocksDB extends RocksObject { } /** - * Return a heap-allocated iterator over the contents of the database. - * The result of newIterator() is initially invalid (caller must - * call one of the Seek methods on the iterator before using it). + *

Return a heap-allocated iterator over the contents of the + * database. The result of newIterator() is initially invalid + * (caller must call one of the Seek methods on the iterator + * before using it).

* - * Caller should close the iterator when it is no longer needed. + *

Caller should close the iterator when it is no longer needed. * The returned iterator should be closed before this db is closed. + *

* * @param columnFamilyHandle {@link org.rocksdb.ColumnFamilyHandle} * instance * @return instance of iterator object. */ public RocksIterator newIterator(ColumnFamilyHandle columnFamilyHandle) { - return new RocksIterator(this, iterator0(nativeHandle_, + return new RocksIterator(this, iteratorCF(nativeHandle_, columnFamilyHandle.nativeHandle_)); } + /** + *

Return a heap-allocated iterator over the contents of the + * database. The result of newIterator() is initially invalid + * (caller must call one of the Seek methods on the iterator + * before using it).

+ * + *

Caller should close the iterator when it is no longer needed. + * The returned iterator should be closed before this db is closed. + *

+ * + * @param columnFamilyHandle {@link org.rocksdb.ColumnFamilyHandle} + * instance + * @param readOptions {@link ReadOptions} instance. + * @return instance of iterator object. + */ + public RocksIterator newIterator(ColumnFamilyHandle columnFamilyHandle, + ReadOptions readOptions) { + return new RocksIterator(this, iteratorCF(nativeHandle_, + columnFamilyHandle.nativeHandle_, readOptions.nativeHandle_)); + } + /** * Returns iterators from a consistent database state across multiple * column families. Iterators are heap allocated and need to be deleted @@ -1184,10 +1226,31 @@ public class RocksDB extends RocksObject { */ public List newIterators( List columnFamilyHandleList) throws RocksDBException { + return newIterators(columnFamilyHandleList, new ReadOptions()); + } + + /** + * Returns iterators from a consistent database state across multiple + * column families. Iterators are heap allocated and need to be deleted + * before the db is deleted + * + * @param columnFamilyHandleList {@link java.util.List} containing + * {@link org.rocksdb.ColumnFamilyHandle} instances. + * @param readOptions {@link ReadOptions} instance. + * @return {@link java.util.List} containing {@link org.rocksdb.RocksIterator} + * instances + * + * @throws RocksDBException thrown if error happens in underlying + * native library. + */ + public List newIterators( + List columnFamilyHandleList, + ReadOptions readOptions) throws RocksDBException { List iterators = new ArrayList<>(columnFamilyHandleList.size()); - long[] iteratorRefs = iterators(nativeHandle_, columnFamilyHandleList); + long[] iteratorRefs = iterators(nativeHandle_, columnFamilyHandleList, + readOptions.nativeHandle_); for (int i=0; i columnFamilyNames) throws RocksDBException; + List columnFamilyNames, long readOptHandle) + throws RocksDBException; protected native long getSnapshot(long nativeHandle); protected native void releaseSnapshot( long nativeHandle, long snapshotHandle); diff --git a/java/org/rocksdb/test/SnapshotTest.java b/java/org/rocksdb/test/SnapshotTest.java index 1b45c517ed..b6dd2a3604 100644 --- a/java/org/rocksdb/test/SnapshotTest.java +++ b/java/org/rocksdb/test/SnapshotTest.java @@ -8,11 +8,7 @@ import org.junit.ClassRule; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TemporaryFolder; -import org.rocksdb.Options; -import org.rocksdb.ReadOptions; -import org.rocksdb.RocksDB; -import org.rocksdb.RocksDBException; -import org.rocksdb.Snapshot; +import org.rocksdb.*; import static org.assertj.core.api.Assertions.assertThat; @@ -94,4 +90,127 @@ public class SnapshotTest { } } } + + @Test + public void iteratorWithSnapshot() throws RocksDBException { + RocksDB db = null; + Options options = null; + ReadOptions readOptions = null; + RocksIterator iterator = null; + RocksIterator snapshotIterator = null; + try { + + options = new Options(); + options.setCreateIfMissing(true); + + db = RocksDB.open(options, dbFolder.getRoot().getAbsolutePath()); + db.put("key".getBytes(), "value".getBytes()); + // Get new Snapshot of database + Snapshot snapshot = db.getSnapshot(); + readOptions = new ReadOptions(); + // set snapshot in ReadOptions + readOptions.setSnapshot(snapshot); + db.put("key2".getBytes(), "value2".getBytes()); + + // iterate over current state of db + iterator = db.newIterator(); + iterator.seekToFirst(); + assertThat(iterator.isValid()).isTrue(); + assertThat(iterator.key()).isEqualTo("key".getBytes()); + iterator.next(); + assertThat(iterator.isValid()).isTrue(); + assertThat(iterator.key()).isEqualTo("key2".getBytes()); + iterator.next(); + assertThat(iterator.isValid()).isFalse(); + + // iterate using a snapshot + snapshotIterator = db.newIterator(readOptions); + snapshotIterator.seekToFirst(); + assertThat(snapshotIterator.isValid()).isTrue(); + assertThat(snapshotIterator.key()).isEqualTo("key".getBytes()); + snapshotIterator.next(); + assertThat(snapshotIterator.isValid()).isFalse(); + + // release Snapshot + db.releaseSnapshot(snapshot); + } finally { + if (iterator != null) { + iterator.dispose(); + } + if (snapshotIterator != null) { + snapshotIterator.dispose(); + } + if (db != null) { + db.close(); + } + if (options != null) { + options.dispose(); + } + if (readOptions != null) { + readOptions.dispose(); + } + } + } + + @Test + public void iteratorWithSnapshotOnColumnFamily() throws RocksDBException { + RocksDB db = null; + Options options = null; + ReadOptions readOptions = null; + RocksIterator iterator = null; + RocksIterator snapshotIterator = null; + try { + + options = new Options(); + options.setCreateIfMissing(true); + + db = RocksDB.open(options, dbFolder.getRoot().getAbsolutePath()); + db.put("key".getBytes(), "value".getBytes()); + // Get new Snapshot of database + Snapshot snapshot = db.getSnapshot(); + readOptions = new ReadOptions(); + // set snapshot in ReadOptions + readOptions.setSnapshot(snapshot); + db.put("key2".getBytes(), "value2".getBytes()); + + // iterate over current state of column family + iterator = db.newIterator(db.getDefaultColumnFamily()); + iterator.seekToFirst(); + assertThat(iterator.isValid()).isTrue(); + assertThat(iterator.key()).isEqualTo("key".getBytes()); + iterator.next(); + assertThat(iterator.isValid()).isTrue(); + assertThat(iterator.key()).isEqualTo("key2".getBytes()); + iterator.next(); + assertThat(iterator.isValid()).isFalse(); + + // iterate using a snapshot on default column family + snapshotIterator = db.newIterator(db.getDefaultColumnFamily(), + readOptions); + snapshotIterator.seekToFirst(); + assertThat(snapshotIterator.isValid()).isTrue(); + assertThat(snapshotIterator.key()).isEqualTo("key".getBytes()); + snapshotIterator.next(); + assertThat(snapshotIterator.isValid()).isFalse(); + + // release Snapshot + db.releaseSnapshot(snapshot); + } finally { + if (iterator != null) { + iterator.dispose(); + } + if (snapshotIterator != null) { + snapshotIterator.dispose(); + } + if (db != null) { + db.close(); + } + if (options != null) { + options.dispose(); + } + if (readOptions != null) { + readOptions.dispose(); + } + } + } } diff --git a/java/rocksjni/rocksjni.cc b/java/rocksjni/rocksjni.cc index dee6aaa14a..1055f87fe1 100644 --- a/java/rocksjni/rocksjni.cc +++ b/java/rocksjni/rocksjni.cc @@ -1142,40 +1142,85 @@ void Java_org_rocksdb_RocksDB_disposeInternal( delete reinterpret_cast(jhandle); } -/* - * Class: org_rocksdb_RocksDB - * Method: iterator0 - * Signature: (J)J - */ -jlong Java_org_rocksdb_RocksDB_iterator0__J( - JNIEnv* env, jobject jdb, jlong db_handle) { - auto db = reinterpret_cast(db_handle); - rocksdb::Iterator* iterator = db->NewIterator(rocksdb::ReadOptions()); +jlong rocksdb_iterator_helper( + rocksdb::DB* db, rocksdb::ReadOptions read_options, + rocksdb::ColumnFamilyHandle* cf_handle) { + rocksdb::Iterator* iterator = nullptr; + if (cf_handle != nullptr) { + iterator = db->NewIterator(read_options, cf_handle); + } else { + iterator = db->NewIterator(read_options); + } return reinterpret_cast(iterator); } /* * Class: org_rocksdb_RocksDB - * Method: iterator0 + * Method: iterator + * Signature: (J)J + */ +jlong Java_org_rocksdb_RocksDB_iterator__J( + JNIEnv* env, jobject jdb, jlong db_handle) { + auto db = reinterpret_cast(db_handle); + return rocksdb_iterator_helper(db, rocksdb::ReadOptions(), + nullptr); +} + +/* + * Class: org_rocksdb_RocksDB + * Method: iterator * Signature: (JJ)J */ -jlong Java_org_rocksdb_RocksDB_iterator0__JJ( +jlong Java_org_rocksdb_RocksDB_iterator__JJ( + JNIEnv* env, jobject jdb, jlong db_handle, + jlong jread_options_handle) { + auto db = reinterpret_cast(db_handle); + auto& read_options = *reinterpret_cast( + jread_options_handle); + return rocksdb_iterator_helper(db, read_options, + nullptr); +} + +/* + * Class: org_rocksdb_RocksDB + * Method: iteratorCF + * Signature: (JJ)J + */ +jlong Java_org_rocksdb_RocksDB_iteratorCF__JJ( JNIEnv* env, jobject jdb, jlong db_handle, jlong jcf_handle) { auto db = reinterpret_cast(db_handle); auto cf_handle = reinterpret_cast(jcf_handle); - rocksdb::Iterator* iterator = db->NewIterator(rocksdb::ReadOptions(), - cf_handle); - return reinterpret_cast(iterator); + return rocksdb_iterator_helper(db, rocksdb::ReadOptions(), + cf_handle); +} + +/* + * Class: org_rocksdb_RocksDB + * Method: iteratorCF + * Signature: (JJJ)J + */ +jlong Java_org_rocksdb_RocksDB_iteratorCF__JJJ( + JNIEnv* env, jobject jdb, jlong db_handle, jlong jcf_handle, + jlong jread_options_handle) { + auto db = reinterpret_cast(db_handle); + auto cf_handle = reinterpret_cast(jcf_handle); + auto& read_options = *reinterpret_cast( + jread_options_handle); + return rocksdb_iterator_helper(db, read_options, + cf_handle); } /* * Class: org_rocksdb_RocksDB * Method: iterators - * Signature: (JLjava/util/List;)[J + * Signature: (JLjava/util/List;J)[J */ jlongArray Java_org_rocksdb_RocksDB_iterators( - JNIEnv* env, jobject jdb, jlong db_handle, jobject jcfhandle_list) { + JNIEnv* env, jobject jdb, jlong db_handle, jobject jcfhandle_list, + jlong jread_options_handle) { auto db = reinterpret_cast(db_handle); + auto& read_options = *reinterpret_cast( + jread_options_handle); std::vector cf_handles; std::vector iterators; @@ -1195,7 +1240,7 @@ jlongArray Java_org_rocksdb_RocksDB_iterators( } } - rocksdb::Status s = db->NewIterators(rocksdb::ReadOptions(), + rocksdb::Status s = db->NewIterators(read_options, cf_handles, &iterators); if (s.ok()) { jlongArray jLongArray =