Add singleDelete to RocksJava (#1275)

* Rename RocksDB#remove -> RocksDB#delete to match C++ API; Added deprecated versions of RocksDB#remove for backwards compatibility.

* Add missing experimental feature RocksDB#singleDelete
main
Adam Retter 8 years ago committed by Yueh-Hsuan Chiang
parent ffdf6eee19
commit 817eeb29b4
  1. 123
      java/rocksjni/rocksjni.cc
  2. 23
      java/src/main/java/org/rocksdb/Experimental.java
  3. 222
      java/src/main/java/org/rocksdb/RocksDB.java
  4. 34
      java/src/test/java/org/rocksdb/RocksDBTest.java

@ -824,7 +824,7 @@ jint Java_org_rocksdb_RocksDB_get__JJ_3BI_3BIJ(
}
//////////////////////////////////////////////////////////////////////////////
// rocksdb::DB::Delete()
void rocksdb_remove_helper(
void rocksdb_delete_helper(
JNIEnv* env, rocksdb::DB* db, const rocksdb::WriteOptions& write_options,
rocksdb::ColumnFamilyHandle* cf_handle, jbyteArray jkey, jint jkey_len) {
jbyte* key = env->GetByteArrayElements(jkey, 0);
@ -850,25 +850,25 @@ void rocksdb_remove_helper(
/*
* Class: org_rocksdb_RocksDB
* Method: remove
* Method: delete
* Signature: (J[BI)V
*/
void Java_org_rocksdb_RocksDB_remove__J_3BI(
void Java_org_rocksdb_RocksDB_delete__J_3BI(
JNIEnv* env, jobject jdb, jlong jdb_handle,
jbyteArray jkey, jint jkey_len) {
auto db = reinterpret_cast<rocksdb::DB*>(jdb_handle);
static const rocksdb::WriteOptions default_write_options =
rocksdb::WriteOptions();
rocksdb_remove_helper(env, db, default_write_options, nullptr,
rocksdb_delete_helper(env, db, default_write_options, nullptr,
jkey, jkey_len);
}
/*
* Class: org_rocksdb_RocksDB
* Method: remove
* Method: delete
* Signature: (J[BIJ)V
*/
void Java_org_rocksdb_RocksDB_remove__J_3BIJ(
void Java_org_rocksdb_RocksDB_delete__J_3BIJ(
JNIEnv* env, jobject jdb, jlong jdb_handle,
jbyteArray jkey, jint jkey_len, jlong jcf_handle) {
auto db = reinterpret_cast<rocksdb::DB*>(jdb_handle);
@ -876,7 +876,7 @@ void Java_org_rocksdb_RocksDB_remove__J_3BIJ(
rocksdb::WriteOptions();
auto cf_handle = reinterpret_cast<rocksdb::ColumnFamilyHandle*>(jcf_handle);
if (cf_handle != nullptr) {
rocksdb_remove_helper(env, db, default_write_options, cf_handle,
rocksdb_delete_helper(env, db, default_write_options, cf_handle,
jkey, jkey_len);
} else {
rocksdb::RocksDBExceptionJni::ThrowNew(env,
@ -886,23 +886,23 @@ void Java_org_rocksdb_RocksDB_remove__J_3BIJ(
/*
* Class: org_rocksdb_RocksDB
* Method: remove
* Method: delete
* Signature: (JJ[BIJ)V
*/
void Java_org_rocksdb_RocksDB_remove__JJ_3BI(
void Java_org_rocksdb_RocksDB_delete__JJ_3BI(
JNIEnv* env, jobject jdb, jlong jdb_handle,
jlong jwrite_options, jbyteArray jkey, jint jkey_len) {
auto db = reinterpret_cast<rocksdb::DB*>(jdb_handle);
auto write_options = reinterpret_cast<rocksdb::WriteOptions*>(jwrite_options);
rocksdb_remove_helper(env, db, *write_options, nullptr, jkey, jkey_len);
rocksdb_delete_helper(env, db, *write_options, nullptr, jkey, jkey_len);
}
/*
* Class: org_rocksdb_RocksDB
* Method: remove
* Method: delete
* Signature: (JJ[BIJ)V
*/
void Java_org_rocksdb_RocksDB_remove__JJ_3BIJ(
void Java_org_rocksdb_RocksDB_delete__JJ_3BIJ(
JNIEnv* env, jobject jdb, jlong jdb_handle,
jlong jwrite_options, jbyteArray jkey, jint jkey_len,
jlong jcf_handle) {
@ -910,12 +910,109 @@ void Java_org_rocksdb_RocksDB_remove__JJ_3BIJ(
auto write_options = reinterpret_cast<rocksdb::WriteOptions*>(jwrite_options);
auto cf_handle = reinterpret_cast<rocksdb::ColumnFamilyHandle*>(jcf_handle);
if (cf_handle != nullptr) {
rocksdb_remove_helper(env, db, *write_options, cf_handle, jkey, jkey_len);
rocksdb_delete_helper(env, db, *write_options, cf_handle, jkey, jkey_len);
} else {
rocksdb::RocksDBExceptionJni::ThrowNew(env,
rocksdb::Status::InvalidArgument("Invalid ColumnFamilyHandle."));
}
}
//////////////////////////////////////////////////////////////////////////////
// rocksdb::DB::SingleDelete()
void rocksdb_single_delete_helper(
JNIEnv* env, rocksdb::DB* db, const rocksdb::WriteOptions& write_options,
rocksdb::ColumnFamilyHandle* cf_handle, jbyteArray jkey, jint jkey_len) {
jbyte* key = env->GetByteArrayElements(jkey, 0);
rocksdb::Slice key_slice(reinterpret_cast<char*>(key), jkey_len);
rocksdb::Status s;
if (cf_handle != nullptr) {
s = db->SingleDelete(write_options, cf_handle, key_slice);
} else {
// backwards compatibility
s = db->SingleDelete(write_options, key_slice);
}
// trigger java unref on key and value.
// by passing JNI_ABORT, it will simply release the reference without
// copying the result back to the java byte array.
env->ReleaseByteArrayElements(jkey, key, JNI_ABORT);
if (!s.ok()) {
rocksdb::RocksDBExceptionJni::ThrowNew(env, s);
}
}
/*
* Class: org_rocksdb_RocksDB
* Method: singleDelete
* Signature: (J[BI)V
*/
void Java_org_rocksdb_RocksDB_singleDelete__J_3BI(
JNIEnv* env, jobject jdb, jlong jdb_handle,
jbyteArray jkey, jint jkey_len) {
auto db = reinterpret_cast<rocksdb::DB*>(jdb_handle);
static const rocksdb::WriteOptions default_write_options =
rocksdb::WriteOptions();
rocksdb_single_delete_helper(env, db, default_write_options, nullptr,
jkey, jkey_len);
}
/*
* Class: org_rocksdb_RocksDB
* Method: singleDelete
* Signature: (J[BIJ)V
*/
void Java_org_rocksdb_RocksDB_singleDelete__J_3BIJ(
JNIEnv* env, jobject jdb, jlong jdb_handle,
jbyteArray jkey, jint jkey_len, jlong jcf_handle) {
auto db = reinterpret_cast<rocksdb::DB*>(jdb_handle);
static const rocksdb::WriteOptions default_write_options =
rocksdb::WriteOptions();
auto cf_handle = reinterpret_cast<rocksdb::ColumnFamilyHandle*>(jcf_handle);
if (cf_handle != nullptr) {
rocksdb_single_delete_helper(env, db, default_write_options, cf_handle,
jkey, jkey_len);
} else {
rocksdb::RocksDBExceptionJni::ThrowNew(env,
rocksdb::Status::InvalidArgument("Invalid ColumnFamilyHandle."));
}
}
/*
* Class: org_rocksdb_RocksDB
* Method: singleDelete
* Signature: (JJ[BIJ)V
*/
void Java_org_rocksdb_RocksDB_singleDelete__JJ_3BI(
JNIEnv* env, jobject jdb, jlong jdb_handle,
jlong jwrite_options, jbyteArray jkey, jint jkey_len) {
auto db = reinterpret_cast<rocksdb::DB*>(jdb_handle);
auto write_options = reinterpret_cast<rocksdb::WriteOptions*>(jwrite_options);
rocksdb_single_delete_helper(env, db, *write_options, nullptr, jkey,
jkey_len);
}
/*
* Class: org_rocksdb_RocksDB
* Method: singleDelete
* Signature: (JJ[BIJ)V
*/
void Java_org_rocksdb_RocksDB_singleDelete__JJ_3BIJ(
JNIEnv* env, jobject jdb, jlong jdb_handle,
jlong jwrite_options, jbyteArray jkey, jint jkey_len,
jlong jcf_handle) {
auto db = reinterpret_cast<rocksdb::DB*>(jdb_handle);
auto write_options = reinterpret_cast<rocksdb::WriteOptions*>(jwrite_options);
auto cf_handle = reinterpret_cast<rocksdb::ColumnFamilyHandle*>(jcf_handle);
if (cf_handle != nullptr) {
rocksdb_single_delete_helper(env, db, *write_options, cf_handle, jkey,
jkey_len);
} else {
rocksdb::RocksDBExceptionJni::ThrowNew(env,
rocksdb::Status::InvalidArgument("Invalid ColumnFamilyHandle."));
}
}
//////////////////////////////////////////////////////////////////////////////
// rocksdb::DB::Merge

@ -0,0 +1,23 @@
// Copyright (c) 2011-present, 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;
import java.lang.annotation.ElementType;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Marks a feature as experimental, meaning that it is likely
* to change or even be removed/re-engineered in the future
*/
@Documented
@Retention(RetentionPolicy.SOURCE)
@Target({ElementType.TYPE, ElementType.METHOD})
public @interface Experimental {
String value();
}

@ -961,9 +961,26 @@ public class RocksDB extends RocksObject {
*
* @throws RocksDBException thrown if error happens in underlying
* native library.
*
* @deprecated Use {@link #delete(byte[])}
*/
@Deprecated
public void remove(final byte[] key) throws RocksDBException {
remove(nativeHandle_, key, key.length);
delete(key);
}
/**
* Delete 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"
* did not exist in the database.
*
* @param key Key to delete within database
*
* @throws RocksDBException thrown if error happens in underlying
* native library.
*/
public void delete(final byte[] key) throws RocksDBException {
delete(nativeHandle_, key, key.length);
}
/**
@ -977,10 +994,30 @@ public class RocksDB extends RocksObject {
*
* @throws RocksDBException thrown if error happens in underlying
* native library.
*
* @deprecated Use {@link #delete(ColumnFamilyHandle, byte[])}
*/
@Deprecated
public void remove(final ColumnFamilyHandle columnFamilyHandle,
final byte[] key) throws RocksDBException {
remove(nativeHandle_, key, key.length, columnFamilyHandle.nativeHandle_);
delete(columnFamilyHandle, key);
}
/**
* Delete 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"
* did not exist in the database.
*
* @param columnFamilyHandle {@link org.rocksdb.ColumnFamilyHandle}
* instance
* @param key Key to delete within database
*
* @throws RocksDBException thrown if error happens in underlying
* native library.
*/
public void delete(final ColumnFamilyHandle columnFamilyHandle,
final byte[] key) throws RocksDBException {
delete(nativeHandle_, key, key.length, columnFamilyHandle.nativeHandle_);
}
/**
@ -993,10 +1030,29 @@ public class RocksDB extends RocksObject {
*
* @throws RocksDBException thrown if error happens in underlying
* native library.
*
* @deprecated Use {@link #delete(WriteOptions, byte[])}
*/
@Deprecated
public void remove(final WriteOptions writeOpt, final byte[] key)
throws RocksDBException {
remove(nativeHandle_, writeOpt.nativeHandle_, key, key.length);
delete(writeOpt, key);
}
/**
* Delete 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"
* did not exist in the database.
*
* @param writeOpt WriteOptions to be used with delete operation
* @param key Key to delete within database
*
* @throws RocksDBException thrown if error happens in underlying
* native library.
*/
public void delete(final WriteOptions writeOpt, final byte[] key)
throws RocksDBException {
delete(nativeHandle_, writeOpt.nativeHandle_, key, key.length);
}
/**
@ -1011,11 +1067,150 @@ public class RocksDB extends RocksObject {
*
* @throws RocksDBException thrown if error happens in underlying
* native library.
*
* @deprecated Use {@link #delete(ColumnFamilyHandle, WriteOptions, byte[])}
*/
@Deprecated
public void remove(final ColumnFamilyHandle columnFamilyHandle,
final WriteOptions writeOpt, final byte[] key)
throws RocksDBException {
remove(nativeHandle_, writeOpt.nativeHandle_, key, key.length,
delete(columnFamilyHandle, writeOpt, key);
}
/**
* Delete 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"
* did not exist in the database.
*
* @param columnFamilyHandle {@link org.rocksdb.ColumnFamilyHandle}
* instance
* @param writeOpt WriteOptions to be used with delete operation
* @param key Key to delete within database
*
* @throws RocksDBException thrown if error happens in underlying
* native library.
*/
public void delete(final ColumnFamilyHandle columnFamilyHandle,
final WriteOptions writeOpt, final byte[] key)
throws RocksDBException {
delete(nativeHandle_, writeOpt.nativeHandle_, key, key.length,
columnFamilyHandle.nativeHandle_);
}
/**
* Remove the database entry for {@code key}. Requires that the key exists
* and was not overwritten. It is not an error if the key did not exist
* in the database.
*
* If a key is overwritten (by calling {@link #put(byte[], byte[])} multiple
* times), then the result of calling SingleDelete() on this key is undefined.
* SingleDelete() only behaves correctly if there has been only one Put()
* for this key since the previous call to SingleDelete() for this key.
*
* This feature is currently an experimental performance optimization
* for a very specific workload. It is up to the caller to ensure that
* SingleDelete is only used for a key that is not deleted using Delete() or
* written using Merge(). Mixing SingleDelete operations with Deletes and
* Merges can result in undefined behavior.
*
* @param key Key to delete within database
*
* @throws RocksDBException thrown if error happens in underlying
* native library.
*/
@Experimental("Performance optimization for a very specific workload")
public void singleDelete(final byte[] key) throws RocksDBException {
singleDelete(nativeHandle_, key, key.length);
}
/**
* Remove the database entry for {@code key}. Requires that the key exists
* and was not overwritten. It is not an error if the key did not exist
* in the database.
*
* If a key is overwritten (by calling {@link #put(byte[], byte[])} multiple
* times), then the result of calling SingleDelete() on this key is undefined.
* SingleDelete() only behaves correctly if there has been only one Put()
* for this key since the previous call to SingleDelete() for this key.
*
* This feature is currently an experimental performance optimization
* for a very specific workload. It is up to the caller to ensure that
* SingleDelete is only used for a key that is not deleted using Delete() or
* written using Merge(). Mixing SingleDelete operations with Deletes and
* Merges can result in undefined behavior.
*
* @param columnFamilyHandle The column family to delete the key from
* @param key Key to delete within database
*
* @throws RocksDBException thrown if error happens in underlying
* native library.
*/
@Experimental("Performance optimization for a very specific workload")
public void singleDelete(final ColumnFamilyHandle columnFamilyHandle,
final byte[] key) throws RocksDBException {
singleDelete(nativeHandle_, key, key.length,
columnFamilyHandle.nativeHandle_);
}
/**
* Remove the database entry for {@code key}. Requires that the key exists
* and was not overwritten. It is not an error if the key did not exist
* in the database.
*
* If a key is overwritten (by calling {@link #put(byte[], byte[])} multiple
* times), then the result of calling SingleDelete() on this key is undefined.
* SingleDelete() only behaves correctly if there has been only one Put()
* for this key since the previous call to SingleDelete() for this key.
*
* This feature is currently an experimental performance optimization
* for a very specific workload. It is up to the caller to ensure that
* SingleDelete is only used for a key that is not deleted using Delete() or
* written using Merge(). Mixing SingleDelete operations with Deletes and
* Merges can result in undefined behavior.
*
* Note: consider setting {@link WriteOptions#setSync(boolean)} true.
*
* @param writeOpt Write options for the delete
* @param key Key to delete within database
*
* @throws RocksDBException thrown if error happens in underlying
* native library.
*/
@Experimental("Performance optimization for a very specific workload")
public void singleDelete(final WriteOptions writeOpt, final byte[] key)
throws RocksDBException {
singleDelete(nativeHandle_, writeOpt.nativeHandle_, key, key.length);
}
/**
* Remove the database entry for {@code key}. Requires that the key exists
* and was not overwritten. It is not an error if the key did not exist
* in the database.
*
* If a key is overwritten (by calling {@link #put(byte[], byte[])} multiple
* times), then the result of calling SingleDelete() on this key is undefined.
* SingleDelete() only behaves correctly if there has been only one Put()
* for this key since the previous call to SingleDelete() for this key.
*
* This feature is currently an experimental performance optimization
* for a very specific workload. It is up to the caller to ensure that
* SingleDelete is only used for a key that is not deleted using Delete() or
* written using Merge(). Mixing SingleDelete operations with Deletes and
* Merges can result in undefined behavior.
*
* Note: consider setting {@link WriteOptions#setSync(boolean)} true.
*
* @param columnFamilyHandle The column family to delete the key from
* @param writeOpt Write options for the delete
* @param key Key to delete within database
*
* @throws RocksDBException thrown if error happens in underlying
* native library.
*/
@Experimental("Performance optimization for a very specific workload")
public void singleDelete(final ColumnFamilyHandle columnFamilyHandle,
final WriteOptions writeOpt, final byte[] key) throws RocksDBException {
singleDelete(nativeHandle_, writeOpt.nativeHandle_, key, key.length,
columnFamilyHandle.nativeHandle_);
}
@ -1849,15 +2044,26 @@ public class RocksDB extends RocksObject {
protected native byte[] get(
long handle, long readOptHandle,
byte[] key, int keyLen, long cfHandle) throws RocksDBException;
protected native void remove(
protected native void delete(
long handle, byte[] key, int keyLen) throws RocksDBException;
protected native void delete(
long handle, byte[] key, int keyLen, long cfHandle)
throws RocksDBException;
protected native void delete(
long handle, long writeOptHandle,
byte[] key, int keyLen) throws RocksDBException;
protected native void delete(
long handle, long writeOptHandle,
byte[] key, int keyLen, long cfHandle) throws RocksDBException;
protected native void singleDelete(
long handle, byte[] key, int keyLen) throws RocksDBException;
protected native void remove(
protected native void singleDelete(
long handle, byte[] key, int keyLen, long cfHandle)
throws RocksDBException;
protected native void remove(
protected native void singleDelete(
long handle, long writeOptHandle,
byte[] key, int keyLen) throws RocksDBException;
protected native void remove(
protected native void singleDelete(
long handle, long writeOptHandle,
byte[] key, int keyLen, long cfHandle) throws RocksDBException;
protected native String getProperty0(long nativeHandle,

@ -192,7 +192,7 @@ public class RocksDBTest {
}
@Test
public void remove() throws RocksDBException {
public void delete() throws RocksDBException {
try (final RocksDB db = RocksDB.open(dbFolder.getRoot().getAbsolutePath());
final WriteOptions wOpt = new WriteOptions()) {
db.put("key1".getBytes(), "value".getBytes());
@ -201,8 +201,36 @@ public class RocksDBTest {
"value".getBytes());
assertThat(db.get("key2".getBytes())).isEqualTo(
"12345678".getBytes());
db.remove("key1".getBytes());
db.remove(wOpt, "key2".getBytes());
db.delete("key1".getBytes());
db.delete(wOpt, "key2".getBytes());
assertThat(db.get("key1".getBytes())).isNull();
assertThat(db.get("key2".getBytes())).isNull();
}
}
@Test
public void singleDelete() throws RocksDBException {
try (final RocksDB db = RocksDB.open(dbFolder.getRoot().getAbsolutePath());
final WriteOptions wOpt = new WriteOptions()) {
db.put("key1".getBytes(), "value".getBytes());
db.put("key2".getBytes(), "12345678".getBytes());
assertThat(db.get("key1".getBytes())).isEqualTo(
"value".getBytes());
assertThat(db.get("key2".getBytes())).isEqualTo(
"12345678".getBytes());
db.singleDelete("key1".getBytes());
db.singleDelete(wOpt, "key2".getBytes());
assertThat(db.get("key1".getBytes())).isNull();
assertThat(db.get("key2".getBytes())).isNull();
}
}
@Test
public void singleDelete_nonExisting() throws RocksDBException {
try (final RocksDB db = RocksDB.open(dbFolder.getRoot().getAbsolutePath());
final WriteOptions wOpt = new WriteOptions()) {
db.singleDelete("key1".getBytes());
db.singleDelete(wOpt, "key2".getBytes());
assertThat(db.get("key1".getBytes())).isNull();
assertThat(db.get("key2".getBytes())).isNull();
}

Loading…
Cancel
Save