Java API consistency between RocksDB.put() , .merge() and Transaction.put() , .merge() (#11019)

Summary:
### Implement new Java API get()/put()/merge() methods, and transactional variants.

The Java API methods are very inconsistent in terms of how they pass parameters (byte[], ByteBuffer), and what variants and defaulted parameters they support. We try to bring some consistency to this.
 * All APIs should support calls with ByteBuffer parameters.
 * Similar methods (RocksDB.get() vs Transaction.get()) should support as similar as possible sets of parameters for predictability.
 * get()-like methods should provide variants where the caller supplies the target buffer, for the sake of efficiency. Allocation costs in Java can be significant when large buffers are repeatedly allocated and freed.

### API Additions

 1. RockDB.get implement indirect ByteBuffers. Added indirect ByteBuffers and supporting native methods for get().
 2. RocksDB.Iterator implement missing (byte[], offset, length) variants for key() and value() parameters.
 3. Transaction.get() implement missing methods, based on RocksDB.get. Added ByteBuffer.get with and without column family. Added byte[]-as-target get.
 4. Transaction.iterator() implement a getIterator() which defaults ReadOptions; as per RocksDB.iterator(). Rationalize support API for this and RocksDB.iterator()
 5. RocksDB.merge implement ByteBuffer methods; both direct and indirect buffers. Shadow the methods of RocksDB.put; RocksDB.put only offers ByteBuffer API with explicit WriteOptions. Duplicated this with RocksDB.merge
 6. Transaction.merge implement methods as per RocksDB.merge methods. Transaction is already constructed with WriteOptions, so no explicit WriteOptions methods required.
 7. Transaction.mergeUntracked implement the same API methods as Transaction.merge except the ones that use assumeTracked, because that’s not a feature of merge untracked.

### Support Changes (C++)

The current JNI code in C++ supports multiple variants of methods through a number of helper functions. There are numerous TODO suggestions in the code proposing that the helpers be re-factored/shared.

We have taken a different approach for the new methods; we have created wrapper classes `JDirectBufferSlice`, `JDirectBufferPinnableSlice`, `JByteArraySlice` and `JByteArrayPinnableSlice` RAII classes which construct slices from JNI parameters and can then be passed directly to RocksDB methods. For instance, the `Java_org_rocksdb_Transaction_getDirect` method is implemented like this:

```
  try {
    ROCKSDB_NAMESPACE::JDirectBufferSlice key(env, jkey_bb, jkey_off,
                                              jkey_part_len);
    ROCKSDB_NAMESPACE::JDirectBufferPinnableSlice value(env, jval_bb, jval_off,
                                                        jval_part_len);
    ROCKSDB_NAMESPACE::KVException::ThrowOnError(
        env, txn->Get(*read_options, column_family_handle, key.slice(),
                      &value.pinnable_slice()));
    return value.Fetch();
  } catch (const ROCKSDB_NAMESPACE::KVException& e) {
    return e.Code();
  }
```
Notice the try/catch mechanism with the `KVException` class, which combined with RAII and the wrapper classes means that there is no ad-hoc cleanup necessary in the JNI methods.

We propose to extend this mechanism to existing JNI methods as further work.

### Support Changes (Java)

Where there are multiple parameter-variant versions of the same method, we use fewer or just one supporting native method for all of them. This makes maintenance a bit easier and reduces the opportunity for coding errors mixing up (untyped) object handles.

In  order to support this efficiently, some classes need to have default values for column families and read options added and cached so that they are not re-constructed on every method call.

This PR closes https://github.com/facebook/rocksdb/issues/9776

Pull Request resolved: https://github.com/facebook/rocksdb/pull/11019

Reviewed By: ajkr

Differential Revision: D52039446

Pulled By: jowlyzhang

fbshipit-source-id: 45d0140a4887e42134d2e56520e9b8efbd349660
This commit is contained in:
Alan Paxton 2023-12-11 11:03:17 -08:00 committed by Facebook GitHub Bot
parent 4f04f96742
commit 5a063ecd34
28 changed files with 3312 additions and 578 deletions

View File

@ -168,6 +168,7 @@ set(JAVA_MAIN_CLASSES
src/main/java/org/rocksdb/FlushJobInfo.java
src/main/java/org/rocksdb/FlushReason.java
src/main/java/org/rocksdb/FlushOptions.java
src/main/java/org/rocksdb/GetStatus.java
src/main/java/org/rocksdb/HashLinkedListMemTableConfig.java
src/main/java/org/rocksdb/HashSkipListMemTableConfig.java
src/main/java/org/rocksdb/HistogramData.java
@ -285,6 +286,7 @@ set(JAVA_MAIN_CLASSES
src/main/java/org/rocksdb/WriteBufferManager.java
src/main/java/org/rocksdb/WriteStallCondition.java
src/main/java/org/rocksdb/WriteStallInfo.java
src/main/java/org/rocksdb/util/BufferUtil.java
src/main/java/org/rocksdb/util/ByteUtil.java
src/main/java/org/rocksdb/util/BytewiseComparator.java
src/main/java/org/rocksdb/util/Environment.java

View File

@ -8,16 +8,16 @@ Mac
```
make clean jclean
DEBUG_LEVEL=0 make -j12 rocksdbjava
(cd java/target; cp rocksdbjni-7.9.0-osx.jar rocksdbjni-7.9.0-SNAPSHOT-osx.jar)
mvn install:install-file -Dfile=./java/target/rocksdbjni-7.9.0-SNAPSHOT-osx.jar -DgroupId=org.rocksdb -DartifactId=rocksdbjni -Dversion=7.9.0-SNAPSHOT -Dpackaging=jar
(cd java/target; cp rocksdbjni-7.10.0-osx.jar rocksdbjni-7.10.0-SNAPSHOT-osx.jar)
mvn install:install-file -Dfile=./java/target/rocksdbjni-7.10.0-SNAPSHOT-osx.jar -DgroupId=org.rocksdb -DartifactId=rocksdbjni -Dversion=7.10.0-SNAPSHOT -Dpackaging=jar
```
Linux
```
make clean jclean
DEBUG_LEVEL=0 make -j12 rocksdbjava
(cd java/target; cp rocksdbjni-7.9.0-linux64.jar rocksdbjni-7.9.0-SNAPSHOT-linux64.jar)
mvn install:install-file -Dfile=./java/target/rocksdbjni-7.9.0-SNAPSHOT-linux64.jar -DgroupId=org.rocksdb -DartifactId=rocksdbjni -Dversion=7.9.0-SNAPSHOT -Dpackaging=jar
(cd java/target; cp rocksdbjni-7.10.0-linux64.jar rocksdbjni-7.10.0-SNAPSHOT-linux64.jar)
mvn install:install-file -Dfile=./java/target/rocksdbjni-7.10.0-SNAPSHOT-linux64.jar -DgroupId=org.rocksdb -DartifactId=rocksdbjni -Dversion=7.10.0-SNAPSHOT -Dpackaging=jar
```
Build jmh test package, on either platform
@ -35,31 +35,10 @@ The long performance run (as big as we can make it on our Ubuntu box without fil
java -jar target/rocksdbjni-jmh-1.0-SNAPSHOT-benchmarks.jar -p keyCount=1000,50000 -p keySize=128 -p valueSize=1024,16384 -p columnFamilyTestType="1_column_family","20_column_families" GetBenchmarks.get GetBenchmarks.preallocatedByteBufferGet GetBenchmarks.preallocatedGet
```
## Results (small runs, Mac)
These are run on a 10-core M1 with 64GB of memory and 2TB of SSD.
They probably reflect the absolute best case for this optimization, hitting in-memory buffers and completely eliminating a buffer copy.
### Before
Benchmark (columnFamilyTestType) (keyCount) (keySize) (multiGetSize) (valueSize) Mode Cnt Score Error Units
GetBenchmarks.get no_column_family 1000 128 N/A 32768 thrpt 25 43496.578 ± 5743.090 ops/s
GetBenchmarks.preallocatedByteBufferGet no_column_family 1000 128 N/A 32768 thrpt 25 70765.578 ± 697.548 ops/s
GetBenchmarks.preallocatedGet no_column_family 1000 128 N/A 32768 thrpt 25 69883.554 ± 944.184 ops/s
### After fixing byte[] (.get and .preallocatedGet)
Benchmark (columnFamilyTestType) (keyCount) (keySize) (multiGetSize) (valueSize) Mode Cnt Score Error Units
GetBenchmarks.get no_column_family 1000 128 N/A 32768 thrpt 25 149207.681 ± 2261.671 ops/s
GetBenchmarks.preallocatedByteBufferGet no_column_family 1000 128 N/A 32768 thrpt 25 68920.489 ± 1574.664 ops/s
GetBenchmarks.preallocatedGet no_column_family 1000 128 N/A 32768 thrpt 25 177399.022 ± 2107.375 ops/s
### After fixing ByteBuffer (.preallocatedByteBufferGet)
Benchmark (columnFamilyTestType) (keyCount) (keySize) (multiGetSize) (valueSize) Mode Cnt Score Error Units
GetBenchmarks.get no_column_family 1000 128 N/A 32768 thrpt 25 150389.259 ± 1371.473 ops/s
GetBenchmarks.preallocatedByteBufferGet no_column_family 1000 128 N/A 32768 thrpt 25 179919.468 ± 1670.714 ops/s
GetBenchmarks.preallocatedGet no_column_family 1000 128 N/A 32768 thrpt 25 178261.938 ± 2630.571 ops/s
## Results (Ubuntu, big runs)
NB - we have removed some test results we initially observed on Mac which were not later reproducible.
These take 3-4 hours
```
java -jar target/rocksdbjni-jmh-1.0-SNAPSHOT-benchmarks.jar -p keyCount=1000,50000 -p keySize=128 -p valueSize=1024,16384 -p columnFamilyTestType="1_column_family","20_column_families" GetBenchmarks.get GetBenchmarks.preallocatedByteBufferGet GetBenchmarks.preallocatedGet
@ -67,6 +46,13 @@ java -jar target/rocksdbjni-jmh-1.0-SNAPSHOT-benchmarks.jar -p keyCount=1000,500
It's clear that all `get()` variants have noticeably improved performance, though not the spectacular gains of the M1.
### With fixes for all of the `get()` instances
The tests which use methods which have had performance improvements applied are:
```java
get()
preallocatedGet()
preallocatedByteBufferGet()
```
Benchmark (columnFamilyTestType) (keyCount) (keySize) (valueSize) Mode Cnt Score Error Units
GetBenchmarks.get 1_column_family 1000 128 1024 thrpt 25 935648.793 ± 22879.910 ops/s
GetBenchmarks.get 1_column_family 1000 128 16384 thrpt 25 204366.301 ± 1326.570 ops/s
@ -159,3 +145,60 @@ GetBenchmarks.preallocatedGet no_column_families 1000
The performance improvement is real.
# Put Performance Benchmarks
Results associated with [Java API consistency between RocksDB.put() , .merge() and Transaction.put() , .merge()](https://github.com/facebook/rocksdb/pull/11019)
This work was not designed specifically as a performance optimization, but we want to confirm that it has not regressed what it has changed, and to provide
a baseline for future possible performance work.
## Build/Run
Building is as above. Running is a different invocation of the same JMH jar.
```
java -jar target/rocksdbjni-jmh-1.0-SNAPSHOT-benchmarks.jar -p keyCount=1000,50000 -p keySize=128 -p valueSize=1024,32768 -p columnFamilyTestType="no_column_family" PutBenchmarks
```
## Before Changes
These results were generated in a private branch with the `PutBenchmarks` from the PR backported onto the current *main*.
Benchmark (bufferListSize) (columnFamilyTestType) (keyCount) (keySize) (valueSize) Mode Cnt Score Error Units
PutBenchmarks.put 16 no_column_family 1000 128 1024 thrpt 25 76670.200 ± 2555.248 ops/s
PutBenchmarks.put 16 no_column_family 1000 128 32768 thrpt 25 3913.692 ± 225.690 ops/s
PutBenchmarks.put 16 no_column_family 50000 128 1024 thrpt 25 74479.589 ± 988.361 ops/s
PutBenchmarks.put 16 no_column_family 50000 128 32768 thrpt 25 4070.800 ± 194.838 ops/s
PutBenchmarks.putByteArrays 16 no_column_family 1000 128 1024 thrpt 25 72150.853 ± 1744.216 ops/s
PutBenchmarks.putByteArrays 16 no_column_family 1000 128 32768 thrpt 25 3896.646 ± 188.629 ops/s
PutBenchmarks.putByteArrays 16 no_column_family 50000 128 1024 thrpt 25 71753.287 ± 1053.904 ops/s
PutBenchmarks.putByteArrays 16 no_column_family 50000 128 32768 thrpt 25 3928.503 ± 264.443 ops/s
PutBenchmarks.putByteBuffers 16 no_column_family 1000 128 1024 thrpt 25 72595.105 ± 1027.258 ops/s
PutBenchmarks.putByteBuffers 16 no_column_family 1000 128 32768 thrpt 25 3890.100 ± 199.131 ops/s
PutBenchmarks.putByteBuffers 16 no_column_family 50000 128 1024 thrpt 25 70878.133 ± 1181.601 ops/s
PutBenchmarks.putByteBuffers 16 no_column_family 50000 128 32768 thrpt 25 3863.181 ± 215.888 ops/s
## After Changes
These results were generated on the PR branch.
Benchmark (bufferListSize) (columnFamilyTestType) (keyCount) (keySize) (valueSize) Mode Cnt Score Error Units
PutBenchmarks.put 16 no_column_family 1000 128 1024 thrpt 25 75178.751 ± 2644.775 ops/s
PutBenchmarks.put 16 no_column_family 1000 128 32768 thrpt 25 3937.175 ± 257.039 ops/s
PutBenchmarks.put 16 no_column_family 50000 128 1024 thrpt 25 74375.519 ± 1776.654 ops/s
PutBenchmarks.put 16 no_column_family 50000 128 32768 thrpt 25 4013.413 ± 257.706 ops/s
PutBenchmarks.putByteArrays 16 no_column_family 1000 128 1024 thrpt 25 71418.303 ± 1610.977 ops/s
PutBenchmarks.putByteArrays 16 no_column_family 1000 128 32768 thrpt 25 4027.581 ± 227.900 ops/s
PutBenchmarks.putByteArrays 16 no_column_family 50000 128 1024 thrpt 25 71229.107 ± 2720.083 ops/s
PutBenchmarks.putByteArrays 16 no_column_family 50000 128 32768 thrpt 25 4022.635 ± 212.540 ops/s
PutBenchmarks.putByteBuffers 16 no_column_family 1000 128 1024 thrpt 25 71718.501 ± 787.537 ops/s
PutBenchmarks.putByteBuffers 16 no_column_family 1000 128 32768 thrpt 25 4078.050 ± 176.331 ops/s
PutBenchmarks.putByteBuffers 16 no_column_family 50000 128 1024 thrpt 25 72736.754 ± 828.971 ops/s
PutBenchmarks.putByteBuffers 16 no_column_family 50000 128 32768 thrpt 25 3987.232 ± 205.577 ops/s
## Discussion
The changes don't appear to have had a material effect on performance. We are happy with this.
* We would obviously advise running future changes before and after to confirm they have no adverse effects.

View File

@ -150,7 +150,9 @@ JAVA_TESTS = \
org.rocksdb.LRUCacheTest\
org.rocksdb.MemoryUtilTest\
org.rocksdb.MemTableTest\
org.rocksdb.MergeCFVariantsTest\
org.rocksdb.MergeTest\
org.rocksdb.MergeVariantsTest\
org.rocksdb.MultiColumnRegressionTest \
org.rocksdb.MultiGetManyKeysTest\
org.rocksdb.MultiGetTest\
@ -167,6 +169,8 @@ JAVA_TESTS = \
org.rocksdb.OptionsTest\
org.rocksdb.PerfLevelTest \
org.rocksdb.PerfContextTest \
org.rocksdb.PutCFVariantsTest\
org.rocksdb.PutVariantsTest\
org.rocksdb.PlainTableConfigTest\
org.rocksdb.RateLimiterTest\
org.rocksdb.ReadOnlyTest\

View File

@ -6,18 +6,19 @@
*/
package org.rocksdb.jmh;
import org.openjdk.jmh.annotations.*;
import org.rocksdb.*;
import org.rocksdb.util.FileUtils;
import static org.rocksdb.util.KVUtils.ba;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import static org.rocksdb.util.KVUtils.ba;
import org.openjdk.jmh.annotations.*;
import org.rocksdb.*;
import org.rocksdb.util.FileUtils;
@State(Scope.Benchmark)
public class PutBenchmarks {
@ -30,12 +31,24 @@ public class PutBenchmarks {
})
String columnFamilyTestType;
@Param({"1000", "100000"}) int keyCount;
@Param({"12", "64", "128"}) int keySize;
@Param({"64", "1024", "65536"}) int valueSize;
@Param({"16"}) int bufferListSize;
Path dbDir;
DBOptions options;
int cfs = 0; // number of column families
private AtomicInteger cfHandlesIdx;
ColumnFamilyHandle[] cfHandles;
RocksDB db;
List<byte[]> keyBuffers = new ArrayList<>(bufferListSize);
List<byte[]> valueBuffers = new ArrayList<>(bufferListSize);
List<ByteBuffer> keyBuffersBB = new ArrayList<>(bufferListSize);
List<ByteBuffer> valueBuffersBB = new ArrayList<>(bufferListSize);
@Setup(Level.Trial)
public void setup() throws IOException, RocksDBException {
@ -68,6 +81,34 @@ public class PutBenchmarks {
final List<ColumnFamilyHandle> cfHandlesList = new ArrayList<>(cfDescriptors.size());
db = RocksDB.open(options, dbDir.toAbsolutePath().toString(), cfDescriptors, cfHandlesList);
cfHandles = cfHandlesList.toArray(new ColumnFamilyHandle[0]);
for (int i = 0; i < bufferListSize; i++) {
final byte[] keyArr = new byte[keySize];
Arrays.fill(keyArr, (byte) 0x30);
keyBuffers.add(keyArr);
}
for (int i = 0; i < bufferListSize; i++) {
final byte[] valueArr = new byte[valueSize];
Arrays.fill(valueArr, (byte) 0x30);
valueBuffers.add(valueArr);
}
for (int i = 0; i < bufferListSize; i++) {
final ByteBuffer keyBB = ByteBuffer.allocateDirect(keySize);
byte[] keyArr = new byte[keySize];
Arrays.fill(keyArr, (byte) 0x30);
keyBB.put(keyArr);
keyBuffersBB.add(keyBB);
}
for (int i = 0; i < bufferListSize; i++) {
final ByteBuffer valueBB = ByteBuffer.allocateDirect(valueSize);
byte[] valueArr = new byte[valueSize];
Arrays.fill(valueArr, (byte) 0x30);
valueBB.put(valueArr);
valueBuffersBB.add(valueBB);
}
}
@TearDown(Level.Trial)
@ -104,9 +145,79 @@ public class PutBenchmarks {
}
}
private <T> T borrow(final List<T> buffers) {
synchronized (buffers) {
while (true) {
if (buffers.isEmpty()) {
try {
Thread.sleep(1000);
} catch (InterruptedException ie) {
return null;
}
continue;
}
return buffers.remove(0);
}
}
}
private <T> void repay(final List<T> buffers, final T buffer) {
synchronized (buffers) {
buffers.add(buffer);
}
}
@Benchmark
public void put(final ComparatorBenchmarks.Counter counter) throws RocksDBException {
public void put(final Counter counter) throws RocksDBException {
byte[] keyBuf = borrow(keyBuffers);
byte[] valueBuf = borrow(valueBuffers);
final int i = counter.next();
db.put(getColumnFamily(), ba("key" + i), ba("value" + i));
final byte[] keyPrefix = ba("key" + i);
final byte[] valuePrefix = ba("value" + i);
System.arraycopy(keyPrefix, 0, keyBuf, 0, keyPrefix.length);
System.arraycopy(valuePrefix, 0, valueBuf, 0, valuePrefix.length);
db.put(getColumnFamily(), keyBuf, valueBuf);
repay(keyBuffers, keyBuf);
repay(valueBuffers, valueBuf);
}
@Benchmark
public void putByteArrays(final Counter counter) throws RocksDBException {
byte[] keyBuf = borrow(keyBuffers);
byte[] valueBuf = borrow(valueBuffers);
final int i = counter.next();
final byte[] keyPrefix = ba("key" + i);
final byte[] valuePrefix = ba("value" + i);
System.arraycopy(keyPrefix, 0, keyBuf, 0, keyPrefix.length);
System.arraycopy(valuePrefix, 0, valueBuf, 0, valuePrefix.length);
db.put(getColumnFamily(), new WriteOptions(), keyBuf, valueBuf);
repay(keyBuffers, keyBuf);
repay(valueBuffers, valueBuf);
}
@Benchmark
public void putByteBuffers(final Counter counter) throws RocksDBException {
ByteBuffer keyBuf = borrow(keyBuffersBB);
keyBuf.clear();
ByteBuffer valueBuf = borrow(valueBuffersBB);
valueBuf.clear();
final int i = counter.next();
final byte[] keyPrefix = ba("key" + i);
final byte[] valuePrefix = ba("value" + i);
keyBuf.put(keyPrefix, 0, keyPrefix.length);
keyBuf.position(keySize);
keyBuf.flip();
valueBuf.put(valuePrefix, 0, valuePrefix.length);
valueBuf.position(valueSize);
valueBuf.flip();
db.put(getColumnFamily(), new WriteOptions(), keyBuf, valueBuf);
repay(keyBuffersBB, keyBuf);
repay(valueBuffersBB, valueBuf);
}
}

View File

@ -21,6 +21,7 @@
<exclude name="FieldNamingConventions"/>
<exclude name="FormalParameterNamingConventions"/>
<exclude name="LinguisticNaming"/>
<exclude name="ConfusingTernary"/>
<!-- These could be fixed if we take the time to do it -->
<exclude name="CommentDefaultAccessModifier"/>
<exclude name="FieldDeclarationsShouldBeAtStartOfClass"/>

284
java/rocksjni/kv_helper.h Normal file
View File

@ -0,0 +1,284 @@
// 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).
//
// This file defines helper methods for Java API write methods
//
#pragma once
#include <jni.h>
#include <cstring>
#include <exception>
#include <functional>
#include <string>
#include "rocksdb/rocksdb_namespace.h"
#include "rocksdb/slice.h"
#include "rocksdb/status.h"
#include "rocksjni/portal.h"
namespace ROCKSDB_NAMESPACE {
/**
* @brief Exception class used to make the flow of key/value (Put(), Get(),
* Merge(), ...) calls clearer.
*
* This class is used by Java API JNI methods in try { save/fetch } catch { ...
* } style.
*
*/
class KVException : public std::exception {
public:
// These values are expected on Java API calls to represent the result of a
// Get() which has failed; a negative length is returned to indicate an error.
static const int kNotFound = -1; // the key was not found in RocksDB
static const int kStatusError =
-2; // there was some other error fetching the value for the key
/**
* @brief Throw a KVException (and potentially a Java exception) if the
* RocksDB status is "bad"
*
* @param env JNI environment needed to create a Java exception
* @param status RocksDB status to examine
*/
static void ThrowOnError(JNIEnv* env, const Status& status) {
if (status.ok()) {
return;
}
if (status.IsNotFound()) {
// IsNotFound does not generate a Java Exception, any other bad status
// does..
throw KVException(kNotFound);
}
ROCKSDB_NAMESPACE::RocksDBExceptionJni::ThrowNew(env, status);
throw KVException(kStatusError);
}
/**
* @brief Throw a KVException and a Java exception
*
* @param env JNI environment needed to create a Java exception
* @param message content of the exception we will throw
*/
static void ThrowNew(JNIEnv* env, const std::string& message) {
ROCKSDB_NAMESPACE::RocksDBExceptionJni::ThrowNew(env, message);
throw KVException(kStatusError);
}
/**
* @brief Throw a KVException if there is already a Java exception in the JNI
* enviroment
*
* @param env
*/
static void ThrowOnError(JNIEnv* env) {
if (env->ExceptionCheck()) {
throw KVException(kStatusError);
}
}
KVException(jint code) : kCode_(code){};
virtual const char* what() const throw() {
return "Exception raised by JNI. There may be a Java exception in the "
"JNIEnv. Please check!";
}
jint Code() const { return kCode_; }
private:
jint kCode_;
};
/**
* @brief Construct a slice with the contents of a Java byte array
*
* The slice refers to an array into which the Java byte array's whole region is
* copied
*/
class JByteArraySlice {
public:
JByteArraySlice(JNIEnv* env, const jbyteArray& jarr, const jint jarr_off,
const jint jarr_len)
: arr_(new jbyte[jarr_len]),
slice_(reinterpret_cast<char*>(arr_), jarr_len) {
env->GetByteArrayRegion(jarr, jarr_off, jarr_len, arr_);
KVException::ThrowOnError(env);
};
~JByteArraySlice() {
slice_.clear();
delete[] arr_;
};
Slice& slice() { return slice_; }
private:
jbyte* arr_;
Slice slice_;
};
/**
* @brief Construct a slice with the contents of a direct Java ByterBuffer
*
* The slice refers directly to the contents of the buffer, no copy is made.
*
*/
class JDirectBufferSlice {
public:
JDirectBufferSlice(JNIEnv* env, const jobject& jbuffer,
const jint jbuffer_off, const jint jbuffer_len)
: slice_(static_cast<char*>(env->GetDirectBufferAddress(jbuffer)) +
jbuffer_off,
jbuffer_len) {
KVException::ThrowOnError(env);
jlong capacity = env->GetDirectBufferCapacity(jbuffer);
if (capacity < jbuffer_off + jbuffer_len) {
auto message = "Direct buffer offset " + std::to_string(jbuffer_off) +
" + length " + std::to_string(jbuffer_len) +
" exceeds capacity " + std::to_string(capacity);
KVException::ThrowNew(env, message);
slice_.clear();
}
}
~JDirectBufferSlice() { slice_.clear(); };
Slice& slice() { return slice_; }
private:
Slice slice_;
};
/**
* @brief Wrap a pinnable slice with a method to retrieve the contents back into
* Java
*
* The Java Byte Array version sets the byte array's region from the slice
*/
class JByteArrayPinnableSlice {
public:
/**
* @brief Construct a new JByteArrayPinnableSlice object referring to an
* existing java byte buffer
*
* @param env
* @param jbuffer
* @param jbuffer_off
* @param jbuffer_len
*/
JByteArrayPinnableSlice(JNIEnv* env, const jbyteArray& jbuffer,
const jint jbuffer_off, const jint jbuffer_len)
: env_(env),
jbuffer_(jbuffer),
jbuffer_off_(jbuffer_off),
jbuffer_len_(jbuffer_len){};
/**
* @brief Construct an empty new JByteArrayPinnableSlice object
*
*/
JByteArrayPinnableSlice(JNIEnv* env) : env_(env){};
PinnableSlice& pinnable_slice() { return pinnable_slice_; }
~JByteArrayPinnableSlice() { pinnable_slice_.Reset(); };
/**
* @brief copy back contents of the pinnable slice into the Java ByteBuffer
*
* @return jint min of size of buffer and number of bytes in value for
* requested key
*/
jint Fetch() {
const jint pinnable_len = static_cast<jint>(pinnable_slice_.size());
const jint result_len = std::min(jbuffer_len_, pinnable_len);
env_->SetByteArrayRegion(
jbuffer_, jbuffer_off_, result_len,
reinterpret_cast<const jbyte*>(pinnable_slice_.data()));
KVException::ThrowOnError(
env_); // exception thrown: ArrayIndexOutOfBoundsException
return pinnable_len;
};
/**
* @brief create a new Java buffer and copy the result into it
*
* @return jbyteArray the java buffer holding the result
*/
jbyteArray NewByteArray() {
const jint pinnable_len = static_cast<jint>(pinnable_slice_.size());
jbyteArray jbuffer = env_->NewByteArray(static_cast<jsize>(pinnable_len));
KVException::ThrowOnError(env_); // OutOfMemoryError
env_->SetByteArrayRegion(
jbuffer, 0, pinnable_len,
reinterpret_cast<const jbyte*>(pinnable_slice_.data()));
KVException::ThrowOnError(env_); // ArrayIndexOutOfBoundsException
return jbuffer;
}
private:
JNIEnv* env_;
jbyteArray jbuffer_;
jint jbuffer_off_;
jint jbuffer_len_;
PinnableSlice pinnable_slice_;
};
/**
* @brief Wrap a pinnable slice with a method to retrieve the contents back into
* Java
*
* The Java Direct Buffer version copies the memory of the buffer from the slice
*/
class JDirectBufferPinnableSlice {
public:
JDirectBufferPinnableSlice(JNIEnv* env, const jobject& jbuffer,
const jint jbuffer_off, const jint jbuffer_len)
: buffer_(static_cast<char*>(env->GetDirectBufferAddress(jbuffer)) +
jbuffer_off),
jbuffer_len_(jbuffer_len) {
jlong capacity = env->GetDirectBufferCapacity(jbuffer);
if (capacity < jbuffer_off + jbuffer_len) {
auto message =
"Invalid value argument. Capacity is less than requested region. "
"offset " +
std::to_string(jbuffer_off) + " + length " +
std::to_string(jbuffer_len) + " exceeds capacity " +
std::to_string(capacity);
KVException::ThrowNew(env, message);
}
}
PinnableSlice& pinnable_slice() { return pinnable_slice_; }
~JDirectBufferPinnableSlice() { pinnable_slice_.Reset(); };
/**
* @brief copy back contents of the pinnable slice into the Java DirectBuffer
*
* @return jint min of size of buffer and number of bytes in value for
* requested key
*/
jint Fetch() {
const jint pinnable_len = static_cast<jint>(pinnable_slice_.size());
const jint result_len = std::min(jbuffer_len_, pinnable_len);
memcpy(buffer_, pinnable_slice_.data(), result_len);
return pinnable_len;
};
private:
char* buffer_;
jint jbuffer_len_;
PinnableSlice pinnable_slice_;
};
} // namespace ROCKSDB_NAMESPACE

View File

@ -26,6 +26,7 @@
#include "rocksdb/types.h"
#include "rocksdb/version.h"
#include "rocksjni/cplusplus_to_java_convert.h"
#include "rocksjni/kv_helper.h"
#include "rocksjni/portal.h"
#ifdef min
@ -600,55 +601,6 @@ void Java_org_rocksdb_RocksDB_dropColumnFamilies(
//////////////////////////////////////////////////////////////////////////////
// ROCKSDB_NAMESPACE::DB::Put
/**
* @return true if the put succeeded, false if a Java Exception was thrown
*/
bool rocksdb_put_helper(JNIEnv* env, ROCKSDB_NAMESPACE::DB* db,
const ROCKSDB_NAMESPACE::WriteOptions& write_options,
ROCKSDB_NAMESPACE::ColumnFamilyHandle* cf_handle,
jbyteArray jkey, jint jkey_off, jint jkey_len,
jbyteArray jval, jint jval_off, jint jval_len) {
jbyte* key = new jbyte[jkey_len];
env->GetByteArrayRegion(jkey, jkey_off, jkey_len, key);
if (env->ExceptionCheck()) {
// exception thrown: ArrayIndexOutOfBoundsException
delete[] key;
return false;
}
jbyte* value = new jbyte[jval_len];
env->GetByteArrayRegion(jval, jval_off, jval_len, value);
if (env->ExceptionCheck()) {
// exception thrown: ArrayIndexOutOfBoundsException
delete[] value;
delete[] key;
return false;
}
ROCKSDB_NAMESPACE::Slice key_slice(reinterpret_cast<char*>(key), jkey_len);
ROCKSDB_NAMESPACE::Slice value_slice(reinterpret_cast<char*>(value),
jval_len);
ROCKSDB_NAMESPACE::Status s;
if (cf_handle != nullptr) {
s = db->Put(write_options, cf_handle, key_slice, value_slice);
} else {
// backwards compatibility
s = db->Put(write_options, key_slice, value_slice);
}
// cleanup
delete[] value;
delete[] key;
if (s.ok()) {
return true;
} else {
ROCKSDB_NAMESPACE::RocksDBExceptionJni::ThrowNew(env, s);
return false;
}
}
/*
* Class: org_rocksdb_RocksDB
* Method: put
@ -662,8 +614,14 @@ void Java_org_rocksdb_RocksDB_put__J_3BII_3BII(JNIEnv* env, jobject,
auto* db = reinterpret_cast<ROCKSDB_NAMESPACE::DB*>(jdb_handle);
static const ROCKSDB_NAMESPACE::WriteOptions default_write_options =
ROCKSDB_NAMESPACE::WriteOptions();
rocksdb_put_helper(env, db, default_write_options, nullptr, jkey, jkey_off,
jkey_len, jval, jval_off, jval_len);
try {
ROCKSDB_NAMESPACE::JByteArraySlice key(env, jkey, jkey_off, jkey_len);
ROCKSDB_NAMESPACE::JByteArraySlice value(env, jval, jval_off, jval_len);
ROCKSDB_NAMESPACE::KVException::ThrowOnError(
env, db->Put(default_write_options, key.slice(), value.slice()));
} catch (ROCKSDB_NAMESPACE::KVException&) {
return;
}
}
/*
@ -682,13 +640,21 @@ void Java_org_rocksdb_RocksDB_put__J_3BII_3BIIJ(JNIEnv* env, jobject,
ROCKSDB_NAMESPACE::WriteOptions();
auto* cf_handle =
reinterpret_cast<ROCKSDB_NAMESPACE::ColumnFamilyHandle*>(jcf_handle);
if (cf_handle != nullptr) {
rocksdb_put_helper(env, db, default_write_options, cf_handle, jkey,
jkey_off, jkey_len, jval, jval_off, jval_len);
} else {
if (cf_handle == nullptr) {
ROCKSDB_NAMESPACE::RocksDBExceptionJni::ThrowNew(
env, ROCKSDB_NAMESPACE::Status::InvalidArgument(
"Invalid ColumnFamilyHandle."));
return;
}
try {
ROCKSDB_NAMESPACE::JByteArraySlice key(env, jkey, jkey_off, jkey_len);
ROCKSDB_NAMESPACE::JByteArraySlice value(env, jval, jval_off, jval_len);
ROCKSDB_NAMESPACE::KVException::ThrowOnError(
env,
db->Put(default_write_options, cf_handle, key.slice(), value.slice()));
} catch (ROCKSDB_NAMESPACE::KVException&) {
return;
}
}
@ -706,8 +672,15 @@ void Java_org_rocksdb_RocksDB_put__JJ_3BII_3BII(JNIEnv* env, jobject,
auto* db = reinterpret_cast<ROCKSDB_NAMESPACE::DB*>(jdb_handle);
auto* write_options =
reinterpret_cast<ROCKSDB_NAMESPACE::WriteOptions*>(jwrite_options_handle);
rocksdb_put_helper(env, db, *write_options, nullptr, jkey, jkey_off, jkey_len,
jval, jval_off, jval_len);
try {
ROCKSDB_NAMESPACE::JByteArraySlice key(env, jkey, jkey_off, jkey_len);
ROCKSDB_NAMESPACE::JByteArraySlice value(env, jval, jval_off, jval_len);
ROCKSDB_NAMESPACE::KVException::ThrowOnError(
env, db->Put(*write_options, key.slice(), value.slice()));
} catch (ROCKSDB_NAMESPACE::KVException&) {
return;
}
}
/*
@ -724,13 +697,19 @@ void Java_org_rocksdb_RocksDB_put__JJ_3BII_3BIIJ(
reinterpret_cast<ROCKSDB_NAMESPACE::WriteOptions*>(jwrite_options_handle);
auto* cf_handle =
reinterpret_cast<ROCKSDB_NAMESPACE::ColumnFamilyHandle*>(jcf_handle);
if (cf_handle != nullptr) {
rocksdb_put_helper(env, db, *write_options, cf_handle, jkey, jkey_off,
jkey_len, jval, jval_off, jval_len);
} else {
if (cf_handle == nullptr) {
ROCKSDB_NAMESPACE::RocksDBExceptionJni::ThrowNew(
env, ROCKSDB_NAMESPACE::Status::InvalidArgument(
"Invalid ColumnFamilyHandle."));
return;
}
try {
ROCKSDB_NAMESPACE::JByteArraySlice key(env, jkey, jkey_off, jkey_len);
ROCKSDB_NAMESPACE::JByteArraySlice value(env, jval, jval_off, jval_len);
ROCKSDB_NAMESPACE::KVException::ThrowOnError(
env, db->Put(*write_options, cf_handle, key.slice(), value.slice()));
} catch (ROCKSDB_NAMESPACE::KVException&) {
return;
}
}
@ -1309,53 +1288,6 @@ jint Java_org_rocksdb_RocksDB_getDirect(JNIEnv* env, jobject /*jdb*/,
//////////////////////////////////////////////////////////////////////////////
// ROCKSDB_NAMESPACE::DB::Merge
/**
* @return true if the merge succeeded, false if a Java Exception was thrown
*/
bool rocksdb_merge_helper(JNIEnv* env, ROCKSDB_NAMESPACE::DB* db,
const ROCKSDB_NAMESPACE::WriteOptions& write_options,
ROCKSDB_NAMESPACE::ColumnFamilyHandle* cf_handle,
jbyteArray jkey, jint jkey_off, jint jkey_len,
jbyteArray jval, jint jval_off, jint jval_len) {
jbyte* key = new jbyte[jkey_len];
env->GetByteArrayRegion(jkey, jkey_off, jkey_len, key);
if (env->ExceptionCheck()) {
// exception thrown: ArrayIndexOutOfBoundsException
delete[] key;
return false;
}
ROCKSDB_NAMESPACE::Slice key_slice(reinterpret_cast<char*>(key), jkey_len);
jbyte* value = new jbyte[jval_len];
env->GetByteArrayRegion(jval, jval_off, jval_len, value);
if (env->ExceptionCheck()) {
// exception thrown: ArrayIndexOutOfBoundsException
delete[] value;
delete[] key;
return false;
}
ROCKSDB_NAMESPACE::Slice value_slice(reinterpret_cast<char*>(value),
jval_len);
ROCKSDB_NAMESPACE::Status s;
if (cf_handle != nullptr) {
s = db->Merge(write_options, cf_handle, key_slice, value_slice);
} else {
s = db->Merge(write_options, key_slice, value_slice);
}
// cleanup
delete[] value;
delete[] key;
if (s.ok()) {
return true;
}
ROCKSDB_NAMESPACE::RocksDBExceptionJni::ThrowNew(env, s);
return false;
}
/*
* Class: org_rocksdb_RocksDB
* Method: merge
@ -1369,8 +1301,14 @@ void Java_org_rocksdb_RocksDB_merge__J_3BII_3BII(JNIEnv* env, jobject,
auto* db = reinterpret_cast<ROCKSDB_NAMESPACE::DB*>(jdb_handle);
static const ROCKSDB_NAMESPACE::WriteOptions default_write_options =
ROCKSDB_NAMESPACE::WriteOptions();
rocksdb_merge_helper(env, db, default_write_options, nullptr, jkey, jkey_off,
jkey_len, jval, jval_off, jval_len);
try {
ROCKSDB_NAMESPACE::JByteArraySlice key(env, jkey, jkey_off, jkey_len);
ROCKSDB_NAMESPACE::JByteArraySlice value(env, jval, jval_off, jval_len);
ROCKSDB_NAMESPACE::KVException::ThrowOnError(
env, db->Merge(default_write_options, key.slice(), value.slice()));
} catch (ROCKSDB_NAMESPACE::KVException&) {
return;
}
}
/*
@ -1388,8 +1326,15 @@ void Java_org_rocksdb_RocksDB_merge__J_3BII_3BIIJ(
auto* cf_handle =
reinterpret_cast<ROCKSDB_NAMESPACE::ColumnFamilyHandle*>(jcf_handle);
if (cf_handle != nullptr) {
rocksdb_merge_helper(env, db, default_write_options, cf_handle, jkey,
jkey_off, jkey_len, jval, jval_off, jval_len);
try {
ROCKSDB_NAMESPACE::JByteArraySlice key(env, jkey, jkey_off, jkey_len);
ROCKSDB_NAMESPACE::JByteArraySlice value(env, jval, jval_off, jval_len);
ROCKSDB_NAMESPACE::KVException::ThrowOnError(
env, db->Merge(default_write_options, cf_handle, key.slice(),
value.slice()));
} catch (ROCKSDB_NAMESPACE::KVException&) {
return;
}
} else {
ROCKSDB_NAMESPACE::RocksDBExceptionJni::ThrowNew(
env, ROCKSDB_NAMESPACE::Status::InvalidArgument(
@ -1409,8 +1354,14 @@ void Java_org_rocksdb_RocksDB_merge__JJ_3BII_3BII(
auto* db = reinterpret_cast<ROCKSDB_NAMESPACE::DB*>(jdb_handle);
auto* write_options =
reinterpret_cast<ROCKSDB_NAMESPACE::WriteOptions*>(jwrite_options_handle);
rocksdb_merge_helper(env, db, *write_options, nullptr, jkey, jkey_off,
jkey_len, jval, jval_off, jval_len);
try {
ROCKSDB_NAMESPACE::JByteArraySlice key(env, jkey, jkey_off, jkey_len);
ROCKSDB_NAMESPACE::JByteArraySlice value(env, jval, jval_off, jval_len);
ROCKSDB_NAMESPACE::KVException::ThrowOnError(
env, db->Merge(*write_options, key.slice(), value.slice()));
} catch (ROCKSDB_NAMESPACE::KVException&) {
return;
}
}
/*
@ -1428,8 +1379,15 @@ void Java_org_rocksdb_RocksDB_merge__JJ_3BII_3BIIJ(
auto* cf_handle =
reinterpret_cast<ROCKSDB_NAMESPACE::ColumnFamilyHandle*>(jcf_handle);
if (cf_handle != nullptr) {
rocksdb_merge_helper(env, db, *write_options, cf_handle, jkey, jkey_off,
jkey_len, jval, jval_off, jval_len);
try {
ROCKSDB_NAMESPACE::JByteArraySlice key(env, jkey, jkey_off, jkey_len);
ROCKSDB_NAMESPACE::JByteArraySlice value(env, jval, jval_off, jval_len);
ROCKSDB_NAMESPACE::KVException::ThrowOnError(
env,
db->Merge(*write_options, cf_handle, key.slice(), value.slice()));
} catch (ROCKSDB_NAMESPACE::KVException&) {
return;
}
} else {
ROCKSDB_NAMESPACE::RocksDBExceptionJni::ThrowNew(
env, ROCKSDB_NAMESPACE::Status::InvalidArgument(
@ -1437,16 +1395,37 @@ void Java_org_rocksdb_RocksDB_merge__JJ_3BII_3BIIJ(
}
}
jlong rocksdb_iterator_helper(
ROCKSDB_NAMESPACE::DB* db, ROCKSDB_NAMESPACE::ReadOptions read_options,
ROCKSDB_NAMESPACE::ColumnFamilyHandle* cf_handle) {
ROCKSDB_NAMESPACE::Iterator* iterator = nullptr;
if (cf_handle != nullptr) {
iterator = db->NewIterator(read_options, cf_handle);
} else {
iterator = db->NewIterator(read_options);
}
return GET_CPLUSPLUS_POINTER(iterator);
/*
* Class: org_rocksdb_RocksDB
* Method: mergeDirect
* Signature: (JJLjava/nio/ByteBuffer;IILjava/nio/ByteBuffer;IIJ)V
*/
void Java_org_rocksdb_RocksDB_mergeDirect(
JNIEnv* env, jobject /*jdb*/, jlong jdb_handle, jlong jwrite_options_handle,
jobject jkey, jint jkey_off, jint jkey_len, jobject jval, jint jval_off,
jint jval_len, jlong jcf_handle) {
auto* db = reinterpret_cast<ROCKSDB_NAMESPACE::DB*>(jdb_handle);
auto* write_options =
reinterpret_cast<ROCKSDB_NAMESPACE::WriteOptions*>(jwrite_options_handle);
auto* cf_handle =
reinterpret_cast<ROCKSDB_NAMESPACE::ColumnFamilyHandle*>(jcf_handle);
auto merge = [&env, &db, &cf_handle, &write_options](
ROCKSDB_NAMESPACE::Slice& key,
ROCKSDB_NAMESPACE::Slice& value) {
ROCKSDB_NAMESPACE::Status s;
if (cf_handle == nullptr) {
s = db->Merge(*write_options, key, value);
} else {
s = db->Merge(*write_options, cf_handle, key, value);
}
if (s.ok()) {
return;
}
ROCKSDB_NAMESPACE::RocksDBExceptionJni::ThrowNew(env, s);
};
ROCKSDB_NAMESPACE::JniUtil::kv_op_direct(merge, env, jkey, jkey_off, jkey_len,
jval, jval_off, jval_len);
}
/*
@ -2644,55 +2623,17 @@ jobjectArray Java_org_rocksdb_RocksDB_keyMayExistFoundValue(
/*
* Class: org_rocksdb_RocksDB
* Method: iterator
* Signature: (J)J
*/
jlong Java_org_rocksdb_RocksDB_iterator__J(JNIEnv*, jobject, jlong db_handle) {
auto* db = reinterpret_cast<ROCKSDB_NAMESPACE::DB*>(db_handle);
return rocksdb_iterator_helper(db, ROCKSDB_NAMESPACE::ReadOptions(), nullptr);
}
/*
* Class: org_rocksdb_RocksDB
* Method: iterator
* Signature: (JJ)J
*/
jlong Java_org_rocksdb_RocksDB_iterator__JJ(JNIEnv*, jobject, jlong db_handle,
jlong jread_options_handle) {
auto* db = reinterpret_cast<ROCKSDB_NAMESPACE::DB*>(db_handle);
auto& read_options =
*reinterpret_cast<ROCKSDB_NAMESPACE::ReadOptions*>(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*, jobject, jlong db_handle,
jlong jcf_handle) {
auto* db = reinterpret_cast<ROCKSDB_NAMESPACE::DB*>(db_handle);
auto* cf_handle =
reinterpret_cast<ROCKSDB_NAMESPACE::ColumnFamilyHandle*>(jcf_handle);
return rocksdb_iterator_helper(db, ROCKSDB_NAMESPACE::ReadOptions(),
cf_handle);
}
/*
* Class: org_rocksdb_RocksDB
* Method: iteratorCF
* Signature: (JJJ)J
*/
jlong Java_org_rocksdb_RocksDB_iteratorCF__JJJ(JNIEnv*, jobject,
jlong db_handle,
jlong jcf_handle,
jlong jread_options_handle) {
jlong Java_org_rocksdb_RocksDB_iterator(JNIEnv*, jobject, jlong db_handle,
jlong jcf_handle,
jlong jread_options_handle) {
auto* db = reinterpret_cast<ROCKSDB_NAMESPACE::DB*>(db_handle);
auto* cf_handle =
reinterpret_cast<ROCKSDB_NAMESPACE::ColumnFamilyHandle*>(jcf_handle);
auto& read_options =
*reinterpret_cast<ROCKSDB_NAMESPACE::ReadOptions*>(jread_options_handle);
return rocksdb_iterator_helper(db, read_options, cf_handle);
return GET_CPLUSPLUS_POINTER(db->NewIterator(read_options, cf_handle));
}
/*

View File

@ -14,6 +14,7 @@
#include "include/org_rocksdb_Transaction.h"
#include "rocksjni/cplusplus_to_java_convert.h"
#include "rocksjni/kv_helper.h"
#include "rocksjni/portal.h"
#if defined(_MSC_VER)
@ -158,90 +159,113 @@ typedef std::function<ROCKSDB_NAMESPACE::Status(
std::string*)>
FnGet;
// TODO(AR) consider refactoring to share this between here and rocksjni.cc
jbyteArray txn_get_helper(JNIEnv* env, const FnGet& fn_get,
const jlong& jread_options_handle,
const jbyteArray& jkey, const jint& jkey_part_len) {
jbyte* key = env->GetByteArrayElements(jkey, nullptr);
if (key == nullptr) {
// exception thrown: OutOfMemoryError
return nullptr;
}
ROCKSDB_NAMESPACE::Slice key_slice(reinterpret_cast<char*>(key),
jkey_part_len);
auto* read_options =
reinterpret_cast<ROCKSDB_NAMESPACE::ReadOptions*>(jread_options_handle);
std::string value;
ROCKSDB_NAMESPACE::Status s = fn_get(*read_options, key_slice, &value);
// trigger java unref on key.
// 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.IsNotFound()) {
return nullptr;
}
if (s.ok()) {
jbyteArray jret_value = env->NewByteArray(static_cast<jsize>(value.size()));
if (jret_value == nullptr) {
// exception thrown: OutOfMemoryError
return nullptr;
}
env->SetByteArrayRegion(
jret_value, 0, static_cast<jsize>(value.size()),
const_cast<jbyte*>(reinterpret_cast<const jbyte*>(value.c_str())));
if (env->ExceptionCheck()) {
// exception thrown: ArrayIndexOutOfBoundsException
return nullptr;
}
return jret_value;
}
ROCKSDB_NAMESPACE::RocksDBExceptionJni::ThrowNew(env, s);
return nullptr;
}
/*
* Class: org_rocksdb_Transaction
* Method: get
* Signature: (JJ[BIJ)[B
* Signature: (JJ[BIIJ)[B
*/
jbyteArray Java_org_rocksdb_Transaction_get__JJ_3BIJ(
jbyteArray Java_org_rocksdb_Transaction_get__JJ_3BIIJ(
JNIEnv* env, jobject /*jobj*/, jlong jhandle, jlong jread_options_handle,
jbyteArray jkey, jint jkey_part_len, jlong jcolumn_family_handle) {
jbyteArray jkey, jint jkey_off, jint jkey_part_len,
jlong jcolumn_family_handle) {
auto* txn = reinterpret_cast<ROCKSDB_NAMESPACE::Transaction*>(jhandle);
auto* read_options =
reinterpret_cast<ROCKSDB_NAMESPACE::ReadOptions*>(jread_options_handle);
auto* column_family_handle =
reinterpret_cast<ROCKSDB_NAMESPACE::ColumnFamilyHandle*>(
jcolumn_family_handle);
FnGet fn_get =
std::bind<ROCKSDB_NAMESPACE::Status (ROCKSDB_NAMESPACE::Transaction::*)(
const ROCKSDB_NAMESPACE::ReadOptions&,
ROCKSDB_NAMESPACE::ColumnFamilyHandle*,
const ROCKSDB_NAMESPACE::Slice&, std::string*)>(
&ROCKSDB_NAMESPACE::Transaction::Get, txn, std::placeholders::_1,
column_family_handle, std::placeholders::_2, std::placeholders::_3);
return txn_get_helper(env, fn_get, jread_options_handle, jkey, jkey_part_len);
try {
ROCKSDB_NAMESPACE::JByteArraySlice key(env, jkey, jkey_off, jkey_part_len);
ROCKSDB_NAMESPACE::JByteArrayPinnableSlice value(env);
ROCKSDB_NAMESPACE::KVException::ThrowOnError(
env, txn->Get(*read_options, column_family_handle, key.slice(),
&value.pinnable_slice()));
return value.NewByteArray();
} catch (ROCKSDB_NAMESPACE::KVException&) {
return nullptr;
}
}
/*
* Class: org_rocksdb_Transaction
* Method: get
* Signature: (JJ[BI)[B
* Signature: (JJ[BII)[B
*/
jbyteArray Java_org_rocksdb_Transaction_get__JJ_3BI(
jbyteArray Java_org_rocksdb_Transaction_get__JJ_3BII(
JNIEnv* env, jobject /*jobj*/, jlong jhandle, jlong jread_options_handle,
jbyteArray jkey, jint jkey_part_len) {
jbyteArray jkey, jint jkey_off, jint jkey_part_len) {
auto* txn = reinterpret_cast<ROCKSDB_NAMESPACE::Transaction*>(jhandle);
FnGet fn_get =
std::bind<ROCKSDB_NAMESPACE::Status (ROCKSDB_NAMESPACE::Transaction::*)(
const ROCKSDB_NAMESPACE::ReadOptions&,
const ROCKSDB_NAMESPACE::Slice&, std::string*)>(
&ROCKSDB_NAMESPACE::Transaction::Get, txn, std::placeholders::_1,
std::placeholders::_2, std::placeholders::_3);
return txn_get_helper(env, fn_get, jread_options_handle, jkey, jkey_part_len);
auto* read_options =
reinterpret_cast<ROCKSDB_NAMESPACE::ReadOptions*>(jread_options_handle);
try {
ROCKSDB_NAMESPACE::JByteArraySlice key(env, jkey, jkey_off, jkey_part_len);
ROCKSDB_NAMESPACE::JByteArrayPinnableSlice value(env);
ROCKSDB_NAMESPACE::KVException::ThrowOnError(
env, txn->Get(*read_options, key.slice(), &value.pinnable_slice()));
return value.NewByteArray();
} catch (ROCKSDB_NAMESPACE::KVException&) {
return nullptr;
}
}
/*
* Class: org_rocksdb_Transaction
* Method: get
* Signature: (JJ[BII[BIIJ)I
*/
jint Java_org_rocksdb_Transaction_get__JJ_3BII_3BIIJ(
JNIEnv* env, jobject /*jobj*/, jlong jhandle, jlong jread_options_handle,
jbyteArray jkey, jint jkey_off, jint jkey_part_len, jbyteArray jval,
jint jval_off, jint jval_part_len, jlong jcolumn_family_handle) {
auto* txn = reinterpret_cast<ROCKSDB_NAMESPACE::Transaction*>(jhandle);
auto* read_options =
reinterpret_cast<ROCKSDB_NAMESPACE::ReadOptions*>(jread_options_handle);
auto* column_family_handle =
reinterpret_cast<ROCKSDB_NAMESPACE::ColumnFamilyHandle*>(
jcolumn_family_handle);
try {
ROCKSDB_NAMESPACE::JByteArraySlice key(env, jkey, jkey_off, jkey_part_len);
ROCKSDB_NAMESPACE::JByteArrayPinnableSlice value(env, jval, jval_off,
jval_part_len);
ROCKSDB_NAMESPACE::KVException::ThrowOnError(
env, txn->Get(*read_options, column_family_handle, key.slice(),
&value.pinnable_slice()));
return value.Fetch();
} catch (const ROCKSDB_NAMESPACE::KVException& e) {
return e.Code();
}
}
/*
* Class: org_rocksdb_Transaction
* Method: getDirect
* Signature: (JJLjava/nio/ByteBuffer;IILjava/nio/ByteBuffer;IIJ)I
*/
jint Java_org_rocksdb_Transaction_getDirect(JNIEnv* env, jobject, jlong jhandle,
jlong jread_options_handle,
jobject jkey_bb, jint jkey_off,
jint jkey_part_len, jobject jval_bb,
jint jval_off, jint jval_part_len,
jlong jcolumn_family_handle) {
auto* txn = reinterpret_cast<ROCKSDB_NAMESPACE::Transaction*>(jhandle);
auto* read_options =
reinterpret_cast<ROCKSDB_NAMESPACE::ReadOptions*>(jread_options_handle);
auto* column_family_handle =
reinterpret_cast<ROCKSDB_NAMESPACE::ColumnFamilyHandle*>(
jcolumn_family_handle);
try {
ROCKSDB_NAMESPACE::JDirectBufferSlice key(env, jkey_bb, jkey_off,
jkey_part_len);
ROCKSDB_NAMESPACE::JDirectBufferPinnableSlice value(env, jval_bb, jval_off,
jval_part_len);
ROCKSDB_NAMESPACE::KVException::ThrowOnError(
env, txn->Get(*read_options, column_family_handle, key.slice(),
&value.pinnable_slice()));
return value.Fetch();
} catch (const ROCKSDB_NAMESPACE::KVException& e) {
return e.Code();
}
}
// TODO(AR) consider refactoring to share this between here and rocksjni.cc
@ -428,47 +452,91 @@ jobjectArray Java_org_rocksdb_Transaction_multiGet__JJ_3_3B(
/*
* Class: org_rocksdb_Transaction
* Method: getForUpdate
* Signature: (JJ[BIJZZ)[B
* Signature: (JJ[BIIJZZ)[B
*/
jbyteArray Java_org_rocksdb_Transaction_getForUpdate__JJ_3BIJZZ(
jbyteArray Java_org_rocksdb_Transaction_getForUpdate__JJ_3BIIJZZ(
JNIEnv* env, jobject /*jobj*/, jlong jhandle, jlong jread_options_handle,
jbyteArray jkey, jint jkey_part_len, jlong jcolumn_family_handle,
jboolean jexclusive, jboolean jdo_validate) {
jbyteArray jkey, jint jkey_off, jint jkey_part_len,
jlong jcolumn_family_handle, jboolean jexclusive, jboolean jdo_validate) {
auto* read_options =
reinterpret_cast<ROCKSDB_NAMESPACE::ReadOptions*>(jread_options_handle);
auto* column_family_handle =
reinterpret_cast<ROCKSDB_NAMESPACE::ColumnFamilyHandle*>(
jcolumn_family_handle);
auto* txn = reinterpret_cast<ROCKSDB_NAMESPACE::Transaction*>(jhandle);
FnGet fn_get_for_update =
std::bind<ROCKSDB_NAMESPACE::Status (ROCKSDB_NAMESPACE::Transaction::*)(
const ROCKSDB_NAMESPACE::ReadOptions&,
ROCKSDB_NAMESPACE::ColumnFamilyHandle*,
const ROCKSDB_NAMESPACE::Slice&, std::string*, bool, bool)>(
&ROCKSDB_NAMESPACE::Transaction::GetForUpdate, txn,
std::placeholders::_1, column_family_handle, std::placeholders::_2,
std::placeholders::_3, jexclusive, jdo_validate);
return txn_get_helper(env, fn_get_for_update, jread_options_handle, jkey,
jkey_part_len);
try {
ROCKSDB_NAMESPACE::JByteArraySlice key(env, jkey, jkey_off, jkey_part_len);
ROCKSDB_NAMESPACE::JByteArrayPinnableSlice value(env);
ROCKSDB_NAMESPACE::KVException::ThrowOnError(
env,
txn->GetForUpdate(*read_options, column_family_handle, key.slice(),
&value.pinnable_slice(), jexclusive, jdo_validate));
return value.NewByteArray();
} catch (ROCKSDB_NAMESPACE::KVException&) {
return nullptr;
}
}
/*
* Class: org_rocksdb_Transaction
* Method: getForUpdate
* Signature: (JJ[BIZZ)[B
* Signature: (JJ[BII[BIIJZZ)I
*/
jbyteArray Java_org_rocksdb_Transaction_getForUpdate__JJ_3BIZZ(
jint Java_org_rocksdb_Transaction_getForUpdate__JJ_3BII_3BIIJZZ(
JNIEnv* env, jobject /*jobj*/, jlong jhandle, jlong jread_options_handle,
jbyteArray jkey, jint jkey_part_len, jboolean jexclusive,
jboolean jdo_validate) {
jbyteArray jkey, jint jkey_off, jint jkey_part_len, jbyteArray jval,
jint jval_off, jint jval_len, jlong jcolumn_family_handle,
jboolean jexclusive, jboolean jdo_validate) {
auto* txn = reinterpret_cast<ROCKSDB_NAMESPACE::Transaction*>(jhandle);
FnGet fn_get_for_update =
std::bind<ROCKSDB_NAMESPACE::Status (ROCKSDB_NAMESPACE::Transaction::*)(
const ROCKSDB_NAMESPACE::ReadOptions&,
const ROCKSDB_NAMESPACE::Slice&, std::string*, bool, bool)>(
&ROCKSDB_NAMESPACE::Transaction::GetForUpdate, txn,
std::placeholders::_1, std::placeholders::_2, std::placeholders::_3,
jexclusive, jdo_validate);
return txn_get_helper(env, fn_get_for_update, jread_options_handle, jkey,
jkey_part_len);
auto* read_options =
reinterpret_cast<ROCKSDB_NAMESPACE::ReadOptions*>(jread_options_handle);
auto* column_family_handle =
reinterpret_cast<ROCKSDB_NAMESPACE::ColumnFamilyHandle*>(
jcolumn_family_handle);
try {
ROCKSDB_NAMESPACE::JByteArraySlice key(env, jkey, jkey_off, jkey_part_len);
ROCKSDB_NAMESPACE::JByteArrayPinnableSlice value(env, jval, jval_off,
jval_len);
ROCKSDB_NAMESPACE::KVException::ThrowOnError(
env,
txn->GetForUpdate(*read_options, column_family_handle, key.slice(),
&value.pinnable_slice(), jexclusive, jdo_validate));
return value.Fetch();
} catch (const ROCKSDB_NAMESPACE::KVException& e) {
return e.Code();
}
}
/*
* Class: org_rocksdb_Transaction
* Method: getDirectForUpdate
* Signature: (JJLjava/nio/ByteBuffer;IILjava/nio/ByteBuffer;IIJZZ)I
*/
jint Java_org_rocksdb_Transaction_getDirectForUpdate(
JNIEnv* env, jobject /*jobj*/, jlong jhandle, jlong jread_options_handle,
jobject jkey_bb, jint jkey_off, jint jkey_part_len, jobject jval_bb,
jint jval_off, jint jval_len, jlong jcolumn_family_handle,
jboolean jexclusive, jboolean jdo_validate) {
auto* txn = reinterpret_cast<ROCKSDB_NAMESPACE::Transaction*>(jhandle);
auto* read_options =
reinterpret_cast<ROCKSDB_NAMESPACE::ReadOptions*>(jread_options_handle);
auto* column_family_handle =
reinterpret_cast<ROCKSDB_NAMESPACE::ColumnFamilyHandle*>(
jcolumn_family_handle);
try {
ROCKSDB_NAMESPACE::JDirectBufferSlice key(env, jkey_bb, jkey_off,
jkey_part_len);
ROCKSDB_NAMESPACE::JDirectBufferPinnableSlice value(env, jval_bb, jval_off,
jval_len);
ROCKSDB_NAMESPACE::KVException::ThrowOnError(
env,
txn->GetForUpdate(*read_options, column_family_handle, key.slice(),
&value.pinnable_slice(), jexclusive, jdo_validate));
return value.Fetch();
} catch (const ROCKSDB_NAMESPACE::KVException& e) {
return e.Code();
}
}
/*
@ -519,29 +587,15 @@ jobjectArray Java_org_rocksdb_Transaction_multiGetForUpdate__JJ_3_3B(
jread_options_handle, jkey_parts);
}
/*
* Class: org_rocksdb_Transaction
* Method: getIterator
* Signature: (JJ)J
*/
jlong Java_org_rocksdb_Transaction_getIterator__JJ(JNIEnv* /*env*/,
jobject /*jobj*/,
jlong jhandle,
jlong jread_options_handle) {
auto* txn = reinterpret_cast<ROCKSDB_NAMESPACE::Transaction*>(jhandle);
auto* read_options =
reinterpret_cast<ROCKSDB_NAMESPACE::ReadOptions*>(jread_options_handle);
return GET_CPLUSPLUS_POINTER(txn->GetIterator(*read_options));
}
/*
* Class: org_rocksdb_Transaction
* Method: getIterator
* Signature: (JJJ)J
*/
jlong Java_org_rocksdb_Transaction_getIterator__JJJ(
JNIEnv* /*env*/, jobject /*jobj*/, jlong jhandle,
jlong jread_options_handle, jlong jcolumn_family_handle) {
jlong Java_org_rocksdb_Transaction_getIterator(JNIEnv* /*env*/,
jobject /*jobj*/, jlong jhandle,
jlong jread_options_handle,
jlong jcolumn_family_handle) {
auto* txn = reinterpret_cast<ROCKSDB_NAMESPACE::Transaction*>(jhandle);
auto* read_options =
reinterpret_cast<ROCKSDB_NAMESPACE::ReadOptions*>(jread_options_handle);
@ -552,84 +606,93 @@ jlong Java_org_rocksdb_Transaction_getIterator__JJJ(
txn->GetIterator(*read_options, column_family_handle));
}
typedef std::function<ROCKSDB_NAMESPACE::Status(
const ROCKSDB_NAMESPACE::Slice&, const ROCKSDB_NAMESPACE::Slice&)>
FnWriteKV;
// TODO(AR) consider refactoring to share this between here and rocksjni.cc
void txn_write_kv_helper(JNIEnv* env, const FnWriteKV& fn_write_kv,
const jbyteArray& jkey, const jint& jkey_part_len,
const jbyteArray& jval, const jint& jval_len) {
jbyte* key = env->GetByteArrayElements(jkey, nullptr);
if (key == nullptr) {
// exception thrown: OutOfMemoryError
/*
* Class: org_rocksdb_Transaction
* Method: put
* Signature: (J[BII[BIIJZ)V
*/
void Java_org_rocksdb_Transaction_put__J_3BII_3BIIJZ(
JNIEnv* env, jobject /*jobj*/, jlong jhandle, jbyteArray jkey,
jint jkey_off, jint jkey_part_len, jbyteArray jval, jint jval_off,
jint jval_len, jlong jcolumn_family_handle, jboolean jassume_tracked) {
auto* txn = reinterpret_cast<ROCKSDB_NAMESPACE::Transaction*>(jhandle);
auto* column_family_handle =
reinterpret_cast<ROCKSDB_NAMESPACE::ColumnFamilyHandle*>(
jcolumn_family_handle);
try {
ROCKSDB_NAMESPACE::JByteArraySlice key(env, jkey, jkey_off, jkey_part_len);
ROCKSDB_NAMESPACE::JByteArraySlice value(env, jval, jval_off, jval_len);
ROCKSDB_NAMESPACE::KVException::ThrowOnError(
env, txn->Put(column_family_handle, key.slice(), value.slice(),
jassume_tracked));
} catch (ROCKSDB_NAMESPACE::KVException&) {
return;
}
jbyte* value = env->GetByteArrayElements(jval, nullptr);
if (value == nullptr) {
// exception thrown: OutOfMemoryError
env->ReleaseByteArrayElements(jkey, key, JNI_ABORT);
return;
}
ROCKSDB_NAMESPACE::Slice key_slice(reinterpret_cast<char*>(key),
jkey_part_len);
ROCKSDB_NAMESPACE::Slice value_slice(reinterpret_cast<char*>(value),
jval_len);
ROCKSDB_NAMESPACE::Status s = fn_write_kv(key_slice, value_slice);
// trigger java unref on key.
// by passing JNI_ABORT, it will simply release the reference without
// copying the result back to the java byte array.
env->ReleaseByteArrayElements(jval, value, JNI_ABORT);
env->ReleaseByteArrayElements(jkey, key, JNI_ABORT);
if (s.ok()) {
return;
}
ROCKSDB_NAMESPACE::RocksDBExceptionJni::ThrowNew(env, s);
}
/*
* Class: org_rocksdb_Transaction
* Method: put
* Signature: (J[BI[BIJZ)V
* Signature: (J[BII[BII)V
*/
void Java_org_rocksdb_Transaction_put__J_3BI_3BIJZ(
void Java_org_rocksdb_Transaction_put__J_3BII_3BII(
JNIEnv* env, jobject /*jobj*/, jlong jhandle, jbyteArray jkey,
jint jkey_part_len, jbyteArray jval, jint jval_len,
jint jkey_off, jint jkey_part_len, jbyteArray jval, jint jval_off,
jint jval_len) {
auto* txn = reinterpret_cast<ROCKSDB_NAMESPACE::Transaction*>(jhandle);
try {
ROCKSDB_NAMESPACE::JByteArraySlice key(env, jkey, jkey_off, jkey_part_len);
ROCKSDB_NAMESPACE::JByteArraySlice value(env, jval, jval_off, jval_len);
ROCKSDB_NAMESPACE::KVException::ThrowOnError(
env, txn->Put(key.slice(), value.slice()));
} catch (ROCKSDB_NAMESPACE::KVException&) {
return;
}
}
/*
* Class: org_rocksdb_Transaction
* Method: putDirect
* Signature: (JLjava/nio/ByteBuffer;IILjava/nio/ByteBuffer;IIJZ)V
*/
void Java_org_rocksdb_Transaction_putDirect__JLjava_nio_ByteBuffer_2IILjava_nio_ByteBuffer_2IIJZ(
JNIEnv* env, jobject, jlong jhandle, jobject jkey_bb, jint jkey_off,
jint jkey_len, jobject jval_bb, jint jval_off, jint jval_len,
jlong jcolumn_family_handle, jboolean jassume_tracked) {
auto* txn = reinterpret_cast<ROCKSDB_NAMESPACE::Transaction*>(jhandle);
auto* column_family_handle =
reinterpret_cast<ROCKSDB_NAMESPACE::ColumnFamilyHandle*>(
jcolumn_family_handle);
FnWriteKV fn_put =
std::bind<ROCKSDB_NAMESPACE::Status (ROCKSDB_NAMESPACE::Transaction::*)(
ROCKSDB_NAMESPACE::ColumnFamilyHandle*,
const ROCKSDB_NAMESPACE::Slice&, const ROCKSDB_NAMESPACE::Slice&,
bool)>(&ROCKSDB_NAMESPACE::Transaction::Put, txn,
column_family_handle, std::placeholders::_1,
std::placeholders::_2, jassume_tracked);
txn_write_kv_helper(env, fn_put, jkey, jkey_part_len, jval, jval_len);
try {
ROCKSDB_NAMESPACE::JDirectBufferSlice key(env, jkey_bb, jkey_off, jkey_len);
ROCKSDB_NAMESPACE::JDirectBufferSlice value(env, jval_bb, jval_off,
jval_len);
ROCKSDB_NAMESPACE::KVException::ThrowOnError(
env, txn->Put(column_family_handle, key.slice(), value.slice(),
jassume_tracked));
} catch (ROCKSDB_NAMESPACE::KVException&) {
return;
}
}
/*
* Class: org_rocksdb_Transaction
* Method: put
* Signature: (J[BI[BI)V
* Method: putDirect
* Signature: (JLjava/nio/ByteBuffer;IILjava/nio/ByteBuffer;II)V
*/
void Java_org_rocksdb_Transaction_put__J_3BI_3BI(JNIEnv* env, jobject /*jobj*/,
jlong jhandle, jbyteArray jkey,
jint jkey_part_len,
jbyteArray jval,
jint jval_len) {
void Java_org_rocksdb_Transaction_putDirect__JLjava_nio_ByteBuffer_2IILjava_nio_ByteBuffer_2II(
JNIEnv* env, jobject, jlong jhandle, jobject jkey_bb, jint jkey_off,
jint jkey_len, jobject jval_bb, jint jval_off, jint jval_len) {
auto* txn = reinterpret_cast<ROCKSDB_NAMESPACE::Transaction*>(jhandle);
FnWriteKV fn_put =
std::bind<ROCKSDB_NAMESPACE::Status (ROCKSDB_NAMESPACE::Transaction::*)(
const ROCKSDB_NAMESPACE::Slice&, const ROCKSDB_NAMESPACE::Slice&)>(
&ROCKSDB_NAMESPACE::Transaction::Put, txn, std::placeholders::_1,
std::placeholders::_2);
txn_write_kv_helper(env, fn_put, jkey, jkey_part_len, jval, jval_len);
try {
ROCKSDB_NAMESPACE::JDirectBufferSlice key(env, jkey_bb, jkey_off, jkey_len);
ROCKSDB_NAMESPACE::JDirectBufferSlice value(env, jval_bb, jval_off,
jval_len);
ROCKSDB_NAMESPACE::KVException::ThrowOnError(
env, txn->Put(key.slice(), value.slice()));
} catch (ROCKSDB_NAMESPACE::KVException&) {
return;
}
}
typedef std::function<ROCKSDB_NAMESPACE::Status(
@ -780,41 +843,92 @@ void Java_org_rocksdb_Transaction_put__J_3_3BI_3_3BI(
/*
* Class: org_rocksdb_Transaction
* Method: merge
* Signature: (J[BI[BIJZ)V
* Signature: (J[BII[BIIJZ)V
*/
void Java_org_rocksdb_Transaction_merge__J_3BI_3BIJZ(
void Java_org_rocksdb_Transaction_merge__J_3BII_3BIIJZ(
JNIEnv* env, jobject /*jobj*/, jlong jhandle, jbyteArray jkey,
jint jkey_part_len, jbyteArray jval, jint jval_len,
jlong jcolumn_family_handle, jboolean jassume_tracked) {
jint jkey_off, jint jkey_part_len, jbyteArray jval, jint jval_off,
jint jval_len, jlong jcolumn_family_handle, jboolean jassume_tracked) {
auto* txn = reinterpret_cast<ROCKSDB_NAMESPACE::Transaction*>(jhandle);
auto* column_family_handle =
reinterpret_cast<ROCKSDB_NAMESPACE::ColumnFamilyHandle*>(
jcolumn_family_handle);
FnWriteKV fn_merge =
std::bind<ROCKSDB_NAMESPACE::Status (ROCKSDB_NAMESPACE::Transaction::*)(
ROCKSDB_NAMESPACE::ColumnFamilyHandle*,
const ROCKSDB_NAMESPACE::Slice&, const ROCKSDB_NAMESPACE::Slice&,
bool)>(&ROCKSDB_NAMESPACE::Transaction::Merge, txn,
column_family_handle, std::placeholders::_1,
std::placeholders::_2, jassume_tracked);
txn_write_kv_helper(env, fn_merge, jkey, jkey_part_len, jval, jval_len);
try {
ROCKSDB_NAMESPACE::JByteArraySlice key(env, jkey, jkey_off, jkey_part_len);
ROCKSDB_NAMESPACE::JByteArraySlice value(env, jval, jval_off, jval_len);
ROCKSDB_NAMESPACE::KVException::ThrowOnError(
env, txn->Merge(column_family_handle, key.slice(), value.slice(),
jassume_tracked));
} catch (ROCKSDB_NAMESPACE::KVException&) {
return;
}
}
/*
* Class: org_rocksdb_Transaction
* Method: merge
* Signature: (J[BI[BI)V
* Signature: (J[BII[BII)V
*/
void Java_org_rocksdb_Transaction_merge__J_3BI_3BI(
void Java_org_rocksdb_Transaction_merge__J_3BII_3BII(
JNIEnv* env, jobject /*jobj*/, jlong jhandle, jbyteArray jkey,
jint jkey_part_len, jbyteArray jval, jint jval_len) {
jint jkey_off, jint jkey_part_len, jbyteArray jval, jint jval_off,
jint jval_len) {
auto* txn = reinterpret_cast<ROCKSDB_NAMESPACE::Transaction*>(jhandle);
FnWriteKV fn_merge =
std::bind<ROCKSDB_NAMESPACE::Status (ROCKSDB_NAMESPACE::Transaction::*)(
const ROCKSDB_NAMESPACE::Slice&, const ROCKSDB_NAMESPACE::Slice&)>(
&ROCKSDB_NAMESPACE::Transaction::Merge, txn, std::placeholders::_1,
std::placeholders::_2);
txn_write_kv_helper(env, fn_merge, jkey, jkey_part_len, jval, jval_len);
try {
ROCKSDB_NAMESPACE::JByteArraySlice key(env, jkey, jkey_off, jkey_part_len);
ROCKSDB_NAMESPACE::JByteArraySlice value(env, jval, jval_off, jval_len);
ROCKSDB_NAMESPACE::KVException::ThrowOnError(
env, txn->Merge(key.slice(), value.slice()));
} catch (ROCKSDB_NAMESPACE::KVException&) {
return;
}
}
/*
* Class: org_rocksdb_Transaction
* Method: mergeDirect
* Signature: (JLjava/nio/ByteBuffer;IILjava/nio/ByteBuffer;IIJZ)V
*/
JNIEXPORT void JNICALL
Java_org_rocksdb_Transaction_mergeDirect__JLjava_nio_ByteBuffer_2IILjava_nio_ByteBuffer_2IIJZ(
JNIEnv* env, jobject, jlong jhandle, jobject jkey_bb, jint jkey_off,
jint jkey_len, jobject jval_bb, jint jval_off, jint jval_len,
jlong jcolumn_family_handle, jboolean jassume_tracked) {
auto* txn = reinterpret_cast<ROCKSDB_NAMESPACE::Transaction*>(jhandle);
auto* column_family_handle =
reinterpret_cast<ROCKSDB_NAMESPACE::ColumnFamilyHandle*>(
jcolumn_family_handle);
try {
ROCKSDB_NAMESPACE::JDirectBufferSlice key(env, jkey_bb, jkey_off, jkey_len);
ROCKSDB_NAMESPACE::JDirectBufferSlice value(env, jval_bb, jval_off,
jval_len);
ROCKSDB_NAMESPACE::KVException::ThrowOnError(
env, txn->Merge(column_family_handle, key.slice(), value.slice(),
jassume_tracked));
} catch (ROCKSDB_NAMESPACE::KVException&) {
return;
}
}
/*
* Class: org_rocksdb_Transaction
* Method: mergeDirect
* Signature: (JLjava/nio/ByteBuffer;IILjava/nio/ByteBuffer;II)V
*/
JNIEXPORT void JNICALL
Java_org_rocksdb_Transaction_mergeDirect__JLjava_nio_ByteBuffer_2IILjava_nio_ByteBuffer_2II(
JNIEnv* env, jobject, jlong jhandle, jobject jkey_bb, jint jkey_off,
jint jkey_len, jobject jval_bb, jint jval_off, jint jval_len) {
auto* txn = reinterpret_cast<ROCKSDB_NAMESPACE::Transaction*>(jhandle);
try {
ROCKSDB_NAMESPACE::JDirectBufferSlice key(env, jkey_bb, jkey_off, jkey_len);
ROCKSDB_NAMESPACE::JDirectBufferSlice value(env, jval_bb, jval_off,
jval_len);
ROCKSDB_NAMESPACE::KVException::ThrowOnError(
env, txn->Merge(key.slice(), value.slice()));
} catch (ROCKSDB_NAMESPACE::KVException&) {
return;
}
}
typedef std::function<ROCKSDB_NAMESPACE::Status(
@ -1065,14 +1179,15 @@ void Java_org_rocksdb_Transaction_putUntracked__J_3BI_3BIJ(
auto* column_family_handle =
reinterpret_cast<ROCKSDB_NAMESPACE::ColumnFamilyHandle*>(
jcolumn_family_handle);
FnWriteKV fn_put_untracked =
std::bind<ROCKSDB_NAMESPACE::Status (ROCKSDB_NAMESPACE::Transaction::*)(
ROCKSDB_NAMESPACE::ColumnFamilyHandle*,
const ROCKSDB_NAMESPACE::Slice&, const ROCKSDB_NAMESPACE::Slice&)>(
&ROCKSDB_NAMESPACE::Transaction::PutUntracked, txn,
column_family_handle, std::placeholders::_1, std::placeholders::_2);
txn_write_kv_helper(env, fn_put_untracked, jkey, jkey_part_len, jval,
jval_len);
try {
ROCKSDB_NAMESPACE::JByteArraySlice key(env, jkey, 0, jkey_part_len);
ROCKSDB_NAMESPACE::JByteArraySlice value(env, jval, 0, jval_len);
ROCKSDB_NAMESPACE::KVException::ThrowOnError(
env,
txn->PutUntracked(column_family_handle, key.slice(), value.slice()));
} catch (ROCKSDB_NAMESPACE::KVException&) {
return;
}
}
/*
@ -1084,13 +1199,14 @@ void Java_org_rocksdb_Transaction_putUntracked__J_3BI_3BI(
JNIEnv* env, jobject /*jobj*/, jlong jhandle, jbyteArray jkey,
jint jkey_part_len, jbyteArray jval, jint jval_len) {
auto* txn = reinterpret_cast<ROCKSDB_NAMESPACE::Transaction*>(jhandle);
FnWriteKV fn_put_untracked =
std::bind<ROCKSDB_NAMESPACE::Status (ROCKSDB_NAMESPACE::Transaction::*)(
const ROCKSDB_NAMESPACE::Slice&, const ROCKSDB_NAMESPACE::Slice&)>(
&ROCKSDB_NAMESPACE::Transaction::PutUntracked, txn,
std::placeholders::_1, std::placeholders::_2);
txn_write_kv_helper(env, fn_put_untracked, jkey, jkey_part_len, jval,
jval_len);
try {
ROCKSDB_NAMESPACE::JByteArraySlice key(env, jkey, 0, jkey_part_len);
ROCKSDB_NAMESPACE::JByteArraySlice value(env, jval, 0, jval_len);
ROCKSDB_NAMESPACE::KVException::ThrowOnError(
env, txn->PutUntracked(key.slice(), value.slice()));
} catch (ROCKSDB_NAMESPACE::KVException&) {
return;
}
}
/*
@ -1137,42 +1253,50 @@ void Java_org_rocksdb_Transaction_putUntracked__J_3_3BI_3_3BI(
/*
* Class: org_rocksdb_Transaction
* Method: mergeUntracked
* Signature: (J[BI[BIJ)V
* Signature: (J[BII[BIIJ)V
*/
void Java_org_rocksdb_Transaction_mergeUntracked__J_3BI_3BIJ(
void Java_org_rocksdb_Transaction_mergeUntracked(
JNIEnv* env, jobject /*jobj*/, jlong jhandle, jbyteArray jkey,
jint jkey_part_len, jbyteArray jval, jint jval_len,
jint jkey_off, jint jkey_part_len, jbyteArray jval, jint jval_off,
jint jval_len, jlong jcolumn_family_handle) {
auto* txn = reinterpret_cast<ROCKSDB_NAMESPACE::Transaction*>(jhandle);
auto* column_family_handle =
reinterpret_cast<ROCKSDB_NAMESPACE::ColumnFamilyHandle*>(
jcolumn_family_handle);
try {
ROCKSDB_NAMESPACE::JByteArraySlice key(env, jkey, jkey_off, jkey_part_len);
ROCKSDB_NAMESPACE::JByteArraySlice value(env, jval, jval_off, jval_len);
ROCKSDB_NAMESPACE::KVException::ThrowOnError(
env,
txn->MergeUntracked(column_family_handle, key.slice(), value.slice()));
} catch (ROCKSDB_NAMESPACE::KVException&) {
return;
}
}
/*
* Class: org_rocksdb_Transaction
* Method: mergeUntrackedDirect
* Signature: (JLjava/nio/ByteBuffer;IILjava/nio/ByteBuffer;IIJ)V
*/
void Java_org_rocksdb_Transaction_mergeUntrackedDirect(
JNIEnv* env, jobject /*jobj*/, jlong jhandle, jobject jkey, jint jkey_off,
jint jkey_part_len, jobject jval, jint jval_off, jint jval_len,
jlong jcolumn_family_handle) {
auto* txn = reinterpret_cast<ROCKSDB_NAMESPACE::Transaction*>(jhandle);
auto* column_family_handle =
reinterpret_cast<ROCKSDB_NAMESPACE::ColumnFamilyHandle*>(
jcolumn_family_handle);
FnWriteKV fn_merge_untracked =
std::bind<ROCKSDB_NAMESPACE::Status (ROCKSDB_NAMESPACE::Transaction::*)(
ROCKSDB_NAMESPACE::ColumnFamilyHandle*,
const ROCKSDB_NAMESPACE::Slice&, const ROCKSDB_NAMESPACE::Slice&)>(
&ROCKSDB_NAMESPACE::Transaction::MergeUntracked, txn,
column_family_handle, std::placeholders::_1, std::placeholders::_2);
txn_write_kv_helper(env, fn_merge_untracked, jkey, jkey_part_len, jval,
jval_len);
}
/*
* Class: org_rocksdb_Transaction
* Method: mergeUntracked
* Signature: (J[BI[BI)V
*/
void Java_org_rocksdb_Transaction_mergeUntracked__J_3BI_3BI(
JNIEnv* env, jobject /*jobj*/, jlong jhandle, jbyteArray jkey,
jint jkey_part_len, jbyteArray jval, jint jval_len) {
auto* txn = reinterpret_cast<ROCKSDB_NAMESPACE::Transaction*>(jhandle);
FnWriteKV fn_merge_untracked =
std::bind<ROCKSDB_NAMESPACE::Status (ROCKSDB_NAMESPACE::Transaction::*)(
const ROCKSDB_NAMESPACE::Slice&, const ROCKSDB_NAMESPACE::Slice&)>(
&ROCKSDB_NAMESPACE::Transaction::MergeUntracked, txn,
std::placeholders::_1, std::placeholders::_2);
txn_write_kv_helper(env, fn_merge_untracked, jkey, jkey_part_len, jval,
jval_len);
try {
ROCKSDB_NAMESPACE::JDirectBufferSlice key(env, jkey, jkey_off,
jkey_part_len);
ROCKSDB_NAMESPACE::JDirectBufferSlice value(env, jval, jval_off, jval_len);
ROCKSDB_NAMESPACE::KVException::ThrowOnError(
env,
txn->MergeUntracked(column_family_handle, key.slice(), value.slice()));
} catch (ROCKSDB_NAMESPACE::KVException&) {
return;
}
}
/*

View File

@ -0,0 +1,32 @@
// Copyright (c) Meta Platforms, Inc. and affiliates.
//
// 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;
/**
* The result for a fetch
* and the total size of the object fetched.
* If the target of the fetch is not big enough, this may be bigger than the contents of the target.
*/
public class GetStatus {
public final Status status;
public final int requiredSize;
/**
* Constructor used for success status, when the value is contained in the buffer
*
* @param status the status of the request to fetch into the buffer
* @param requiredSize the size of the data, which may be bigger than the buffer
*/
GetStatus(final Status status, final int requiredSize) {
this.status = status;
this.requiredSize = requiredSize;
}
static GetStatus fromStatusCode(final Status.Code code, final int requiredSize) {
return new GetStatus(new Status(code, Status.SubCode.getSubCode((byte) 0), null), requiredSize);
}
}

View File

@ -86,6 +86,7 @@ public class OptimisticTransactionDB extends RocksDB
// in RocksDB can prevent Java to GC during the life-time of
// the currently-created RocksDB.
otdb.storeOptionsInstance(dbOptions);
otdb.storeDefaultColumnFamilyHandle(otdb.makeDefaultColumnFamilyHandle());
for (int i = 1; i < handles.length; i++) {
columnFamilyHandles.add(new ColumnFamilyHandle(otdb, handles[i]));

View File

@ -573,7 +573,6 @@ public class ReadOptions extends RocksObject {
* @see #iterStartTs()
* @return Reference to timestamp or null if there is no timestamp defined.
*/
@SuppressWarnings("PMD.ConfusingTernary")
public Slice timestamp() {
assert (isOwningHandle());
final long timestampSliceHandle = timestamp(nativeHandle_);
@ -623,7 +622,6 @@ public class ReadOptions extends RocksObject {
* @return Reference to lower bound timestamp or null if there is no lower bound timestamp
* defined.
*/
@SuppressWarnings("PMD.ConfusingTernary")
public Slice iterStartTs() {
assert (isOwningHandle());
final long iterStartTsHandle = iterStartTs(nativeHandle_);

View File

@ -6,6 +6,7 @@
package org.rocksdb;
import static java.nio.charset.StandardCharsets.UTF_8;
import static org.rocksdb.util.BufferUtil.CheckBounds;
import java.io.IOException;
import java.nio.ByteBuffer;
@ -39,6 +40,11 @@ public class RocksDB extends RocksObject {
static final String PERFORMANCE_OPTIMIZATION_FOR_A_VERY_SPECIFIC_WORKLOAD =
"Performance optimization for a very specific workload";
private static final String BB_ALL_DIRECT_OR_INDIRECT =
"ByteBuffer parameters must all be direct, or must all be indirect";
private ColumnFamilyHandle defaultColumnFamilyHandle_;
private final ReadOptions defaultReadOptions_ = new ReadOptions();
private final List<ColumnFamilyHandle> ownedColumnFamilyHandles = new ArrayList<>();
/**
@ -256,6 +262,7 @@ public class RocksDB extends RocksObject {
// the currently-created RocksDB.
final RocksDB db = new RocksDB(open(options.nativeHandle_, path));
db.storeOptionsInstance(options);
db.storeDefaultColumnFamilyHandle(db.makeDefaultColumnFamilyHandle());
return db;
}
@ -321,6 +328,7 @@ public class RocksDB extends RocksObject {
}
db.ownedColumnFamilyHandles.addAll(columnFamilyHandles);
db.storeDefaultColumnFamilyHandle(db.makeDefaultColumnFamilyHandle());
return db;
}
@ -395,6 +403,7 @@ public class RocksDB extends RocksObject {
// the currently-created RocksDB.
final RocksDB db = new RocksDB(openROnly(options.nativeHandle_, path, errorIfWalFileExists));
db.storeOptionsInstance(options);
db.storeDefaultColumnFamilyHandle(db.makeDefaultColumnFamilyHandle());
return db;
}
@ -505,6 +514,7 @@ public class RocksDB extends RocksObject {
}
db.ownedColumnFamilyHandles.addAll(columnFamilyHandles);
db.storeDefaultColumnFamilyHandle(db.makeDefaultColumnFamilyHandle());
return db;
}
@ -542,6 +552,7 @@ public class RocksDB extends RocksObject {
// the currently-created RocksDB.
final RocksDB db = new RocksDB(openAsSecondary(options.nativeHandle_, path, secondaryPath));
db.storeOptionsInstance(options);
db.storeDefaultColumnFamilyHandle(db.makeDefaultColumnFamilyHandle());
return db;
}
@ -602,6 +613,7 @@ public class RocksDB extends RocksObject {
}
db.ownedColumnFamilyHandles.addAll(columnFamilyHandles);
db.storeDefaultColumnFamilyHandle(db.makeDefaultColumnFamilyHandle());
return db;
}
@ -887,8 +899,8 @@ public class RocksDB extends RocksObject {
public void put(final byte[] key, final int offset, final int len,
final byte[] value, final int vOffset, final int vLen)
throws RocksDBException {
checkBounds(offset, len, key.length);
checkBounds(vOffset, vLen, value.length);
CheckBounds(offset, len, key.length);
CheckBounds(vOffset, vLen, value.length);
put(nativeHandle_, key, offset, len, value, vOffset, vLen);
}
@ -937,8 +949,8 @@ public class RocksDB extends RocksObject {
final byte[] key, final int offset, final int len,
final byte[] value, final int vOffset, final int vLen)
throws RocksDBException {
checkBounds(offset, len, key.length);
checkBounds(vOffset, vLen, value.length);
CheckBounds(offset, len, key.length);
CheckBounds(vOffset, vLen, value.length);
put(nativeHandle_, key, offset, len, value, vOffset, vLen,
columnFamilyHandle.nativeHandle_);
}
@ -982,8 +994,8 @@ public class RocksDB extends RocksObject {
final byte[] key, final int offset, final int len,
final byte[] value, final int vOffset, final int vLen)
throws RocksDBException {
checkBounds(offset, len, key.length);
checkBounds(vOffset, vLen, value.length);
CheckBounds(offset, len, key.length);
CheckBounds(vOffset, vLen, value.length);
put(nativeHandle_, writeOpts.nativeHandle_,
key, offset, len, value, vOffset, vLen);
}
@ -1031,9 +1043,18 @@ public class RocksDB extends RocksObject {
*/
public void put(final ColumnFamilyHandle columnFamilyHandle, final WriteOptions writeOpts,
final ByteBuffer key, final ByteBuffer value) throws RocksDBException {
assert key.isDirect() && value.isDirect();
putDirect(nativeHandle_, writeOpts.nativeHandle_, key, key.position(), key.remaining(), value,
value.position(), value.remaining(), columnFamilyHandle.nativeHandle_);
if (key.isDirect() && value.isDirect()) {
putDirect(nativeHandle_, writeOpts.nativeHandle_, key, key.position(), key.remaining(), value,
value.position(), value.remaining(), columnFamilyHandle.nativeHandle_);
} else if (!key.isDirect() && !value.isDirect()) {
assert key.hasArray();
assert value.hasArray();
put(nativeHandle_, writeOpts.nativeHandle_, key.array(), key.arrayOffset() + key.position(),
key.remaining(), value.array(), value.arrayOffset() + value.position(), value.remaining(),
columnFamilyHandle.nativeHandle_);
} else {
throw new RocksDBException(BB_ALL_DIRECT_OR_INDIRECT);
}
key.position(key.limit());
value.position(value.limit());
}
@ -1055,9 +1076,18 @@ public class RocksDB extends RocksObject {
*/
public void put(final WriteOptions writeOpts, final ByteBuffer key, final ByteBuffer value)
throws RocksDBException {
assert key.isDirect() && value.isDirect();
putDirect(nativeHandle_, writeOpts.nativeHandle_, key, key.position(), key.remaining(), value,
value.position(), value.remaining(), 0);
if (key.isDirect() && value.isDirect()) {
putDirect(nativeHandle_, writeOpts.nativeHandle_, key, key.position(), key.remaining(), value,
value.position(), value.remaining(), 0);
} else if (!key.isDirect() && !value.isDirect()) {
assert key.hasArray();
assert value.hasArray();
put(nativeHandle_, writeOpts.nativeHandle_, key.array(), key.arrayOffset() + key.position(),
key.remaining(), value.array(), value.arrayOffset() + value.position(),
value.remaining());
} else {
throw new RocksDBException(BB_ALL_DIRECT_OR_INDIRECT);
}
key.position(key.limit());
value.position(value.limit());
}
@ -1089,8 +1119,8 @@ public class RocksDB extends RocksObject {
final byte[] key, final int offset, final int len,
final byte[] value, final int vOffset, final int vLen)
throws RocksDBException {
checkBounds(offset, len, key.length);
checkBounds(vOffset, vLen, value.length);
CheckBounds(offset, len, key.length);
CheckBounds(vOffset, vLen, value.length);
put(nativeHandle_, writeOpts.nativeHandle_, key, offset, len, value,
vOffset, vLen, columnFamilyHandle.nativeHandle_);
}
@ -1268,9 +1298,18 @@ public class RocksDB extends RocksObject {
*/
public int get(final ReadOptions opt, final ByteBuffer key, final ByteBuffer value)
throws RocksDBException {
assert key.isDirect() && value.isDirect();
final int result = getDirect(nativeHandle_, opt.nativeHandle_, key, key.position(),
key.remaining(), value, value.position(), value.remaining(), 0);
final int result;
if (key.isDirect() && value.isDirect()) {
result = getDirect(nativeHandle_, opt.nativeHandle_, key, key.position(), key.remaining(),
value, value.position(), value.remaining(), 0);
} else if (!key.isDirect() && !value.isDirect()) {
result =
get(nativeHandle_, opt.nativeHandle_, key.array(), key.arrayOffset() + key.position(),
key.remaining(), value.array(), value.arrayOffset() + value.position(),
value.remaining(), defaultColumnFamilyHandle_.nativeHandle_);
} else {
throw new RocksDBException(BB_ALL_DIRECT_OR_INDIRECT);
}
if (result != NOT_FOUND) {
value.limit(Math.min(value.limit(), value.position() + result));
}
@ -1556,8 +1595,8 @@ public class RocksDB extends RocksObject {
*/
public void merge(final byte[] key, final int offset, final int len, final byte[] value,
final int vOffset, final int vLen) throws RocksDBException {
checkBounds(offset, len, key.length);
checkBounds(vOffset, vLen, value.length);
CheckBounds(offset, len, key.length);
CheckBounds(vOffset, vLen, value.length);
merge(nativeHandle_, key, offset, len, value, vOffset, vLen);
}
@ -1601,8 +1640,8 @@ public class RocksDB extends RocksObject {
public void merge(final ColumnFamilyHandle columnFamilyHandle,
final byte[] key, final int offset, final int len, final byte[] value,
final int vOffset, final int vLen) throws RocksDBException {
checkBounds(offset, len, key.length);
checkBounds(vOffset, vLen, value.length);
CheckBounds(offset, len, key.length);
CheckBounds(vOffset, vLen, value.length);
merge(nativeHandle_, key, offset, len, value, vOffset, vLen,
columnFamilyHandle.nativeHandle_);
}
@ -1648,12 +1687,48 @@ public class RocksDB extends RocksObject {
final byte[] key, final int offset, final int len,
final byte[] value, final int vOffset, final int vLen)
throws RocksDBException {
checkBounds(offset, len, key.length);
checkBounds(vOffset, vLen, value.length);
CheckBounds(offset, len, key.length);
CheckBounds(vOffset, vLen, value.length);
merge(nativeHandle_, writeOpts.nativeHandle_,
key, offset, len, value, vOffset, vLen);
}
public void merge(final WriteOptions writeOpts, final ByteBuffer key, final ByteBuffer value)
throws RocksDBException {
if (key.isDirect() && value.isDirect()) {
mergeDirect(nativeHandle_, writeOpts.nativeHandle_, key, key.position(), key.remaining(),
value, value.position(), value.remaining(), 0);
} else if (!key.isDirect() && !value.isDirect()) {
assert key.hasArray();
assert value.hasArray();
merge(nativeHandle_, writeOpts.nativeHandle_, key.array(), key.arrayOffset() + key.position(),
key.remaining(), value.array(), value.arrayOffset() + value.position(),
value.remaining());
} else {
throw new RocksDBException(BB_ALL_DIRECT_OR_INDIRECT);
}
key.position(key.limit());
value.position(value.limit());
}
public void merge(final ColumnFamilyHandle columnFamilyHandle, final WriteOptions writeOpts,
final ByteBuffer key, final ByteBuffer value) throws RocksDBException {
if (key.isDirect() && value.isDirect()) {
mergeDirect(nativeHandle_, writeOpts.nativeHandle_, key, key.position(), key.remaining(),
value, value.position(), value.remaining(), columnFamilyHandle.nativeHandle_);
} else if (!key.isDirect() && !value.isDirect()) {
assert key.hasArray();
assert value.hasArray();
merge(nativeHandle_, writeOpts.nativeHandle_, key.array(), key.arrayOffset() + key.position(),
key.remaining(), value.array(), value.arrayOffset() + value.position(), value.remaining(),
columnFamilyHandle.nativeHandle_);
} else {
throw new RocksDBException(BB_ALL_DIRECT_OR_INDIRECT);
}
key.position(key.limit());
value.position(value.limit());
}
/**
* 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"
@ -1740,8 +1815,8 @@ public class RocksDB extends RocksObject {
final byte[] key, final int offset, final int len,
final byte[] value, final int vOffset, final int vLen)
throws RocksDBException {
checkBounds(offset, len, key.length);
checkBounds(vOffset, vLen, value.length);
CheckBounds(offset, len, key.length);
CheckBounds(vOffset, vLen, value.length);
merge(nativeHandle_, writeOpts.nativeHandle_,
key, offset, len, value, vOffset, vLen,
columnFamilyHandle.nativeHandle_);
@ -1825,8 +1900,8 @@ public class RocksDB extends RocksObject {
public int get(final byte[] key, final int offset, final int len,
final byte[] value, final int vOffset, final int vLen)
throws RocksDBException {
checkBounds(offset, len, key.length);
checkBounds(vOffset, vLen, value.length);
CheckBounds(offset, len, key.length);
CheckBounds(vOffset, vLen, value.length);
return get(nativeHandle_, key, offset, len, value, vOffset, vLen);
}
@ -1882,8 +1957,8 @@ public class RocksDB extends RocksObject {
public int get(final ColumnFamilyHandle columnFamilyHandle, final byte[] key,
final int offset, final int len, final byte[] value, final int vOffset,
final int vLen) throws RocksDBException, IllegalArgumentException {
checkBounds(offset, len, key.length);
checkBounds(vOffset, vLen, value.length);
CheckBounds(offset, len, key.length);
CheckBounds(vOffset, vLen, value.length);
return get(nativeHandle_, key, offset, len, value, vOffset, vLen,
columnFamilyHandle.nativeHandle_);
}
@ -1937,8 +2012,8 @@ public class RocksDB extends RocksObject {
public int get(final ReadOptions opt, final byte[] key, final int offset,
final int len, final byte[] value, final int vOffset, final int vLen)
throws RocksDBException {
checkBounds(offset, len, key.length);
checkBounds(vOffset, vLen, value.length);
CheckBounds(offset, len, key.length);
CheckBounds(vOffset, vLen, value.length);
return get(nativeHandle_, opt.nativeHandle_,
key, offset, len, value, vOffset, vLen);
}
@ -1998,8 +2073,8 @@ public class RocksDB extends RocksObject {
final ReadOptions opt, final byte[] key, final int offset, final int len,
final byte[] value, final int vOffset, final int vLen)
throws RocksDBException {
checkBounds(offset, len, key.length);
checkBounds(vOffset, vLen, value.length);
CheckBounds(offset, len, key.length);
CheckBounds(vOffset, vLen, value.length);
return get(nativeHandle_, opt.nativeHandle_, key, offset, len, value,
vOffset, vLen, columnFamilyHandle.nativeHandle_);
}
@ -2038,7 +2113,7 @@ public class RocksDB extends RocksObject {
*/
public byte[] get(final byte[] key, final int offset,
final int len) throws RocksDBException {
checkBounds(offset, len, key.length);
CheckBounds(offset, len, key.length);
return get(nativeHandle_, key, offset, len);
}
@ -2083,7 +2158,7 @@ public class RocksDB extends RocksObject {
public byte[] get(final ColumnFamilyHandle columnFamilyHandle,
final byte[] key, final int offset, final int len)
throws RocksDBException {
checkBounds(offset, len, key.length);
CheckBounds(offset, len, key.length);
return get(nativeHandle_, key, offset, len,
columnFamilyHandle.nativeHandle_);
}
@ -2125,7 +2200,7 @@ public class RocksDB extends RocksObject {
*/
public byte[] get(final ReadOptions opt, final byte[] key, final int offset,
final int len) throws RocksDBException {
checkBounds(offset, len, key.length);
CheckBounds(offset, len, key.length);
return get(nativeHandle_, opt.nativeHandle_, key, offset, len);
}
@ -2172,7 +2247,7 @@ public class RocksDB extends RocksObject {
public byte[] get(final ColumnFamilyHandle columnFamilyHandle,
final ReadOptions opt, final byte[] key, final int offset, final int len)
throws RocksDBException {
checkBounds(offset, len, key.length);
CheckBounds(offset, len, key.length);
return get(nativeHandle_, opt.nativeHandle_, key, offset, len,
columnFamilyHandle.nativeHandle_);
}
@ -2973,7 +3048,7 @@ public class RocksDB extends RocksObject {
final ReadOptions readOptions,
final byte[] key, final int offset, final int len,
/* @Nullable */ final Holder<byte[]> valueHolder) {
checkBounds(offset, len, key.length);
CheckBounds(offset, len, key.length);
if (valueHolder == null) {
return keyMayExist(nativeHandle_,
columnFamilyHandle == null ? 0 : columnFamilyHandle.nativeHandle_,
@ -3113,9 +3188,11 @@ public class RocksDB extends RocksObject {
final ReadOptions readOptions, final ByteBuffer key) {
assert key != null : "key ByteBuffer parameter cannot be null";
assert key.isDirect() : "key parameter must be a direct ByteBuffer";
return keyMayExistDirect(nativeHandle_,
final boolean result = keyMayExistDirect(nativeHandle_,
columnFamilyHandle == null ? 0 : columnFamilyHandle.nativeHandle_,
readOptions == null ? 0 : readOptions.nativeHandle_, key, key.position(), key.limit());
key.position(key.limit());
return result;
}
/**
@ -3148,6 +3225,7 @@ public class RocksDB extends RocksObject {
value, value.position(), value.remaining());
final int valueLength = result[1];
value.limit(value.position() + Math.min(valueLength, value.remaining()));
key.position(key.limit());
return new KeyMayExist(KeyMayExist.KeyMayExistEnum.values()[result[0]], valueLength);
}
@ -3164,7 +3242,9 @@ public class RocksDB extends RocksObject {
* @return instance of iterator object.
*/
public RocksIterator newIterator() {
return new RocksIterator(this, iterator(nativeHandle_));
return new RocksIterator(this,
iterator(nativeHandle_, defaultColumnFamilyHandle_.nativeHandle_,
defaultReadOptions_.nativeHandle_));
}
/**
@ -3181,8 +3261,9 @@ public class RocksDB extends RocksObject {
* @return instance of iterator object.
*/
public RocksIterator newIterator(final ReadOptions readOptions) {
return new RocksIterator(this, iterator(nativeHandle_,
readOptions.nativeHandle_));
return new RocksIterator(this,
iterator(
nativeHandle_, defaultColumnFamilyHandle_.nativeHandle_, readOptions.nativeHandle_));
}
/**
@ -3201,8 +3282,9 @@ public class RocksDB extends RocksObject {
*/
public RocksIterator newIterator(
final ColumnFamilyHandle columnFamilyHandle) {
return new RocksIterator(this, iteratorCF(nativeHandle_,
columnFamilyHandle.nativeHandle_));
return new RocksIterator(this,
iterator(
nativeHandle_, columnFamilyHandle.nativeHandle_, defaultReadOptions_.nativeHandle_));
}
/**
@ -3222,8 +3304,8 @@ public class RocksDB extends RocksObject {
*/
public RocksIterator newIterator(final ColumnFamilyHandle columnFamilyHandle,
final ReadOptions readOptions) {
return new RocksIterator(this, iteratorCF(nativeHandle_,
columnFamilyHandle.nativeHandle_, readOptions.nativeHandle_));
return new RocksIterator(
this, iterator(nativeHandle_, columnFamilyHandle.nativeHandle_, readOptions.nativeHandle_));
}
/**
@ -4438,8 +4520,17 @@ public class RocksDB extends RocksObject {
* @return The handle of the default column family
*/
public ColumnFamilyHandle getDefaultColumnFamily() {
final ColumnFamilyHandle cfHandle = new ColumnFamilyHandle(this,
getDefaultColumnFamily(nativeHandle_));
return defaultColumnFamilyHandle_;
}
/**
* Create a handle for the default CF on open
*
* @return the default family handle
*/
protected ColumnFamilyHandle makeDefaultColumnFamilyHandle() {
final ColumnFamilyHandle cfHandle =
new ColumnFamilyHandle(this, getDefaultColumnFamily(nativeHandle_));
cfHandle.disOwnNativeHandle();
return cfHandle;
}
@ -4688,7 +4779,11 @@ public class RocksDB extends RocksObject {
options_ = options;
}
private static void checkBounds(final int offset, final int len, final int size) {
protected void storeDefaultColumnFamilyHandle(ColumnFamilyHandle columnFamilyHandle) {
defaultColumnFamilyHandle_ = columnFamilyHandle;
}
private static void checkBounds(int offset, int len, int size) {
if ((offset | len | (offset + len) | (size - (offset + len))) < 0) {
throw new IndexOutOfBoundsException(String.format("offset(%d), len(%d), size(%d)", offset, len, size));
}
@ -4833,6 +4928,10 @@ public class RocksDB extends RocksObject {
final byte[] key, final int keyOffset, final int keyLength,
final byte[] value, final int valueOffset, final int valueLength,
final long cfHandle) throws RocksDBException;
private native void mergeDirect(long handle, long writeOptHandle, ByteBuffer key, int keyOffset,
int keyLength, ByteBuffer value, int valueOffset, int valueLength, long cfHandle)
throws RocksDBException;
private native void write0(final long handle, final long writeOptHandle,
final long wbHandle) throws RocksDBException;
private native void write1(final long handle, final long writeOptHandle,
@ -4894,11 +4993,7 @@ public class RocksDB extends RocksObject {
private native void putDirect(long handle, long writeOptHandle, ByteBuffer key, int keyOffset,
int keyLength, ByteBuffer value, int valueOffset, int valueLength, long cfHandle)
throws RocksDBException;
private native long iterator(final long handle);
private native long iterator(final long handle, final long readOptHandle);
private native long iteratorCF(final long handle, final long cfHandle);
private native long iteratorCF(final long handle, final long cfHandle,
final long readOptHandle);
private native long iterator(final long handle, final long cfHandle, final long readOptHandle);
private native long[] iterators(final long handle,
final long[] columnFamilyHandles, final long readOptHandle)
throws RocksDBException;

View File

@ -5,6 +5,8 @@
package org.rocksdb;
import static org.rocksdb.util.BufferUtil.CheckBounds;
import java.nio.ByteBuffer;
/**
@ -39,6 +41,45 @@ public class RocksIterator extends AbstractRocksIterator<RocksDB> {
return key0(nativeHandle_);
}
/**
* <p>Return the key for the current entry. The underlying storage for
* the returned slice is valid only until the next modification of
* the iterator.</p>
*
* <p>REQUIRES: {@link #isValid()}</p>
*
* @param key the out-value to receive the retrieved key.
* @return The size of the actual key. If the return key is greater than
* the length of the buffer {@code key}, then it indicates that the size of the
* input buffer {@code key} is insufficient and partial result will
* be returned.
*/
public int key(final byte[] key) {
assert isOwningHandle();
return keyByteArray0(nativeHandle_, key, 0, key.length);
}
/**
* <p>Return the key for the current entry. The underlying storage for
* the returned slice is valid only until the next modification of
* the iterator.</p>
*
* <p>REQUIRES: {@link #isValid()}</p>
*
* @param key the out-value to receive the retrieved key.
* @param offset in {@code key} at which to place the retrieved key
* @param len limit to length of received key returned
* @return The size of the actual key. If the return key is greater than
* {@code len}, then it indicates that the size of the
* input buffer {@code key} is insufficient and partial result will
* be returned.
*/
public int key(final byte[] key, final int offset, final int len) {
assert isOwningHandle();
CheckBounds(offset, len, key.length);
return keyByteArray0(nativeHandle_, key, offset, len);
}
/**
* <p>Return the key for the current entry. The underlying storage for
* the returned slice is valid only until the next modification of
@ -48,7 +89,6 @@ public class RocksIterator extends AbstractRocksIterator<RocksDB> {
*
* @param key the out-value to receive the retrieved key.
* It is using position and limit. Limit is set according to key size.
* Supports direct buffer only.
* @return The size of the actual key. If the return key is greater than the
* length of {@code key}, then it indicates that the size of the
* input buffer {@code key} is insufficient and partial result will
@ -90,7 +130,6 @@ public class RocksIterator extends AbstractRocksIterator<RocksDB> {
*
* @param value the out-value to receive the retrieved value.
* It is using position and limit. Limit is set according to value size.
* Supports direct buffer only.
* @return The size of the actual value. 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
@ -110,6 +149,45 @@ public class RocksIterator extends AbstractRocksIterator<RocksDB> {
return result;
}
/**
* <p>Return the value for the current entry. The underlying storage for
* the returned slice is valid only until the next modification of
* the iterator.</p>
*
* <p>REQUIRES: {@link #isValid()}</p>
*
* @param value the out-value to receive the retrieved value.
* @return The size of the actual value. 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.
*/
public int value(final byte[] value) {
assert isOwningHandle();
return valueByteArray0(nativeHandle_, value, 0, value.length);
}
/**
* <p>Return the value for the current entry. The underlying storage for
* the returned slice is valid only until the next modification of
* the iterator.</p>
*
* <p>REQUIRES: {@link #isValid()}</p>
*
* @param value the out-value to receive the retrieved value.
* @param offset the offset within value at which to place the result
* @param len the length available in value after offset, for placing the result
* @return The size of the actual value. If the return value is greater than {@code len},
* then it indicates that the size of the
* input buffer {@code value} is insufficient and partial result will
* be returned.
*/
public int value(final byte[] value, final int offset, final int len) {
assert isOwningHandle();
CheckBounds(offset, len, value.length);
return valueByteArray0(nativeHandle_, value, offset, len);
}
@Override protected final native void disposeInternal(final long handle);
@Override final native boolean isValid0(long handle);
@Override final native void seekToFirst0(long handle);

File diff suppressed because it is too large Load Diff

View File

@ -50,6 +50,7 @@ public class TransactionDB extends RocksDB
// the currently-created RocksDB.
tdb.storeOptionsInstance(options);
tdb.storeTransactionDbOptions(transactionDbOptions);
tdb.storeDefaultColumnFamilyHandle(tdb.makeDefaultColumnFamilyHandle());
return tdb;
}
@ -94,6 +95,7 @@ public class TransactionDB extends RocksDB
// in RocksDB can prevent Java to GC during the life-time of
// the currently-created RocksDB.
tdb.storeOptionsInstance(dbOptions);
tdb.storeDefaultColumnFamilyHandle(tdb.makeDefaultColumnFamilyHandle());
tdb.storeTransactionDbOptions(transactionDbOptions);
for (int i = 1; i < handles.length; i++) {

View File

@ -0,0 +1,16 @@
// Copyright (c) Meta Platforms, Inc. and affiliates.
//
// 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.util;
public class BufferUtil {
public static void CheckBounds(final int offset, final int len, final int size) {
if ((offset | len | (offset + len) | (size - (offset + len))) < 0) {
throw new IndexOutOfBoundsException(
String.format("offset(%d), len(%d), size(%d)", offset, len, size));
}
}
}

View File

@ -8,10 +8,12 @@ package org.rocksdb;
import static java.nio.charset.StandardCharsets.UTF_8;
import static org.assertj.core.api.Assertions.assertThat;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Random;
import java.util.function.Function;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
@ -181,8 +183,10 @@ public abstract class AbstractTransactionTest {
final ReadOptions readOptions = new ReadOptions();
final Transaction txn = dbContainer.beginTransaction()) {
final ColumnFamilyHandle testCf = dbContainer.getTestColumnFamily();
assertThat(txn.get(readOptions, testCf, k1)).isNull();
assertThat(txn.get(testCf, readOptions, k1)).isNull();
txn.put(testCf, k1, v1);
assertThat(txn.get(readOptions, testCf, k1)).isEqualTo(v1);
assertThat(txn.get(testCf, readOptions, k1)).isEqualTo(v1);
}
}
@ -200,6 +204,135 @@ public abstract class AbstractTransactionTest {
}
}
@Test
public void getPutTargetBuffer_cf() throws RocksDBException {
final byte[] k1 = "key1".getBytes(UTF_8);
final byte[] v1 = "value1".getBytes(UTF_8);
try (final DBContainer dbContainer = startDb();
final ReadOptions readOptions = new ReadOptions();
final Transaction txn = dbContainer.beginTransaction()) {
final ColumnFamilyHandle testCf = dbContainer.getTestColumnFamily();
final byte[] target = "overwrite1".getBytes(UTF_8);
GetStatus status = txn.get(readOptions, testCf, k1, target);
assertThat(status.status.getCode()).isEqualTo(Status.Code.NotFound);
assertThat(status.requiredSize).isEqualTo(0);
txn.put(testCf, k1, v1);
status = txn.get(readOptions, testCf, k1, target);
assertThat(status.status.getCode()).isEqualTo(Status.Code.Ok);
assertThat(status.requiredSize).isEqualTo(v1.length);
assertThat(target).isEqualTo("value1ite1".getBytes());
}
}
@Test
public void getPutTargetBuffer() throws RocksDBException {
final byte[] k1 = "key1".getBytes(UTF_8);
final byte[] v1 = "value1".getBytes(UTF_8);
try (final DBContainer dbContainer = startDb();
final ReadOptions readOptions = new ReadOptions();
final Transaction txn = dbContainer.beginTransaction()) {
final byte[] target = "overwrite1".getBytes(UTF_8);
GetStatus status = txn.get(readOptions, k1, target);
assertThat(status.status.getCode()).isEqualTo(Status.Code.NotFound);
assertThat(status.requiredSize).isEqualTo(0);
txn.put(k1, v1);
status = txn.get(readOptions, k1, target);
assertThat(status.status.getCode()).isEqualTo(Status.Code.Ok);
assertThat(status.requiredSize).isEqualTo(v1.length);
assertThat(target).isEqualTo("value1ite1".getBytes());
}
}
public void getPutByteBuffer(final Function<Integer, ByteBuffer> allocateBuffer)
throws RocksDBException {
final ByteBuffer k1 = allocateBuffer.apply(100).put("key1".getBytes(UTF_8));
k1.flip();
final ByteBuffer v1 = allocateBuffer.apply(100).put("value1".getBytes(UTF_8));
v1.flip();
final ByteBuffer vEmpty = allocateBuffer.apply(0);
try (final DBContainer dbContainer = startDb();
final ReadOptions readOptions = new ReadOptions();
final Transaction txn = dbContainer.beginTransaction()) {
final ByteBuffer vGet = allocateBuffer.apply(100);
assertThat(txn.get(readOptions, k1, vGet).status.getCode()).isEqualTo(Status.Code.NotFound);
txn.put(k1, v1);
final GetStatus getStatusError = txn.get(readOptions, k1, vEmpty);
assertThat(getStatusError.status.getCode()).isEqualTo(Status.Code.Ok);
assertThat(getStatusError.requiredSize).isEqualTo("value1".getBytes(UTF_8).length);
assertThat(vEmpty.position()).isEqualTo(0);
assertThat(vEmpty.remaining()).isEqualTo(0);
vGet.put("12345".getBytes(UTF_8));
final GetStatus getStatus = txn.get(readOptions, k1, vGet);
assertThat(getStatusError.status.getCode()).isEqualTo(Status.Code.Ok);
assertThat(getStatusError.requiredSize).isEqualTo("value1".getBytes(UTF_8).length);
vGet.put("67890".getBytes(UTF_8));
vGet.flip();
final byte[] bytes = new byte[vGet.limit()];
vGet.get(bytes);
assertThat(new String(bytes, UTF_8)).isEqualTo("12345value167890");
}
}
@Test
public void getPutDirectByteBuffer() throws RocksDBException {
getPutByteBuffer(ByteBuffer::allocateDirect);
}
@Test
public void getPutIndirectByteBuffer() throws RocksDBException {
getPutByteBuffer(ByteBuffer::allocate);
}
public void getPutByteBuffer_cf(final Function<Integer, ByteBuffer> allocateBuffer)
throws RocksDBException {
final ByteBuffer k1 = allocateBuffer.apply(100).put("key1".getBytes(UTF_8));
k1.flip();
final ByteBuffer v1 = allocateBuffer.apply(100).put("value1".getBytes(UTF_8));
v1.flip();
final ByteBuffer vEmpty = allocateBuffer.apply(0);
try (final DBContainer dbContainer = startDb();
final ReadOptions readOptions = new ReadOptions();
final Transaction txn = dbContainer.beginTransaction()) {
final ColumnFamilyHandle testCf = dbContainer.getTestColumnFamily();
final ByteBuffer vGet = allocateBuffer.apply(100);
assertThat(txn.get(readOptions, testCf, k1, vGet).status.getCode())
.isEqualTo(Status.Code.NotFound);
txn.put(testCf, k1, v1);
final GetStatus getStatusError = txn.get(readOptions, testCf, k1, vEmpty);
assertThat(getStatusError.status.getCode()).isEqualTo(Status.Code.Ok);
assertThat(getStatusError.requiredSize).isEqualTo("value1".getBytes(UTF_8).length);
assertThat(vEmpty.position()).isEqualTo(0);
assertThat(vEmpty.remaining()).isEqualTo(0);
vGet.put("12345".getBytes(UTF_8));
final GetStatus getStatus = txn.get(readOptions, testCf, k1, vGet);
assertThat(getStatus.status.getCode()).isEqualTo(Status.Code.Ok);
assertThat(getStatus.requiredSize).isEqualTo("value1".getBytes(UTF_8).length);
vGet.put("67890".getBytes(UTF_8));
vGet.flip();
final byte[] bytes = new byte[vGet.limit()];
vGet.get(bytes);
assertThat(new String(bytes, UTF_8)).isEqualTo("12345value167890");
}
}
@Test
public void getPutDirectByteBuffer_cf() throws RocksDBException {
getPutByteBuffer_cf(ByteBuffer::allocateDirect);
}
@Test
public void getPutIndirectByteBuffer_cf() throws RocksDBException {
getPutByteBuffer_cf(ByteBuffer::allocate);
}
@Test
public void multiGetPut_cf() throws RocksDBException {
final byte[][] keys = new byte[][] {"key1".getBytes(UTF_8), "key2".getBytes(UTF_8)};
@ -300,6 +433,162 @@ public abstract class AbstractTransactionTest {
}
}
@Test
public void getForUpdateByteArray_cf_doValidate() throws RocksDBException {
final byte[] k1 = "key1".getBytes(UTF_8);
final byte[] v1 = "value1".getBytes(UTF_8);
try (final DBContainer dbContainer = startDb();
final ReadOptions readOptions = new ReadOptions();
final Transaction txn = dbContainer.beginTransaction()) {
final ColumnFamilyHandle testCf = dbContainer.getTestColumnFamily();
final byte[] vNonExistent = new byte[1];
final GetStatus sNonExistent =
txn.getForUpdate(readOptions, testCf, k1, vNonExistent, true, true);
assertThat(sNonExistent.status.getCode()).isEqualTo(Status.Code.NotFound);
txn.put(testCf, k1, v1);
final byte[] vPartial = new byte[4];
final GetStatus sPartial = txn.getForUpdate(readOptions, testCf, k1, vPartial, true, true);
assertThat(sPartial.status.getCode()).isEqualTo(Status.Code.Ok);
assertThat(sPartial.requiredSize).isEqualTo(v1.length);
assertThat(vPartial).isEqualTo(Arrays.copyOfRange(v1, 0, vPartial.length));
final byte[] vTotal = new byte[sPartial.requiredSize];
final GetStatus sTotal = txn.getForUpdate(readOptions, testCf, k1, vTotal, true, true);
assertThat(sTotal.status.getCode()).isEqualTo(Status.Code.Ok);
assertThat(sTotal.requiredSize).isEqualTo(v1.length);
assertThat(vTotal).isEqualTo(v1);
}
}
public void getForUpdateByteArray_cf() throws RocksDBException {
final byte[] k1 = "key1".getBytes(UTF_8);
final byte[] v1 = "value1".getBytes(UTF_8);
try (final DBContainer dbContainer = startDb();
final ReadOptions readOptions = new ReadOptions();
final Transaction txn = dbContainer.beginTransaction()) {
final ColumnFamilyHandle testCf = dbContainer.getTestColumnFamily();
final byte[] vNonExistent = new byte[1];
final GetStatus sNonExistent = txn.getForUpdate(readOptions, testCf, k1, vNonExistent, true);
assertThat(sNonExistent.status.getCode()).isEqualTo(Status.Code.NotFound);
txn.put(testCf, k1, v1);
final byte[] vPartial = new byte[4];
final GetStatus sPartial = txn.getForUpdate(readOptions, testCf, k1, vPartial, true);
assertThat(sPartial.status.getCode()).isEqualTo(Status.Code.Ok);
assertThat(sPartial.requiredSize).isEqualTo(v1.length);
assertThat(vPartial).isEqualTo(Arrays.copyOfRange(v1, 0, vPartial.length));
final byte[] vTotal = new byte[sPartial.requiredSize];
final GetStatus sTotal = txn.getForUpdate(readOptions, testCf, k1, vTotal, true);
assertThat(sTotal.status.getCode()).isEqualTo(Status.Code.Ok);
assertThat(sTotal.requiredSize).isEqualTo(v1.length);
assertThat(vTotal).isEqualTo(v1);
}
}
@Test
public void getForUpdateByteArray() throws RocksDBException {
final byte[] k1 = "key1".getBytes(UTF_8);
final byte[] v1 = "value1".getBytes(UTF_8);
try (final DBContainer dbContainer = startDb();
final ReadOptions readOptions = new ReadOptions();
final Transaction txn = dbContainer.beginTransaction()) {
final byte[] vNonExistent = new byte[1];
final GetStatus sNonExistent = txn.getForUpdate(readOptions, k1, vNonExistent, true);
assertThat(sNonExistent.status.getCode()).isEqualTo(Status.Code.NotFound);
txn.put(k1, v1);
final byte[] vPartial = new byte[4];
final GetStatus sPartial = txn.getForUpdate(readOptions, k1, vPartial, true);
assertThat(sPartial.status.getCode()).isEqualTo(Status.Code.Ok);
assertThat(sPartial.requiredSize).isEqualTo(v1.length);
assertThat(vPartial).isEqualTo(Arrays.copyOfRange(v1, 0, vPartial.length));
final byte[] vTotal = new byte[sPartial.requiredSize];
final GetStatus sTotal = txn.getForUpdate(readOptions, k1, vTotal, true);
assertThat(sTotal.status.getCode()).isEqualTo(Status.Code.Ok);
assertThat(sTotal.requiredSize).isEqualTo(v1.length);
assertThat(vTotal).isEqualTo(v1);
}
}
@Test
public void getForUpdateDirectByteBuffer() throws Exception {
getForUpdateByteBuffer(ByteBuffer::allocateDirect);
}
@Test
public void getForUpdateIndirectByteBuffer() throws Exception {
getForUpdateByteBuffer(ByteBuffer::allocate);
}
public void getForUpdateByteBuffer(final Function<Integer, ByteBuffer> allocateBuffer)
throws Exception {
final ByteBuffer k1 = allocateBuffer.apply(20).put("key1".getBytes(UTF_8));
k1.flip();
final ByteBuffer v1 = allocateBuffer.apply(20).put("value1".getBytes(UTF_8));
v1.flip();
try (final DBContainer dbContainer = startDb();
final ReadOptions readOptions = new ReadOptions();
final Transaction txn = dbContainer.beginTransaction()) {
final ByteBuffer v1Read1 = allocateBuffer.apply(20);
final GetStatus getStatus1 = txn.getForUpdate(readOptions, k1, v1Read1, true);
assertThat(getStatus1.status.getCode()).isEqualTo(Status.Code.NotFound);
txn.put(k1, v1);
final ByteBuffer v1Read2 = allocateBuffer.apply(20);
final GetStatus getStatus2 = txn.getForUpdate(readOptions, k1, v1Read2, true);
assertThat(getStatus2.status.getCode()).isEqualTo(Status.Code.Ok);
assertThat(getStatus2.requiredSize).isEqualTo("value1".getBytes(UTF_8).length);
assertThat(v1Read2).isEqualTo(allocateBuffer.apply(20).put("value1".getBytes(UTF_8)));
}
}
@Test
public void getForUpdateDirectByteBuffer_cf() throws Exception {
getForUpdateByteBuffer_cf(ByteBuffer::allocateDirect);
}
@Test
public void getForUpdateIndirectByteBuffer_cf() throws Exception {
getForUpdateByteBuffer_cf(ByteBuffer::allocate);
}
public void getForUpdateByteBuffer_cf(final Function<Integer, ByteBuffer> allocateBuffer)
throws Exception {
final ByteBuffer k1 = allocateBuffer.apply(20).put("key1".getBytes(UTF_8));
k1.flip();
final ByteBuffer v1 = allocateBuffer.apply(20).put("value1".getBytes(UTF_8));
v1.flip();
final ByteBuffer k2 = allocateBuffer.apply(20).put("key2".getBytes(UTF_8));
k2.flip();
final ByteBuffer v2 = allocateBuffer.apply(20).put("value2".getBytes(UTF_8));
v2.flip();
try (final DBContainer dbContainer = startDb();
final ReadOptions readOptions = new ReadOptions();
final Transaction txn = dbContainer.beginTransaction()) {
final ColumnFamilyHandle testCf = dbContainer.getTestColumnFamily();
final ByteBuffer v1Read1 = allocateBuffer.apply(20);
final GetStatus getStatus1 = txn.getForUpdate(readOptions, testCf, k1, v1Read1, true);
assertThat(getStatus1.status.getCode()).isEqualTo(Status.Code.NotFound);
txn.put(k1, v1);
k1.flip();
v1.flip();
txn.put(testCf, k2, v2);
k2.flip();
v2.flip();
final ByteBuffer v1Read2 = allocateBuffer.apply(20);
final GetStatus getStatus2 = txn.getForUpdate(readOptions, testCf, k1, v1Read2, true);
assertThat(getStatus2.status.getCode()).isEqualTo(Status.Code.NotFound);
k1.flip();
txn.put(testCf, k1, v1);
k1.flip();
v1.flip();
final ByteBuffer v1Read3 = allocateBuffer.apply(20);
final GetStatus getStatus3 = txn.getForUpdate(readOptions, testCf, k1, v1Read3, true);
assertThat(getStatus3.status.getCode()).isEqualTo(Status.Code.Ok);
assertThat(getStatus3.requiredSize).isEqualTo("value1".getBytes(UTF_8).length);
assertThat(v1Read3).isEqualTo(allocateBuffer.apply(20).put("value1".getBytes(UTF_8)));
}
}
@Test
public void multiGetForUpdate_cf() throws RocksDBException {
final byte[][] keys = new byte[][] {"key1".getBytes(UTF_8), "key2".getBytes(UTF_8)};
@ -401,6 +690,13 @@ public abstract class AbstractTransactionTest {
assertThat(iterator.key()).isEqualTo(k1);
assertThat(iterator.value()).isEqualTo(v1);
}
try (final RocksIterator iterator = txn.getIterator()) {
iterator.seek(k1);
assertThat(iterator.isValid()).isTrue();
assertThat(iterator.key()).isEqualTo(k1);
assertThat(iterator.value()).isEqualTo(v1);
}
}
}
@ -422,6 +718,13 @@ public abstract class AbstractTransactionTest {
assertThat(iterator.key()).isEqualTo(k1);
assertThat(iterator.value()).isEqualTo(v1);
}
try (final RocksIterator iterator = txn.getIterator(testCf)) {
iterator.seek(k1);
assertThat(iterator.isValid()).isTrue();
assertThat(iterator.key()).isEqualTo(k1);
assertThat(iterator.value()).isEqualTo(v1);
}
}
}
@ -429,11 +732,15 @@ public abstract class AbstractTransactionTest {
public void merge_cf() throws RocksDBException {
final byte[] k1 = "key1".getBytes(UTF_8);
final byte[] v1 = "value1".getBytes(UTF_8);
final byte[] v2 = "value2".getBytes(UTF_8);
try(final DBContainer dbContainer = startDb();
final Transaction txn = dbContainer.beginTransaction()) {
final ColumnFamilyHandle testCf = dbContainer.getTestColumnFamily();
txn.merge(testCf, k1, v1);
txn.put(testCf, k1, v1);
txn.merge(testCf, k1, v2);
assertThat(txn.get(new ReadOptions(), testCf, k1)).isEqualTo("value1**value2".getBytes());
assertThat(txn.get(testCf, new ReadOptions(), k1)).isEqualTo("value1**value2".getBytes());
}
}
@ -441,13 +748,94 @@ public abstract class AbstractTransactionTest {
public void merge() throws RocksDBException {
final byte[] k1 = "key1".getBytes(UTF_8);
final byte[] v1 = "value1".getBytes(UTF_8);
final byte[] v2 = "value2".getBytes(UTF_8);
try(final DBContainer dbContainer = startDb();
final Transaction txn = dbContainer.beginTransaction()) {
txn.merge(k1, v1);
try (final DBContainer dbContainer = startDb();
final Transaction txn = dbContainer.beginTransaction()) {
txn.put(k1, v1);
txn.merge(k1, v2);
assertThat(txn.get(new ReadOptions(), k1)).isEqualTo("value1++value2".getBytes());
}
}
@Test
public void mergeDirectByteBuffer() throws RocksDBException {
final ByteBuffer k1 = ByteBuffer.allocateDirect(100).put("key1".getBytes(UTF_8));
final ByteBuffer v1 = ByteBuffer.allocateDirect(100).put("value1".getBytes(UTF_8));
final ByteBuffer v2 = ByteBuffer.allocateDirect(100).put("value2".getBytes(UTF_8));
k1.flip();
v1.flip();
v2.flip();
try (final DBContainer dbContainer = startDb();
final Transaction txn = dbContainer.beginTransaction()) {
txn.put(k1, v1);
k1.flip();
v1.flip();
txn.merge(k1, v2);
assertThat(txn.get(new ReadOptions(), "key1".getBytes(UTF_8)))
.isEqualTo("value1++value2".getBytes());
}
}
public void mergeIndirectByteBuffer() throws RocksDBException {
final ByteBuffer k1 = ByteBuffer.allocate(100).put("key1".getBytes(UTF_8));
k1.flip();
final ByteBuffer v1 = ByteBuffer.allocate(100).put("value1".getBytes(UTF_8));
v1.flip();
final ByteBuffer v2 = ByteBuffer.allocate(100).put("value2".getBytes(UTF_8));
v2.flip();
try(final DBContainer dbContainer = startDb();
final Transaction txn = dbContainer.beginTransaction()) {
txn.put(k1, v1);
txn.merge(k1, v2);
assertThat(txn.get(new ReadOptions(), "key1".getBytes(UTF_8)))
.isEqualTo("value1++value2".getBytes());
}
}
@Test
public void mergeDirectByteBuffer_cf() throws RocksDBException {
final ByteBuffer k1 = ByteBuffer.allocateDirect(100).put("key1".getBytes(UTF_8));
final ByteBuffer v1 = ByteBuffer.allocateDirect(100).put("value1".getBytes(UTF_8));
final ByteBuffer v2 = ByteBuffer.allocateDirect(100).put("value2".getBytes(UTF_8));
k1.flip();
v1.flip();
v2.flip();
try (final DBContainer dbContainer = startDb();
final Transaction txn = dbContainer.beginTransaction()) {
final ColumnFamilyHandle testCf = dbContainer.getTestColumnFamily();
txn.put(testCf, k1, v1);
k1.flip();
v1.flip();
txn.merge(testCf, k1, v2);
assertThat(txn.get(new ReadOptions(), testCf, "key1".getBytes(UTF_8)))
.isEqualTo("value1**value2".getBytes());
assertThat(txn.get(testCf, new ReadOptions(), "key1".getBytes(UTF_8)))
.isEqualTo("value1**value2".getBytes());
}
}
public void mergeIndirectByteBuffer_cf() throws RocksDBException {
final ByteBuffer k1 = ByteBuffer.allocate(100).put("key1".getBytes(UTF_8));
k1.flip();
final ByteBuffer v1 = ByteBuffer.allocate(100).put("value1".getBytes(UTF_8));
v1.flip();
final ByteBuffer v2 = ByteBuffer.allocate(100).put("value2".getBytes(UTF_8));
v2.flip();
try (final DBContainer dbContainer = startDb();
final Transaction txn = dbContainer.beginTransaction()) {
final ColumnFamilyHandle testCf = dbContainer.getTestColumnFamily();
txn.put(testCf, k1, v1);
txn.merge(testCf, k1, v2);
assertThat(txn.get(new ReadOptions(), testCf, "key1".getBytes(UTF_8)))
.isEqualTo("value1**value2".getBytes());
assertThat(txn.get(testCf, new ReadOptions(), "key1".getBytes(UTF_8)))
.isEqualTo("value1**value2".getBytes());
}
}
@Test
public void delete_cf() throws RocksDBException {
@ -459,9 +847,11 @@ public abstract class AbstractTransactionTest {
final Transaction txn = dbContainer.beginTransaction()) {
final ColumnFamilyHandle testCf = dbContainer.getTestColumnFamily();
txn.put(testCf, k1, v1);
assertThat(txn.get(readOptions, testCf, k1)).isEqualTo(v1);
assertThat(txn.get(testCf, readOptions, k1)).isEqualTo(v1);
txn.delete(testCf, k1);
assertThat(txn.get(readOptions, testCf, k1)).isNull();
assertThat(txn.get(testCf, readOptions, k1)).isNull();
}
}
@ -495,11 +885,12 @@ public abstract class AbstractTransactionTest {
final ColumnFamilyHandle testCf = dbContainer.getTestColumnFamily();
txn.put(testCf, keyParts, valueParts);
assertThat(txn.get(testCf, readOptions, key)).isEqualTo(value);
assertThat(txn.get(readOptions, testCf, key)).isEqualTo(value);
txn.delete(testCf, keyParts);
assertThat(txn.get(testCf, readOptions, key))
.isNull();
assertThat(txn.get(readOptions, testCf, key)).isNull();
assertThat(txn.get(testCf, readOptions, key)).isNull();
}
}
@ -532,8 +923,10 @@ public abstract class AbstractTransactionTest {
final ReadOptions readOptions = new ReadOptions();
final Transaction txn = dbContainer.beginTransaction()) {
final ColumnFamilyHandle testCf = dbContainer.getTestColumnFamily();
assertThat(txn.get(readOptions, testCf, k1)).isNull();
assertThat(txn.get(testCf, readOptions, k1)).isNull();
txn.putUntracked(testCf, k1, v1);
assertThat(txn.get(readOptions, testCf, k1)).isEqualTo(v1);
assertThat(txn.get(testCf, readOptions, k1)).isEqualTo(v1);
}
}
@ -628,11 +1021,19 @@ public abstract class AbstractTransactionTest {
public void mergeUntracked_cf() throws RocksDBException {
final byte[] k1 = "key1".getBytes(UTF_8);
final byte[] v1 = "value1".getBytes(UTF_8);
final byte[] v2 = "value2".getBytes(UTF_8);
try(final DBContainer dbContainer = startDb();
final Transaction txn = dbContainer.beginTransaction()) {
final ColumnFamilyHandle testCf = dbContainer.getTestColumnFamily();
txn.mergeUntracked(testCf, k1, v1);
txn.mergeUntracked(testCf, k1, v2);
txn.commit();
}
try (final DBContainer dbContainer = startDb();
final Transaction txn = dbContainer.beginTransaction()) {
final ColumnFamilyHandle testCf = dbContainer.getTestColumnFamily();
assertThat(txn.get(new ReadOptions(), testCf, k1)).isEqualTo("value1**value2".getBytes());
}
}
@ -640,10 +1041,89 @@ public abstract class AbstractTransactionTest {
public void mergeUntracked() throws RocksDBException {
final byte[] k1 = "key1".getBytes(UTF_8);
final byte[] v1 = "value1".getBytes(UTF_8);
final byte[] v2 = "value2".getBytes(UTF_8);
try(final DBContainer dbContainer = startDb();
final Transaction txn = dbContainer.beginTransaction()) {
txn.mergeUntracked(k1, v1);
txn.mergeUntracked(k1, v2);
txn.commit();
}
try (final DBContainer dbContainer = startDb();
final Transaction txn = dbContainer.beginTransaction()) {
assertThat(txn.get(new ReadOptions(), k1)).isEqualTo("value1++value2".getBytes());
}
}
@Test
public void mergeUntrackedByteBuffer() throws RocksDBException {
final ByteBuffer k1 = ByteBuffer.allocateDirect(20).put("key1".getBytes(UTF_8));
final ByteBuffer v1 = ByteBuffer.allocateDirect(20).put("value1".getBytes(UTF_8));
final ByteBuffer v2 = ByteBuffer.allocateDirect(20).put("value2".getBytes(UTF_8));
k1.flip();
v1.flip();
v2.flip();
try (final DBContainer dbContainer = startDb();
final Transaction txn = dbContainer.beginTransaction()) {
txn.mergeUntracked(k1, v1);
k1.flip();
v1.flip();
txn.mergeUntracked(k1, v2);
k1.flip();
v2.flip();
txn.commit();
}
try (final DBContainer dbContainer = startDb();
final Transaction txn = dbContainer.beginTransaction()) {
final ByteBuffer v = ByteBuffer.allocateDirect(20);
final GetStatus status = txn.get(new ReadOptions(), k1, v);
assertThat(status.status.getCode()).isEqualTo(Status.Code.Ok);
k1.flip();
v.flip();
final int expectedLength = "value1++value2".length();
assertThat(v.remaining()).isEqualTo(expectedLength);
final byte[] vBytes = new byte[expectedLength];
v.get(vBytes);
assertThat(vBytes).isEqualTo("value1++value2".getBytes());
}
}
@Test
public void mergeUntrackedByteBuffer_cf() throws RocksDBException {
final ByteBuffer k1 = ByteBuffer.allocateDirect(20).put("key1".getBytes(UTF_8));
final ByteBuffer v1 = ByteBuffer.allocateDirect(20).put("value1".getBytes(UTF_8));
final ByteBuffer v2 = ByteBuffer.allocateDirect(20).put("value2".getBytes(UTF_8));
k1.flip();
v1.flip();
v2.flip();
try (final DBContainer dbContainer = startDb();
final Transaction txn = dbContainer.beginTransaction()) {
final ColumnFamilyHandle testCf = dbContainer.getTestColumnFamily();
txn.mergeUntracked(testCf, k1, v1);
k1.flip();
v1.flip();
txn.mergeUntracked(testCf, k1, v2);
k1.flip();
v2.flip();
txn.commit();
}
try (final DBContainer dbContainer = startDb();
final Transaction txn = dbContainer.beginTransaction()) {
final ByteBuffer v = ByteBuffer.allocateDirect(20);
final ColumnFamilyHandle testCf = dbContainer.getTestColumnFamily();
final GetStatus status = txn.get(new ReadOptions(), testCf, k1, v);
assertThat(status.status.getCode()).isEqualTo(Status.Code.Ok);
k1.flip();
v.flip();
final int expectedLength = "value1++value2".length();
assertThat(v.remaining()).isEqualTo(expectedLength);
final byte[] vBytes = new byte[expectedLength];
v.get(vBytes);
assertThat(vBytes).isEqualTo("value1**value2".getBytes());
}
}
@ -657,9 +1137,11 @@ public abstract class AbstractTransactionTest {
final Transaction txn = dbContainer.beginTransaction()) {
final ColumnFamilyHandle testCf = dbContainer.getTestColumnFamily();
txn.put(testCf, k1, v1);
assertThat(txn.get(readOptions, testCf, k1)).isEqualTo(v1);
assertThat(txn.get(testCf, readOptions, k1)).isEqualTo(v1);
txn.deleteUntracked(testCf, k1);
assertThat(txn.get(readOptions, testCf, k1)).isNull();
assertThat(txn.get(testCf, readOptions, k1)).isNull();
}
}
@ -692,9 +1174,11 @@ public abstract class AbstractTransactionTest {
final Transaction txn = dbContainer.beginTransaction()) {
final ColumnFamilyHandle testCf = dbContainer.getTestColumnFamily();
txn.put(testCf, keyParts, valueParts);
assertThat(txn.get(readOptions, testCf, key)).isEqualTo(value);
assertThat(txn.get(testCf, readOptions, key)).isEqualTo(value);
txn.deleteUntracked(testCf, keyParts);
assertThat(txn.get(readOptions, testCf, key)).isNull();
assertThat(txn.get(testCf, readOptions, key)).isNull();
}
}

View File

@ -261,10 +261,12 @@ public class KeyMayExistTest {
keyBuffer.flip();
assertThat(db.keyMayExist(keyBuffer)).isEqualTo(true);
keyBuffer.flip();
final ByteBuffer valueBuffer = ByteBuffer.allocateDirect(value.length + 24);
valueBuffer.position(12);
KeyMayExist keyMayExist = db.keyMayExist(keyBuffer, valueBuffer);
keyBuffer.flip();
assertThat(keyMayExist.exists).isEqualTo(KeyMayExist.KeyMayExistEnum.kExistsWithValue);
assertThat(keyMayExist.valueLength).isEqualTo(value.length);
assertThat(valueBuffer.position()).isEqualTo(12);
@ -303,10 +305,11 @@ public class KeyMayExistTest {
try (final ReadOptions readOptions = new ReadOptions()) {
assertThat(db.keyMayExist(readOptions, keyBuffer)).isEqualTo(true);
keyBuffer.flip();
final ByteBuffer valueBuffer = ByteBuffer.allocateDirect(value.length + 24);
valueBuffer.position(12);
KeyMayExist keyMayExist = db.keyMayExist(readOptions, keyBuffer, valueBuffer);
keyBuffer.flip();
assertThat(keyMayExist.exists).isEqualTo(KeyMayExist.KeyMayExistEnum.kExistsWithValue);
assertThat(keyMayExist.valueLength).isEqualTo(value.length);
assertThat(valueBuffer.position()).isEqualTo(12);
@ -318,6 +321,7 @@ public class KeyMayExistTest {
valueBuffer.limit(value.length + 24);
valueBuffer.position(25);
keyMayExist = db.keyMayExist(readOptions, keyBuffer, valueBuffer);
keyBuffer.flip();
assertThat(keyMayExist.exists).isEqualTo(KeyMayExist.KeyMayExistEnum.kExistsWithValue);
assertThat(keyMayExist.valueLength).isEqualTo(value.length);
assertThat(valueBuffer.position()).isEqualTo(25);
@ -362,7 +366,9 @@ public class KeyMayExistTest {
keyBuffer.flip();
assertThat(db.keyMayExist(keyBuffer)).isEqualTo(true);
keyBuffer.flip();
assertThat(db.keyMayExist(columnFamilyHandleList.get(1), keyBuffer)).isEqualTo(false);
keyBuffer.flip();
assertThat(db.keyMayExist(columnFamilyHandleList.get(0), keyBuffer)).isEqualTo(true);
// 1 is just a CF
@ -372,8 +378,11 @@ public class KeyMayExistTest {
keyBuffer.flip();
assertThat(db.keyMayExist(keyBuffer)).isEqualTo(false);
keyBuffer.flip();
assertThat(db.keyMayExist(columnFamilyHandleList.get(1), keyBuffer)).isEqualTo(true);
keyBuffer.flip();
assertThat(db.keyMayExist(columnFamilyHandleList.get(0), keyBuffer)).isEqualTo(false);
keyBuffer.flip();
exceptionRule.expect(AssertionError.class);
exceptionRule.expectMessage(
@ -395,8 +404,10 @@ public class KeyMayExistTest {
try (final ReadOptions readOptions = new ReadOptions()) {
assertThat(db.keyMayExist(keyBuffer)).isEqualTo(true);
keyBuffer.flip();
assertThat(db.keyMayExist(columnFamilyHandleList.get(1), readOptions, keyBuffer))
.isEqualTo(false);
keyBuffer.flip();
assertThat(db.keyMayExist(columnFamilyHandleList.get(0), readOptions, keyBuffer))
.isEqualTo(true);
@ -407,8 +418,10 @@ public class KeyMayExistTest {
keyBuffer.flip();
assertThat(db.keyMayExist(readOptions, keyBuffer)).isEqualTo(false);
keyBuffer.flip();
assertThat(db.keyMayExist(columnFamilyHandleList.get(1), readOptions, keyBuffer))
.isEqualTo(true);
keyBuffer.flip();
assertThat(db.keyMayExist(columnFamilyHandleList.get(0), readOptions, keyBuffer))
.isEqualTo(false);
@ -432,10 +445,11 @@ public class KeyMayExistTest {
keyBuffer.flip();
assertThat(db.keyMayExist(columnFamilyHandleList.get(1), keyBuffer)).isEqualTo(true);
keyBuffer.flip();
final ByteBuffer valueBuffer = ByteBuffer.allocateDirect(value.length + 24);
valueBuffer.position(12);
KeyMayExist keyMayExist = db.keyMayExist(columnFamilyHandleList.get(1), keyBuffer, valueBuffer);
keyBuffer.flip();
assertThat(keyMayExist.exists).isEqualTo(KeyMayExist.KeyMayExistEnum.kExistsWithValue);
assertThat(keyMayExist.valueLength).isEqualTo(value.length);
assertThat(valueBuffer.position()).isEqualTo(12);
@ -474,11 +488,12 @@ public class KeyMayExistTest {
try (final ReadOptions readOptions = new ReadOptions()) {
assertThat(db.keyMayExist(columnFamilyHandleList.get(1), readOptions, keyBuffer))
.isEqualTo(true);
keyBuffer.flip();
final ByteBuffer valueBuffer = ByteBuffer.allocateDirect(value.length + 24);
valueBuffer.position(12);
KeyMayExist keyMayExist =
db.keyMayExist(columnFamilyHandleList.get(1), readOptions, keyBuffer, valueBuffer);
keyBuffer.flip();
assertThat(keyMayExist.exists).isEqualTo(KeyMayExist.KeyMayExistEnum.kExistsWithValue);
assertThat(keyMayExist.valueLength).isEqualTo(value.length);
assertThat(valueBuffer.position()).isEqualTo(12);
@ -491,6 +506,7 @@ public class KeyMayExistTest {
valueBuffer.position(25);
keyMayExist =
db.keyMayExist(columnFamilyHandleList.get(1), readOptions, keyBuffer, valueBuffer);
keyBuffer.flip();
assertThat(keyMayExist.exists).isEqualTo(KeyMayExist.KeyMayExistEnum.kExistsWithValue);
assertThat(keyMayExist.valueLength).isEqualTo(value.length);
assertThat(valueBuffer.position()).isEqualTo(25);

View File

@ -0,0 +1,126 @@
// 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 static org.assertj.core.api.Assertions.assertThat;
import static org.rocksdb.MergeTest.longFromByteArray;
import static org.rocksdb.MergeTest.longToByteArray;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.junit.ClassRule;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@RunWith(Parameterized.class)
public class MergeCFVariantsTest {
@FunctionalInterface
interface FunctionMerge<PDatabase, PColumnFamilyHandle, PLeft, PRight> {
public void apply(PDatabase db, PColumnFamilyHandle one, PLeft two, PRight three)
throws RocksDBException;
}
@Parameterized.Parameters
public static List<FunctionMerge<RocksDB, ColumnFamilyHandle, byte[], byte[]>> data() {
return Arrays.asList(RocksDB::merge,
(db, cfh, left, right)
-> db.merge(cfh, new WriteOptions(), left, right),
(db, cfh, left, right)
-> {
final byte[] left0 =
("1234567" + new String(left, StandardCharsets.UTF_8) + "890").getBytes();
final byte[] right0 =
("1234" + new String(right, StandardCharsets.UTF_8) + "567890ab").getBytes();
db.merge(cfh, left0, 7, left.length, right0, 4, right.length);
},
(db, cfh, left, right)
-> {
final byte[] left0 =
("1234567" + new String(left, StandardCharsets.UTF_8) + "890").getBytes();
final byte[] right0 =
("1234" + new String(right, StandardCharsets.UTF_8) + "567890ab").getBytes();
db.merge(cfh, new WriteOptions(), left0, 7, left.length, right0, 4, right.length);
},
(db, cfh, left, right)
-> {
final ByteBuffer bbLeft = ByteBuffer.allocateDirect(100);
final ByteBuffer bbRight = ByteBuffer.allocateDirect(100);
bbLeft.put(left).flip();
bbRight.put(right).flip();
db.merge(cfh, new WriteOptions(), bbLeft, bbRight);
},
(db, cfh, left, right) -> {
final ByteBuffer bbLeft = ByteBuffer.allocate(100);
final ByteBuffer bbRight = ByteBuffer.allocate(100);
bbLeft.put(left).flip();
bbRight.put(right).flip();
db.merge(cfh, new WriteOptions(), bbLeft, bbRight);
});
}
@Parameterized.Parameter
public FunctionMerge<RocksDB, ColumnFamilyHandle, byte[], byte[]> mergeFunction;
@ClassRule
public static final RocksNativeLibraryResource ROCKS_NATIVE_LIBRARY_RESOURCE =
new RocksNativeLibraryResource();
@Rule public TemporaryFolder dbFolder = new TemporaryFolder();
@Test
public void cFUInt64AddOperatorOption() throws InterruptedException, RocksDBException {
try (final UInt64AddOperator uint64AddOperator = new UInt64AddOperator();
final ColumnFamilyOptions cfOpt1 =
new ColumnFamilyOptions().setMergeOperator(uint64AddOperator);
final ColumnFamilyOptions cfOpt2 =
new ColumnFamilyOptions().setMergeOperator(uint64AddOperator)) {
final List<ColumnFamilyDescriptor> cfDescriptors =
Arrays.asList(new ColumnFamilyDescriptor(RocksDB.DEFAULT_COLUMN_FAMILY, cfOpt1),
new ColumnFamilyDescriptor("new_cf".getBytes(), cfOpt2));
final List<ColumnFamilyHandle> columnFamilyHandleList = new ArrayList<>();
try (final DBOptions opt =
new DBOptions().setCreateIfMissing(true).setCreateMissingColumnFamilies(true);
final RocksDB db = RocksDB.open(
opt, dbFolder.getRoot().getAbsolutePath(), cfDescriptors, columnFamilyHandleList)) {
try {
// writing (long)100 under key
db.put(columnFamilyHandleList.get(1), "cfkey".getBytes(), longToByteArray(100));
// merge (long)1 under key
mergeFunction.apply(
db, columnFamilyHandleList.get(1), "cfkey".getBytes(), longToByteArray(1));
byte[] value = db.get(columnFamilyHandleList.get(1), "cfkey".getBytes());
long longValue = longFromByteArray(value);
// Test also with createColumnFamily
try (final ColumnFamilyOptions cfHandleOpts =
new ColumnFamilyOptions().setMergeOperator(uint64AddOperator);
final ColumnFamilyHandle cfHandle = db.createColumnFamily(
new ColumnFamilyDescriptor("new_cf2".getBytes(), cfHandleOpts))) {
// writing (long)200 under cfkey2
db.put(cfHandle, "cfkey2".getBytes(), longToByteArray(200));
// merge (long)50 under cfkey2
db.merge(cfHandle, new WriteOptions(), "cfkey2".getBytes(), longToByteArray(50));
value = db.get(cfHandle, "cfkey2".getBytes());
long longValueTmpCf = longFromByteArray(value);
assertThat(longValue).isEqualTo(101);
assertThat(longValueTmpCf).isEqualTo(250);
}
} finally {
for (final ColumnFamilyHandle columnFamilyHandle : columnFamilyHandleList) {
columnFamilyHandle.close();
}
}
}
}
}
}

View File

@ -45,14 +45,14 @@ public class MergeTest {
}
}
private byte[] longToByteArray(final long l) {
static byte[] longToByteArray(final long l) {
final ByteBuffer buf =
ByteBuffer.allocate(Long.SIZE / Byte.SIZE).order(ByteOrder.LITTLE_ENDIAN);
buf.putLong(l);
return buf.array();
}
private long longFromByteArray(final byte[] a) {
static long longFromByteArray(final byte[] a) {
final ByteBuffer buf =
ByteBuffer.allocate(Long.SIZE / Byte.SIZE).order(ByteOrder.LITTLE_ENDIAN);
buf.put(a);

View File

@ -0,0 +1,95 @@
// 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 static org.assertj.core.api.Assertions.assertThat;
import static org.rocksdb.MergeTest.longFromByteArray;
import static org.rocksdb.MergeTest.longToByteArray;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.List;
import org.junit.ClassRule;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@RunWith(Parameterized.class)
public class MergeVariantsTest {
@FunctionalInterface
interface FunctionMerge<PDatabase, PLeft, PRight> {
public void apply(PDatabase db, PLeft two, PRight three) throws RocksDBException;
}
@Parameterized.Parameters
public static List<MergeVariantsTest.FunctionMerge<RocksDB, byte[], byte[]>> data() {
return Arrays.asList(RocksDB::merge,
(db, left, right)
-> db.merge(new WriteOptions(), left, right),
(db, left, right)
-> {
final byte[] left0 =
("1234567" + new String(left, StandardCharsets.UTF_8) + "890").getBytes();
final byte[] right0 =
("1234" + new String(right, StandardCharsets.UTF_8) + "567890ab").getBytes();
db.merge(left0, 7, left.length, right0, 4, right.length);
},
(db, left, right)
-> {
final byte[] left0 =
("1234567" + new String(left, StandardCharsets.UTF_8) + "890").getBytes();
final byte[] right0 =
("1234" + new String(right, StandardCharsets.UTF_8) + "567890ab").getBytes();
db.merge(new WriteOptions(), left0, 7, left.length, right0, 4, right.length);
},
(db, left, right)
-> {
final ByteBuffer bbLeft = ByteBuffer.allocateDirect(100);
final ByteBuffer bbRight = ByteBuffer.allocateDirect(100);
bbLeft.put(left).flip();
bbRight.put(right).flip();
db.merge(new WriteOptions(), bbLeft, bbRight);
},
(db, left, right) -> {
final ByteBuffer bbLeft = ByteBuffer.allocate(100);
final ByteBuffer bbRight = ByteBuffer.allocate(100);
bbLeft.put(left).flip();
bbRight.put(right).flip();
db.merge(new WriteOptions(), bbLeft, bbRight);
});
}
@Parameterized.Parameter
public MergeVariantsTest.FunctionMerge<RocksDB, byte[], byte[]> mergeFunction;
@ClassRule
public static final RocksNativeLibraryResource ROCKS_NATIVE_LIBRARY_RESOURCE =
new RocksNativeLibraryResource();
@Rule public TemporaryFolder dbFolder = new TemporaryFolder();
@Test
public void uint64AddOperatorOption() throws InterruptedException, RocksDBException {
try (final UInt64AddOperator uint64AddOperator = new UInt64AddOperator();
final Options opt =
new Options().setCreateIfMissing(true).setMergeOperator(uint64AddOperator);
final RocksDB db = RocksDB.open(opt, dbFolder.getRoot().getAbsolutePath())) {
// Writing (long)100 under key
db.put("key".getBytes(), longToByteArray(100));
// Writing (long)1 under key
mergeFunction.apply(db, "key".getBytes(), longToByteArray(1));
final byte[] value = db.get("key".getBytes());
final long longValue = longFromByteArray(value);
assertThat(longValue).isEqualTo(101);
}
}
}

View File

@ -373,12 +373,13 @@ public class OptimisticTransactionTest extends AbstractTransactionTest {
.setCreateIfMissing(true)
.setCreateMissingColumnFamilies(true);
final ColumnFamilyOptions defaultColumnFamilyOptions = new ColumnFamilyOptions();
defaultColumnFamilyOptions.setMergeOperator(new StringAppendOperator("++"));
final ColumnFamilyOptions columnFamilyOptions = new ColumnFamilyOptions();
final List<ColumnFamilyDescriptor> columnFamilyDescriptors =
Arrays.asList(
new ColumnFamilyDescriptor(RocksDB.DEFAULT_COLUMN_FAMILY),
new ColumnFamilyDescriptor(TXN_TEST_COLUMN_FAMILY,
columnFamilyOptions));
columnFamilyOptions.setMergeOperator(new StringAppendOperator("**"));
final List<ColumnFamilyDescriptor> columnFamilyDescriptors = Arrays.asList(
new ColumnFamilyDescriptor(RocksDB.DEFAULT_COLUMN_FAMILY, defaultColumnFamilyOptions),
new ColumnFamilyDescriptor(TXN_TEST_COLUMN_FAMILY, columnFamilyOptions));
final List<ColumnFamilyHandle> columnFamilyHandles = new ArrayList<>();
final OptimisticTransactionDB optimisticTxnDb;

View File

@ -0,0 +1,126 @@
// 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 static org.assertj.core.api.Assertions.assertThat;
import static org.rocksdb.MergeTest.longFromByteArray;
import static org.rocksdb.MergeTest.longToByteArray;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.junit.ClassRule;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@RunWith(Parameterized.class)
public class PutCFVariantsTest {
@FunctionalInterface
interface FunctionCFPut<PDatabase, PColumnFamilyHandle, PLeft, PRight> {
public void apply(PDatabase db, PColumnFamilyHandle cfh, PLeft two, PRight three)
throws RocksDBException;
}
@Parameterized.Parameters
public static List<PutCFVariantsTest.FunctionCFPut<RocksDB, ColumnFamilyHandle, byte[], byte[]>>
data() {
return Arrays.asList(RocksDB::put,
(db, cfh, left, right)
-> db.put(cfh, new WriteOptions(), left, right),
(db, cfh, left, right)
-> {
final byte[] left0 =
("1234567" + new String(left, StandardCharsets.UTF_8) + "890").getBytes();
final byte[] right0 =
("1234" + new String(right, StandardCharsets.UTF_8) + "567890ab").getBytes();
db.put(cfh, left0, 7, left.length, right0, 4, right.length);
},
(db, cfh, left, right)
-> {
final byte[] left0 =
("1234567" + new String(left, StandardCharsets.UTF_8) + "890").getBytes();
final byte[] right0 =
("1234" + new String(right, StandardCharsets.UTF_8) + "567890ab").getBytes();
db.put(cfh, new WriteOptions(), left0, 7, left.length, right0, 4, right.length);
},
(db, cfh, left, right)
-> {
final ByteBuffer bbLeft = ByteBuffer.allocateDirect(100);
final ByteBuffer bbRight = ByteBuffer.allocateDirect(100);
bbLeft.put(left).flip();
bbRight.put(right).flip();
db.put(cfh, new WriteOptions(), bbLeft, bbRight);
},
(db, cfh, left, right) -> {
final ByteBuffer bbLeft = ByteBuffer.allocate(100);
final ByteBuffer bbRight = ByteBuffer.allocate(100);
bbLeft.put(left).flip();
bbRight.put(right).flip();
db.put(cfh, new WriteOptions(), bbLeft, bbRight);
});
}
@Parameterized.Parameter
public PutCFVariantsTest.FunctionCFPut<RocksDB, ColumnFamilyHandle, byte[], byte[]> putFunction;
@ClassRule
public static final RocksNativeLibraryResource ROCKS_NATIVE_LIBRARY_RESOURCE =
new RocksNativeLibraryResource();
@Rule public TemporaryFolder dbFolder = new TemporaryFolder();
@Test
public void writeAndRead() throws InterruptedException, RocksDBException {
try (final UInt64AddOperator uint64AddOperator = new UInt64AddOperator();
final ColumnFamilyOptions cfOpt1 =
new ColumnFamilyOptions().setMergeOperator(uint64AddOperator);
final ColumnFamilyOptions cfOpt2 =
new ColumnFamilyOptions().setMergeOperator(uint64AddOperator)) {
final List<ColumnFamilyDescriptor> cfDescriptors =
Arrays.asList(new ColumnFamilyDescriptor(RocksDB.DEFAULT_COLUMN_FAMILY, cfOpt1),
new ColumnFamilyDescriptor("new_cf".getBytes(), cfOpt2));
final List<ColumnFamilyHandle> columnFamilyHandleList = new ArrayList<>();
try (final DBOptions opt =
new DBOptions().setCreateIfMissing(true).setCreateMissingColumnFamilies(true);
final RocksDB db = RocksDB.open(
opt, dbFolder.getRoot().getAbsolutePath(), cfDescriptors, columnFamilyHandleList)) {
try {
// writing (long)100 under key
putFunction.apply(
db, columnFamilyHandleList.get(1), "cfkey".getBytes(), longToByteArray(100));
// merge (long)1 under key
byte[] value = db.get(columnFamilyHandleList.get(1), "cfkey".getBytes());
final long longValue = longFromByteArray(value);
// Test also with createColumnFamily
try (final ColumnFamilyOptions cfHandleOpts =
new ColumnFamilyOptions().setMergeOperator(uint64AddOperator);
final ColumnFamilyHandle cfHandle = db.createColumnFamily(
new ColumnFamilyDescriptor("new_cf2".getBytes(), cfHandleOpts))) {
// writing (long)200 under cfkey2
db.put(cfHandle, "cfkey2".getBytes(), longToByteArray(200));
// merge (long)50 under cfkey2
value = db.get(cfHandle, "cfkey2".getBytes());
final long longValueTmpCf = longFromByteArray(value);
assertThat(longValue).isEqualTo(100);
assertThat(longValueTmpCf).isEqualTo(200);
}
} finally {
for (final ColumnFamilyHandle columnFamilyHandle : columnFamilyHandleList) {
columnFamilyHandle.close();
}
}
}
}
}
}

View File

@ -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 static org.assertj.core.api.Assertions.assertThat;
import static org.rocksdb.MergeTest.longFromByteArray;
import static org.rocksdb.MergeTest.longToByteArray;
import java.nio.ByteBuffer;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.List;
import org.junit.ClassRule;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@RunWith(Parameterized.class)
public class PutVariantsTest {
@FunctionalInterface
interface FunctionPut<PDatabase, PLeft, PRight> {
public void apply(PDatabase db, PLeft two, PRight three) throws RocksDBException;
}
@Parameterized.Parameters
public static List<PutVariantsTest.FunctionPut<RocksDB, byte[], byte[]>> data() {
return Arrays.asList(RocksDB::put,
(db, left, right)
-> db.put(new WriteOptions(), left, right),
(db, left, right)
-> {
final byte[] left0 =
("1234567" + new String(left, StandardCharsets.UTF_8) + "890").getBytes();
final byte[] right0 =
("1234" + new String(right, StandardCharsets.UTF_8) + "567890ab").getBytes();
db.put(left0, 7, left.length, right0, 4, right.length);
},
(db, left, right)
-> {
final byte[] left0 =
("1234567" + new String(left, StandardCharsets.UTF_8) + "890").getBytes();
final byte[] right0 =
("1234" + new String(right, StandardCharsets.UTF_8) + "567890ab").getBytes();
db.put(new WriteOptions(), left0, 7, left.length, right0, 4, right.length);
},
(db, left, right)
-> {
final ByteBuffer bbLeft = ByteBuffer.allocateDirect(100);
final ByteBuffer bbRight = ByteBuffer.allocateDirect(100);
bbLeft.put(left).flip();
bbRight.put(right).flip();
db.put(new WriteOptions(), bbLeft, bbRight);
},
(db, left, right) -> {
final ByteBuffer bbLeft = ByteBuffer.allocate(100);
final ByteBuffer bbRight = ByteBuffer.allocate(100);
bbLeft.put(left).flip();
bbRight.put(right).flip();
db.put(new WriteOptions(), bbLeft, bbRight);
});
}
@Parameterized.Parameter public PutVariantsTest.FunctionPut<RocksDB, byte[], byte[]> putFunction;
@ClassRule
public static final RocksNativeLibraryResource ROCKS_NATIVE_LIBRARY_RESOURCE =
new RocksNativeLibraryResource();
@Rule public TemporaryFolder dbFolder = new TemporaryFolder();
@Test
public void writeAndRead() throws InterruptedException, RocksDBException {
try (final UInt64AddOperator uint64AddOperator = new UInt64AddOperator();
final Options opt =
new Options().setCreateIfMissing(true).setMergeOperator(uint64AddOperator);
final RocksDB db = RocksDB.open(opt, dbFolder.getRoot().getAbsolutePath())) {
// Writing (long)100 under key
putFunction.apply(db, "key".getBytes(), longToByteArray(100));
final byte[] value = db.get("key".getBytes());
final long longValue = longFromByteArray(value);
assertThat(longValue).isEqualTo(100);
}
}
}

View File

@ -184,10 +184,8 @@ public class RocksDBTest {
final WriteOptions opt = new WriteOptions(); final ReadOptions optr = new ReadOptions()) {
db.put("key1".getBytes(), "value".getBytes());
db.put(opt, "key2".getBytes(), "12345678".getBytes());
assertThat(db.get("key1".getBytes())).isEqualTo(
"value".getBytes());
assertThat(db.get("key2".getBytes())).isEqualTo(
"12345678".getBytes());
assertThat(db.get("key1".getBytes())).isEqualTo("value".getBytes());
assertThat(db.get("key2".getBytes())).isEqualTo("12345678".getBytes());
final ByteBuffer key = ByteBuffer.allocateDirect(12);
final ByteBuffer value = ByteBuffer.allocateDirect(12);
@ -245,8 +243,73 @@ public class RocksDBTest {
}
}
private static Segment sliceSegment(final String key) {
final ByteBuffer rawKey = ByteBuffer.allocate(key.length() + 4);
@Test
public void putIndirectByteBuffers() throws RocksDBException {
try (final RocksDB db = RocksDB.open(dbFolder.getRoot().getAbsolutePath());
final WriteOptions opt = new WriteOptions(); final ReadOptions optr = new ReadOptions()) {
db.put("key1".getBytes(), "value".getBytes());
db.put(opt, "key2".getBytes(), "12345678".getBytes());
assertThat(db.get("key1".getBytes())).isEqualTo("value".getBytes());
assertThat(db.get("key2".getBytes())).isEqualTo("12345678".getBytes());
ByteBuffer key = ByteBuffer.allocate(12);
ByteBuffer value = ByteBuffer.allocate(12);
key.position(4);
key.put("key3".getBytes());
key.position(4).limit(8);
value.position(4);
value.put("val3".getBytes());
value.position(4).limit(8);
db.put(opt, key, value);
assertThat(key.position()).isEqualTo(8);
assertThat(key.limit()).isEqualTo(8);
assertThat(value.position()).isEqualTo(8);
assertThat(value.limit()).isEqualTo(8);
key.position(4);
ByteBuffer result = ByteBuffer.allocate(12);
assertThat(db.get(optr, key, result)).isEqualTo(4);
assertThat(result.position()).isEqualTo(0);
assertThat(result.limit()).isEqualTo(4);
assertThat(key.position()).isEqualTo(8);
assertThat(key.limit()).isEqualTo(8);
byte[] tmp = new byte[4];
result.get(tmp);
assertThat(tmp).isEqualTo("val3".getBytes());
key.position(4);
result.clear().position(9);
assertThat(db.get(optr, key, result)).isEqualTo(4);
assertThat(result.position()).isEqualTo(9);
assertThat(result.limit()).isEqualTo(12);
assertThat(key.position()).isEqualTo(8);
assertThat(key.limit()).isEqualTo(8);
byte[] tmp2 = new byte[3];
result.get(tmp2);
assertThat(tmp2).isEqualTo("val".getBytes());
// put
Segment key3 = sliceSegment("key3");
Segment key4 = sliceSegment("key4");
Segment value0 = sliceSegment("value 0");
Segment value1 = sliceSegment("value 1");
db.put(key3.data, key3.offset, key3.len, value0.data, value0.offset, value0.len);
db.put(opt, key4.data, key4.offset, key4.len, value1.data, value1.offset, value1.len);
// compare
Assert.assertTrue(value0.isSamePayload(db.get(key3.data, key3.offset, key3.len)));
Assert.assertTrue(value1.isSamePayload(db.get(key4.data, key4.offset, key4.len)));
}
}
private static Segment sliceSegment(String key) {
ByteBuffer rawKey = ByteBuffer.allocate(key.length() + 4);
rawKey.put((byte)0);
rawKey.put((byte)0);
rawKey.put(key.getBytes());

View File

@ -5,6 +5,7 @@
package org.rocksdb;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.Assert.fail;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
@ -45,7 +46,7 @@ public class RocksIteratorTest {
}
@Test
public void rocksIterator() throws RocksDBException {
public void rocksIteratorByteBuffers() throws RocksDBException {
try (final Options options =
new Options().setCreateIfMissing(true).setCreateMissingColumnFamilies(true);
final RocksDB db = RocksDB.open(options, dbFolder.getRoot().getAbsolutePath())) {
@ -72,6 +73,103 @@ public class RocksIteratorTest {
validateKey(iterator, ByteBuffer.allocate(5), "key1");
validateValue(iterator, ByteBuffer.allocate(2), "value1");
validateValue(iterator, ByteBuffer.allocate(8), "value1");
}
}
}
@Test
public void rocksIteratorByteArrayValues() throws RocksDBException {
try (final Options options =
new Options().setCreateIfMissing(true).setCreateMissingColumnFamilies(true);
final RocksDB db = RocksDB.open(options, dbFolder.getRoot().getAbsolutePath())) {
db.put("key1".getBytes(), "value1".getBytes());
db.put("key2".getBytes(), "value2".getBytes());
try (final RocksIterator iterator = db.newIterator()) {
iterator.seekToFirst();
assertThat(iterator.isValid()).isTrue();
assertThat(iterator.key()).isEqualTo("key1".getBytes());
assertThat(iterator.value()).isEqualTo("value1".getBytes());
final byte[] valueArray0 = new byte[2];
assertThat(iterator.value(valueArray0)).isEqualTo(6);
assertThat(valueArray0).isEqualTo("va".getBytes());
final byte[] valueArray1 = new byte[8];
assertThat(iterator.value(valueArray1)).isEqualTo(6);
assertThat(valueArray1).isEqualTo("value1\0\0".getBytes());
final byte[] valueArray2 = new byte[10];
assertThat(iterator.value(valueArray2, 2, 6)).isEqualTo(6);
assertThat(valueArray2).isEqualTo("\0\0value1\0\0".getBytes());
final byte[] valueArray3 = new byte[10];
assertThat(iterator.value(valueArray3, 5, 5)).isEqualTo(6);
assertThat(valueArray3).isEqualTo("\0\0\0\0\0value".getBytes());
final byte[] valueArray4 = new byte[6];
try {
iterator.value(valueArray4, 1, 6);
fail("Expected IndexOutOfBoundsException");
} catch (final IndexOutOfBoundsException ignored) {
// we should arrive here
}
final byte[] valueArray5 = new byte[7];
assertThat(iterator.value(valueArray5, 1, 6)).isEqualTo(6);
assertThat(valueArray5).isEqualTo("\0value1".getBytes());
}
}
}
@Test
public void rocksIteratorByteArrayKeys() throws RocksDBException {
try (final Options options =
new Options().setCreateIfMissing(true).setCreateMissingColumnFamilies(true);
final RocksDB db = RocksDB.open(options, dbFolder.getRoot().getAbsolutePath())) {
db.put("key1".getBytes(), "value1".getBytes());
db.put("key2".getBytes(), "value2".getBytes());
try (final RocksIterator iterator = db.newIterator()) {
iterator.seekToFirst();
assertThat(iterator.isValid()).isTrue();
assertThat(iterator.key()).isEqualTo("key1".getBytes());
assertThat(iterator.value()).isEqualTo("value1".getBytes());
final byte[] keyArray0 = new byte[2];
assertThat(iterator.key(keyArray0)).isEqualTo(4);
assertThat(keyArray0).isEqualTo("ke".getBytes());
final byte[] keyArray1 = new byte[8];
assertThat(iterator.key(keyArray1)).isEqualTo(4);
assertThat(keyArray1).isEqualTo("key1\0\0\0\0".getBytes());
final byte[] keyArray2 = new byte[10];
assertThat(iterator.key(keyArray2, 2, 6)).isEqualTo(4);
assertThat(keyArray2).isEqualTo("\0\0key1\0\0\0\0".getBytes());
final byte[] keyArray3 = new byte[10];
assertThat(iterator.key(keyArray3, 5, 3)).isEqualTo(4);
assertThat(keyArray3).isEqualTo("\0\0\0\0\0key\0\0".getBytes());
final byte[] keyArray4 = new byte[4];
try {
iterator.key(keyArray4, 1, 4);
fail("Expected IndexOutOfBoundsException");
} catch (final IndexOutOfBoundsException ignored) {
// we should arrive here
}
final byte[] keyArray5 = new byte[5];
assertThat(iterator.key(keyArray5, 1, 4)).isEqualTo(4);
assertThat(keyArray5).isEqualTo("\0key1".getBytes());
}
}
}
@Test
public void rocksIteratorSimple() throws RocksDBException {
try (final Options options =
new Options().setCreateIfMissing(true).setCreateMissingColumnFamilies(true);
final RocksDB db = RocksDB.open(options, dbFolder.getRoot().getAbsolutePath())) {
db.put("key1".getBytes(), "value1".getBytes());
db.put("key2".getBytes(), "value2".getBytes());
try (final RocksIterator iterator = db.newIterator()) {
iterator.seekToFirst();
assertThat(iterator.isValid()).isTrue();
assertThat(iterator.key()).isEqualTo("key1".getBytes());
assertThat(iterator.value()).isEqualTo("value1".getBytes());
iterator.next();
assertThat(iterator.isValid()).isTrue();
@ -90,6 +188,23 @@ public class RocksIteratorTest {
assertThat(iterator.key()).isEqualTo("key2".getBytes());
assertThat(iterator.value()).isEqualTo("value2".getBytes());
iterator.status();
}
}
}
@Test
public void rocksIterator() throws RocksDBException {
try (final Options options =
new Options().setCreateIfMissing(true).setCreateMissingColumnFamilies(true);
final RocksDB db = RocksDB.open(options, dbFolder.getRoot().getAbsolutePath())) {
db.put("key1".getBytes(), "value1".getBytes());
db.put("key2".getBytes(), "value2".getBytes());
try (final RocksIterator iterator = db.newIterator()) {
iterator.seekToFirst();
assertThat(iterator.isValid()).isTrue();
assertThat(iterator.key()).isEqualTo("key1".getBytes());
assertThat(iterator.value()).isEqualTo("value1".getBytes());
{
final ByteBuffer key = ByteBuffer.allocate(12);

View File

@ -416,12 +416,13 @@ public class TransactionTest extends AbstractTransactionTest {
.setCreateIfMissing(true)
.setCreateMissingColumnFamilies(true);
final TransactionDBOptions txnDbOptions = new TransactionDBOptions();
final ColumnFamilyOptions defaultColumnFamilyOptions = new ColumnFamilyOptions();
defaultColumnFamilyOptions.setMergeOperator(new StringAppendOperator("++"));
final ColumnFamilyOptions columnFamilyOptions = new ColumnFamilyOptions();
final List<ColumnFamilyDescriptor> columnFamilyDescriptors =
Arrays.asList(
new ColumnFamilyDescriptor(RocksDB.DEFAULT_COLUMN_FAMILY),
new ColumnFamilyDescriptor(TXN_TEST_COLUMN_FAMILY,
columnFamilyOptions));
columnFamilyOptions.setMergeOperator(new StringAppendOperator("**"));
final List<ColumnFamilyDescriptor> columnFamilyDescriptors = Arrays.asList(
new ColumnFamilyDescriptor(RocksDB.DEFAULT_COLUMN_FAMILY, defaultColumnFamilyOptions),
new ColumnFamilyDescriptor(TXN_TEST_COLUMN_FAMILY, columnFamilyOptions));
final List<ColumnFamilyHandle> columnFamilyHandles = new ArrayList<>();
final TransactionDB txnDb;

View File

@ -0,0 +1,16 @@
* Java API extensions to improve consistency and completeness of APIs
1 Extended `RocksDB.get([ColumnFamilyHandle columnFamilyHandle,] ReadOptions opt, ByteBuffer key, ByteBuffer value)` which now accepts indirect buffer parameters as well as direct buffer parameters
2 Extended `RocksDB.put( [ColumnFamilyHandle columnFamilyHandle,] WriteOptions writeOpts, final ByteBuffer key, final ByteBuffer value)` which now accepts indirect buffer parameters as well as direct buffer parameters
3 Added `RocksDB.merge([ColumnFamilyHandle columnFamilyHandle,] WriteOptions writeOptions, ByteBuffer key, ByteBuffer value)` methods with the same parameter options as `put(...)` - direct and indirect buffers are supported
4 Added `RocksIterator.key( byte[] key [, int offset, int len])` methods which retrieve the iterator key into the supplied buffer
5 Added `RocksIterator.value( byte[] value [, int offset, int len])` methods which retrieve the iterator value into the supplied buffer
6 Deprecated `get(final ColumnFamilyHandle columnFamilyHandle, final ReadOptions readOptions, byte[])` in favour of `get(final ReadOptions readOptions, final ColumnFamilyHandle columnFamilyHandle, byte[])` which has consistent parameter ordering with other methods in the same class
7 Added `Transaction.get( ReadOptions opt, [ColumnFamilyHandle columnFamilyHandle, ] byte[] key, byte[] value)` methods which retrieve the requested value into the supplied buffer
8 Added `Transaction.get( ReadOptions opt, [ColumnFamilyHandle columnFamilyHandle, ] ByteBuffer key, ByteBuffer value)` methods which retrieve the requested value into the supplied buffer
9 Added `Transaction.getForUpdate( ReadOptions readOptions, [ColumnFamilyHandle columnFamilyHandle, ] byte[] key, byte[] value, boolean exclusive [, boolean doValidate])` methods which retrieve the requested value into the supplied buffer
10 Added `Transaction.getForUpdate( ReadOptions readOptions, [ColumnFamilyHandle columnFamilyHandle, ] ByteBuffer key, ByteBuffer value, boolean exclusive [, boolean doValidate])` methods which retrieve the requested value into the supplied buffer
11 Added `Transaction.getIterator()` method as a convenience which defaults the `ReadOptions` value supplied to existing `Transaction.iterator()` methods. This mirrors the existing `RocksDB.iterator()` method.
12 Added `Transaction.put([ColumnFamilyHandle columnFamilyHandle, ] ByteBuffer key, ByteBuffer value [, boolean assumeTracked])` methods which supply the key, and the value to be written in a `ByteBuffer` parameter
13 Added `Transaction.merge([ColumnFamilyHandle columnFamilyHandle, ] ByteBuffer key, ByteBuffer value [, boolean assumeTracked])` methods which supply the key, and the value to be written/merged in a `ByteBuffer` parameter
14 Added `Transaction.mergeUntracked([ColumnFamilyHandle columnFamilyHandle, ] ByteBuffer key, ByteBuffer value)` methods which supply the key, and the value to be written/merged in a `ByteBuffer` parameter