diff --git a/java/Makefile b/java/Makefile index fdee3ba287887220ffc8222ab801ce7154a423a1..5311af14f5a1143e00ce554c8358411e089b7fb2 100644 --- a/java/Makefile +++ b/java/Makefile @@ -1,4 +1,4 @@ -NATIVE_JAVA_CLASSES = org.rocksdb.RocksDB org.rocksdb.Options org.rocksdb.WriteBatch org.rocksdb.WriteBatchInternal org.rocksdb.WriteBatchTest org.rocksdb.WriteOptions org.rocksdb.BackupableDB org.rocksdb.BackupableDBOptions org.rocksdb.Statistics org.rocksdb.Iterator org.rocksdb.VectorMemTableConfig org.rocksdb.SkipListMemTableConfig org.rocksdb.HashLinkedListMemTableConfig org.rocksdb.HashSkipListMemTableConfig org.rocksdb.PlainTableConfig +NATIVE_JAVA_CLASSES = org.rocksdb.RocksDB org.rocksdb.Options org.rocksdb.WriteBatch org.rocksdb.WriteBatchInternal org.rocksdb.WriteBatchTest org.rocksdb.WriteOptions org.rocksdb.BackupableDB org.rocksdb.BackupableDBOptions org.rocksdb.Statistics org.rocksdb.Iterator org.rocksdb.VectorMemTableConfig org.rocksdb.SkipListMemTableConfig org.rocksdb.HashLinkedListMemTableConfig org.rocksdb.HashSkipListMemTableConfig org.rocksdb.PlainTableConfig org.rocksdb.ReadOptions NATIVE_INCLUDE = ./include ROCKSDB_JAR = rocksdbjni.jar @@ -25,6 +25,7 @@ test: java java -ea -Djava.library.path=.:../ -cp "$(ROCKSDB_JAR):.:./*" org.rocksdb.WriteBatchTest java -ea -Djava.library.path=.:../ -cp "$(ROCKSDB_JAR):.:./*" org.rocksdb.test.BackupableDBTest java -ea -Djava.library.path=.:../ -cp "$(ROCKSDB_JAR):.:./*" org.rocksdb.test.OptionsTest + java -ea -Djava.library.path=.:../ -cp "$(ROCKSDB_JAR):.:./*" org.rocksdb.test.ReadOptionsTest db_bench: java javac org/rocksdb/benchmark/*.java diff --git a/java/RocksDBSample.java b/java/RocksDBSample.java index bdb800ec23582a4603232aebd520c51545e492d8..940b4bd40846bb04dc96d0f0c31bd3c6207b268d 100644 --- a/java/RocksDBSample.java +++ b/java/RocksDBSample.java @@ -84,6 +84,9 @@ public class RocksDBSample { // be sure to release the c++ pointer db.close(); + ReadOptions readOptions = new ReadOptions(); + readOptions.setFillCache(false); + try { db = RocksDB.open(options, db_path); db.put("hello".getBytes(), "world".getBytes()); @@ -110,6 +113,8 @@ public class RocksDBSample { assert(value != null); value = db.get("world".getBytes()); assert(value == null); + value = db.get(readOptions, "world".getBytes()); + assert(value == null); byte[] testKey = "asdf".getBytes(); byte[] testValue = @@ -119,6 +124,10 @@ public class RocksDBSample { assert(testResult != null); assert(Arrays.equals(testValue, testResult)); assert(new String(testValue).equals(new String(testResult))); + testResult = db.get(readOptions, testKey); + assert(testResult != null); + assert(Arrays.equals(testValue, testResult)); + assert(new String(testValue).equals(new String(testResult))); byte[] insufficientArray = new byte[10]; byte[] enoughArray = new byte[50]; @@ -130,6 +139,13 @@ public class RocksDBSample { len = db.get(testKey, enoughArray); assert(len == testValue.length); + len = db.get(readOptions, testKey, insufficientArray); + assert(len > insufficientArray.length); + len = db.get(readOptions, "asdfjkl;".getBytes(), enoughArray); + assert(len == RocksDB.NOT_FOUND); + len = db.get(readOptions, testKey, enoughArray); + assert(len == testValue.length); + db.remove(testKey); len = db.get(testKey, enoughArray); assert(len == RocksDB.NOT_FOUND); @@ -207,5 +223,6 @@ public class RocksDBSample { } // be sure to dispose c++ pointers options.dispose(); + readOptions.dispose(); } } diff --git a/java/org/rocksdb/ReadOptions.java b/java/org/rocksdb/ReadOptions.java new file mode 100644 index 0000000000000000000000000000000000000000..c9ab547cc542740a27da4be519cce79e9b7749b4 --- /dev/null +++ b/java/org/rocksdb/ReadOptions.java @@ -0,0 +1,165 @@ +// 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; + +/** + * The class that controls the get behavior. + * + * Note that dispose() must be called before an Options instance + * become out-of-scope to release the allocated memory in c++. + */ +public class ReadOptions { + public ReadOptions() { + nativeHandle_ = 0; + newReadOptions(); + } + private native void newReadOptions(); + + /** + * Release the memory allocated for the current instance + * in the c++ side. + * + * Calling other methods after dispose() leads to undefined behavior. + */ + public synchronized void dispose() { + if (isInitialized()) { + dispose(nativeHandle_); + } + } + private native void dispose(long handle); + + /** + * If true, all data read from underlying storage will be + * verified against corresponding checksums. + * Default: true + * + * @return true if checksum verification is on. + */ + public boolean verifyChecksums() { + assert(isInitialized()); + return verifyChecksums(nativeHandle_); + } + private native boolean verifyChecksums(long handle); + + /** + * If true, all data read from underlying storage will be + * verified against corresponding checksums. + * Default: true + * + * @param verifyChecksums if true, then checksum verification + * will be performed on every read. + * @return the reference to the current ReadOptions. + */ + public ReadOptions setVerifyChecksums(boolean verifyChecksums) { + assert(isInitialized()); + setVerifyChecksums(nativeHandle_, verifyChecksums); + return this; + } + private native void setVerifyChecksums( + long handle, boolean verifyChecksums); + + // TODO(yhchiang): this option seems to be block-based table only. + // move this to a better place? + /** + * Fill the cache when loading the block-based sst formated db. + * Callers may wish to set this field to false for bulk scans. + * Default: true + * + * @return true if the fill-cache behavior is on. + */ + public boolean fillCache() { + assert(isInitialized()); + return fillCache(nativeHandle_); + } + private native boolean fillCache(long handle); + + /** + * Fill the cache when loading the block-based sst formated db. + * Callers may wish to set this field to false for bulk scans. + * Default: true + * + * @param fillCache if true, then fill-cache behavior will be + * performed. + * @return the reference to the current ReadOptions. + */ + public ReadOptions setFillCache(boolean fillCache) { + assert(isInitialized()); + setFillCache(nativeHandle_, fillCache); + return this; + } + private native void setFillCache( + long handle, boolean fillCache); + + /** + * If this option is set and memtable implementation allows, Seek + * might only return keys with the same prefix as the seek-key + * Default: false + * + * @return true if prefix-seek is enabled. + */ + public boolean prefixSeek() { + assert(isInitialized()); + return prefixSeek(nativeHandle_); + } + private native boolean prefixSeek(long handle); + + /** + * If this option is set and memtable implementation allows, Seek + * might only return keys with the same prefix as the seek-key + * + * @param prefixSeek if true, then prefix-seek will be enabled. + * @return the reference to the current ReadOptions. + */ + public ReadOptions setPrefixSeek(boolean prefixSeek) { + assert(isInitialized()); + setPrefixSeek(nativeHandle_, prefixSeek); + return this; + } + private native void setPrefixSeek( + long handle, boolean prefixSeek); + + /** + * 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 + * added data) and is optimized for sequential reads. It will return records + * that were inserted into the database after the creation of the iterator. + * Default: false + * Not supported in ROCKSDB_LITE mode! + * + * @return true if tailing iterator is enabled. + */ + public boolean tailing() { + assert(isInitialized()); + return tailing(nativeHandle_); + } + private native boolean tailing(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 + * added data) and is optimized for sequential reads. It will return records + * that were inserted into the database after the creation of the iterator. + * Default: false + * Not supported in ROCKSDB_LITE mode! + * + * @param tailing if true, then tailing iterator will be enabled. + * @return the reference to the current ReadOptions. + */ + public ReadOptions setTailing(boolean tailing) { + assert(isInitialized()); + setTailing(nativeHandle_, tailing); + return this; + } + private native void setTailing( + long handle, boolean tailing); + + protected long nativeHandle_; + + private boolean isInitialized() { + return nativeHandle_ != 0; + } +} + diff --git a/java/org/rocksdb/RocksDB.java b/java/org/rocksdb/RocksDB.java index 38426d50ce08992b9008d3c54319c87c7d6d6269..0b78450d58a2f85d474ababef4dad802dbc8d1fc 100644 --- a/java/org/rocksdb/RocksDB.java +++ b/java/org/rocksdb/RocksDB.java @@ -75,7 +75,8 @@ public class RocksDB { */ public void put(WriteOptions writeOpts, byte[] key, byte[] value) throws RocksDBException { - put(nativeHandle_, writeOpts.nativeHandle_, key, key.length, value, value.length); + put(nativeHandle_, writeOpts.nativeHandle_, + key, key.length, value, value.length); } /** @@ -102,6 +103,24 @@ public class RocksDB { return get(nativeHandle_, key, key.length, value, value.length); } + /** + * Get the value associated with the specified key. + * + * @param key the key to retrieve the value. + * @param value the out-value to receive the retrieved value. + * @return The size of the actual value that matches the specified + * {@code key} in byte. If the return value is greater than the + * length of {@code value}, then it indicates that the size of the + * input buffer {@code value} is insufficient and partial result will + * be returned. RocksDB.NOT_FOUND will be returned if the value not + * found. + */ + public int get(ReadOptions opt, byte[] key, byte[] value) + throws RocksDBException { + return get(nativeHandle_, opt.nativeHandle_, + key, key.length, value, value.length); + } + /** * The simplified version of get which returns a new byte array storing * the value associated with the specified input key if any. null will be @@ -117,6 +136,21 @@ public class RocksDB { return get(nativeHandle_, key, key.length); } + /** + * The simplified version of get which returns a new byte array storing + * the value associated with the specified input key if any. null will be + * returned if the specified key is not found. + * + * @param key the key retrieve the value. + * @return a byte array storing the value associated with the input key if + * any. null if it does not find the specified key. + * + * @see RocksDBException + */ + public byte[] get(ReadOptions opt, byte[] key) throws RocksDBException { + return get(nativeHandle_, opt.nativeHandle_, key, key.length); + } + /** * Remove the database entry (if any) for "key". Returns OK on * success, and a non-OK status on error. It is not an error if "key" @@ -176,8 +210,14 @@ public class RocksDB { protected native int get( long handle, byte[] key, int keyLen, byte[] value, int valueLen) throws RocksDBException; + protected native int get( + long handle, long readOptHandle, byte[] key, int keyLen, + byte[] value, int valueLen) throws RocksDBException; protected native byte[] get( long handle, byte[] key, int keyLen) throws RocksDBException; + protected native byte[] get( + long handle, long readOptHandle, + byte[] key, int keyLen) throws RocksDBException; protected native void remove( long handle, byte[] key, int keyLen) throws RocksDBException; protected native void remove( diff --git a/java/org/rocksdb/test/ReadOptionsTest.java b/java/org/rocksdb/test/ReadOptionsTest.java new file mode 100644 index 0000000000000000000000000000000000000000..1fb6c51f169514fa517d48ebafedce47916c08c6 --- /dev/null +++ b/java/org/rocksdb/test/ReadOptionsTest.java @@ -0,0 +1,45 @@ +// 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.Random; +import org.rocksdb.*; + +public class ReadOptionsTest { + static { + System.loadLibrary("rocksdbjni"); + } + public static void main(String[] args) { + ReadOptions opt = new ReadOptions(); + Random rand = new Random(); + { // VerifyChecksums test + boolean boolValue = rand.nextBoolean(); + opt.setVerifyChecksums(boolValue); + assert(opt.verifyChecksums() == boolValue); + } + + { // FillCache test + boolean boolValue = rand.nextBoolean(); + opt.setFillCache(boolValue); + assert(opt.fillCache() == boolValue); + } + + { // PrefixSeek test + boolean boolValue = rand.nextBoolean(); + opt.setPrefixSeek(boolValue); + assert(opt.prefixSeek() == boolValue); + } + + { // Tailing test + boolean boolValue = rand.nextBoolean(); + opt.setTailing(boolValue); + assert(opt.tailing() == boolValue); + } + + opt.dispose(); + System.out.println("Passed ReadOptionsTest"); + } +} diff --git a/java/rocksjni/options.cc b/java/rocksjni/options.cc index 6b847772f7f609e1090442aabc598ce98339c27e..99b6b605e8b51044557c684e1b1a0c7c8c265279 100644 --- a/java/rocksjni/options.cc +++ b/java/rocksjni/options.cc @@ -13,6 +13,7 @@ #include "include/org_rocksdb_Options.h" #include "include/org_rocksdb_WriteOptions.h" +#include "include/org_rocksdb_ReadOptions.h" #include "rocksjni/portal.h" #include "rocksdb/db.h" #include "rocksdb/options.h" @@ -923,3 +924,114 @@ jboolean Java_org_rocksdb_WriteOptions_disableWAL( JNIEnv* env, jobject jwrite_options, jlong jhandle) { return reinterpret_cast(jhandle)->disableWAL; } + +///////////////////////////////////////////////////////////////////// +// rocksdb::ReadOptions + +/* + * Class: org_rocksdb_ReadOptions + * Method: newReadOptions + * Signature: ()V + */ +void Java_org_rocksdb_ReadOptions_newReadOptions( + JNIEnv* env, jobject jobj) { + auto read_opt = new rocksdb::ReadOptions(); + rocksdb::ReadOptionsJni::setHandle(env, jobj, read_opt); +} + +/* + * Class: org_rocksdb_ReadOptions + * Method: dispose + * Signature: (J)V + */ +void Java_org_rocksdb_ReadOptions_dispose( + JNIEnv* env, jobject jobj, jlong jhandle) { + delete reinterpret_cast(jhandle); + rocksdb::ReadOptionsJni::setHandle(env, jobj, nullptr); +} + +/* + * Class: org_rocksdb_ReadOptions + * Method: verifyChecksums + * Signature: (J)Z + */ +jboolean Java_org_rocksdb_ReadOptions_verifyChecksums( + JNIEnv* env, jobject jobj, jlong jhandle) { + return reinterpret_cast( + jhandle)->verify_checksums; +} + +/* + * Class: org_rocksdb_ReadOptions + * Method: setVerifyChecksums + * Signature: (JZ)V + */ +void Java_org_rocksdb_ReadOptions_setVerifyChecksums( + JNIEnv* env, jobject jobj, jlong jhandle, + jboolean jverify_checksums) { + reinterpret_cast(jhandle)->verify_checksums = + static_cast(jverify_checksums); +} + +/* + * Class: org_rocksdb_ReadOptions + * Method: fillCache + * Signature: (J)Z + */ +jboolean Java_org_rocksdb_ReadOptions_fillCache( + JNIEnv* env, jobject jobj, jlong jhandle) { + return reinterpret_cast(jhandle)->fill_cache; +} + +/* + * Class: org_rocksdb_ReadOptions + * Method: setFillCache + * Signature: (JZ)V + */ +void Java_org_rocksdb_ReadOptions_setFillCache( + JNIEnv* env, jobject jobj, jlong jhandle, jboolean jfill_cache) { + reinterpret_cast(jhandle)->fill_cache = + static_cast(jfill_cache); +} + +/* + * Class: org_rocksdb_ReadOptions + * Method: prefixSeek + * Signature: (J)Z + */ +jboolean Java_org_rocksdb_ReadOptions_prefixSeek( + JNIEnv* env, jobject jobj, jlong jhandle) { + return reinterpret_cast(jhandle)->prefix_seek; +} + +/* + * Class: org_rocksdb_ReadOptions + * Method: setPrefixSeek + * Signature: (JZ)V + */ +void Java_org_rocksdb_ReadOptions_setPrefixSeek( + JNIEnv* env, jobject jobj, jlong jhandle, jboolean jprefix_seek) { + reinterpret_cast(jhandle)->prefix_seek = + static_cast(jprefix_seek); +} + +/* + * Class: org_rocksdb_ReadOptions + * Method: tailing + * Signature: (J)Z + */ +jboolean Java_org_rocksdb_ReadOptions_tailing( + JNIEnv* env, jobject jobj, jlong jhandle) { + return reinterpret_cast(jhandle)->tailing; +} + +/* + * Class: org_rocksdb_ReadOptions + * Method: setTailing + * Signature: (JZ)V + */ +void Java_org_rocksdb_ReadOptions_setTailing( + JNIEnv* env, jobject jobj, jlong jhandle, jboolean jtailing) { + reinterpret_cast(jhandle)->tailing = + static_cast(jtailing); +} diff --git a/java/rocksjni/portal.h b/java/rocksjni/portal.h index a4e2dc21ac967a5ba7d156d0195e42bc34ecb444..0b485e2e3675276120b0777058ef770138dfdccc 100644 --- a/java/rocksjni/portal.h +++ b/java/rocksjni/portal.h @@ -142,6 +142,41 @@ class WriteOptionsJni { } }; + +class ReadOptionsJni { + public: + // Get the java class id of org.rocksdb.ReadOptions. + static jclass getJClass(JNIEnv* env) { + static jclass jclazz = env->FindClass("org/rocksdb/ReadOptions"); + assert(jclazz != nullptr); + return jclazz; + } + + // Get the field id of the member variable of org.rocksdb.ReadOptions + // that stores the pointer to rocksdb::ReadOptions + static jfieldID getHandleFieldID(JNIEnv* env) { + static jfieldID fid = env->GetFieldID( + getJClass(env), "nativeHandle_", "J"); + assert(fid != nullptr); + return fid; + } + + // Get the pointer to rocksdb::ReadOptions + static rocksdb::ReadOptions* getHandle(JNIEnv* env, jobject jobj) { + return reinterpret_cast( + env->GetLongField(jobj, getHandleFieldID(env))); + } + + // Pass the rocksdb::ReadOptions pointer to the java side. + static void setHandle(JNIEnv* env, jobject jobj, + rocksdb::ReadOptions* op) { + env->SetLongField( + jobj, getHandleFieldID(env), + reinterpret_cast(op)); + } +}; + + class WriteBatchJni { public: static jclass getJClass(JNIEnv* env) { diff --git a/java/rocksjni/rocksjni.cc b/java/rocksjni/rocksjni.cc index f3f9c831b94c1a21fa24f8865dac9c0ee13144e6..17c7b8b10c40fc34277e92acd1b2c60991db7578 100644 --- a/java/rocksjni/rocksjni.cc +++ b/java/rocksjni/rocksjni.cc @@ -137,16 +137,9 @@ void Java_org_rocksdb_RocksDB_write( ////////////////////////////////////////////////////////////////////////////// // rocksdb::DB::Get -/* - * Class: org_rocksdb_RocksDB - * Method: get - * Signature: (J[BI)[B - */ -jbyteArray Java_org_rocksdb_RocksDB_get__J_3BI( - JNIEnv* env, jobject jdb, jlong jdb_handle, +jbyteArray rocksdb_get_helper( + JNIEnv* env, rocksdb::DB* db, const rocksdb::ReadOptions& read_opt, jbyteArray jkey, jint jkey_len) { - auto db = reinterpret_cast(jdb_handle); - jboolean isCopy; jbyte* key = env->GetByteArrayElements(jkey, &isCopy); rocksdb::Slice key_slice( @@ -154,8 +147,7 @@ jbyteArray Java_org_rocksdb_RocksDB_get__J_3BI( std::string value; rocksdb::Status s = db->Get( - rocksdb::ReadOptions(), - key_slice, &value); + read_opt, key_slice, &value); // trigger java unref on key. // by passing JNI_ABORT, it will simply release the reference without @@ -181,15 +173,37 @@ jbyteArray Java_org_rocksdb_RocksDB_get__J_3BI( /* * Class: org_rocksdb_RocksDB * Method: get - * Signature: (J[BI[BI)I + * Signature: (J[BI)[B */ -jint Java_org_rocksdb_RocksDB_get__J_3BI_3BI( +jbyteArray Java_org_rocksdb_RocksDB_get__J_3BI( JNIEnv* env, jobject jdb, jlong jdb_handle, + jbyteArray jkey, jint jkey_len) { + return rocksdb_get_helper(env, + reinterpret_cast(jdb_handle), + rocksdb::ReadOptions(), + jkey, jkey_len); +} + +/* + * Class: org_rocksdb_RocksDB + * Method: get + * Signature: (JJ[BI)[B + */ +jbyteArray Java_org_rocksdb_RocksDB_get__JJ_3BI( + JNIEnv* env, jobject jdb, jlong jdb_handle, jlong jropt_handle, + jbyteArray jkey, jint jkey_len) { + return rocksdb_get_helper(env, + reinterpret_cast(jdb_handle), + *reinterpret_cast(jropt_handle), + jkey, jkey_len); +} + +jint rocksdb_get_helper( + JNIEnv* env, rocksdb::DB* db, const rocksdb::ReadOptions& read_options, jbyteArray jkey, jint jkey_len, jbyteArray jvalue, jint jvalue_len) { static const int kNotFound = -1; static const int kStatusError = -2; - auto db = reinterpret_cast(jdb_handle); jbyte* key = env->GetByteArrayElements(jkey, 0); rocksdb::Slice key_slice( @@ -199,7 +213,7 @@ jint Java_org_rocksdb_RocksDB_get__J_3BI_3BI( // a DB::Get() function which takes preallocated jbyte* as input. std::string cvalue; rocksdb::Status s = db->Get( - rocksdb::ReadOptions(), key_slice, &cvalue); + read_options, key_slice, &cvalue); // trigger java unref on key. // by passing JNI_ABORT, it will simply release the reference without @@ -230,6 +244,36 @@ jint Java_org_rocksdb_RocksDB_get__J_3BI_3BI( return cvalue_len; } +/* + * Class: org_rocksdb_RocksDB + * Method: get + * Signature: (J[BI[BI)I + */ +jint Java_org_rocksdb_RocksDB_get__J_3BI_3BI( + JNIEnv* env, jobject jdb, jlong jdb_handle, + jbyteArray jkey, jint jkey_len, + jbyteArray jvalue, jint jvalue_len) { + return rocksdb_get_helper(env, + reinterpret_cast(jdb_handle), + rocksdb::ReadOptions(), + jkey, jkey_len, jvalue, jvalue_len); +} + +/* + * Class: org_rocksdb_RocksDB + * Method: get + * Signature: (JJ[BI[BI)I + */ +jint Java_org_rocksdb_RocksDB_get__JJ_3BI_3BI( + JNIEnv* env, jobject jdb, jlong jdb_handle, jlong jropt_handle, + jbyteArray jkey, jint jkey_len, + jbyteArray jvalue, jint jvalue_len) { + return rocksdb_get_helper(env, + reinterpret_cast(jdb_handle), + *reinterpret_cast(jropt_handle), + jkey, jkey_len, jvalue, jvalue_len); +} + ////////////////////////////////////////////////////////////////////////////// // rocksdb::DB::Delete() void rocksdb_remove_helper(