Summary: This is an abstraction for working with custom Comparators implemented in native C++ code from Java. Native code must directly extend `rocksdb::Comparator`. When the native code comparator is compiled into the RocksDB codebase, you can then create a Java Class, and JNI stub to wrap it. Useful if the C++/JNI barrier overhead is too much for your applications comparator performance. An example is provided in `java/rocksjni/native_comparator_wrapper_test.cc` and `java/src/main/java/org/rocksdb/NativeComparatorWrapperTest.java`. Closes https://github.com/facebook/rocksdb/pull/3334 Differential Revision: D7172605 Pulled By: miasantreble fbshipit-source-id: e24b7eb267a3bcb6afa214e0379a1d5e8a2ceabemain
parent
e476d0e252
commit
c5302a8a58
@ -0,0 +1,50 @@ |
||||
// Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
|
||||
// This source code is licensed under both the GPLv2 (found in the
|
||||
// COPYING file in the root directory) and Apache 2.0 License
|
||||
// (found in the LICENSE.Apache file in the root directory).
|
||||
|
||||
#include <jni.h> |
||||
#include <string> |
||||
|
||||
#include "rocksdb/comparator.h" |
||||
#include "rocksdb/slice.h" |
||||
|
||||
#include "include/org_rocksdb_NativeComparatorWrapperTest_NativeStringComparatorWrapper.h" |
||||
|
||||
namespace rocksdb { |
||||
|
||||
class NativeComparatorWrapperTestStringComparator |
||||
: public Comparator { |
||||
|
||||
const char* Name() const { |
||||
return "NativeComparatorWrapperTestStringComparator"; |
||||
} |
||||
|
||||
int Compare( |
||||
const Slice& a, const Slice& b) const { |
||||
return a.ToString().compare(b.ToString()); |
||||
} |
||||
|
||||
void FindShortestSeparator( |
||||
std::string* start, const Slice& limit) const { |
||||
return; |
||||
} |
||||
|
||||
void FindShortSuccessor( |
||||
std::string* key) const { |
||||
return; |
||||
} |
||||
}; |
||||
} // end of rocksdb namespace
|
||||
|
||||
/*
|
||||
* Class: org_rocksdb_NativeComparatorWrapperTest_NativeStringComparatorWrapper |
||||
* Method: newStringComparator |
||||
* Signature: ()J |
||||
*/ |
||||
jlong Java_org_rocksdb_NativeComparatorWrapperTest_00024NativeStringComparatorWrapper_newStringComparator( |
||||
JNIEnv* env , jobject jobj) { |
||||
auto* comparator = |
||||
new rocksdb::NativeComparatorWrapperTestStringComparator(); |
||||
return reinterpret_cast<jlong>(comparator); |
||||
} |
@ -0,0 +1,49 @@ |
||||
// Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
|
||||
// This source code is licensed under both the GPLv2 (found in the
|
||||
// COPYING file in the root directory) and Apache 2.0 License
|
||||
// (found in the LICENSE.Apache file in the root directory).
|
||||
|
||||
package org.rocksdb; |
||||
|
||||
enum ComparatorType { |
||||
JAVA_COMPARATOR((byte)0x0), |
||||
JAVA_DIRECT_COMPARATOR((byte)0x1), |
||||
JAVA_NATIVE_COMPARATOR_WRAPPER((byte)0x2); |
||||
|
||||
private final byte value; |
||||
|
||||
ComparatorType(final byte value) { |
||||
this.value = value; |
||||
} |
||||
|
||||
/** |
||||
* <p>Returns the byte value of the enumerations value.</p> |
||||
* |
||||
* @return byte representation |
||||
*/ |
||||
byte getValue() { |
||||
return value; |
||||
} |
||||
|
||||
/** |
||||
* <p>Get the ComparatorType enumeration value by |
||||
* passing the byte identifier to this method.</p> |
||||
* |
||||
* @param byteIdentifier of ComparatorType. |
||||
* |
||||
* @return ComparatorType instance. |
||||
* |
||||
* @throws IllegalArgumentException if the comparator type for the byteIdentifier |
||||
* cannot be found |
||||
*/ |
||||
static ComparatorType getComparatorType(final byte byteIdentifier) { |
||||
for (final ComparatorType comparatorType : ComparatorType.values()) { |
||||
if (comparatorType.getValue() == byteIdentifier) { |
||||
return comparatorType; |
||||
} |
||||
} |
||||
|
||||
throw new IllegalArgumentException( |
||||
"Illegal value provided for ComparatorType."); |
||||
} |
||||
} |
@ -0,0 +1,57 @@ |
||||
// Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
|
||||
// This source code is licensed under both the GPLv2 (found in the
|
||||
// COPYING file in the root directory) and Apache 2.0 License
|
||||
// (found in the LICENSE.Apache file in the root directory).
|
||||
|
||||
package org.rocksdb; |
||||
|
||||
/** |
||||
* A simple abstraction to allow a Java class to wrap a custom comparator |
||||
* implemented in C++. |
||||
* |
||||
* The native comparator must directly extend rocksdb::Comparator. |
||||
*/ |
||||
public abstract class NativeComparatorWrapper |
||||
extends AbstractComparator<Slice> { |
||||
|
||||
@Override |
||||
final ComparatorType getComparatorType() { |
||||
return ComparatorType.JAVA_NATIVE_COMPARATOR_WRAPPER; |
||||
} |
||||
|
||||
@Override |
||||
public final String name() { |
||||
throw new IllegalStateException("This should not be called. " + |
||||
"Implementation is in Native code"); |
||||
} |
||||
|
||||
@Override |
||||
public final int compare(final Slice s1, final Slice s2) { |
||||
throw new IllegalStateException("This should not be called. " + |
||||
"Implementation is in Native code"); |
||||
} |
||||
|
||||
@Override |
||||
public final String findShortestSeparator(final String start, final Slice limit) { |
||||
throw new IllegalStateException("This should not be called. " + |
||||
"Implementation is in Native code"); |
||||
} |
||||
|
||||
@Override |
||||
public final String findShortSuccessor(final String key) { |
||||
throw new IllegalStateException("This should not be called. " + |
||||
"Implementation is in Native code"); |
||||
} |
||||
|
||||
/** |
||||
* We override {@link RocksCallbackObject#disposeInternal()} |
||||
* as disposing of a native rocksd::Comparator extension requires |
||||
* a slightly different approach as it is not really a RocksCallbackObject |
||||
*/ |
||||
@Override |
||||
protected void disposeInternal() { |
||||
disposeInternal(nativeHandle_); |
||||
} |
||||
|
||||
private native void disposeInternal(final long handle); |
||||
} |
@ -0,0 +1,92 @@ |
||||
// Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
|
||||
// This source code is licensed under both the GPLv2 (found in the
|
||||
// COPYING file in the root directory) and Apache 2.0 License
|
||||
// (found in the LICENSE.Apache file in the root directory).
|
||||
|
||||
package org.rocksdb; |
||||
|
||||
import org.junit.Rule; |
||||
import org.junit.Test; |
||||
import org.junit.rules.TemporaryFolder; |
||||
|
||||
import java.util.*; |
||||
import java.util.Comparator; |
||||
|
||||
import static org.junit.Assert.assertEquals; |
||||
|
||||
public class NativeComparatorWrapperTest { |
||||
|
||||
@Rule |
||||
public TemporaryFolder dbFolder = new TemporaryFolder(); |
||||
|
||||
private static final Random random = new Random(); |
||||
|
||||
@Test |
||||
public void rountrip() throws RocksDBException { |
||||
final String dbPath = dbFolder.getRoot().getAbsolutePath(); |
||||
final int ITERATIONS = 1_000; |
||||
|
||||
final String[] storedKeys = new String[ITERATIONS]; |
||||
try (final NativeStringComparatorWrapper comparator = new NativeStringComparatorWrapper(); |
||||
final Options opt = new Options() |
||||
.setCreateIfMissing(true) |
||||
.setComparator(comparator)) { |
||||
|
||||
// store random integer keys
|
||||
try (final RocksDB db = RocksDB.open(opt, dbPath)) { |
||||
for (int i = 0; i < ITERATIONS; i++) { |
||||
final String strKey = randomString(); |
||||
final byte key[] = strKey.getBytes(); |
||||
// does key already exist (avoid duplicates)
|
||||
if (i > 0 && db.get(key) != null) { |
||||
i--; // generate a different key
|
||||
} else { |
||||
db.put(key, "value".getBytes()); |
||||
storedKeys[i] = strKey; |
||||
} |
||||
} |
||||
} |
||||
|
||||
// sort the stored keys into ascending alpha-numeric order
|
||||
Arrays.sort(storedKeys, new Comparator<String>() { |
||||
@Override |
||||
public int compare(final String o1, final String o2) { |
||||
return o1.compareTo(o2); |
||||
} |
||||
}); |
||||
|
||||
// re-open db and read from start to end
|
||||
// string keys should be in ascending
|
||||
// order
|
||||
try (final RocksDB db = RocksDB.open(opt, dbPath); |
||||
final RocksIterator it = db.newIterator()) { |
||||
int count = 0; |
||||
for (it.seekToFirst(); it.isValid(); it.next()) { |
||||
final String strKey = new String(it.key()); |
||||
assertEquals(storedKeys[count++], strKey); |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
private String randomString() { |
||||
final char[] chars = new char[12]; |
||||
for(int i = 0; i < 12; i++) { |
||||
final int letterCode = random.nextInt(24); |
||||
final char letter = (char) (((int) 'a') + letterCode); |
||||
chars[i] = letter; |
||||
} |
||||
return String.copyValueOf(chars); |
||||
} |
||||
|
||||
public static class NativeStringComparatorWrapper |
||||
extends NativeComparatorWrapper { |
||||
|
||||
@Override |
||||
protected long initializeNative(final long... nativeParameterHandles) { |
||||
return newStringComparator(); |
||||
} |
||||
|
||||
private native long newStringComparator(); |
||||
} |
||||
} |
Loading…
Reference in new issue