Add TransactionDB and OptimisticTransactionDB to the Java API
Summary: Closes https://github.com/facebook/rocksdb/issues/697 Closes https://github.com/facebook/rocksdb/issues/1151 Closes https://github.com/facebook/rocksdb/pull/1298 Differential Revision: D7131402 Pulled By: sagar0 fbshipit-source-id: bcd34ce95ed88cc641786089ff4232df7b2f089f
This commit is contained in:
parent
d060421c77
commit
2ac988c67e
@ -1,6 +1,7 @@
|
|||||||
NATIVE_JAVA_CLASSES = org.rocksdb.AbstractCompactionFilter\
|
NATIVE_JAVA_CLASSES = org.rocksdb.AbstractCompactionFilter\
|
||||||
org.rocksdb.AbstractCompactionFilterFactory\
|
org.rocksdb.AbstractCompactionFilterFactory\
|
||||||
org.rocksdb.AbstractSlice\
|
org.rocksdb.AbstractSlice\
|
||||||
|
org.rocksdb.AbstractTransactionNotifier\
|
||||||
org.rocksdb.BackupEngine\
|
org.rocksdb.BackupEngine\
|
||||||
org.rocksdb.BackupableDBOptions\
|
org.rocksdb.BackupableDBOptions\
|
||||||
org.rocksdb.BlockBasedTableConfig\
|
org.rocksdb.BlockBasedTableConfig\
|
||||||
@ -29,6 +30,8 @@ NATIVE_JAVA_CLASSES = org.rocksdb.AbstractCompactionFilter\
|
|||||||
org.rocksdb.Logger\
|
org.rocksdb.Logger\
|
||||||
org.rocksdb.LRUCache\
|
org.rocksdb.LRUCache\
|
||||||
org.rocksdb.MergeOperator\
|
org.rocksdb.MergeOperator\
|
||||||
|
org.rocksdb.OptimisticTransactionDB\
|
||||||
|
org.rocksdb.OptimisticTransactionOptions\
|
||||||
org.rocksdb.Options\
|
org.rocksdb.Options\
|
||||||
org.rocksdb.OptionsUtil\
|
org.rocksdb.OptionsUtil\
|
||||||
org.rocksdb.PlainTableConfig\
|
org.rocksdb.PlainTableConfig\
|
||||||
@ -45,6 +48,10 @@ NATIVE_JAVA_CLASSES = org.rocksdb.AbstractCompactionFilter\
|
|||||||
org.rocksdb.Slice\
|
org.rocksdb.Slice\
|
||||||
org.rocksdb.SstFileWriter\
|
org.rocksdb.SstFileWriter\
|
||||||
org.rocksdb.Statistics\
|
org.rocksdb.Statistics\
|
||||||
|
org.rocksdb.Transaction\
|
||||||
|
org.rocksdb.TransactionDB\
|
||||||
|
org.rocksdb.TransactionDBOptions\
|
||||||
|
org.rocksdb.TransactionOptions\
|
||||||
org.rocksdb.TransactionLogIterator\
|
org.rocksdb.TransactionLogIterator\
|
||||||
org.rocksdb.TtlDB\
|
org.rocksdb.TtlDB\
|
||||||
org.rocksdb.VectorMemTableConfig\
|
org.rocksdb.VectorMemTableConfig\
|
||||||
@ -105,6 +112,9 @@ JAVA_TESTS = org.rocksdb.BackupableDBOptionsTest\
|
|||||||
org.rocksdb.MixedOptionsTest\
|
org.rocksdb.MixedOptionsTest\
|
||||||
org.rocksdb.MutableColumnFamilyOptionsTest\
|
org.rocksdb.MutableColumnFamilyOptionsTest\
|
||||||
org.rocksdb.NativeLibraryLoaderTest\
|
org.rocksdb.NativeLibraryLoaderTest\
|
||||||
|
org.rocksdb.OptimisticTransactionTest\
|
||||||
|
org.rocksdb.OptimisticTransactionDBTest\
|
||||||
|
org.rocksdb.OptimisticTransactionOptionsTest\
|
||||||
org.rocksdb.OptionsUtilTest\
|
org.rocksdb.OptionsUtilTest\
|
||||||
org.rocksdb.OptionsTest\
|
org.rocksdb.OptionsTest\
|
||||||
org.rocksdb.PlainTableConfigTest\
|
org.rocksdb.PlainTableConfigTest\
|
||||||
@ -120,6 +130,10 @@ JAVA_TESTS = org.rocksdb.BackupableDBOptionsTest\
|
|||||||
org.rocksdb.SliceTest\
|
org.rocksdb.SliceTest\
|
||||||
org.rocksdb.SnapshotTest\
|
org.rocksdb.SnapshotTest\
|
||||||
org.rocksdb.SstFileWriterTest\
|
org.rocksdb.SstFileWriterTest\
|
||||||
|
org.rocksdb.TransactionTest\
|
||||||
|
org.rocksdb.TransactionDBTest\
|
||||||
|
org.rocksdb.TransactionOptionsTest\
|
||||||
|
org.rocksdb.TransactionDBOptionsTest\
|
||||||
org.rocksdb.TransactionLogIteratorTest\
|
org.rocksdb.TransactionLogIteratorTest\
|
||||||
org.rocksdb.TtlDBTest\
|
org.rocksdb.TtlDBTest\
|
||||||
org.rocksdb.StatisticsTest\
|
org.rocksdb.StatisticsTest\
|
||||||
@ -209,6 +223,20 @@ column_family_sample: java
|
|||||||
java $(JAVA_ARGS) -Djava.library.path=target -cp $(MAIN_CLASSES):$(SAMPLES_MAIN_CLASSES) RocksDBColumnFamilySample /tmp/rocksdbjni
|
java $(JAVA_ARGS) -Djava.library.path=target -cp $(MAIN_CLASSES):$(SAMPLES_MAIN_CLASSES) RocksDBColumnFamilySample /tmp/rocksdbjni
|
||||||
$(AM_V_at)@rm -rf /tmp/rocksdbjni
|
$(AM_V_at)@rm -rf /tmp/rocksdbjni
|
||||||
|
|
||||||
|
transaction_sample: java
|
||||||
|
$(AM_V_GEN)mkdir -p $(SAMPLES_MAIN_CLASSES)
|
||||||
|
$(AM_V_at)javac -cp $(MAIN_CLASSES) -d $(SAMPLES_MAIN_CLASSES) $(SAMPLES_MAIN_SRC)/TransactionSample.java
|
||||||
|
$(AM_V_at)@rm -rf /tmp/rocksdbjni
|
||||||
|
java -ea -Xcheck:jni -Djava.library.path=target -cp $(MAIN_CLASSES):$(SAMPLES_MAIN_CLASSES) TransactionSample /tmp/rocksdbjni
|
||||||
|
$(AM_V_at)@rm -rf /tmp/rocksdbjni
|
||||||
|
|
||||||
|
optimistic_transaction_sample: java
|
||||||
|
$(AM_V_GEN)mkdir -p $(SAMPLES_MAIN_CLASSES)
|
||||||
|
$(AM_V_at)javac -cp $(MAIN_CLASSES) -d $(SAMPLES_MAIN_CLASSES) $(SAMPLES_MAIN_SRC)/OptimisticTransactionSample.java
|
||||||
|
$(AM_V_at)@rm -rf /tmp/rocksdbjni
|
||||||
|
java -ea -Xcheck:jni -Djava.library.path=target -cp $(MAIN_CLASSES):$(SAMPLES_MAIN_CLASSES) OptimisticTransactionSample /tmp/rocksdbjni
|
||||||
|
$(AM_V_at)@rm -rf /tmp/rocksdbjni
|
||||||
|
|
||||||
resolve_test_deps:
|
resolve_test_deps:
|
||||||
test -d "$(JAVA_TEST_LIBDIR)" || mkdir -p "$(JAVA_TEST_LIBDIR)"
|
test -d "$(JAVA_TEST_LIBDIR)" || mkdir -p "$(JAVA_TEST_LIBDIR)"
|
||||||
test -s "$(JAVA_JUNIT_JAR)" || cp $(MVN_LOCAL)/junit/junit/4.12/junit-4.12.jar $(JAVA_TEST_LIBDIR) || curl -k -L -o $(JAVA_JUNIT_JAR) $(SEARCH_REPO_URL)junit/junit/4.12/junit-4.12.jar
|
test -s "$(JAVA_JUNIT_JAR)" || cp $(MVN_LOCAL)/junit/junit/4.12/junit-4.12.jar $(JAVA_TEST_LIBDIR) || curl -k -L -o $(JAVA_JUNIT_JAR) $(SEARCH_REPO_URL)junit/junit/4.12/junit-4.12.jar
|
||||||
|
@ -3,8 +3,8 @@
|
|||||||
// COPYING file in the root directory) and Apache 2.0 License
|
// COPYING file in the root directory) and Apache 2.0 License
|
||||||
// (found in the LICENSE.Apache file in the root directory).
|
// (found in the LICENSE.Apache file in the root directory).
|
||||||
//
|
//
|
||||||
// This file implements the "bridge" between Java and C++ and enables
|
// This file implements the "bridge" between Java and C++ for
|
||||||
// calling c++ rocksdb::Iterator methods from Java side.
|
// rocksdb::ColumnFamilyHandle.
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
@ -13,14 +13,56 @@
|
|||||||
#include "include/org_rocksdb_ColumnFamilyHandle.h"
|
#include "include/org_rocksdb_ColumnFamilyHandle.h"
|
||||||
#include "rocksjni/portal.h"
|
#include "rocksjni/portal.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Class: org_rocksdb_ColumnFamilyHandle
|
||||||
|
* Method: getName
|
||||||
|
* Signature: (J)[B
|
||||||
|
*/
|
||||||
|
jbyteArray Java_org_rocksdb_ColumnFamilyHandle_getName(
|
||||||
|
JNIEnv* env, jobject jobj, jlong jhandle) {
|
||||||
|
auto* cfh = reinterpret_cast<rocksdb::ColumnFamilyHandle*>(jhandle);
|
||||||
|
std::string cf_name = cfh->GetName();
|
||||||
|
return rocksdb::JniUtil::copyBytes(env, cf_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Class: org_rocksdb_ColumnFamilyHandle
|
||||||
|
* Method: getID
|
||||||
|
* Signature: (J)I
|
||||||
|
*/
|
||||||
|
jint Java_org_rocksdb_ColumnFamilyHandle_getID(
|
||||||
|
JNIEnv* env, jobject jobj, jlong jhandle) {
|
||||||
|
auto* cfh = reinterpret_cast<rocksdb::ColumnFamilyHandle*>(jhandle);
|
||||||
|
const int32_t id = cfh->GetID();
|
||||||
|
return static_cast<jint>(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Class: org_rocksdb_ColumnFamilyHandle
|
||||||
|
* Method: getDescriptor
|
||||||
|
* Signature: (J)Lorg/rocksdb/ColumnFamilyDescriptor;
|
||||||
|
*/
|
||||||
|
jobject Java_org_rocksdb_ColumnFamilyHandle_getDescriptor(
|
||||||
|
JNIEnv* env, jobject jobj, jlong jhandle) {
|
||||||
|
auto* cfh = reinterpret_cast<rocksdb::ColumnFamilyHandle*>(jhandle);
|
||||||
|
rocksdb::ColumnFamilyDescriptor desc;
|
||||||
|
rocksdb::Status s = cfh->GetDescriptor(&desc);
|
||||||
|
if (s.ok()) {
|
||||||
|
return rocksdb::ColumnFamilyDescriptorJni::construct(env, &desc);
|
||||||
|
} else {
|
||||||
|
rocksdb::RocksDBExceptionJni::ThrowNew(env, s);
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Class: org_rocksdb_ColumnFamilyHandle
|
* Class: org_rocksdb_ColumnFamilyHandle
|
||||||
* Method: disposeInternal
|
* Method: disposeInternal
|
||||||
* Signature: (J)V
|
* Signature: (J)V
|
||||||
*/
|
*/
|
||||||
void Java_org_rocksdb_ColumnFamilyHandle_disposeInternal(
|
void Java_org_rocksdb_ColumnFamilyHandle_disposeInternal(
|
||||||
JNIEnv* env, jobject jobj, jlong handle) {
|
JNIEnv* env, jobject jobj, jlong jhandle) {
|
||||||
auto* cfh = reinterpret_cast<rocksdb::ColumnFamilyHandle*>(handle);
|
auto* cfh = reinterpret_cast<rocksdb::ColumnFamilyHandle*>(jhandle);
|
||||||
assert(cfh != nullptr);
|
assert(cfh != nullptr);
|
||||||
delete cfh;
|
delete cfh;
|
||||||
}
|
}
|
||||||
|
267
java/rocksjni/optimistic_transaction_db.cc
Normal file
267
java/rocksjni/optimistic_transaction_db.cc
Normal file
@ -0,0 +1,267 @@
|
|||||||
|
// 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 implements the "bridge" between Java and C++
|
||||||
|
// for rocksdb::TransactionDB.
|
||||||
|
|
||||||
|
#include <jni.h>
|
||||||
|
|
||||||
|
#include "include/org_rocksdb_OptimisticTransactionDB.h"
|
||||||
|
|
||||||
|
#include "rocksdb/options.h"
|
||||||
|
#include "rocksdb/utilities/optimistic_transaction_db.h"
|
||||||
|
#include "rocksdb/utilities/transaction.h"
|
||||||
|
|
||||||
|
#include "rocksjni/portal.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Class: org_rocksdb_OptimisticTransactionDB
|
||||||
|
* Method: open
|
||||||
|
* Signature: (JLjava/lang/String;)J
|
||||||
|
*/
|
||||||
|
jlong Java_org_rocksdb_OptimisticTransactionDB_open__JLjava_lang_String_2(
|
||||||
|
JNIEnv* env, jclass jcls, jlong joptions_handle, jstring jdb_path) {
|
||||||
|
const char* db_path = env->GetStringUTFChars(jdb_path, nullptr);
|
||||||
|
if (db_path == nullptr) {
|
||||||
|
// exception thrown: OutOfMemoryError
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto* options = reinterpret_cast<rocksdb::Options*>(joptions_handle);
|
||||||
|
rocksdb::OptimisticTransactionDB* otdb = nullptr;
|
||||||
|
rocksdb::Status s =
|
||||||
|
rocksdb::OptimisticTransactionDB::Open(*options, db_path, &otdb);
|
||||||
|
env->ReleaseStringUTFChars(jdb_path, db_path);
|
||||||
|
|
||||||
|
if (s.ok()) {
|
||||||
|
return reinterpret_cast<jlong>(otdb);
|
||||||
|
} else {
|
||||||
|
rocksdb::RocksDBExceptionJni::ThrowNew(env, s);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Class: org_rocksdb_OptimisticTransactionDB
|
||||||
|
* Method: open
|
||||||
|
* Signature: (JLjava/lang/String;[[B[J)[J
|
||||||
|
*/
|
||||||
|
jlongArray Java_org_rocksdb_OptimisticTransactionDB_open__JLjava_lang_String_2_3_3B_3J(
|
||||||
|
JNIEnv* env, jclass jcls, jlong jdb_options_handle, jstring jdb_path,
|
||||||
|
jobjectArray jcolumn_names, jlongArray jcolumn_options_handles) {
|
||||||
|
const char* db_path = env->GetStringUTFChars(jdb_path, nullptr);
|
||||||
|
if (db_path == nullptr) {
|
||||||
|
// exception thrown: OutOfMemoryError
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<rocksdb::ColumnFamilyDescriptor> column_families;
|
||||||
|
const jsize len_cols = env->GetArrayLength(jcolumn_names);
|
||||||
|
if (len_cols > 0) {
|
||||||
|
if (env->EnsureLocalCapacity(len_cols) != 0) {
|
||||||
|
// out of memory
|
||||||
|
env->ReleaseStringUTFChars(jdb_path, db_path);
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
jlong* jco =
|
||||||
|
env->GetLongArrayElements(jcolumn_options_handles, nullptr);
|
||||||
|
if(jco == nullptr) {
|
||||||
|
// exception thrown: OutOfMemoryError
|
||||||
|
env->ReleaseStringUTFChars(jdb_path, db_path);
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < len_cols; i++) {
|
||||||
|
const jobject jcn = env->GetObjectArrayElement(jcolumn_names, i);
|
||||||
|
if (env->ExceptionCheck()) {
|
||||||
|
// exception thrown: ArrayIndexOutOfBoundsException
|
||||||
|
env->ReleaseLongArrayElements(jcolumn_options_handles, jco, JNI_ABORT);
|
||||||
|
env->ReleaseStringUTFChars(jdb_path, db_path);
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
const jbyteArray jcn_ba = reinterpret_cast<jbyteArray>(jcn);
|
||||||
|
const jsize jcf_name_len = env->GetArrayLength(jcn_ba);
|
||||||
|
if (env->EnsureLocalCapacity(jcf_name_len) != 0) {
|
||||||
|
// out of memory
|
||||||
|
env->DeleteLocalRef(jcn);
|
||||||
|
env->ReleaseLongArrayElements(jcolumn_options_handles, jco, JNI_ABORT);
|
||||||
|
env->ReleaseStringUTFChars(jdb_path, db_path);
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
jbyte* jcf_name = env->GetByteArrayElements(jcn_ba, nullptr);
|
||||||
|
if (jcf_name == nullptr) {
|
||||||
|
// exception thrown: OutOfMemoryError
|
||||||
|
env->DeleteLocalRef(jcn);
|
||||||
|
env->ReleaseLongArrayElements(jcolumn_options_handles, jco, JNI_ABORT);
|
||||||
|
env->ReleaseStringUTFChars(jdb_path, db_path);
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string cf_name(reinterpret_cast<char *>(jcf_name), jcf_name_len);
|
||||||
|
const rocksdb::ColumnFamilyOptions* cf_options =
|
||||||
|
reinterpret_cast<rocksdb::ColumnFamilyOptions*>(jco[i]);
|
||||||
|
column_families.push_back(
|
||||||
|
rocksdb::ColumnFamilyDescriptor(cf_name, *cf_options));
|
||||||
|
|
||||||
|
env->ReleaseByteArrayElements(jcn_ba, jcf_name, JNI_ABORT);
|
||||||
|
env->DeleteLocalRef(jcn);
|
||||||
|
}
|
||||||
|
env->ReleaseLongArrayElements(jcolumn_options_handles, jco, JNI_ABORT);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto* db_options = reinterpret_cast<rocksdb::DBOptions*>(jdb_options_handle);
|
||||||
|
std::vector<rocksdb::ColumnFamilyHandle*> handles;
|
||||||
|
rocksdb::OptimisticTransactionDB* otdb = nullptr;
|
||||||
|
const rocksdb::Status s = rocksdb::OptimisticTransactionDB::Open(*db_options,
|
||||||
|
db_path, column_families, &handles, &otdb);
|
||||||
|
|
||||||
|
env->ReleaseStringUTFChars(jdb_path, db_path);
|
||||||
|
|
||||||
|
// check if open operation was successful
|
||||||
|
if (s.ok()) {
|
||||||
|
const jsize resultsLen = 1 + len_cols; // db handle + column family handles
|
||||||
|
std::unique_ptr<jlong[]> results =
|
||||||
|
std::unique_ptr<jlong[]>(new jlong[resultsLen]);
|
||||||
|
results[0] = reinterpret_cast<jlong>(otdb);
|
||||||
|
for (int i = 1; i <= len_cols; i++) {
|
||||||
|
results[i] = reinterpret_cast<jlong>(handles[i - 1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
jlongArray jresults = env->NewLongArray(resultsLen);
|
||||||
|
if (jresults == nullptr) {
|
||||||
|
// exception thrown: OutOfMemoryError
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
env->SetLongArrayRegion(jresults, 0, resultsLen, results.get());
|
||||||
|
if (env->ExceptionCheck()) {
|
||||||
|
// exception thrown: ArrayIndexOutOfBoundsException
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
return jresults;
|
||||||
|
}
|
||||||
|
|
||||||
|
rocksdb::RocksDBExceptionJni::ThrowNew(env, s);
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Class: org_rocksdb_OptimisticTransactionDB
|
||||||
|
* Method: beginTransaction
|
||||||
|
* Signature: (JJ)J
|
||||||
|
*/
|
||||||
|
jlong Java_org_rocksdb_OptimisticTransactionDB_beginTransaction__JJ(
|
||||||
|
JNIEnv* env, jobject jobj, jlong jhandle, jlong jwrite_options_handle) {
|
||||||
|
auto* optimistic_txn_db =
|
||||||
|
reinterpret_cast<rocksdb::OptimisticTransactionDB*>(jhandle);
|
||||||
|
auto* write_options =
|
||||||
|
reinterpret_cast<rocksdb::WriteOptions*>(jwrite_options_handle);
|
||||||
|
rocksdb::Transaction* txn =
|
||||||
|
optimistic_txn_db->BeginTransaction(*write_options);
|
||||||
|
return reinterpret_cast<jlong>(txn);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Class: org_rocksdb_OptimisticTransactionDB
|
||||||
|
* Method: beginTransaction
|
||||||
|
* Signature: (JJJ)J
|
||||||
|
*/
|
||||||
|
jlong Java_org_rocksdb_OptimisticTransactionDB_beginTransaction__JJJ(
|
||||||
|
JNIEnv* env, jobject jobj, jlong jhandle, jlong jwrite_options_handle,
|
||||||
|
jlong joptimistic_txn_options_handle) {
|
||||||
|
auto* optimistic_txn_db =
|
||||||
|
reinterpret_cast<rocksdb::OptimisticTransactionDB*>(jhandle);
|
||||||
|
auto* write_options =
|
||||||
|
reinterpret_cast<rocksdb::WriteOptions*>(jwrite_options_handle);
|
||||||
|
auto* optimistic_txn_options =
|
||||||
|
reinterpret_cast<rocksdb::OptimisticTransactionOptions*>(
|
||||||
|
joptimistic_txn_options_handle);
|
||||||
|
rocksdb::Transaction* txn =
|
||||||
|
optimistic_txn_db->BeginTransaction(*write_options,
|
||||||
|
*optimistic_txn_options);
|
||||||
|
return reinterpret_cast<jlong>(txn);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Class: org_rocksdb_OptimisticTransactionDB
|
||||||
|
* Method: beginTransaction_withOld
|
||||||
|
* Signature: (JJJ)J
|
||||||
|
*/
|
||||||
|
jlong Java_org_rocksdb_OptimisticTransactionDB_beginTransaction_1withOld__JJJ(
|
||||||
|
JNIEnv* env, jobject jobj, jlong jhandle, jlong jwrite_options_handle,
|
||||||
|
jlong jold_txn_handle) {
|
||||||
|
auto* optimistic_txn_db =
|
||||||
|
reinterpret_cast<rocksdb::OptimisticTransactionDB*>(jhandle);
|
||||||
|
auto* write_options =
|
||||||
|
reinterpret_cast<rocksdb::WriteOptions*>(jwrite_options_handle);
|
||||||
|
auto* old_txn =
|
||||||
|
reinterpret_cast<rocksdb::Transaction*>(
|
||||||
|
jold_txn_handle);
|
||||||
|
rocksdb::OptimisticTransactionOptions optimistic_txn_options;
|
||||||
|
rocksdb::Transaction* txn =
|
||||||
|
optimistic_txn_db->BeginTransaction(*write_options,
|
||||||
|
optimistic_txn_options, old_txn);
|
||||||
|
|
||||||
|
// RocksJava relies on the assumption that
|
||||||
|
// we do not allocate a new Transaction object
|
||||||
|
// when providing an old_optimistic_txn
|
||||||
|
assert(txn == old_txn);
|
||||||
|
|
||||||
|
return reinterpret_cast<jlong>(txn);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Class: org_rocksdb_OptimisticTransactionDB
|
||||||
|
* Method: beginTransaction_withOld
|
||||||
|
* Signature: (JJJJ)J
|
||||||
|
*/
|
||||||
|
jlong Java_org_rocksdb_OptimisticTransactionDB_beginTransaction_1withOld__JJJJ(
|
||||||
|
JNIEnv* env, jobject jobj, jlong jhandle, jlong jwrite_options_handle,
|
||||||
|
jlong joptimistic_txn_options_handle, jlong jold_txn_handle) {
|
||||||
|
auto* optimistic_txn_db =
|
||||||
|
reinterpret_cast<rocksdb::OptimisticTransactionDB*>(jhandle);
|
||||||
|
auto* write_options =
|
||||||
|
reinterpret_cast<rocksdb::WriteOptions*>(jwrite_options_handle);
|
||||||
|
auto* optimistic_txn_options =
|
||||||
|
reinterpret_cast<rocksdb::OptimisticTransactionOptions*>(
|
||||||
|
joptimistic_txn_options_handle);
|
||||||
|
auto* old_txn =
|
||||||
|
reinterpret_cast<rocksdb::Transaction*>(
|
||||||
|
jold_txn_handle);
|
||||||
|
rocksdb::Transaction* txn =
|
||||||
|
optimistic_txn_db->BeginTransaction(*write_options,
|
||||||
|
*optimistic_txn_options, old_txn);
|
||||||
|
|
||||||
|
// RocksJava relies on the assumption that
|
||||||
|
// we do not allocate a new Transaction object
|
||||||
|
// when providing an old_optimisic_txn
|
||||||
|
assert(txn == old_txn);
|
||||||
|
|
||||||
|
return reinterpret_cast<jlong>(txn);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Class: org_rocksdb_OptimisticTransactionDB
|
||||||
|
* Method: getBaseDB
|
||||||
|
* Signature: (J)J
|
||||||
|
*/
|
||||||
|
jlong Java_org_rocksdb_OptimisticTransactionDB_getBaseDB(
|
||||||
|
JNIEnv* env, jobject jobj, jlong jhandle) {
|
||||||
|
auto* optimistic_txn_db =
|
||||||
|
reinterpret_cast<rocksdb::OptimisticTransactionDB*>(jhandle);
|
||||||
|
return reinterpret_cast<jlong>(optimistic_txn_db->GetBaseDB());
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Class: org_rocksdb_OptimisticTransactionDB
|
||||||
|
* Method: disposeInternal
|
||||||
|
* Signature: (J)V
|
||||||
|
*/
|
||||||
|
void Java_org_rocksdb_OptimisticTransactionDB_disposeInternal(JNIEnv* env,
|
||||||
|
jobject jobj, jlong jhandle) {
|
||||||
|
delete reinterpret_cast<rocksdb::OptimisticTransactionDB*>(jhandle);
|
||||||
|
}
|
72
java/rocksjni/optimistic_transaction_options.cc
Normal file
72
java/rocksjni/optimistic_transaction_options.cc
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
// 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 implements the "bridge" between Java and C++
|
||||||
|
// for rocksdb::OptimisticTransactionOptions.
|
||||||
|
|
||||||
|
#include <jni.h>
|
||||||
|
|
||||||
|
#include "include/org_rocksdb_OptimisticTransactionOptions.h"
|
||||||
|
|
||||||
|
#include "rocksdb/comparator.h"
|
||||||
|
#include "rocksdb/utilities/optimistic_transaction_db.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Class: org_rocksdb_OptimisticTransactionOptions
|
||||||
|
* Method: newOptimisticTransactionOptions
|
||||||
|
* Signature: ()J
|
||||||
|
*/
|
||||||
|
jlong Java_org_rocksdb_OptimisticTransactionOptions_newOptimisticTransactionOptions(
|
||||||
|
JNIEnv* env, jclass jcls) {
|
||||||
|
rocksdb::OptimisticTransactionOptions* opts =
|
||||||
|
new rocksdb::OptimisticTransactionOptions();
|
||||||
|
return reinterpret_cast<jlong>(opts);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Class: org_rocksdb_OptimisticTransactionOptions
|
||||||
|
* Method: isSetSnapshot
|
||||||
|
* Signature: (J)Z
|
||||||
|
*/
|
||||||
|
jboolean Java_org_rocksdb_OptimisticTransactionOptions_isSetSnapshot(
|
||||||
|
JNIEnv* env, jobject jobj, jlong jhandle) {
|
||||||
|
auto* opts =
|
||||||
|
reinterpret_cast<rocksdb::OptimisticTransactionOptions*>(jhandle);
|
||||||
|
return opts->set_snapshot;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Class: org_rocksdb_OptimisticTransactionOptions
|
||||||
|
* Method: setSetSnapshot
|
||||||
|
* Signature: (JZ)V
|
||||||
|
*/
|
||||||
|
void Java_org_rocksdb_OptimisticTransactionOptions_setSetSnapshot(JNIEnv* env,
|
||||||
|
jobject jobj, jlong jhandle, jboolean jset_snapshot) {
|
||||||
|
auto* opts =
|
||||||
|
reinterpret_cast<rocksdb::OptimisticTransactionOptions*>(jhandle);
|
||||||
|
opts->set_snapshot = jset_snapshot;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Class: org_rocksdb_OptimisticTransactionOptions
|
||||||
|
* Method: setComparator
|
||||||
|
* Signature: (JJ)V
|
||||||
|
*/
|
||||||
|
void Java_org_rocksdb_OptimisticTransactionOptions_setComparator(
|
||||||
|
JNIEnv* env, jobject jobj, jlong jhandle, jlong jcomparator_handle) {
|
||||||
|
auto* opts =
|
||||||
|
reinterpret_cast<rocksdb::OptimisticTransactionOptions*>(jhandle);
|
||||||
|
opts->cmp = reinterpret_cast<rocksdb::Comparator*>(jcomparator_handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Class: org_rocksdb_OptimisticTransactionOptions
|
||||||
|
* Method: disposeInternal
|
||||||
|
* Signature: (J)V
|
||||||
|
*/
|
||||||
|
void Java_org_rocksdb_OptimisticTransactionOptions_disposeInternal(JNIEnv* env,
|
||||||
|
jobject jobj, jlong jhandle) {
|
||||||
|
delete reinterpret_cast<rocksdb::OptimisticTransactionOptions*>(jhandle);
|
||||||
|
}
|
@ -14,9 +14,11 @@
|
|||||||
#include <jni.h>
|
#include <jni.h>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
#include <iterator>
|
||||||
#include <limits>
|
#include <limits>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <type_traits>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "rocksdb/db.h"
|
#include "rocksdb/db.h"
|
||||||
@ -24,10 +26,12 @@
|
|||||||
#include "rocksdb/rate_limiter.h"
|
#include "rocksdb/rate_limiter.h"
|
||||||
#include "rocksdb/status.h"
|
#include "rocksdb/status.h"
|
||||||
#include "rocksdb/utilities/backupable_db.h"
|
#include "rocksdb/utilities/backupable_db.h"
|
||||||
|
#include "rocksdb/utilities/transaction_db.h"
|
||||||
#include "rocksdb/utilities/write_batch_with_index.h"
|
#include "rocksdb/utilities/write_batch_with_index.h"
|
||||||
#include "rocksjni/compaction_filter_factory_jnicallback.h"
|
#include "rocksjni/compaction_filter_factory_jnicallback.h"
|
||||||
#include "rocksjni/comparatorjnicallback.h"
|
#include "rocksjni/comparatorjnicallback.h"
|
||||||
#include "rocksjni/loggerjnicallback.h"
|
#include "rocksjni/loggerjnicallback.h"
|
||||||
|
#include "rocksjni/transaction_notifier_jnicallback.h"
|
||||||
#include "rocksjni/writebatchhandlerjnicallback.h"
|
#include "rocksjni/writebatchhandlerjnicallback.h"
|
||||||
|
|
||||||
// Remove macro on windows
|
// Remove macro on windows
|
||||||
@ -1087,6 +1091,31 @@ class AbstractCompactionFilterFactoryJni : public RocksDBNativeClass<
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// The portal class for org.rocksdb.AbstractTransactionNotifier
|
||||||
|
class AbstractTransactionNotifierJni : public RocksDBNativeClass<
|
||||||
|
const rocksdb::TransactionNotifierJniCallback*,
|
||||||
|
AbstractTransactionNotifierJni> {
|
||||||
|
public:
|
||||||
|
static jclass getJClass(JNIEnv* env) {
|
||||||
|
return RocksDBNativeClass::getJClass(env,
|
||||||
|
"org/rocksdb/AbstractTransactionNotifier");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the java method `snapshotCreated`
|
||||||
|
// of org.rocksdb.AbstractTransactionNotifier.
|
||||||
|
static jmethodID getSnapshotCreatedMethodId(JNIEnv* env) {
|
||||||
|
jclass jclazz = getJClass(env);
|
||||||
|
if(jclazz == nullptr) {
|
||||||
|
// exception occurred accessing class
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
static jmethodID mid = env->GetMethodID(jclazz, "snapshotCreated", "(J)V");
|
||||||
|
assert(mid != nullptr);
|
||||||
|
return mid;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// The portal class for org.rocksdb.AbstractComparator
|
// The portal class for org.rocksdb.AbstractComparator
|
||||||
class AbstractComparatorJni : public RocksDBNativeClass<
|
class AbstractComparatorJni : public RocksDBNativeClass<
|
||||||
const rocksdb::BaseComparatorJniCallback*,
|
const rocksdb::BaseComparatorJniCallback*,
|
||||||
@ -2974,6 +3003,334 @@ class RateLimiterModeJni {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// The portal class for org.rocksdb.Transaction
|
||||||
|
class TransactionJni : public JavaClass {
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Get the Java Class org.rocksdb.Transaction
|
||||||
|
*
|
||||||
|
* @param env A pointer to the Java environment
|
||||||
|
*
|
||||||
|
* @return The Java Class or nullptr if one of the
|
||||||
|
* ClassFormatError, ClassCircularityError, NoClassDefFoundError,
|
||||||
|
* OutOfMemoryError or ExceptionInInitializerError exceptions is thrown
|
||||||
|
*/
|
||||||
|
static jclass getJClass(JNIEnv* env) {
|
||||||
|
return JavaClass::getJClass(env,
|
||||||
|
"org/rocksdb/Transaction");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new Java org.rocksdb.Transaction.WaitingTransactions object
|
||||||
|
*
|
||||||
|
* @param env A pointer to the Java environment
|
||||||
|
* @param jtransaction A Java org.rocksdb.Transaction object
|
||||||
|
* @param column_family_id The id of the column family
|
||||||
|
* @param key The key
|
||||||
|
* @param transaction_ids The transaction ids
|
||||||
|
*
|
||||||
|
* @return A reference to a Java
|
||||||
|
* org.rocksdb.Transaction.WaitingTransactions object,
|
||||||
|
* or nullptr if an an exception occurs
|
||||||
|
*/
|
||||||
|
static jobject newWaitingTransactions(JNIEnv* env, jobject jtransaction,
|
||||||
|
const uint32_t column_family_id, const std::string &key,
|
||||||
|
const std::vector<TransactionID> &transaction_ids) {
|
||||||
|
jclass jclazz = getJClass(env);
|
||||||
|
if(jclazz == nullptr) {
|
||||||
|
// exception occurred accessing class
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
jmethodID mid = env->GetMethodID(
|
||||||
|
jclazz, "newWaitingTransactions", "(JLjava/lang/String;[J)Lorg/rocksdb/Transaction$WaitingTransactions;");
|
||||||
|
if(mid == nullptr) {
|
||||||
|
// exception thrown: NoSuchMethodException or OutOfMemoryError
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
jstring jkey = env->NewStringUTF(key.c_str());
|
||||||
|
if(jkey == nullptr) {
|
||||||
|
// exception thrown: OutOfMemoryError
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
const size_t len = transaction_ids.size();
|
||||||
|
jlongArray jtransaction_ids = env->NewLongArray(static_cast<jsize>(len));
|
||||||
|
if(jtransaction_ids == nullptr) {
|
||||||
|
// exception thrown: OutOfMemoryError
|
||||||
|
env->DeleteLocalRef(jkey);
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
jlong *body = env->GetLongArrayElements(jtransaction_ids, nullptr);
|
||||||
|
if(body == nullptr) {
|
||||||
|
// exception thrown: OutOfMemoryError
|
||||||
|
env->DeleteLocalRef(jkey);
|
||||||
|
env->DeleteLocalRef(jtransaction_ids);
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
for(size_t i = 0; i < len; ++i) {
|
||||||
|
body[i] = static_cast<jlong>(transaction_ids[i]);
|
||||||
|
}
|
||||||
|
env->ReleaseLongArrayElements(jtransaction_ids, body, 0);
|
||||||
|
|
||||||
|
jobject jwaiting_transactions = env->CallObjectMethod(jtransaction,
|
||||||
|
mid, static_cast<jlong>(column_family_id), jkey, jtransaction_ids);
|
||||||
|
if(env->ExceptionCheck()) {
|
||||||
|
// exception thrown: InstantiationException or OutOfMemoryError
|
||||||
|
env->DeleteLocalRef(jkey);
|
||||||
|
env->DeleteLocalRef(jtransaction_ids);
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
return jwaiting_transactions;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// The portal class for org.rocksdb.TransactionDB
|
||||||
|
class TransactionDBJni : public JavaClass {
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Get the Java Class org.rocksdb.TransactionDB
|
||||||
|
*
|
||||||
|
* @param env A pointer to the Java environment
|
||||||
|
*
|
||||||
|
* @return The Java Class or nullptr if one of the
|
||||||
|
* ClassFormatError, ClassCircularityError, NoClassDefFoundError,
|
||||||
|
* OutOfMemoryError or ExceptionInInitializerError exceptions is thrown
|
||||||
|
*/
|
||||||
|
static jclass getJClass(JNIEnv* env) {
|
||||||
|
return JavaClass::getJClass(env,
|
||||||
|
"org/rocksdb/TransactionDB");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new Java org.rocksdb.TransactionDB.DeadlockInfo object
|
||||||
|
*
|
||||||
|
* @param env A pointer to the Java environment
|
||||||
|
* @param jtransaction A Java org.rocksdb.Transaction object
|
||||||
|
* @param column_family_id The id of the column family
|
||||||
|
* @param key The key
|
||||||
|
* @param transaction_ids The transaction ids
|
||||||
|
*
|
||||||
|
* @return A reference to a Java
|
||||||
|
* org.rocksdb.Transaction.WaitingTransactions object,
|
||||||
|
* or nullptr if an an exception occurs
|
||||||
|
*/
|
||||||
|
static jobject newDeadlockInfo(JNIEnv* env, jobject jtransaction_db,
|
||||||
|
const rocksdb::TransactionID transaction_id,
|
||||||
|
const uint32_t column_family_id, const std::string &waiting_key,
|
||||||
|
const bool exclusive) {
|
||||||
|
jclass jclazz = getJClass(env);
|
||||||
|
if(jclazz == nullptr) {
|
||||||
|
// exception occurred accessing class
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
jmethodID mid = env->GetMethodID(
|
||||||
|
jclazz, "newDeadlockInfo", "(JJLjava/lang/String;Z)Lorg/rocksdb/TransactionDB$DeadlockInfo;");
|
||||||
|
if(mid == nullptr) {
|
||||||
|
// exception thrown: NoSuchMethodException or OutOfMemoryError
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
jstring jwaiting_key = env->NewStringUTF(waiting_key.c_str());
|
||||||
|
if(jwaiting_key == nullptr) {
|
||||||
|
// exception thrown: OutOfMemoryError
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// resolve the column family id to a ColumnFamilyHandle
|
||||||
|
jobject jdeadlock_info = env->CallObjectMethod(jtransaction_db,
|
||||||
|
mid, transaction_id, static_cast<jlong>(column_family_id),
|
||||||
|
jwaiting_key, exclusive);
|
||||||
|
if(env->ExceptionCheck()) {
|
||||||
|
// exception thrown: InstantiationException or OutOfMemoryError
|
||||||
|
env->DeleteLocalRef(jwaiting_key);
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
return jdeadlock_info;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// The portal class for org.rocksdb.TxnDBWritePolicy
|
||||||
|
class TxnDBWritePolicyJni {
|
||||||
|
public:
|
||||||
|
// Returns the equivalent org.rocksdb.TxnDBWritePolicy for the provided
|
||||||
|
// C++ rocksdb::TxnDBWritePolicy enum
|
||||||
|
static jbyte toJavaTxnDBWritePolicy(
|
||||||
|
const rocksdb::TxnDBWritePolicy& txndb_write_policy) {
|
||||||
|
switch(txndb_write_policy) {
|
||||||
|
case rocksdb::TxnDBWritePolicy::WRITE_COMMITTED:
|
||||||
|
return 0x0;
|
||||||
|
case rocksdb::TxnDBWritePolicy::WRITE_PREPARED:
|
||||||
|
return 0x1;
|
||||||
|
case rocksdb::TxnDBWritePolicy::WRITE_UNPREPARED:
|
||||||
|
return 0x2;
|
||||||
|
default:
|
||||||
|
return 0x7F; // undefined
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns the equivalent C++ rocksdb::TxnDBWritePolicy enum for the
|
||||||
|
// provided Java org.rocksdb.TxnDBWritePolicy
|
||||||
|
static rocksdb::TxnDBWritePolicy toCppTxnDBWritePolicy(
|
||||||
|
jbyte jtxndb_write_policy) {
|
||||||
|
switch(jtxndb_write_policy) {
|
||||||
|
case 0x0:
|
||||||
|
return rocksdb::TxnDBWritePolicy::WRITE_COMMITTED;
|
||||||
|
case 0x1:
|
||||||
|
return rocksdb::TxnDBWritePolicy::WRITE_PREPARED;
|
||||||
|
case 0x2:
|
||||||
|
return rocksdb::TxnDBWritePolicy::WRITE_UNPREPARED;
|
||||||
|
default:
|
||||||
|
// undefined/default
|
||||||
|
return rocksdb::TxnDBWritePolicy::WRITE_COMMITTED;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// The portal class for org.rocksdb.TransactionDB.KeyLockInfo
|
||||||
|
class KeyLockInfoJni : public JavaClass {
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Get the Java Class org.rocksdb.TransactionDB.KeyLockInfo
|
||||||
|
*
|
||||||
|
* @param env A pointer to the Java environment
|
||||||
|
*
|
||||||
|
* @return The Java Class or nullptr if one of the
|
||||||
|
* ClassFormatError, ClassCircularityError, NoClassDefFoundError,
|
||||||
|
* OutOfMemoryError or ExceptionInInitializerError exceptions is thrown
|
||||||
|
*/
|
||||||
|
static jclass getJClass(JNIEnv* env) {
|
||||||
|
return JavaClass::getJClass(env,
|
||||||
|
"org/rocksdb/TransactionDB$KeyLockInfo");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new Java org.rocksdb.TransactionDB.KeyLockInfo object
|
||||||
|
* with the same properties as the provided C++ rocksdb::KeyLockInfo object
|
||||||
|
*
|
||||||
|
* @param env A pointer to the Java environment
|
||||||
|
* @param key_lock_info The rocksdb::KeyLockInfo object
|
||||||
|
*
|
||||||
|
* @return A reference to a Java
|
||||||
|
* org.rocksdb.TransactionDB.KeyLockInfo object,
|
||||||
|
* or nullptr if an an exception occurs
|
||||||
|
*/
|
||||||
|
static jobject construct(JNIEnv* env,
|
||||||
|
const rocksdb::KeyLockInfo& key_lock_info) {
|
||||||
|
jclass jclazz = getJClass(env);
|
||||||
|
if(jclazz == nullptr) {
|
||||||
|
// exception occurred accessing class
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
jmethodID mid = env->GetMethodID(
|
||||||
|
jclazz, "<init>", "(Ljava/lang/String;[JZ)V");
|
||||||
|
if (mid == nullptr) {
|
||||||
|
// exception thrown: NoSuchMethodException or OutOfMemoryError
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
jstring jkey = env->NewStringUTF(key_lock_info.key.c_str());
|
||||||
|
if (jkey == nullptr) {
|
||||||
|
// exception thrown: OutOfMemoryError
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
const jsize jtransaction_ids_len = static_cast<jsize>(key_lock_info.ids.size());
|
||||||
|
jlongArray jtransactions_ids = env->NewLongArray(jtransaction_ids_len);
|
||||||
|
if (jtransactions_ids == nullptr) {
|
||||||
|
// exception thrown: OutOfMemoryError
|
||||||
|
env->DeleteLocalRef(jkey);
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
const jobject jkey_lock_info = env->NewObject(jclazz, mid,
|
||||||
|
jkey, jtransactions_ids, key_lock_info.exclusive);
|
||||||
|
if(jkey_lock_info == nullptr) {
|
||||||
|
// exception thrown: InstantiationException or OutOfMemoryError
|
||||||
|
env->DeleteLocalRef(jtransactions_ids);
|
||||||
|
env->DeleteLocalRef(jkey);
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
return jkey_lock_info;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// The portal class for org.rocksdb.TransactionDB.DeadlockInfo
|
||||||
|
class DeadlockInfoJni : public JavaClass {
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Get the Java Class org.rocksdb.TransactionDB.DeadlockInfo
|
||||||
|
*
|
||||||
|
* @param env A pointer to the Java environment
|
||||||
|
*
|
||||||
|
* @return The Java Class or nullptr if one of the
|
||||||
|
* ClassFormatError, ClassCircularityError, NoClassDefFoundError,
|
||||||
|
* OutOfMemoryError or ExceptionInInitializerError exceptions is thrown
|
||||||
|
*/
|
||||||
|
static jclass getJClass(JNIEnv* env) {
|
||||||
|
return JavaClass::getJClass(env,"org/rocksdb/TransactionDB$DeadlockInfo");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// The portal class for org.rocksdb.TransactionDB.DeadlockPath
|
||||||
|
class DeadlockPathJni : public JavaClass {
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Get the Java Class org.rocksdb.TransactionDB.DeadlockPath
|
||||||
|
*
|
||||||
|
* @param env A pointer to the Java environment
|
||||||
|
*
|
||||||
|
* @return The Java Class or nullptr if one of the
|
||||||
|
* ClassFormatError, ClassCircularityError, NoClassDefFoundError,
|
||||||
|
* OutOfMemoryError or ExceptionInInitializerError exceptions is thrown
|
||||||
|
*/
|
||||||
|
static jclass getJClass(JNIEnv* env) {
|
||||||
|
return JavaClass::getJClass(env,
|
||||||
|
"org/rocksdb/TransactionDB$DeadlockPath");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new Java org.rocksdb.TransactionDB.DeadlockPath object
|
||||||
|
*
|
||||||
|
* @param env A pointer to the Java environment
|
||||||
|
*
|
||||||
|
* @return A reference to a Java
|
||||||
|
* org.rocksdb.TransactionDB.DeadlockPath object,
|
||||||
|
* or nullptr if an an exception occurs
|
||||||
|
*/
|
||||||
|
static jobject construct(JNIEnv* env,
|
||||||
|
const jobjectArray jdeadlock_infos, const bool limit_exceeded) {
|
||||||
|
jclass jclazz = getJClass(env);
|
||||||
|
if(jclazz == nullptr) {
|
||||||
|
// exception occurred accessing class
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
jmethodID mid = env->GetMethodID(
|
||||||
|
jclazz, "<init>", "([LDeadlockInfo;Z)V");
|
||||||
|
if (mid == nullptr) {
|
||||||
|
// exception thrown: NoSuchMethodException or OutOfMemoryError
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
const jobject jdeadlock_path = env->NewObject(jclazz, mid,
|
||||||
|
jdeadlock_infos, limit_exceeded);
|
||||||
|
if(jdeadlock_path == nullptr) {
|
||||||
|
// exception thrown: InstantiationException or OutOfMemoryError
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
return jdeadlock_path;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// various utility functions for working with RocksDB and JNI
|
// various utility functions for working with RocksDB and JNI
|
||||||
class JniUtil {
|
class JniUtil {
|
||||||
public:
|
public:
|
||||||
@ -3497,7 +3854,7 @@ class ColumnFamilyDescriptorJni : public JavaClass {
|
|||||||
* nullptr if an an exception occurs
|
* nullptr if an an exception occurs
|
||||||
*/
|
*/
|
||||||
static jobject construct(JNIEnv* env, ColumnFamilyDescriptor* cfd) {
|
static jobject construct(JNIEnv* env, ColumnFamilyDescriptor* cfd) {
|
||||||
jbyteArray cfname = JniUtil::copyBytes(env, cfd->name);
|
jbyteArray jcf_name = JniUtil::copyBytes(env, cfd->name);
|
||||||
jobject cfopts = ColumnFamilyOptionsJni::construct(env, &(cfd->options));
|
jobject cfopts = ColumnFamilyOptionsJni::construct(env, &(cfd->options));
|
||||||
|
|
||||||
jclass jclazz = getJClass(env);
|
jclass jclazz = getJClass(env);
|
||||||
@ -3510,11 +3867,13 @@ class ColumnFamilyDescriptorJni : public JavaClass {
|
|||||||
"([BLorg/rocksdb/ColumnFamilyOptions;)V");
|
"([BLorg/rocksdb/ColumnFamilyOptions;)V");
|
||||||
if (mid == nullptr) {
|
if (mid == nullptr) {
|
||||||
// exception thrown: NoSuchMethodException or OutOfMemoryError
|
// exception thrown: NoSuchMethodException or OutOfMemoryError
|
||||||
|
env->DeleteLocalRef(jcf_name);
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
jobject jcfd = env->NewObject(jclazz, mid, cfname, cfopts);
|
jobject jcfd = env->NewObject(jclazz, mid, jcf_name, cfopts);
|
||||||
if (env->ExceptionCheck()) {
|
if (env->ExceptionCheck()) {
|
||||||
|
env->DeleteLocalRef(jcf_name);
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3563,5 +3922,169 @@ class ColumnFamilyDescriptorJni : public JavaClass {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class MapJni : public JavaClass {
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Get the Java Class java.util.Map
|
||||||
|
*
|
||||||
|
* @param env A pointer to the Java environment
|
||||||
|
*
|
||||||
|
* @return The Java Class or nullptr if one of the
|
||||||
|
* ClassFormatError, ClassCircularityError, NoClassDefFoundError,
|
||||||
|
* OutOfMemoryError or ExceptionInInitializerError exceptions is thrown
|
||||||
|
*/
|
||||||
|
static jclass getClass(JNIEnv* env) {
|
||||||
|
return JavaClass::getJClass(env, "java/util/Map");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the Java Method: Map#put
|
||||||
|
*
|
||||||
|
* @param env A pointer to the Java environment
|
||||||
|
*
|
||||||
|
* @return The Java Method ID or nullptr if the class or method id could not
|
||||||
|
* be retieved
|
||||||
|
*/
|
||||||
|
static jmethodID getMapPutMethodId(JNIEnv* env) {
|
||||||
|
jclass jlist_clazz = getClass(env);
|
||||||
|
if(jlist_clazz == nullptr) {
|
||||||
|
// exception occurred accessing class
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
static jmethodID mid =
|
||||||
|
env->GetMethodID(jlist_clazz, "put", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");
|
||||||
|
assert(mid != nullptr);
|
||||||
|
return mid;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class HashMapJni : public JavaClass {
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Get the Java Class java.util.HashMap
|
||||||
|
*
|
||||||
|
* @param env A pointer to the Java environment
|
||||||
|
*
|
||||||
|
* @return The Java Class or nullptr if one of the
|
||||||
|
* ClassFormatError, ClassCircularityError, NoClassDefFoundError,
|
||||||
|
* OutOfMemoryError or ExceptionInInitializerError exceptions is thrown
|
||||||
|
*/
|
||||||
|
static jclass getJClass(JNIEnv* env) {
|
||||||
|
return JavaClass::getJClass(env, "java/util/HashMap");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new Java java.util.HashMap object.
|
||||||
|
*
|
||||||
|
* @param env A pointer to the Java environment
|
||||||
|
*
|
||||||
|
* @return A reference to a Java java.util.HashMap object, or
|
||||||
|
* nullptr if an an exception occurs
|
||||||
|
*/
|
||||||
|
static jobject construct(JNIEnv* env, const uint32_t initial_capacity = 16) {
|
||||||
|
jclass jclazz = getJClass(env);
|
||||||
|
if (jclazz == nullptr) {
|
||||||
|
// exception occurred accessing class
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
jmethodID mid = env->GetMethodID(jclazz, "<init>", "(I)V");
|
||||||
|
if (mid == nullptr) {
|
||||||
|
// exception thrown: NoSuchMethodException or OutOfMemoryError
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
jobject jhash_map = env->NewObject(jclazz, mid, static_cast<jint>(initial_capacity));
|
||||||
|
if (env->ExceptionCheck()) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
return jhash_map;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A function which maps a std::pair<K,V> to a std::pair<jobject, jobject>
|
||||||
|
*
|
||||||
|
* @return Either a pointer to a std::pair<jobject, jobject>, or nullptr
|
||||||
|
* if an error occurs during the mapping
|
||||||
|
*/
|
||||||
|
template <typename K, typename V>
|
||||||
|
using FnMapKV = std::function<std::unique_ptr<std::pair<jobject, jobject>> (const std::pair<K, V>&)>;
|
||||||
|
|
||||||
|
// template <class I, typename K, typename V, typename K1, typename V1, typename std::enable_if<std::is_same<typename std::iterator_traits<I>::value_type, std::pair<const K,V>>::value, int32_t>::type = 0>
|
||||||
|
// static void putAll(JNIEnv* env, const jobject jhash_map, I iterator, const FnMapKV<const K,V,K1,V1> &fn_map_kv) {
|
||||||
|
/**
|
||||||
|
* Returns true if it succeeds, false if an error occurs
|
||||||
|
*/
|
||||||
|
template<class iterator_type, typename K, typename V>
|
||||||
|
static bool putAll(JNIEnv* env, const jobject jhash_map, iterator_type iterator, iterator_type end, const FnMapKV<K, V> &fn_map_kv) {
|
||||||
|
const jmethodID jmid_put = rocksdb::MapJni::getMapPutMethodId(env);
|
||||||
|
if (jmid_put == nullptr) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto it = iterator; it != end; ++it) {
|
||||||
|
const std::unique_ptr<std::pair<jobject, jobject>> result = fn_map_kv(*it);
|
||||||
|
if (result == nullptr) {
|
||||||
|
// an error occurred during fn_map_kv
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
env->CallObjectMethod(jhash_map, jmid_put, result->first, result->second);
|
||||||
|
if (env->ExceptionCheck()) {
|
||||||
|
// exception occurred
|
||||||
|
env->DeleteLocalRef(result->second);
|
||||||
|
env->DeleteLocalRef(result->first);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// release local references
|
||||||
|
env->DeleteLocalRef(result->second);
|
||||||
|
env->DeleteLocalRef(result->first);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class LongJni : public JavaClass {
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Get the Java Class java.lang.Long
|
||||||
|
*
|
||||||
|
* @param env A pointer to the Java environment
|
||||||
|
*
|
||||||
|
* @return The Java Class or nullptr if one of the
|
||||||
|
* ClassFormatError, ClassCircularityError, NoClassDefFoundError,
|
||||||
|
* OutOfMemoryError or ExceptionInInitializerError exceptions is thrown
|
||||||
|
*/
|
||||||
|
static jclass getJClass(JNIEnv* env) {
|
||||||
|
return JavaClass::getJClass(env, "java/lang/Long");
|
||||||
|
}
|
||||||
|
|
||||||
|
static jobject valueOf(JNIEnv* env, jlong jprimitive_long) {
|
||||||
|
jclass jclazz = getJClass(env);
|
||||||
|
if (jclazz == nullptr) {
|
||||||
|
// exception occurred accessing class
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
jmethodID mid =
|
||||||
|
env->GetStaticMethodID(jclazz, "valueOf", "(J)Ljava/lang/Long;");
|
||||||
|
if (mid == nullptr) {
|
||||||
|
// exception thrown: NoSuchMethodException or OutOfMemoryError
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
const jobject jlong_obj =
|
||||||
|
env->CallStaticObjectMethod(jclazz, mid, jprimitive_long);
|
||||||
|
if (env->ExceptionCheck()) {
|
||||||
|
// exception occurred
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
return jlong_obj;
|
||||||
|
}
|
||||||
|
};
|
||||||
} // namespace rocksdb
|
} // namespace rocksdb
|
||||||
#endif // JAVA_ROCKSJNI_PORTAL_H_
|
#endif // JAVA_ROCKSJNI_PORTAL_H_
|
||||||
|
1535
java/rocksjni/transaction.cc
Normal file
1535
java/rocksjni/transaction.cc
Normal file
File diff suppressed because it is too large
Load Diff
431
java/rocksjni/transaction_db.cc
Normal file
431
java/rocksjni/transaction_db.cc
Normal file
@ -0,0 +1,431 @@
|
|||||||
|
// 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 implements the "bridge" between Java and C++
|
||||||
|
// for rocksdb::TransactionDB.
|
||||||
|
|
||||||
|
#include <jni.h>
|
||||||
|
#include <functional>
|
||||||
|
#include <memory>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
|
||||||
|
#include "include/org_rocksdb_TransactionDB.h"
|
||||||
|
|
||||||
|
#include "rocksdb/options.h"
|
||||||
|
#include "rocksdb/utilities/transaction.h"
|
||||||
|
#include "rocksdb/utilities/transaction_db.h"
|
||||||
|
|
||||||
|
#include "rocksjni/portal.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Class: org_rocksdb_TransactionDB
|
||||||
|
* Method: open
|
||||||
|
* Signature: (JJLjava/lang/String;)J
|
||||||
|
*/
|
||||||
|
jlong Java_org_rocksdb_TransactionDB_open__JJLjava_lang_String_2(JNIEnv* env,
|
||||||
|
jclass jcls, jlong joptions_handle, jlong jtxn_db_options_handle,
|
||||||
|
jstring jdb_path) {
|
||||||
|
auto* options = reinterpret_cast<rocksdb::Options*>(joptions_handle);
|
||||||
|
auto* txn_db_options =
|
||||||
|
reinterpret_cast<rocksdb::TransactionDBOptions*>(jtxn_db_options_handle);
|
||||||
|
rocksdb::TransactionDB* tdb = nullptr;
|
||||||
|
const char* db_path = env->GetStringUTFChars(jdb_path, nullptr);
|
||||||
|
if (db_path == nullptr) {
|
||||||
|
// exception thrown: OutOfMemoryError
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
rocksdb::Status s =
|
||||||
|
rocksdb::TransactionDB::Open(*options, *txn_db_options, db_path, &tdb);
|
||||||
|
env->ReleaseStringUTFChars(jdb_path, db_path);
|
||||||
|
|
||||||
|
if (s.ok()) {
|
||||||
|
return reinterpret_cast<jlong>(tdb);
|
||||||
|
} else {
|
||||||
|
rocksdb::RocksDBExceptionJni::ThrowNew(env, s);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Class: org_rocksdb_TransactionDB
|
||||||
|
* Method: open
|
||||||
|
* Signature: (JJLjava/lang/String;[[B[J)[J
|
||||||
|
*/
|
||||||
|
jlongArray Java_org_rocksdb_TransactionDB_open__JJLjava_lang_String_2_3_3B_3J(
|
||||||
|
JNIEnv* env, jclass jcls, jlong jdb_options_handle,
|
||||||
|
jlong jtxn_db_options_handle, jstring jdb_path,
|
||||||
|
jobjectArray jcolumn_names,
|
||||||
|
jlongArray jcolumn_options_handles) {
|
||||||
|
const char* db_path = env->GetStringUTFChars(jdb_path, nullptr);
|
||||||
|
if (db_path == nullptr) {
|
||||||
|
// exception thrown: OutOfMemoryError
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
const jsize len_cols = env->GetArrayLength(jcolumn_names);
|
||||||
|
if (env->EnsureLocalCapacity(len_cols) != 0) {
|
||||||
|
// out of memory
|
||||||
|
env->ReleaseStringUTFChars(jdb_path, db_path);
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
jlong* jco = env->GetLongArrayElements(jcolumn_options_handles, nullptr);
|
||||||
|
if (jco == nullptr) {
|
||||||
|
// exception thrown: OutOfMemoryError
|
||||||
|
env->ReleaseStringUTFChars(jdb_path, db_path);
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
std::vector<rocksdb::ColumnFamilyDescriptor> column_families;
|
||||||
|
for (int i = 0; i < len_cols; i++) {
|
||||||
|
const jobject jcn = env->GetObjectArrayElement(jcolumn_names, i);
|
||||||
|
if (env->ExceptionCheck()) {
|
||||||
|
// exception thrown: ArrayIndexOutOfBoundsException
|
||||||
|
env->ReleaseLongArrayElements(jcolumn_options_handles, jco, JNI_ABORT);
|
||||||
|
env->ReleaseStringUTFChars(jdb_path, db_path);
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
const jbyteArray jcn_ba = reinterpret_cast<jbyteArray>(jcn);
|
||||||
|
jbyte* jcf_name = env->GetByteArrayElements(jcn_ba, nullptr);
|
||||||
|
if (jcf_name == nullptr) {
|
||||||
|
// exception thrown: OutOfMemoryError
|
||||||
|
env->DeleteLocalRef(jcn);
|
||||||
|
env->ReleaseLongArrayElements(jcolumn_options_handles, jco, JNI_ABORT);
|
||||||
|
env->ReleaseStringUTFChars(jdb_path, db_path);
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
const int jcf_name_len = env->GetArrayLength(jcn_ba);
|
||||||
|
if (env->EnsureLocalCapacity(jcf_name_len) != 0) {
|
||||||
|
// out of memory
|
||||||
|
env->ReleaseByteArrayElements(jcn_ba, jcf_name, JNI_ABORT);
|
||||||
|
env->DeleteLocalRef(jcn);
|
||||||
|
env->ReleaseLongArrayElements(jcolumn_options_handles, jco, JNI_ABORT);
|
||||||
|
env->ReleaseStringUTFChars(jdb_path, db_path);
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
const std::string cf_name(reinterpret_cast<char *>(jcf_name), jcf_name_len);
|
||||||
|
const rocksdb::ColumnFamilyOptions* cf_options =
|
||||||
|
reinterpret_cast<rocksdb::ColumnFamilyOptions*>(jco[i]);
|
||||||
|
column_families.push_back(
|
||||||
|
rocksdb::ColumnFamilyDescriptor(cf_name, *cf_options));
|
||||||
|
|
||||||
|
env->ReleaseByteArrayElements(jcn_ba, jcf_name, JNI_ABORT);
|
||||||
|
env->DeleteLocalRef(jcn);
|
||||||
|
}
|
||||||
|
env->ReleaseLongArrayElements(jcolumn_options_handles, jco, JNI_ABORT);
|
||||||
|
|
||||||
|
auto* db_options = reinterpret_cast<rocksdb::DBOptions*>(jdb_options_handle);
|
||||||
|
auto* txn_db_options =
|
||||||
|
reinterpret_cast<rocksdb::TransactionDBOptions*>(jtxn_db_options_handle);
|
||||||
|
std::vector<rocksdb::ColumnFamilyHandle*> handles;
|
||||||
|
rocksdb::TransactionDB* tdb = nullptr;
|
||||||
|
const rocksdb::Status s = rocksdb::TransactionDB::Open(*db_options, *txn_db_options,
|
||||||
|
db_path, column_families, &handles, &tdb);
|
||||||
|
|
||||||
|
// check if open operation was successful
|
||||||
|
if (s.ok()) {
|
||||||
|
const jsize resultsLen = 1 + len_cols; // db handle + column family handles
|
||||||
|
std::unique_ptr<jlong[]> results =
|
||||||
|
std::unique_ptr<jlong[]>(new jlong[resultsLen]);
|
||||||
|
results[0] = reinterpret_cast<jlong>(tdb);
|
||||||
|
for (int i = 1; i <= len_cols; i++) {
|
||||||
|
results[i] = reinterpret_cast<jlong>(handles[i - 1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
jlongArray jresults = env->NewLongArray(resultsLen);
|
||||||
|
if (jresults == nullptr) {
|
||||||
|
// exception thrown: OutOfMemoryError
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
env->SetLongArrayRegion(jresults, 0, resultsLen, results.get());
|
||||||
|
if (env->ExceptionCheck()) {
|
||||||
|
// exception thrown: ArrayIndexOutOfBoundsException
|
||||||
|
env->DeleteLocalRef(jresults);
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
return jresults;
|
||||||
|
} else {
|
||||||
|
rocksdb::RocksDBExceptionJni::ThrowNew(env, s);
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Class: org_rocksdb_TransactionDB
|
||||||
|
* Method: beginTransaction
|
||||||
|
* Signature: (JJ)J
|
||||||
|
*/
|
||||||
|
jlong Java_org_rocksdb_TransactionDB_beginTransaction__JJ(JNIEnv* env,
|
||||||
|
jobject jobj, jlong jhandle, jlong jwrite_options_handle) {
|
||||||
|
auto* txn_db = reinterpret_cast<rocksdb::TransactionDB*>(jhandle);
|
||||||
|
auto* write_options =
|
||||||
|
reinterpret_cast<rocksdb::WriteOptions*>(jwrite_options_handle);
|
||||||
|
rocksdb::Transaction* txn = txn_db->BeginTransaction(*write_options);
|
||||||
|
return reinterpret_cast<jlong>(txn);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Class: org_rocksdb_TransactionDB
|
||||||
|
* Method: beginTransaction
|
||||||
|
* Signature: (JJJ)J
|
||||||
|
*/
|
||||||
|
jlong Java_org_rocksdb_TransactionDB_beginTransaction__JJJ(JNIEnv* env,
|
||||||
|
jobject jobj, jlong jhandle, jlong jwrite_options_handle,
|
||||||
|
jlong jtxn_options_handle) {
|
||||||
|
auto* txn_db = reinterpret_cast<rocksdb::TransactionDB*>(jhandle);
|
||||||
|
auto* write_options =
|
||||||
|
reinterpret_cast<rocksdb::WriteOptions*>(jwrite_options_handle);
|
||||||
|
auto* txn_options =
|
||||||
|
reinterpret_cast<rocksdb::TransactionOptions*>(jtxn_options_handle);
|
||||||
|
rocksdb::Transaction* txn =
|
||||||
|
txn_db->BeginTransaction(*write_options, *txn_options);
|
||||||
|
return reinterpret_cast<jlong>(txn);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Class: org_rocksdb_TransactionDB
|
||||||
|
* Method: beginTransaction_withOld
|
||||||
|
* Signature: (JJJ)J
|
||||||
|
*/
|
||||||
|
jlong Java_org_rocksdb_TransactionDB_beginTransaction_1withOld__JJJ(
|
||||||
|
JNIEnv* env, jobject jobj, jlong jhandle, jlong jwrite_options_handle,
|
||||||
|
jlong jold_txn_handle) {
|
||||||
|
auto* txn_db = reinterpret_cast<rocksdb::TransactionDB*>(jhandle);
|
||||||
|
auto* write_options =
|
||||||
|
reinterpret_cast<rocksdb::WriteOptions*>(jwrite_options_handle);
|
||||||
|
auto* old_txn = reinterpret_cast<rocksdb::Transaction*>(jold_txn_handle);
|
||||||
|
rocksdb::TransactionOptions txn_options;
|
||||||
|
rocksdb::Transaction* txn =
|
||||||
|
txn_db->BeginTransaction(*write_options, txn_options, old_txn);
|
||||||
|
|
||||||
|
// RocksJava relies on the assumption that
|
||||||
|
// we do not allocate a new Transaction object
|
||||||
|
// when providing an old_txn
|
||||||
|
assert(txn == old_txn);
|
||||||
|
|
||||||
|
return reinterpret_cast<jlong>(txn);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Class: org_rocksdb_TransactionDB
|
||||||
|
* Method: beginTransaction_withOld
|
||||||
|
* Signature: (JJJJ)J
|
||||||
|
*/
|
||||||
|
jlong Java_org_rocksdb_TransactionDB_beginTransaction_1withOld__JJJJ(
|
||||||
|
JNIEnv* env, jobject jobj, jlong jhandle, jlong jwrite_options_handle,
|
||||||
|
jlong jtxn_options_handle, jlong jold_txn_handle) {
|
||||||
|
auto* txn_db = reinterpret_cast<rocksdb::TransactionDB*>(jhandle);
|
||||||
|
auto* write_options =
|
||||||
|
reinterpret_cast<rocksdb::WriteOptions*>(jwrite_options_handle);
|
||||||
|
auto* txn_options =
|
||||||
|
reinterpret_cast<rocksdb::TransactionOptions*>(jtxn_options_handle);
|
||||||
|
auto* old_txn = reinterpret_cast<rocksdb::Transaction*>(jold_txn_handle);
|
||||||
|
rocksdb::Transaction* txn = txn_db->BeginTransaction(*write_options,
|
||||||
|
*txn_options, old_txn);
|
||||||
|
|
||||||
|
// RocksJava relies on the assumption that
|
||||||
|
// we do not allocate a new Transaction object
|
||||||
|
// when providing an old_txn
|
||||||
|
assert(txn == old_txn);
|
||||||
|
|
||||||
|
return reinterpret_cast<jlong>(txn);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Class: org_rocksdb_TransactionDB
|
||||||
|
* Method: getTransactionByName
|
||||||
|
* Signature: (JLjava/lang/String;)J
|
||||||
|
*/
|
||||||
|
jlong Java_org_rocksdb_TransactionDB_getTransactionByName(JNIEnv* env,
|
||||||
|
jobject jobj, jlong jhandle, jstring jname) {
|
||||||
|
auto* txn_db = reinterpret_cast<rocksdb::TransactionDB*>(jhandle);
|
||||||
|
const char* name = env->GetStringUTFChars(jname, nullptr);
|
||||||
|
if (name == nullptr) {
|
||||||
|
// exception thrown: OutOfMemoryError
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
rocksdb::Transaction* txn = txn_db->GetTransactionByName(name);
|
||||||
|
env->ReleaseStringUTFChars(jname, name);
|
||||||
|
return reinterpret_cast<jlong>(txn);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Class: org_rocksdb_TransactionDB
|
||||||
|
* Method: getAllPreparedTransactions
|
||||||
|
* Signature: (J)[J
|
||||||
|
*/
|
||||||
|
jlongArray Java_org_rocksdb_TransactionDB_getAllPreparedTransactions(
|
||||||
|
JNIEnv* env, jobject jobj, jlong jhandle) {
|
||||||
|
auto* txn_db = reinterpret_cast<rocksdb::TransactionDB*>(jhandle);
|
||||||
|
std::vector<rocksdb::Transaction*> txns;
|
||||||
|
txn_db->GetAllPreparedTransactions(&txns);
|
||||||
|
|
||||||
|
const size_t size = txns.size();
|
||||||
|
assert(size < UINT32_MAX); // does it fit in a jint?
|
||||||
|
|
||||||
|
const jsize len = static_cast<jsize>(size);
|
||||||
|
jlong tmp[len];
|
||||||
|
for (jsize i = 0; i < len; ++i) {
|
||||||
|
tmp[i] = reinterpret_cast<jlong>(txns[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
jlongArray jtxns = env->NewLongArray(len);
|
||||||
|
if (jtxns == nullptr) {
|
||||||
|
// exception thrown: OutOfMemoryError
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
env->SetLongArrayRegion(jtxns, 0, len, tmp);
|
||||||
|
if (env->ExceptionCheck()) {
|
||||||
|
// exception thrown: ArrayIndexOutOfBoundsException
|
||||||
|
env->DeleteLocalRef(jtxns);
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
return jtxns;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Class: org_rocksdb_TransactionDB
|
||||||
|
* Method: getLockStatusData
|
||||||
|
* Signature: (J)Ljava/util/Map;
|
||||||
|
*/
|
||||||
|
jobject Java_org_rocksdb_TransactionDB_getLockStatusData(
|
||||||
|
JNIEnv* env, jobject jobj, jlong jhandle) {
|
||||||
|
auto* txn_db = reinterpret_cast<rocksdb::TransactionDB*>(jhandle);
|
||||||
|
const std::unordered_multimap<uint32_t, rocksdb::KeyLockInfo> lock_status_data =
|
||||||
|
txn_db->GetLockStatusData();
|
||||||
|
const jobject jlock_status_data = rocksdb::HashMapJni::construct(env,
|
||||||
|
static_cast<uint32_t>(lock_status_data.size()));
|
||||||
|
if (jlock_status_data == nullptr) {
|
||||||
|
// exception occurred
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
const rocksdb::HashMapJni::FnMapKV<const int32_t, const rocksdb::KeyLockInfo> fn_map_kv =
|
||||||
|
[env, txn_db, &lock_status_data](const std::pair<const int32_t, const rocksdb::KeyLockInfo>& pair) {
|
||||||
|
const jobject jlong_column_family_id =
|
||||||
|
rocksdb::LongJni::valueOf(env, pair.first);
|
||||||
|
if (jlong_column_family_id == nullptr) {
|
||||||
|
// an error occurred
|
||||||
|
return std::unique_ptr<std::pair<jobject, jobject>>(nullptr);
|
||||||
|
}
|
||||||
|
const jobject jkey_lock_info =
|
||||||
|
rocksdb::KeyLockInfoJni::construct(env, pair.second);
|
||||||
|
if (jkey_lock_info == nullptr) {
|
||||||
|
// an error occurred
|
||||||
|
return std::unique_ptr<std::pair<jobject, jobject>>(nullptr);
|
||||||
|
}
|
||||||
|
return std::unique_ptr<std::pair<jobject, jobject>>(new std::pair<jobject, jobject>(jlong_column_family_id,
|
||||||
|
jkey_lock_info));
|
||||||
|
};
|
||||||
|
|
||||||
|
if(!rocksdb::HashMapJni::putAll(env, jlock_status_data,
|
||||||
|
lock_status_data.begin(), lock_status_data.end(), fn_map_kv)) {
|
||||||
|
// exception occcurred
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
return jlock_status_data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Class: org_rocksdb_TransactionDB
|
||||||
|
* Method: getDeadlockInfoBuffer
|
||||||
|
* Signature: (J)[Lorg/rocksdb/TransactionDB/DeadlockPath;
|
||||||
|
*/
|
||||||
|
jobjectArray Java_org_rocksdb_TransactionDB_getDeadlockInfoBuffer(
|
||||||
|
JNIEnv* env, jobject jobj, jlong jhandle) {
|
||||||
|
auto* txn_db = reinterpret_cast<rocksdb::TransactionDB*>(jhandle);
|
||||||
|
const std::vector<rocksdb::DeadlockPath> deadlock_info_buffer =
|
||||||
|
txn_db->GetDeadlockInfoBuffer();
|
||||||
|
|
||||||
|
const jsize deadlock_info_buffer_len =
|
||||||
|
static_cast<jsize>(deadlock_info_buffer.size());
|
||||||
|
jobjectArray jdeadlock_info_buffer =
|
||||||
|
env->NewObjectArray(deadlock_info_buffer_len,
|
||||||
|
rocksdb::DeadlockPathJni::getJClass(env), nullptr);
|
||||||
|
if (jdeadlock_info_buffer == nullptr) {
|
||||||
|
// exception thrown: OutOfMemoryError
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
jsize jdeadlock_info_buffer_offset = 0;
|
||||||
|
|
||||||
|
auto buf_end = deadlock_info_buffer.end();
|
||||||
|
for (auto buf_it = deadlock_info_buffer.begin(); buf_it != buf_end; ++buf_it) {
|
||||||
|
const rocksdb::DeadlockPath deadlock_path = *buf_it;
|
||||||
|
const std::vector<rocksdb::DeadlockInfo> deadlock_infos
|
||||||
|
= deadlock_path.path;
|
||||||
|
const jsize deadlock_infos_len =
|
||||||
|
static_cast<jsize>(deadlock_info_buffer.size());
|
||||||
|
jobjectArray jdeadlock_infos = env->NewObjectArray(deadlock_infos_len,
|
||||||
|
rocksdb::DeadlockInfoJni::getJClass(env), nullptr);
|
||||||
|
if (jdeadlock_infos == nullptr) {
|
||||||
|
// exception thrown: OutOfMemoryError
|
||||||
|
env->DeleteLocalRef(jdeadlock_info_buffer);
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
jsize jdeadlock_infos_offset = 0;
|
||||||
|
|
||||||
|
auto infos_end = deadlock_infos.end();
|
||||||
|
for (auto infos_it = deadlock_infos.begin(); infos_it != infos_end; ++infos_it) {
|
||||||
|
const rocksdb::DeadlockInfo deadlock_info = *infos_it;
|
||||||
|
const jobject jdeadlock_info = rocksdb::TransactionDBJni::newDeadlockInfo(
|
||||||
|
env, jobj, deadlock_info.m_txn_id, deadlock_info.m_cf_id,
|
||||||
|
deadlock_info.m_waiting_key, deadlock_info.m_exclusive);
|
||||||
|
if (jdeadlock_info == nullptr) {
|
||||||
|
// exception occcurred
|
||||||
|
env->DeleteLocalRef(jdeadlock_info_buffer);
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
env->SetObjectArrayElement(jdeadlock_infos, jdeadlock_infos_offset++, jdeadlock_info);
|
||||||
|
if (env->ExceptionCheck()) {
|
||||||
|
// exception thrown: ArrayIndexOutOfBoundsException or ArrayStoreException
|
||||||
|
env->DeleteLocalRef(jdeadlock_info);
|
||||||
|
env->DeleteLocalRef(jdeadlock_info_buffer);
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const jobject jdeadlock_path =
|
||||||
|
rocksdb::DeadlockPathJni::construct(env, jdeadlock_infos,
|
||||||
|
deadlock_path.limit_exceeded);
|
||||||
|
if(jdeadlock_path == nullptr) {
|
||||||
|
// exception occcurred
|
||||||
|
env->DeleteLocalRef(jdeadlock_info_buffer);
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
env->SetObjectArrayElement(jdeadlock_info_buffer, jdeadlock_info_buffer_offset++, jdeadlock_path);
|
||||||
|
if (env->ExceptionCheck()) {
|
||||||
|
// exception thrown: ArrayIndexOutOfBoundsException or ArrayStoreException
|
||||||
|
env->DeleteLocalRef(jdeadlock_path);
|
||||||
|
env->DeleteLocalRef(jdeadlock_info_buffer);
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return jdeadlock_info_buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Class: org_rocksdb_TransactionDB
|
||||||
|
* Method: setDeadlockInfoBufferSize
|
||||||
|
* Signature: (JI)V
|
||||||
|
*/
|
||||||
|
void Java_org_rocksdb_TransactionDB_setDeadlockInfoBufferSize(
|
||||||
|
JNIEnv* env, jobject jobj, jlong jhandle, jint jdeadlock_info_buffer_size) {
|
||||||
|
auto* txn_db = reinterpret_cast<rocksdb::TransactionDB*>(jhandle);
|
||||||
|
txn_db->SetDeadlockInfoBufferSize(jdeadlock_info_buffer_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Class: org_rocksdb_TransactionDB
|
||||||
|
* Method: disposeInternal
|
||||||
|
* Signature: (J)V
|
||||||
|
*/
|
||||||
|
void Java_org_rocksdb_TransactionDB_disposeInternal(JNIEnv* env, jobject jobj,
|
||||||
|
jlong jhandle) {
|
||||||
|
delete reinterpret_cast<rocksdb::TransactionDB*>(jhandle);
|
||||||
|
}
|
147
java/rocksjni/transaction_db_options.cc
Normal file
147
java/rocksjni/transaction_db_options.cc
Normal file
@ -0,0 +1,147 @@
|
|||||||
|
// 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 implements the "bridge" between Java and C++
|
||||||
|
// for rocksdb::TransactionDBOptions.
|
||||||
|
|
||||||
|
#include <jni.h>
|
||||||
|
|
||||||
|
#include "include/org_rocksdb_TransactionDBOptions.h"
|
||||||
|
|
||||||
|
#include "rocksdb/utilities/transaction_db.h"
|
||||||
|
|
||||||
|
#include "rocksjni/portal.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Class: org_rocksdb_TransactionDBOptions
|
||||||
|
* Method: newTransactionDBOptions
|
||||||
|
* Signature: ()J
|
||||||
|
*/
|
||||||
|
jlong Java_org_rocksdb_TransactionDBOptions_newTransactionDBOptions(
|
||||||
|
JNIEnv* env, jclass jcls) {
|
||||||
|
rocksdb::TransactionDBOptions* opts = new rocksdb::TransactionDBOptions();
|
||||||
|
return reinterpret_cast<jlong>(opts);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Class: org_rocksdb_TransactionDBOptions
|
||||||
|
* Method: getMaxNumLocks
|
||||||
|
* Signature: (J)J
|
||||||
|
*/
|
||||||
|
jlong Java_org_rocksdb_TransactionDBOptions_getMaxNumLocks(JNIEnv* env,
|
||||||
|
jobject jobj, jlong jhandle) {
|
||||||
|
auto* opts = reinterpret_cast<rocksdb::TransactionDBOptions*>(jhandle);
|
||||||
|
return opts->max_num_locks;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Class: org_rocksdb_TransactionDBOptions
|
||||||
|
* Method: setMaxNumLocks
|
||||||
|
* Signature: (JJ)V
|
||||||
|
*/
|
||||||
|
void Java_org_rocksdb_TransactionDBOptions_setMaxNumLocks(JNIEnv* env,
|
||||||
|
jobject jobj, jlong jhandle, jlong jmax_num_locks) {
|
||||||
|
auto* opts = reinterpret_cast<rocksdb::TransactionDBOptions*>(jhandle);
|
||||||
|
opts->max_num_locks = jmax_num_locks;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Class: org_rocksdb_TransactionDBOptions
|
||||||
|
* Method: getNumStripes
|
||||||
|
* Signature: (J)J
|
||||||
|
*/
|
||||||
|
jlong Java_org_rocksdb_TransactionDBOptions_getNumStripes(JNIEnv* env,
|
||||||
|
jobject jobj, jlong jhandle) {
|
||||||
|
auto* opts = reinterpret_cast<rocksdb::TransactionDBOptions*>(jhandle);
|
||||||
|
return opts->num_stripes;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Class: org_rocksdb_TransactionDBOptions
|
||||||
|
* Method: setNumStripes
|
||||||
|
* Signature: (JJ)V
|
||||||
|
*/
|
||||||
|
void Java_org_rocksdb_TransactionDBOptions_setNumStripes(JNIEnv* env,
|
||||||
|
jobject jobj, jlong jhandle, jlong jnum_stripes) {
|
||||||
|
auto* opts = reinterpret_cast<rocksdb::TransactionDBOptions*>(jhandle);
|
||||||
|
opts->num_stripes = jnum_stripes;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Class: org_rocksdb_TransactionDBOptions
|
||||||
|
* Method: getTransactionLockTimeout
|
||||||
|
* Signature: (J)J
|
||||||
|
*/
|
||||||
|
jlong Java_org_rocksdb_TransactionDBOptions_getTransactionLockTimeout(
|
||||||
|
JNIEnv* env, jobject jobj, jlong jhandle) {
|
||||||
|
auto* opts = reinterpret_cast<rocksdb::TransactionDBOptions*>(jhandle);
|
||||||
|
return opts->transaction_lock_timeout;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Class: org_rocksdb_TransactionDBOptions
|
||||||
|
* Method: setTransactionLockTimeout
|
||||||
|
* Signature: (JJ)V
|
||||||
|
*/
|
||||||
|
void Java_org_rocksdb_TransactionDBOptions_setTransactionLockTimeout(
|
||||||
|
JNIEnv* env, jobject jobj, jlong jhandle, jlong jtransaction_lock_timeout) {
|
||||||
|
auto* opts = reinterpret_cast<rocksdb::TransactionDBOptions*>(jhandle);
|
||||||
|
opts->transaction_lock_timeout = jtransaction_lock_timeout;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Class: org_rocksdb_TransactionDBOptions
|
||||||
|
* Method: getDefaultLockTimeout
|
||||||
|
* Signature: (J)J
|
||||||
|
*/
|
||||||
|
jlong Java_org_rocksdb_TransactionDBOptions_getDefaultLockTimeout(
|
||||||
|
JNIEnv* env, jobject jobj, jlong jhandle) {
|
||||||
|
auto* opts = reinterpret_cast<rocksdb::TransactionDBOptions*>(jhandle);
|
||||||
|
return opts->default_lock_timeout;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Class: org_rocksdb_TransactionDBOptions
|
||||||
|
* Method: setDefaultLockTimeout
|
||||||
|
* Signature: (JJ)V
|
||||||
|
*/
|
||||||
|
void Java_org_rocksdb_TransactionDBOptions_setDefaultLockTimeout(
|
||||||
|
JNIEnv* env, jobject jobj, jlong jhandle, jlong jdefault_lock_timeout) {
|
||||||
|
auto* opts = reinterpret_cast<rocksdb::TransactionDBOptions*>(jhandle);
|
||||||
|
opts->default_lock_timeout = jdefault_lock_timeout;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Class: org_rocksdb_TransactionDBOptions
|
||||||
|
* Method: getWritePolicy
|
||||||
|
* Signature: (J)B
|
||||||
|
*/
|
||||||
|
jbyte Java_org_rocksdb_TransactionDBOptions_getWritePolicy(
|
||||||
|
JNIEnv* env, jobject jobj, jlong jhandle) {
|
||||||
|
auto* opts = reinterpret_cast<rocksdb::TransactionDBOptions*>(jhandle);
|
||||||
|
return rocksdb::TxnDBWritePolicyJni::toJavaTxnDBWritePolicy(opts->write_policy);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Class: org_rocksdb_TransactionDBOptions
|
||||||
|
* Method: setWritePolicy
|
||||||
|
* Signature: (JB)V
|
||||||
|
*/
|
||||||
|
void Java_org_rocksdb_TransactionDBOptions_setWritePolicy(
|
||||||
|
JNIEnv* env, jobject jobj, jlong jhandle, jbyte jwrite_policy) {
|
||||||
|
auto* opts = reinterpret_cast<rocksdb::TransactionDBOptions*>(jhandle);
|
||||||
|
opts->write_policy =
|
||||||
|
rocksdb::TxnDBWritePolicyJni::toCppTxnDBWritePolicy(jwrite_policy);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Class: org_rocksdb_TransactionDBOptions
|
||||||
|
* Method: disposeInternal
|
||||||
|
* Signature: (J)V
|
||||||
|
*/
|
||||||
|
void Java_org_rocksdb_TransactionDBOptions_disposeInternal(JNIEnv* env,
|
||||||
|
jobject jobj, jlong jhandle) {
|
||||||
|
delete reinterpret_cast<rocksdb::TransactionDBOptions*>(jhandle);
|
||||||
|
}
|
42
java/rocksjni/transaction_notifier.cc
Normal file
42
java/rocksjni/transaction_notifier.cc
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
// 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 implements the "bridge" between Java and C++
|
||||||
|
// for rocksdb::TransactionNotifier.
|
||||||
|
|
||||||
|
#include <jni.h>
|
||||||
|
|
||||||
|
#include "include/org_rocksdb_AbstractTransactionNotifier.h"
|
||||||
|
#include "rocksjni/transaction_notifier_jnicallback.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Class: org_rocksdb_AbstractTransactionNotifier
|
||||||
|
* Method: createNewTransactionNotifier
|
||||||
|
* Signature: ()J
|
||||||
|
*/
|
||||||
|
jlong Java_org_rocksdb_AbstractTransactionNotifier_createNewTransactionNotifier(
|
||||||
|
JNIEnv* env, jobject jobj) {
|
||||||
|
auto* transaction_notifier =
|
||||||
|
new rocksdb::TransactionNotifierJniCallback(env, jobj);
|
||||||
|
auto* sptr_transaction_notifier =
|
||||||
|
new std::shared_ptr<rocksdb::TransactionNotifierJniCallback>(
|
||||||
|
transaction_notifier);
|
||||||
|
return reinterpret_cast<jlong>(sptr_transaction_notifier);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Class: org_rocksdb_AbstractTransactionNotifier
|
||||||
|
* Method: disposeInternal
|
||||||
|
* Signature: (J)V
|
||||||
|
*/
|
||||||
|
void Java_org_rocksdb_AbstractTransactionNotifier_disposeInternal(JNIEnv* env,
|
||||||
|
jobject jobj, jlong jhandle) {
|
||||||
|
// TODO(AR) refactor to use JniCallback::JniCallback
|
||||||
|
// when https://github.com/facebook/rocksdb/pull/1241/ is merged
|
||||||
|
std::shared_ptr<rocksdb::TransactionNotifierJniCallback>* handle =
|
||||||
|
reinterpret_cast<std::shared_ptr<
|
||||||
|
rocksdb::TransactionNotifierJniCallback>*>(jhandle);
|
||||||
|
delete handle;
|
||||||
|
}
|
39
java/rocksjni/transaction_notifier_jnicallback.cc
Normal file
39
java/rocksjni/transaction_notifier_jnicallback.cc
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
// 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 implements the callback "bridge" between Java and C++ for
|
||||||
|
// rocksdb::TransactionNotifier.
|
||||||
|
|
||||||
|
#include "rocksjni/transaction_notifier_jnicallback.h"
|
||||||
|
#include "rocksjni/portal.h"
|
||||||
|
|
||||||
|
namespace rocksdb {
|
||||||
|
|
||||||
|
TransactionNotifierJniCallback::TransactionNotifierJniCallback(JNIEnv* env,
|
||||||
|
jobject jtransaction_notifier) : JniCallback(env, jtransaction_notifier) {
|
||||||
|
// we cache the method id for the JNI callback
|
||||||
|
m_jsnapshot_created_methodID =
|
||||||
|
AbstractTransactionNotifierJni::getSnapshotCreatedMethodId(env);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TransactionNotifierJniCallback::SnapshotCreated(
|
||||||
|
const Snapshot* newSnapshot) {
|
||||||
|
jboolean attached_thread = JNI_FALSE;
|
||||||
|
JNIEnv* env = getJniEnv(&attached_thread);
|
||||||
|
assert(env != nullptr);
|
||||||
|
|
||||||
|
env->CallVoidMethod(m_jcallback_obj,
|
||||||
|
m_jsnapshot_created_methodID, reinterpret_cast<jlong>(newSnapshot));
|
||||||
|
|
||||||
|
if(env->ExceptionCheck()) {
|
||||||
|
// exception thrown from CallVoidMethod
|
||||||
|
env->ExceptionDescribe(); // print out exception to stderr
|
||||||
|
releaseJniEnv(attached_thread);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
releaseJniEnv(attached_thread);
|
||||||
|
}
|
||||||
|
} // namespace rocksdb
|
42
java/rocksjni/transaction_notifier_jnicallback.h
Normal file
42
java/rocksjni/transaction_notifier_jnicallback.h
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
// 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 implements the callback "bridge" between Java and C++ for
|
||||||
|
// rocksdb::TransactionNotifier.
|
||||||
|
|
||||||
|
#ifndef JAVA_ROCKSJNI_TRANSACTION_NOTIFIER_JNICALLBACK_H_
|
||||||
|
#define JAVA_ROCKSJNI_TRANSACTION_NOTIFIER_JNICALLBACK_H_
|
||||||
|
|
||||||
|
#include <jni.h>
|
||||||
|
|
||||||
|
#include "rocksdb/utilities/transaction.h"
|
||||||
|
#include "rocksjni/jnicallback.h"
|
||||||
|
|
||||||
|
namespace rocksdb {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class acts as a bridge between C++
|
||||||
|
* and Java. The methods in this class will be
|
||||||
|
* called back from the RocksDB TransactionDB or OptimisticTransactionDB (C++),
|
||||||
|
* we then callback to the appropriate Java method
|
||||||
|
* this enables TransactionNotifier to be implemented in Java.
|
||||||
|
*
|
||||||
|
* Unlike RocksJava's Comparator JNI Callback, we do not attempt
|
||||||
|
* to reduce Java object allocations by caching the Snapshot object
|
||||||
|
* presented to the callback. This could be revisited in future
|
||||||
|
* if performance is lacking.
|
||||||
|
*/
|
||||||
|
class TransactionNotifierJniCallback: public JniCallback,
|
||||||
|
public TransactionNotifier {
|
||||||
|
public:
|
||||||
|
TransactionNotifierJniCallback(JNIEnv* env, jobject jtransaction_notifier);
|
||||||
|
virtual void SnapshotCreated(const Snapshot* newSnapshot);
|
||||||
|
|
||||||
|
private:
|
||||||
|
jmethodID m_jsnapshot_created_methodID;
|
||||||
|
};
|
||||||
|
} // namespace rocksdb
|
||||||
|
|
||||||
|
#endif // JAVA_ROCKSJNI_TRANSACTION_NOTIFIER_JNICALLBACK_H_
|
166
java/rocksjni/transaction_options.cc
Normal file
166
java/rocksjni/transaction_options.cc
Normal file
@ -0,0 +1,166 @@
|
|||||||
|
// 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 implements the "bridge" between Java and C++
|
||||||
|
// for rocksdb::TransactionOptions.
|
||||||
|
|
||||||
|
#include <jni.h>
|
||||||
|
|
||||||
|
#include "include/org_rocksdb_TransactionOptions.h"
|
||||||
|
|
||||||
|
#include "rocksdb/utilities/transaction_db.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Class: org_rocksdb_TransactionOptions
|
||||||
|
* Method: newTransactionOptions
|
||||||
|
* Signature: ()J
|
||||||
|
*/
|
||||||
|
jlong Java_org_rocksdb_TransactionOptions_newTransactionOptions(JNIEnv* env,
|
||||||
|
jclass jcls) {
|
||||||
|
auto* opts = new rocksdb::TransactionOptions();
|
||||||
|
return reinterpret_cast<jlong>(opts);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Class: org_rocksdb_TransactionOptions
|
||||||
|
* Method: isSetSnapshot
|
||||||
|
* Signature: (J)Z
|
||||||
|
*/
|
||||||
|
jboolean Java_org_rocksdb_TransactionOptions_isSetSnapshot(JNIEnv* env,
|
||||||
|
jobject jobj, jlong jhandle) {
|
||||||
|
auto* opts = reinterpret_cast<rocksdb::TransactionOptions*>(jhandle);
|
||||||
|
return opts->set_snapshot;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Class: org_rocksdb_TransactionOptions
|
||||||
|
* Method: setSetSnapshot
|
||||||
|
* Signature: (JZ)V
|
||||||
|
*/
|
||||||
|
void Java_org_rocksdb_TransactionOptions_setSetSnapshot(JNIEnv* env,
|
||||||
|
jobject jobj, jlong jhandle, jboolean jset_snapshot) {
|
||||||
|
auto* opts = reinterpret_cast<rocksdb::TransactionOptions*>(jhandle);
|
||||||
|
opts->set_snapshot = jset_snapshot;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Class: org_rocksdb_TransactionOptions
|
||||||
|
* Method: isDeadlockDetect
|
||||||
|
* Signature: (J)Z
|
||||||
|
*/
|
||||||
|
jboolean Java_org_rocksdb_TransactionOptions_isDeadlockDetect(
|
||||||
|
JNIEnv* env, jobject jobj, jlong jhandle) {
|
||||||
|
auto* opts = reinterpret_cast<rocksdb::TransactionOptions*>(jhandle);
|
||||||
|
return opts->deadlock_detect;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Class: org_rocksdb_TransactionOptions
|
||||||
|
* Method: setDeadlockDetect
|
||||||
|
* Signature: (JZ)V
|
||||||
|
*/
|
||||||
|
void Java_org_rocksdb_TransactionOptions_setDeadlockDetect(
|
||||||
|
JNIEnv* env, jobject jobj, jlong jhandle, jboolean jdeadlock_detect) {
|
||||||
|
auto* opts = reinterpret_cast<rocksdb::TransactionOptions*>(jhandle);
|
||||||
|
opts->deadlock_detect = jdeadlock_detect;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Class: org_rocksdb_TransactionOptions
|
||||||
|
* Method: getLockTimeout
|
||||||
|
* Signature: (J)J
|
||||||
|
*/
|
||||||
|
jlong Java_org_rocksdb_TransactionOptions_getLockTimeout(JNIEnv* env,
|
||||||
|
jobject jobj, jlong jhandle) {
|
||||||
|
auto* opts = reinterpret_cast<rocksdb::TransactionOptions*>(jhandle);
|
||||||
|
return opts->lock_timeout;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Class: org_rocksdb_TransactionOptions
|
||||||
|
* Method: setLockTimeout
|
||||||
|
* Signature: (JJ)V
|
||||||
|
*/
|
||||||
|
void Java_org_rocksdb_TransactionOptions_setLockTimeout(JNIEnv* env,
|
||||||
|
jobject jobj, jlong jhandle, jlong jlock_timeout) {
|
||||||
|
auto* opts = reinterpret_cast<rocksdb::TransactionOptions*>(jhandle);
|
||||||
|
opts->lock_timeout = jlock_timeout;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Class: org_rocksdb_TransactionOptions
|
||||||
|
* Method: getExpiration
|
||||||
|
* Signature: (J)J
|
||||||
|
*/
|
||||||
|
jlong Java_org_rocksdb_TransactionOptions_getExpiration(JNIEnv* env,
|
||||||
|
jobject jobj, jlong jhandle) {
|
||||||
|
auto* opts = reinterpret_cast<rocksdb::TransactionOptions*>(jhandle);
|
||||||
|
return opts->expiration;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Class: org_rocksdb_TransactionOptions
|
||||||
|
* Method: setExpiration
|
||||||
|
* Signature: (JJ)V
|
||||||
|
*/
|
||||||
|
void Java_org_rocksdb_TransactionOptions_setExpiration(JNIEnv* env,
|
||||||
|
jobject jobj, jlong jhandle, jlong jexpiration) {
|
||||||
|
auto* opts = reinterpret_cast<rocksdb::TransactionOptions*>(jhandle);
|
||||||
|
opts->expiration = jexpiration;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Class: org_rocksdb_TransactionOptions
|
||||||
|
* Method: getDeadlockDetectDepth
|
||||||
|
* Signature: (J)J
|
||||||
|
*/
|
||||||
|
jlong Java_org_rocksdb_TransactionOptions_getDeadlockDetectDepth(
|
||||||
|
JNIEnv* env, jobject jobj, jlong jhandle) {
|
||||||
|
auto* opts = reinterpret_cast<rocksdb::TransactionOptions*>(jhandle);
|
||||||
|
return opts->deadlock_detect_depth;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Class: org_rocksdb_TransactionOptions
|
||||||
|
* Method: setDeadlockDetectDepth
|
||||||
|
* Signature: (JJ)V
|
||||||
|
*/
|
||||||
|
void Java_org_rocksdb_TransactionOptions_setDeadlockDetectDepth(
|
||||||
|
JNIEnv* env, jobject jobj, jlong jhandle, jlong jdeadlock_detect_depth) {
|
||||||
|
auto* opts = reinterpret_cast<rocksdb::TransactionOptions*>(jhandle);
|
||||||
|
opts->deadlock_detect_depth = jdeadlock_detect_depth;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Class: org_rocksdb_TransactionOptions
|
||||||
|
* Method: getMaxWriteBatchSize
|
||||||
|
* Signature: (J)J
|
||||||
|
*/
|
||||||
|
jlong Java_org_rocksdb_TransactionOptions_getMaxWriteBatchSize(
|
||||||
|
JNIEnv* env, jobject jobj, jlong jhandle) {
|
||||||
|
auto* opts = reinterpret_cast<rocksdb::TransactionOptions*>(jhandle);
|
||||||
|
return opts->max_write_batch_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Class: org_rocksdb_TransactionOptions
|
||||||
|
* Method: setMaxWriteBatchSize
|
||||||
|
* Signature: (JJ)V
|
||||||
|
*/
|
||||||
|
void Java_org_rocksdb_TransactionOptions_setMaxWriteBatchSize(
|
||||||
|
JNIEnv* env, jobject jobj, jlong jhandle, jlong jmax_write_batch_size) {
|
||||||
|
auto* opts = reinterpret_cast<rocksdb::TransactionOptions*>(jhandle);
|
||||||
|
opts->max_write_batch_size = jmax_write_batch_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Class: org_rocksdb_TransactionOptions
|
||||||
|
* Method: disposeInternal
|
||||||
|
* Signature: (J)V
|
||||||
|
*/
|
||||||
|
void Java_org_rocksdb_TransactionOptions_disposeInternal(JNIEnv* env,
|
||||||
|
jobject jobj, jlong jhandle) {
|
||||||
|
delete reinterpret_cast<rocksdb::TransactionOptions*>(jhandle);
|
||||||
|
}
|
184
java/samples/src/main/java/OptimisticTransactionSample.java
Normal file
184
java/samples/src/main/java/OptimisticTransactionSample.java
Normal file
@ -0,0 +1,184 @@
|
|||||||
|
// 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).
|
||||||
|
|
||||||
|
import org.rocksdb.*;
|
||||||
|
|
||||||
|
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Demonstrates using Transactions on an OptimisticTransactionDB with
|
||||||
|
* varying isolation guarantees
|
||||||
|
*/
|
||||||
|
public class OptimisticTransactionSample {
|
||||||
|
private static final String dbPath = "/tmp/rocksdb_optimistic_transaction_example";
|
||||||
|
|
||||||
|
public static final void main(final String args[]) throws RocksDBException {
|
||||||
|
|
||||||
|
try(final Options options = new Options()
|
||||||
|
.setCreateIfMissing(true);
|
||||||
|
final OptimisticTransactionDB txnDb =
|
||||||
|
OptimisticTransactionDB.open(options, dbPath)) {
|
||||||
|
|
||||||
|
try (final WriteOptions writeOptions = new WriteOptions();
|
||||||
|
final ReadOptions readOptions = new ReadOptions()) {
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// Simple OptimisticTransaction Example ("Read Committed")
|
||||||
|
//
|
||||||
|
////////////////////////////////////////////////////////
|
||||||
|
readCommitted(txnDb, writeOptions, readOptions);
|
||||||
|
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// "Repeatable Read" (Snapshot Isolation) Example
|
||||||
|
// -- Using a single Snapshot
|
||||||
|
//
|
||||||
|
////////////////////////////////////////////////////////
|
||||||
|
repeatableRead(txnDb, writeOptions, readOptions);
|
||||||
|
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// "Read Committed" (Monotonic Atomic Views) Example
|
||||||
|
// --Using multiple Snapshots
|
||||||
|
//
|
||||||
|
////////////////////////////////////////////////////////
|
||||||
|
readCommitted_monotonicAtomicViews(txnDb, writeOptions, readOptions);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Demonstrates "Read Committed" isolation
|
||||||
|
*/
|
||||||
|
private static void readCommitted(final OptimisticTransactionDB txnDb,
|
||||||
|
final WriteOptions writeOptions, final ReadOptions readOptions)
|
||||||
|
throws RocksDBException {
|
||||||
|
final byte key1[] = "abc".getBytes(UTF_8);
|
||||||
|
final byte value1[] = "def".getBytes(UTF_8);
|
||||||
|
|
||||||
|
final byte key2[] = "xyz".getBytes(UTF_8);
|
||||||
|
final byte value2[] = "zzz".getBytes(UTF_8);
|
||||||
|
|
||||||
|
// Start a transaction
|
||||||
|
try(final Transaction txn = txnDb.beginTransaction(writeOptions)) {
|
||||||
|
// Read a key in this transaction
|
||||||
|
byte[] value = txn.get(readOptions, key1);
|
||||||
|
assert(value == null);
|
||||||
|
|
||||||
|
// Write a key in this transaction
|
||||||
|
txn.put(key1, value1);
|
||||||
|
|
||||||
|
// Read a key OUTSIDE this transaction. Does not affect txn.
|
||||||
|
value = txnDb.get(readOptions, key1);
|
||||||
|
assert(value == null);
|
||||||
|
|
||||||
|
// Write a key OUTSIDE of this transaction.
|
||||||
|
// Does not affect txn since this is an unrelated key.
|
||||||
|
// If we wrote key 'abc' here, the transaction would fail to commit.
|
||||||
|
txnDb.put(writeOptions, key2, value2);
|
||||||
|
|
||||||
|
// Commit transaction
|
||||||
|
txn.commit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Demonstrates "Repeatable Read" (Snapshot Isolation) isolation
|
||||||
|
*/
|
||||||
|
private static void repeatableRead(final OptimisticTransactionDB txnDb,
|
||||||
|
final WriteOptions writeOptions, final ReadOptions readOptions)
|
||||||
|
throws RocksDBException {
|
||||||
|
|
||||||
|
final byte key1[] = "ghi".getBytes(UTF_8);
|
||||||
|
final byte value1[] = "jkl".getBytes(UTF_8);
|
||||||
|
|
||||||
|
// Set a snapshot at start of transaction by setting setSnapshot(true)
|
||||||
|
try(final OptimisticTransactionOptions txnOptions =
|
||||||
|
new OptimisticTransactionOptions().setSetSnapshot(true);
|
||||||
|
final Transaction txn =
|
||||||
|
txnDb.beginTransaction(writeOptions, txnOptions)) {
|
||||||
|
|
||||||
|
final Snapshot snapshot = txn.getSnapshot();
|
||||||
|
|
||||||
|
// Write a key OUTSIDE of transaction
|
||||||
|
txnDb.put(writeOptions, key1, value1);
|
||||||
|
|
||||||
|
// Read a key using the snapshot.
|
||||||
|
readOptions.setSnapshot(snapshot);
|
||||||
|
final byte[] value = txn.getForUpdate(readOptions, key1, true);
|
||||||
|
assert(value == value1);
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Attempt to commit transaction
|
||||||
|
txn.commit();
|
||||||
|
throw new IllegalStateException();
|
||||||
|
} catch(final RocksDBException e) {
|
||||||
|
// Transaction could not commit since the write outside of the txn
|
||||||
|
// conflicted with the read!
|
||||||
|
assert(e.getStatus().getCode() == Status.Code.Busy);
|
||||||
|
}
|
||||||
|
|
||||||
|
txn.rollback();
|
||||||
|
} finally {
|
||||||
|
// Clear snapshot from read options since it is no longer valid
|
||||||
|
readOptions.setSnapshot(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Demonstrates "Read Committed" (Monotonic Atomic Views) isolation
|
||||||
|
*
|
||||||
|
* In this example, we set the snapshot multiple times. This is probably
|
||||||
|
* only necessary if you have very strict isolation requirements to
|
||||||
|
* implement.
|
||||||
|
*/
|
||||||
|
private static void readCommitted_monotonicAtomicViews(
|
||||||
|
final OptimisticTransactionDB txnDb, final WriteOptions writeOptions,
|
||||||
|
final ReadOptions readOptions) throws RocksDBException {
|
||||||
|
|
||||||
|
final byte keyX[] = "x".getBytes(UTF_8);
|
||||||
|
final byte valueX[] = "x".getBytes(UTF_8);
|
||||||
|
|
||||||
|
final byte keyY[] = "y".getBytes(UTF_8);
|
||||||
|
final byte valueY[] = "y".getBytes(UTF_8);
|
||||||
|
|
||||||
|
try (final OptimisticTransactionOptions txnOptions =
|
||||||
|
new OptimisticTransactionOptions().setSetSnapshot(true);
|
||||||
|
final Transaction txn =
|
||||||
|
txnDb.beginTransaction(writeOptions, txnOptions)) {
|
||||||
|
|
||||||
|
// Do some reads and writes to key "x"
|
||||||
|
Snapshot snapshot = txnDb.getSnapshot();
|
||||||
|
readOptions.setSnapshot(snapshot);
|
||||||
|
byte[] value = txn.get(readOptions, keyX);
|
||||||
|
txn.put(valueX, valueX);
|
||||||
|
|
||||||
|
// Do a write outside of the transaction to key "y"
|
||||||
|
txnDb.put(writeOptions, keyY, valueY);
|
||||||
|
|
||||||
|
// Set a new snapshot in the transaction
|
||||||
|
txn.setSnapshot();
|
||||||
|
snapshot = txnDb.getSnapshot();
|
||||||
|
readOptions.setSnapshot(snapshot);
|
||||||
|
|
||||||
|
// Do some reads and writes to key "y"
|
||||||
|
// Since the snapshot was advanced, the write done outside of the
|
||||||
|
// transaction does not conflict.
|
||||||
|
value = txn.getForUpdate(readOptions, keyY, true);
|
||||||
|
txn.put(keyY, valueY);
|
||||||
|
|
||||||
|
// Commit. Since the snapshot was advanced, the write done outside of the
|
||||||
|
// transaction does not prevent this transaction from Committing.
|
||||||
|
txn.commit();
|
||||||
|
|
||||||
|
} finally {
|
||||||
|
// Clear snapshot from read options since it is no longer valid
|
||||||
|
readOptions.setSnapshot(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
183
java/samples/src/main/java/TransactionSample.java
Normal file
183
java/samples/src/main/java/TransactionSample.java
Normal file
@ -0,0 +1,183 @@
|
|||||||
|
// 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).
|
||||||
|
|
||||||
|
import org.rocksdb.*;
|
||||||
|
|
||||||
|
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Demonstrates using Transactions on a TransactionDB with
|
||||||
|
* varying isolation guarantees
|
||||||
|
*/
|
||||||
|
public class TransactionSample {
|
||||||
|
private static final String dbPath = "/tmp/rocksdb_transaction_example";
|
||||||
|
|
||||||
|
public static final void main(final String args[]) throws RocksDBException {
|
||||||
|
|
||||||
|
try(final Options options = new Options()
|
||||||
|
.setCreateIfMissing(true);
|
||||||
|
final TransactionDBOptions txnDbOptions = new TransactionDBOptions();
|
||||||
|
final TransactionDB txnDb =
|
||||||
|
TransactionDB.open(options, txnDbOptions, dbPath)) {
|
||||||
|
|
||||||
|
try (final WriteOptions writeOptions = new WriteOptions();
|
||||||
|
final ReadOptions readOptions = new ReadOptions()) {
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// Simple Transaction Example ("Read Committed")
|
||||||
|
//
|
||||||
|
////////////////////////////////////////////////////////
|
||||||
|
readCommitted(txnDb, writeOptions, readOptions);
|
||||||
|
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// "Repeatable Read" (Snapshot Isolation) Example
|
||||||
|
// -- Using a single Snapshot
|
||||||
|
//
|
||||||
|
////////////////////////////////////////////////////////
|
||||||
|
repeatableRead(txnDb, writeOptions, readOptions);
|
||||||
|
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// "Read Committed" (Monotonic Atomic Views) Example
|
||||||
|
// --Using multiple Snapshots
|
||||||
|
//
|
||||||
|
////////////////////////////////////////////////////////
|
||||||
|
readCommitted_monotonicAtomicViews(txnDb, writeOptions, readOptions);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Demonstrates "Read Committed" isolation
|
||||||
|
*/
|
||||||
|
private static void readCommitted(final TransactionDB txnDb,
|
||||||
|
final WriteOptions writeOptions, final ReadOptions readOptions)
|
||||||
|
throws RocksDBException {
|
||||||
|
final byte key1[] = "abc".getBytes(UTF_8);
|
||||||
|
final byte value1[] = "def".getBytes(UTF_8);
|
||||||
|
|
||||||
|
final byte key2[] = "xyz".getBytes(UTF_8);
|
||||||
|
final byte value2[] = "zzz".getBytes(UTF_8);
|
||||||
|
|
||||||
|
// Start a transaction
|
||||||
|
try(final Transaction txn = txnDb.beginTransaction(writeOptions)) {
|
||||||
|
// Read a key in this transaction
|
||||||
|
byte[] value = txn.get(readOptions, key1);
|
||||||
|
assert(value == null);
|
||||||
|
|
||||||
|
// Write a key in this transaction
|
||||||
|
txn.put(key1, value1);
|
||||||
|
|
||||||
|
// Read a key OUTSIDE this transaction. Does not affect txn.
|
||||||
|
value = txnDb.get(readOptions, key1);
|
||||||
|
assert(value == null);
|
||||||
|
|
||||||
|
// Write a key OUTSIDE of this transaction.
|
||||||
|
// Does not affect txn since this is an unrelated key.
|
||||||
|
// If we wrote key 'abc' here, the transaction would fail to commit.
|
||||||
|
txnDb.put(writeOptions, key2, value2);
|
||||||
|
|
||||||
|
// Commit transaction
|
||||||
|
txn.commit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Demonstrates "Repeatable Read" (Snapshot Isolation) isolation
|
||||||
|
*/
|
||||||
|
private static void repeatableRead(final TransactionDB txnDb,
|
||||||
|
final WriteOptions writeOptions, final ReadOptions readOptions)
|
||||||
|
throws RocksDBException {
|
||||||
|
|
||||||
|
final byte key1[] = "ghi".getBytes(UTF_8);
|
||||||
|
final byte value1[] = "jkl".getBytes(UTF_8);
|
||||||
|
|
||||||
|
// Set a snapshot at start of transaction by setting setSnapshot(true)
|
||||||
|
try(final TransactionOptions txnOptions = new TransactionOptions()
|
||||||
|
.setSetSnapshot(true);
|
||||||
|
final Transaction txn =
|
||||||
|
txnDb.beginTransaction(writeOptions, txnOptions)) {
|
||||||
|
|
||||||
|
final Snapshot snapshot = txn.getSnapshot();
|
||||||
|
|
||||||
|
// Write a key OUTSIDE of transaction
|
||||||
|
txnDb.put(writeOptions, key1, value1);
|
||||||
|
|
||||||
|
// Attempt to read a key using the snapshot. This will fail since
|
||||||
|
// the previous write outside this txn conflicts with this read.
|
||||||
|
readOptions.setSnapshot(snapshot);
|
||||||
|
|
||||||
|
try {
|
||||||
|
final byte[] value = txn.getForUpdate(readOptions, key1, true);
|
||||||
|
throw new IllegalStateException();
|
||||||
|
} catch(final RocksDBException e) {
|
||||||
|
assert(e.getStatus().getCode() == Status.Code.Busy);
|
||||||
|
}
|
||||||
|
|
||||||
|
txn.rollback();
|
||||||
|
} finally {
|
||||||
|
// Clear snapshot from read options since it is no longer valid
|
||||||
|
readOptions.setSnapshot(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Demonstrates "Read Committed" (Monotonic Atomic Views) isolation
|
||||||
|
*
|
||||||
|
* In this example, we set the snapshot multiple times. This is probably
|
||||||
|
* only necessary if you have very strict isolation requirements to
|
||||||
|
* implement.
|
||||||
|
*/
|
||||||
|
private static void readCommitted_monotonicAtomicViews(
|
||||||
|
final TransactionDB txnDb, final WriteOptions writeOptions,
|
||||||
|
final ReadOptions readOptions) throws RocksDBException {
|
||||||
|
|
||||||
|
final byte keyX[] = "x".getBytes(UTF_8);
|
||||||
|
final byte valueX[] = "x".getBytes(UTF_8);
|
||||||
|
|
||||||
|
final byte keyY[] = "y".getBytes(UTF_8);
|
||||||
|
final byte valueY[] = "y".getBytes(UTF_8);
|
||||||
|
|
||||||
|
try (final TransactionOptions txnOptions = new TransactionOptions()
|
||||||
|
.setSetSnapshot(true);
|
||||||
|
final Transaction txn =
|
||||||
|
txnDb.beginTransaction(writeOptions, txnOptions)) {
|
||||||
|
|
||||||
|
// Do some reads and writes to key "x"
|
||||||
|
Snapshot snapshot = txnDb.getSnapshot();
|
||||||
|
readOptions.setSnapshot(snapshot);
|
||||||
|
byte[] value = txn.get(readOptions, keyX);
|
||||||
|
txn.put(valueX, valueX);
|
||||||
|
|
||||||
|
// Do a write outside of the transaction to key "y"
|
||||||
|
txnDb.put(writeOptions, keyY, valueY);
|
||||||
|
|
||||||
|
// Set a new snapshot in the transaction
|
||||||
|
txn.setSnapshot();
|
||||||
|
txn.setSavePoint();
|
||||||
|
snapshot = txnDb.getSnapshot();
|
||||||
|
readOptions.setSnapshot(snapshot);
|
||||||
|
|
||||||
|
// Do some reads and writes to key "y"
|
||||||
|
// Since the snapshot was advanced, the write done outside of the
|
||||||
|
// transaction does not conflict.
|
||||||
|
value = txn.getForUpdate(readOptions, keyY, true);
|
||||||
|
txn.put(keyY, valueY);
|
||||||
|
|
||||||
|
// Decide we want to revert the last write from this transaction.
|
||||||
|
txn.rollbackToSavePoint();
|
||||||
|
|
||||||
|
// Commit.
|
||||||
|
txn.commit();
|
||||||
|
} finally {
|
||||||
|
// Clear snapshot from read options since it is no longer valid
|
||||||
|
readOptions.setSnapshot(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,54 @@
|
|||||||
|
// 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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides notification to the caller of SetSnapshotOnNextOperation when
|
||||||
|
* the actual snapshot gets created
|
||||||
|
*/
|
||||||
|
public abstract class AbstractTransactionNotifier
|
||||||
|
extends RocksCallbackObject {
|
||||||
|
|
||||||
|
protected AbstractTransactionNotifier() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implement this method to receive notification when a snapshot is
|
||||||
|
* requested via {@link Transaction#setSnapshotOnNextOperation()}.
|
||||||
|
*
|
||||||
|
* @param newSnapshot the snapshot that has been created.
|
||||||
|
*/
|
||||||
|
public abstract void snapshotCreated(final Snapshot newSnapshot);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is intentionally private as it is the callback hook
|
||||||
|
* from JNI
|
||||||
|
*/
|
||||||
|
private void snapshotCreated(final long snapshotHandle) {
|
||||||
|
snapshotCreated(new Snapshot(snapshotHandle));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected long initializeNative(final long... nativeParameterHandles) {
|
||||||
|
return createNewTransactionNotifier();
|
||||||
|
}
|
||||||
|
|
||||||
|
private native long createNewTransactionNotifier();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deletes underlying C++ TransactionNotifier pointer.
|
||||||
|
*
|
||||||
|
* Note that this function should be called only after all
|
||||||
|
* Transactions referencing the comparator are closed.
|
||||||
|
* Otherwise an undefined behavior will occur.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
protected void disposeInternal() {
|
||||||
|
disposeInternal(nativeHandle_);
|
||||||
|
}
|
||||||
|
protected final native void disposeInternal(final long handle);
|
||||||
|
}
|
@ -5,6 +5,8 @@
|
|||||||
|
|
||||||
package org.rocksdb;
|
package org.rocksdb;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>Describes a column family with a
|
* <p>Describes a column family with a
|
||||||
* name and respective Options.</p>
|
* name and respective Options.</p>
|
||||||
@ -43,19 +45,65 @@ public class ColumnFamilyDescriptor {
|
|||||||
* @return column family name.
|
* @return column family name.
|
||||||
* @since 3.10.0
|
* @since 3.10.0
|
||||||
*/
|
*/
|
||||||
public byte[] columnFamilyName() {
|
public byte[] getName() {
|
||||||
return columnFamilyName_;
|
return columnFamilyName_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve name of column family.
|
||||||
|
*
|
||||||
|
* @return column family name.
|
||||||
|
* @since 3.10.0
|
||||||
|
*
|
||||||
|
* @deprecated Use {@link #getName()} instead.
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
|
public byte[] columnFamilyName() {
|
||||||
|
return getName();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve assigned options instance.
|
* Retrieve assigned options instance.
|
||||||
*
|
*
|
||||||
* @return Options instance assigned to this instance.
|
* @return Options instance assigned to this instance.
|
||||||
*/
|
*/
|
||||||
public ColumnFamilyOptions columnFamilyOptions() {
|
public ColumnFamilyOptions getOptions() {
|
||||||
return columnFamilyOptions_;
|
return columnFamilyOptions_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve assigned options instance.
|
||||||
|
*
|
||||||
|
* @return Options instance assigned to this instance.
|
||||||
|
*
|
||||||
|
* @deprecated Use {@link #getOptions()} instead.
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
|
public ColumnFamilyOptions columnFamilyOptions() {
|
||||||
|
return getOptions();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(final Object o) {
|
||||||
|
if (this == o) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (o == null || getClass() != o.getClass()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
final ColumnFamilyDescriptor that = (ColumnFamilyDescriptor) o;
|
||||||
|
return Arrays.equals(columnFamilyName_, that.columnFamilyName_)
|
||||||
|
&& columnFamilyOptions_.nativeHandle_ == that.columnFamilyOptions_.nativeHandle_;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
int result = (int) (columnFamilyOptions_.nativeHandle_ ^ (columnFamilyOptions_.nativeHandle_ >>> 32));
|
||||||
|
result = 31 * result + Arrays.hashCode(columnFamilyName_);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
private final byte[] columnFamilyName_;
|
private final byte[] columnFamilyName_;
|
||||||
private final ColumnFamilyOptions columnFamilyOptions_;
|
private final ColumnFamilyOptions columnFamilyOptions_;
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,9 @@
|
|||||||
|
|
||||||
package org.rocksdb;
|
package org.rocksdb;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ColumnFamilyHandle class to hold handles to underlying rocksdb
|
* ColumnFamilyHandle class to hold handles to underlying rocksdb
|
||||||
* ColumnFamily Pointers.
|
* ColumnFamily Pointers.
|
||||||
@ -21,6 +24,63 @@ public class ColumnFamilyHandle extends RocksObject {
|
|||||||
this.rocksDB_ = rocksDB;
|
this.rocksDB_ = rocksDB;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the name of the Column Family.
|
||||||
|
*
|
||||||
|
* @return The name of the Column Family.
|
||||||
|
*/
|
||||||
|
public byte[] getName() {
|
||||||
|
return getName(nativeHandle_);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the ID of the Column Family.
|
||||||
|
*
|
||||||
|
* @return the ID of the Column Family.
|
||||||
|
*/
|
||||||
|
public int getID() {
|
||||||
|
return getID(nativeHandle_);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the up-to-date descriptor of the column family
|
||||||
|
* associated with this handle. Since it fills "*desc" with the up-to-date
|
||||||
|
* information, this call might internally lock and release DB mutex to
|
||||||
|
* access the up-to-date CF options. In addition, all the pointer-typed
|
||||||
|
* options cannot be referenced any longer than the original options exist.
|
||||||
|
*
|
||||||
|
* Note that this function is not supported in RocksDBLite.
|
||||||
|
*
|
||||||
|
* @return the up-to-date descriptor.
|
||||||
|
*
|
||||||
|
* @throws RocksDBException if an error occurs whilst retrieving the
|
||||||
|
* descriptor.
|
||||||
|
*/
|
||||||
|
public ColumnFamilyDescriptor getDescriptor() throws RocksDBException {
|
||||||
|
assert(isOwningHandle());
|
||||||
|
return getDescriptor(nativeHandle_);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(final Object o) {
|
||||||
|
if (this == o) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (o == null || getClass() != o.getClass()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
final ColumnFamilyHandle that = (ColumnFamilyHandle) o;
|
||||||
|
return rocksDB_.nativeHandle_ == that.rocksDB_.nativeHandle_ &&
|
||||||
|
getID() == that.getID() &&
|
||||||
|
Arrays.equals(getName(), that.getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return Objects.hash(getName(), getID(), rocksDB_.nativeHandle_);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>Deletes underlying C++ iterator pointer.</p>
|
* <p>Deletes underlying C++ iterator pointer.</p>
|
||||||
*
|
*
|
||||||
@ -36,6 +96,9 @@ public class ColumnFamilyHandle extends RocksObject {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private native byte[] getName(final long handle);
|
||||||
|
private native int getID(final long handle);
|
||||||
|
private native ColumnFamilyDescriptor getDescriptor(final long handle) throws RocksDBException;
|
||||||
@Override protected final native void disposeInternal(final long handle);
|
@Override protected final native void disposeInternal(final long handle);
|
||||||
|
|
||||||
private final RocksDB rocksDB_;
|
private final RocksDB rocksDB_;
|
||||||
|
@ -53,6 +53,18 @@ public class ColumnFamilyOptions extends RocksObject
|
|||||||
this.compressionOptions_ = other.compressionOptions_;
|
this.compressionOptions_ = other.compressionOptions_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Constructor to be used by
|
||||||
|
* {@link #getColumnFamilyOptionsFromProps(java.util.Properties)},
|
||||||
|
* {@link ColumnFamilyDescriptor#columnFamilyOptions()}
|
||||||
|
* and also called via JNI.</p>
|
||||||
|
*
|
||||||
|
* @param handle native handle to ColumnFamilyOptions instance.
|
||||||
|
*/
|
||||||
|
ColumnFamilyOptions(final long handle) {
|
||||||
|
super(handle);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>Method to get a options instance by using pre-configured
|
* <p>Method to get a options instance by using pre-configured
|
||||||
* property values. If one or many values are undefined in
|
* property values. If one or many values are undefined in
|
||||||
@ -788,17 +800,6 @@ public class ColumnFamilyOptions extends RocksObject
|
|||||||
return forceConsistencyChecks(nativeHandle_);
|
return forceConsistencyChecks(nativeHandle_);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* <p>Constructor to be used by
|
|
||||||
* {@link #getColumnFamilyOptionsFromProps(java.util.Properties)}</p>
|
|
||||||
* and also called via JNI.
|
|
||||||
*
|
|
||||||
* @param handle native handle to ColumnFamilyOptions instance.
|
|
||||||
*/
|
|
||||||
public ColumnFamilyOptions(final long handle) {
|
|
||||||
super(handle);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static native long getColumnFamilyOptionsFromProps(
|
private static native long getColumnFamilyOptionsFromProps(
|
||||||
String optString);
|
String optString);
|
||||||
|
|
||||||
|
@ -269,7 +269,10 @@ public interface DBOptionsInterface<T extends DBOptionsInterface> {
|
|||||||
* Statistics objects should not be shared between DB instances as
|
* Statistics objects should not be shared between DB instances as
|
||||||
* it does not use any locks to prevent concurrent updates.</p>
|
* it does not use any locks to prevent concurrent updates.</p>
|
||||||
*
|
*
|
||||||
|
* @param statistics The statistics to set
|
||||||
|
*
|
||||||
* @return the instance of the current object.
|
* @return the instance of the current object.
|
||||||
|
*
|
||||||
* @see RocksDB#open(org.rocksdb.Options, String)
|
* @see RocksDB#open(org.rocksdb.Options, String)
|
||||||
*/
|
*/
|
||||||
T setStatistics(final Statistics statistics);
|
T setStatistics(final Statistics statistics);
|
||||||
@ -277,7 +280,9 @@ public interface DBOptionsInterface<T extends DBOptionsInterface> {
|
|||||||
/**
|
/**
|
||||||
* <p>Returns statistics object.</p>
|
* <p>Returns statistics object.</p>
|
||||||
*
|
*
|
||||||
* @return the instance of the statistics object or null if there is no statistics object.
|
* @return the instance of the statistics object or null if there is no
|
||||||
|
* statistics object.
|
||||||
|
*
|
||||||
* @see #setStatistics(Statistics)
|
* @see #setStatistics(Statistics)
|
||||||
*/
|
*/
|
||||||
Statistics statistics();
|
Statistics statistics();
|
||||||
|
175
java/src/main/java/org/rocksdb/OptimisticTransactionDB.java
Normal file
175
java/src/main/java/org/rocksdb/OptimisticTransactionDB.java
Normal file
@ -0,0 +1,175 @@
|
|||||||
|
// 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 java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Database with Transaction support.
|
||||||
|
*/
|
||||||
|
public class OptimisticTransactionDB extends RocksDB
|
||||||
|
implements TransactionalDB<OptimisticTransactionOptions> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Private constructor.
|
||||||
|
*
|
||||||
|
* @param nativeHandle The native handle of the C++ OptimisticTransactionDB
|
||||||
|
* object
|
||||||
|
*/
|
||||||
|
private OptimisticTransactionDB(final long nativeHandle) {
|
||||||
|
super(nativeHandle);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Open an OptimisticTransactionDB similar to
|
||||||
|
* {@link RocksDB#open(Options, String)}.
|
||||||
|
*
|
||||||
|
* @param options {@link org.rocksdb.Options} instance.
|
||||||
|
* @param path the path to the rocksdb.
|
||||||
|
*
|
||||||
|
* @return a {@link OptimisticTransactionDB} instance on success, null if the
|
||||||
|
* specified {@link OptimisticTransactionDB} can not be opened.
|
||||||
|
*
|
||||||
|
* @throws RocksDBException if an error occurs whilst opening the database.
|
||||||
|
*/
|
||||||
|
public static OptimisticTransactionDB open(final Options options,
|
||||||
|
final String path) throws RocksDBException {
|
||||||
|
final OptimisticTransactionDB otdb = new OptimisticTransactionDB(open(
|
||||||
|
options.nativeHandle_, path));
|
||||||
|
|
||||||
|
// when non-default Options is used, keeping an Options reference
|
||||||
|
// in RocksDB can prevent Java to GC during the life-time of
|
||||||
|
// the currently-created RocksDB.
|
||||||
|
otdb.storeOptionsInstance(options);
|
||||||
|
|
||||||
|
return otdb;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Open an OptimisticTransactionDB similar to
|
||||||
|
* {@link RocksDB#open(DBOptions, String, List, List)}.
|
||||||
|
*
|
||||||
|
* @param dbOptions {@link org.rocksdb.DBOptions} instance.
|
||||||
|
* @param path the path to the rocksdb.
|
||||||
|
* @param columnFamilyDescriptors list of column family descriptors
|
||||||
|
* @param columnFamilyHandles will be filled with ColumnFamilyHandle instances
|
||||||
|
*
|
||||||
|
* @return a {@link OptimisticTransactionDB} instance on success, null if the
|
||||||
|
* specified {@link OptimisticTransactionDB} can not be opened.
|
||||||
|
*
|
||||||
|
* @throws RocksDBException if an error occurs whilst opening the database.
|
||||||
|
*/
|
||||||
|
public static OptimisticTransactionDB open(final DBOptions dbOptions,
|
||||||
|
final String path,
|
||||||
|
final List<ColumnFamilyDescriptor> columnFamilyDescriptors,
|
||||||
|
final List<ColumnFamilyHandle> columnFamilyHandles)
|
||||||
|
throws RocksDBException {
|
||||||
|
|
||||||
|
final byte[][] cfNames = new byte[columnFamilyDescriptors.size()][];
|
||||||
|
final long[] cfOptionHandles = new long[columnFamilyDescriptors.size()];
|
||||||
|
for (int i = 0; i < columnFamilyDescriptors.size(); i++) {
|
||||||
|
final ColumnFamilyDescriptor cfDescriptor = columnFamilyDescriptors
|
||||||
|
.get(i);
|
||||||
|
cfNames[i] = cfDescriptor.columnFamilyName();
|
||||||
|
cfOptionHandles[i] = cfDescriptor.columnFamilyOptions().nativeHandle_;
|
||||||
|
}
|
||||||
|
|
||||||
|
final long[] handles = open(dbOptions.nativeHandle_, path, cfNames,
|
||||||
|
cfOptionHandles);
|
||||||
|
final OptimisticTransactionDB otdb =
|
||||||
|
new OptimisticTransactionDB(handles[0]);
|
||||||
|
|
||||||
|
// when non-default Options is used, keeping an Options reference
|
||||||
|
// in RocksDB can prevent Java to GC during the life-time of
|
||||||
|
// the currently-created RocksDB.
|
||||||
|
otdb.storeOptionsInstance(dbOptions);
|
||||||
|
|
||||||
|
for (int i = 1; i < handles.length; i++) {
|
||||||
|
columnFamilyHandles.add(new ColumnFamilyHandle(otdb, handles[i]));
|
||||||
|
}
|
||||||
|
|
||||||
|
return otdb;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Transaction beginTransaction(final WriteOptions writeOptions) {
|
||||||
|
return new Transaction(this, beginTransaction(nativeHandle_,
|
||||||
|
writeOptions.nativeHandle_));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Transaction beginTransaction(final WriteOptions writeOptions,
|
||||||
|
final OptimisticTransactionOptions optimisticTransactionOptions) {
|
||||||
|
return new Transaction(this, beginTransaction(nativeHandle_,
|
||||||
|
writeOptions.nativeHandle_,
|
||||||
|
optimisticTransactionOptions.nativeHandle_));
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(AR) consider having beingTransaction(... oldTransaction) set a
|
||||||
|
// reference count inside Transaction, so that we can always call
|
||||||
|
// Transaction#close but the object is only disposed when there are as many
|
||||||
|
// closes as beginTransaction. Makes the try-with-resources paradigm easier for
|
||||||
|
// java developers
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Transaction beginTransaction(final WriteOptions writeOptions,
|
||||||
|
final Transaction oldTransaction) {
|
||||||
|
final long jtxn_handle = beginTransaction_withOld(nativeHandle_,
|
||||||
|
writeOptions.nativeHandle_, oldTransaction.nativeHandle_);
|
||||||
|
|
||||||
|
// RocksJava relies on the assumption that
|
||||||
|
// we do not allocate a new Transaction object
|
||||||
|
// when providing an old_txn
|
||||||
|
assert(jtxn_handle == oldTransaction.nativeHandle_);
|
||||||
|
|
||||||
|
return oldTransaction;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Transaction beginTransaction(final WriteOptions writeOptions,
|
||||||
|
final OptimisticTransactionOptions optimisticTransactionOptions,
|
||||||
|
final Transaction oldTransaction) {
|
||||||
|
final long jtxn_handle = beginTransaction_withOld(nativeHandle_,
|
||||||
|
writeOptions.nativeHandle_, optimisticTransactionOptions.nativeHandle_,
|
||||||
|
oldTransaction.nativeHandle_);
|
||||||
|
|
||||||
|
// RocksJava relies on the assumption that
|
||||||
|
// we do not allocate a new Transaction object
|
||||||
|
// when providing an old_txn
|
||||||
|
assert(jtxn_handle == oldTransaction.nativeHandle_);
|
||||||
|
|
||||||
|
return oldTransaction;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the underlying database that was opened.
|
||||||
|
*
|
||||||
|
* @return The underlying database that was opened.
|
||||||
|
*/
|
||||||
|
public RocksDB getBaseDB() {
|
||||||
|
final RocksDB db = new RocksDB(getBaseDB(nativeHandle_));
|
||||||
|
db.disOwnNativeHandle();
|
||||||
|
return db;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static native long open(final long optionsHandle,
|
||||||
|
final String path) throws RocksDBException;
|
||||||
|
protected static native long[] open(final long handle, final String path,
|
||||||
|
final byte[][] columnFamilyNames, final long[] columnFamilyOptions);
|
||||||
|
private native long beginTransaction(final long handle,
|
||||||
|
final long writeOptionsHandle);
|
||||||
|
private native long beginTransaction(final long handle,
|
||||||
|
final long writeOptionsHandle,
|
||||||
|
final long optimisticTransactionOptionsHandle);
|
||||||
|
private native long beginTransaction_withOld(final long handle,
|
||||||
|
final long writeOptionsHandle, final long oldTransactionHandle);
|
||||||
|
private native long beginTransaction_withOld(final long handle,
|
||||||
|
final long writeOptionsHandle,
|
||||||
|
final long optimisticTransactionOptionsHandle,
|
||||||
|
final long oldTransactionHandle);
|
||||||
|
private native long getBaseDB(final long handle);
|
||||||
|
@Override protected final native void disposeInternal(final long handle);
|
||||||
|
}
|
@ -0,0 +1,53 @@
|
|||||||
|
// 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;
|
||||||
|
|
||||||
|
public class OptimisticTransactionOptions extends RocksObject
|
||||||
|
implements TransactionalOptions {
|
||||||
|
|
||||||
|
public OptimisticTransactionOptions() {
|
||||||
|
super(newOptimisticTransactionOptions());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isSetSnapshot() {
|
||||||
|
assert(isOwningHandle());
|
||||||
|
return isSetSnapshot(nativeHandle_);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public OptimisticTransactionOptions setSetSnapshot(
|
||||||
|
final boolean setSnapshot) {
|
||||||
|
assert(isOwningHandle());
|
||||||
|
setSetSnapshot(nativeHandle_, setSnapshot);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Should be set if the DB has a non-default comparator.
|
||||||
|
* See comment in
|
||||||
|
* {@link WriteBatchWithIndex#WriteBatchWithIndex(AbstractComparator, int, boolean)}
|
||||||
|
* constructor.
|
||||||
|
*
|
||||||
|
* @param comparator The comparator to use for the transaction.
|
||||||
|
*
|
||||||
|
* @return this OptimisticTransactionOptions instance
|
||||||
|
*/
|
||||||
|
public OptimisticTransactionOptions setComparator(
|
||||||
|
final AbstractComparator<? extends AbstractSlice<?>> comparator) {
|
||||||
|
assert(isOwningHandle());
|
||||||
|
setComparator(nativeHandle_, comparator.nativeHandle_);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
private native static long newOptimisticTransactionOptions();
|
||||||
|
private native boolean isSetSnapshot(final long handle);
|
||||||
|
private native void setSetSnapshot(final long handle,
|
||||||
|
final boolean setSnapshot);
|
||||||
|
private native void setComparator(final long handle,
|
||||||
|
final long comparatorHandle);
|
||||||
|
@Override protected final native void disposeInternal(final long handle);
|
||||||
|
}
|
@ -435,7 +435,7 @@ public class RocksDB extends RocksObject {
|
|||||||
path));
|
path));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void storeOptionsInstance(DBOptionsInterface options) {
|
protected void storeOptionsInstance(DBOptionsInterface options) {
|
||||||
options_ = options;
|
options_ = options;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1683,7 +1683,7 @@ public class RocksDB extends RocksObject {
|
|||||||
* @return The handle of the default column family
|
* @return The handle of the default column family
|
||||||
*/
|
*/
|
||||||
public ColumnFamilyHandle getDefaultColumnFamily() {
|
public ColumnFamilyHandle getDefaultColumnFamily() {
|
||||||
ColumnFamilyHandle cfHandle = new ColumnFamilyHandle(this,
|
final ColumnFamilyHandle cfHandle = new ColumnFamilyHandle(this,
|
||||||
getDefaultColumnFamily(nativeHandle_));
|
getDefaultColumnFamily(nativeHandle_));
|
||||||
cfHandle.disOwnNativeHandle();
|
cfHandle.disOwnNativeHandle();
|
||||||
return cfHandle;
|
return cfHandle;
|
||||||
@ -2359,8 +2359,9 @@ public class RocksDB extends RocksObject {
|
|||||||
final long[] columnFamilyHandles, final long readOptHandle)
|
final long[] columnFamilyHandles, final long readOptHandle)
|
||||||
throws RocksDBException;
|
throws RocksDBException;
|
||||||
protected native long getSnapshot(long nativeHandle);
|
protected native long getSnapshot(long nativeHandle);
|
||||||
protected native void releaseSnapshot(long nativeHandle, long snapshotHandle);
|
protected native void releaseSnapshot(
|
||||||
@Override protected final native void disposeInternal(final long handle);
|
long nativeHandle, long snapshotHandle);
|
||||||
|
@Override protected native void disposeInternal(final long handle);
|
||||||
private native long getDefaultColumnFamily(long handle);
|
private native long getDefaultColumnFamily(long handle);
|
||||||
private native long createColumnFamily(final long handle,
|
private native long createColumnFamily(final long handle,
|
||||||
final byte[] columnFamilyName, final long columnFamilyOptions)
|
final byte[] columnFamilyName, final long columnFamilyOptions)
|
||||||
|
@ -11,6 +11,10 @@ package org.rocksdb;
|
|||||||
public class Snapshot extends RocksObject {
|
public class Snapshot extends RocksObject {
|
||||||
Snapshot(final long nativeHandle) {
|
Snapshot(final long nativeHandle) {
|
||||||
super(nativeHandle);
|
super(nativeHandle);
|
||||||
|
|
||||||
|
// The pointer to the snapshot is always released
|
||||||
|
// by the database instance.
|
||||||
|
disOwnNativeHandle();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -20,17 +24,17 @@ public class Snapshot extends RocksObject {
|
|||||||
* this snapshot.
|
* this snapshot.
|
||||||
*/
|
*/
|
||||||
public long getSequenceNumber() {
|
public long getSequenceNumber() {
|
||||||
assert(isOwningHandle());
|
|
||||||
return getSequenceNumber(nativeHandle_);
|
return getSequenceNumber(nativeHandle_);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected final void disposeInternal(final long handle) {
|
||||||
/**
|
/**
|
||||||
* Dont release C++ Snapshot pointer. The pointer
|
* Nothing to release, we never own the pointer for a
|
||||||
|
* Snapshot. The pointer
|
||||||
* to the snapshot is released by the database
|
* to the snapshot is released by the database
|
||||||
* instance.
|
* instance.
|
||||||
*/
|
*/
|
||||||
@Override
|
|
||||||
protected final void disposeInternal(final long handle) {
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private native long getSequenceNumber(long handle);
|
private native long getSequenceNumber(long handle);
|
||||||
|
@ -117,6 +117,8 @@ public class Statistics extends RocksObject {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Resets all ticker and histogram stats.
|
* Resets all ticker and histogram stats.
|
||||||
|
*
|
||||||
|
* @throws RocksDBException if an error occurs when resetting the statistics.
|
||||||
*/
|
*/
|
||||||
public void reset() throws RocksDBException {
|
public void reset() throws RocksDBException {
|
||||||
assert(isOwningHandle());
|
assert(isOwningHandle());
|
||||||
@ -126,6 +128,7 @@ public class Statistics extends RocksObject {
|
|||||||
/**
|
/**
|
||||||
* String representation of the statistic object.
|
* String representation of the statistic object.
|
||||||
*/
|
*/
|
||||||
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
assert(isOwningHandle());
|
assert(isOwningHandle());
|
||||||
return toString(nativeHandle_);
|
return toString(nativeHandle_);
|
||||||
|
1761
java/src/main/java/org/rocksdb/Transaction.java
Normal file
1761
java/src/main/java/org/rocksdb/Transaction.java
Normal file
File diff suppressed because it is too large
Load Diff
354
java/src/main/java/org/rocksdb/TransactionDB.java
Normal file
354
java/src/main/java/org/rocksdb/TransactionDB.java
Normal file
@ -0,0 +1,354 @@
|
|||||||
|
// 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 java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Database with Transaction support
|
||||||
|
*/
|
||||||
|
public class TransactionDB extends RocksDB
|
||||||
|
implements TransactionalDB<TransactionOptions> {
|
||||||
|
|
||||||
|
private TransactionDBOptions transactionDbOptions_;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Private constructor.
|
||||||
|
*
|
||||||
|
* @param nativeHandle The native handle of the C++ TransactionDB object
|
||||||
|
*/
|
||||||
|
private TransactionDB(final long nativeHandle) {
|
||||||
|
super(nativeHandle);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Open a TransactionDB, similar to {@link RocksDB#open(Options, String)}.
|
||||||
|
*
|
||||||
|
* @param options {@link org.rocksdb.Options} instance.
|
||||||
|
* @param transactionDbOptions {@link org.rocksdb.TransactionDBOptions}
|
||||||
|
* instance.
|
||||||
|
* @param path the path to the rocksdb.
|
||||||
|
*
|
||||||
|
* @return a {@link TransactionDB} instance on success, null if the specified
|
||||||
|
* {@link TransactionDB} can not be opened.
|
||||||
|
*
|
||||||
|
* @throws RocksDBException if an error occurs whilst opening the database.
|
||||||
|
*/
|
||||||
|
public static TransactionDB open(final Options options,
|
||||||
|
final TransactionDBOptions transactionDbOptions, final String path)
|
||||||
|
throws RocksDBException {
|
||||||
|
final TransactionDB tdb = new TransactionDB(open(options.nativeHandle_,
|
||||||
|
transactionDbOptions.nativeHandle_, path));
|
||||||
|
|
||||||
|
// when non-default Options is used, keeping an Options reference
|
||||||
|
// in RocksDB can prevent Java to GC during the life-time of
|
||||||
|
// the currently-created RocksDB.
|
||||||
|
tdb.storeOptionsInstance(options);
|
||||||
|
tdb.storeTransactionDbOptions(transactionDbOptions);
|
||||||
|
|
||||||
|
return tdb;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Open a TransactionDB, similar to
|
||||||
|
* {@link RocksDB#open(DBOptions, String, List, List)}.
|
||||||
|
*
|
||||||
|
* @param dbOptions {@link org.rocksdb.DBOptions} instance.
|
||||||
|
* @param transactionDbOptions {@link org.rocksdb.TransactionDBOptions}
|
||||||
|
* instance.
|
||||||
|
* @param path the path to the rocksdb.
|
||||||
|
* @param columnFamilyDescriptors list of column family descriptors
|
||||||
|
* @param columnFamilyHandles will be filled with ColumnFamilyHandle instances
|
||||||
|
*
|
||||||
|
* @return a {@link TransactionDB} instance on success, null if the specified
|
||||||
|
* {@link TransactionDB} can not be opened.
|
||||||
|
*
|
||||||
|
* @throws RocksDBException if an error occurs whilst opening the database.
|
||||||
|
*/
|
||||||
|
public static TransactionDB open(final DBOptions dbOptions,
|
||||||
|
final TransactionDBOptions transactionDbOptions,
|
||||||
|
final String path,
|
||||||
|
final List<ColumnFamilyDescriptor> columnFamilyDescriptors,
|
||||||
|
final List<ColumnFamilyHandle> columnFamilyHandles)
|
||||||
|
throws RocksDBException {
|
||||||
|
|
||||||
|
final byte[][] cfNames = new byte[columnFamilyDescriptors.size()][];
|
||||||
|
final long[] cfOptionHandles = new long[columnFamilyDescriptors.size()];
|
||||||
|
for (int i = 0; i < columnFamilyDescriptors.size(); i++) {
|
||||||
|
final ColumnFamilyDescriptor cfDescriptor = columnFamilyDescriptors
|
||||||
|
.get(i);
|
||||||
|
cfNames[i] = cfDescriptor.columnFamilyName();
|
||||||
|
cfOptionHandles[i] = cfDescriptor.columnFamilyOptions().nativeHandle_;
|
||||||
|
}
|
||||||
|
|
||||||
|
final long[] handles = open(dbOptions.nativeHandle_,
|
||||||
|
transactionDbOptions.nativeHandle_, path, cfNames, cfOptionHandles);
|
||||||
|
final TransactionDB tdb = new TransactionDB(handles[0]);
|
||||||
|
|
||||||
|
// when non-default Options is used, keeping an Options reference
|
||||||
|
// in RocksDB can prevent Java to GC during the life-time of
|
||||||
|
// the currently-created RocksDB.
|
||||||
|
tdb.storeOptionsInstance(dbOptions);
|
||||||
|
tdb.storeTransactionDbOptions(transactionDbOptions);
|
||||||
|
|
||||||
|
for (int i = 1; i < handles.length; i++) {
|
||||||
|
columnFamilyHandles.add(new ColumnFamilyHandle(tdb, handles[i]));
|
||||||
|
}
|
||||||
|
|
||||||
|
return tdb;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Transaction beginTransaction(final WriteOptions writeOptions) {
|
||||||
|
return new Transaction(this, beginTransaction(nativeHandle_,
|
||||||
|
writeOptions.nativeHandle_));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Transaction beginTransaction(final WriteOptions writeOptions,
|
||||||
|
final TransactionOptions transactionOptions) {
|
||||||
|
return new Transaction(this, beginTransaction(nativeHandle_,
|
||||||
|
writeOptions.nativeHandle_, transactionOptions.nativeHandle_));
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(AR) consider having beingTransaction(... oldTransaction) set a
|
||||||
|
// reference count inside Transaction, so that we can always call
|
||||||
|
// Transaction#close but the object is only disposed when there are as many
|
||||||
|
// closes as beginTransaction. Makes the try-with-resources paradigm easier for
|
||||||
|
// java developers
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Transaction beginTransaction(final WriteOptions writeOptions,
|
||||||
|
final Transaction oldTransaction) {
|
||||||
|
final long jtxnHandle = beginTransaction_withOld(nativeHandle_,
|
||||||
|
writeOptions.nativeHandle_, oldTransaction.nativeHandle_);
|
||||||
|
|
||||||
|
// RocksJava relies on the assumption that
|
||||||
|
// we do not allocate a new Transaction object
|
||||||
|
// when providing an old_txn
|
||||||
|
assert(jtxnHandle == oldTransaction.nativeHandle_);
|
||||||
|
|
||||||
|
return oldTransaction;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Transaction beginTransaction(final WriteOptions writeOptions,
|
||||||
|
final TransactionOptions transactionOptions,
|
||||||
|
final Transaction oldTransaction) {
|
||||||
|
final long jtxn_handle = beginTransaction_withOld(nativeHandle_,
|
||||||
|
writeOptions.nativeHandle_, transactionOptions.nativeHandle_,
|
||||||
|
oldTransaction.nativeHandle_);
|
||||||
|
|
||||||
|
// RocksJava relies on the assumption that
|
||||||
|
// we do not allocate a new Transaction object
|
||||||
|
// when providing an old_txn
|
||||||
|
assert(jtxn_handle == oldTransaction.nativeHandle_);
|
||||||
|
|
||||||
|
return oldTransaction;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Transaction getTransactionByName(final String transactionName) {
|
||||||
|
final long jtxnHandle = getTransactionByName(nativeHandle_, transactionName);
|
||||||
|
if(jtxnHandle == 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
final Transaction txn = new Transaction(this, jtxnHandle);
|
||||||
|
|
||||||
|
// this instance doesn't own the underlying C++ object
|
||||||
|
txn.disOwnNativeHandle();
|
||||||
|
|
||||||
|
return txn;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Transaction> getAllPreparedTransactions() {
|
||||||
|
final long[] jtxnHandles = getAllPreparedTransactions(nativeHandle_);
|
||||||
|
|
||||||
|
final List<Transaction> txns = new ArrayList<>();
|
||||||
|
for(final long jtxnHandle : jtxnHandles) {
|
||||||
|
final Transaction txn = new Transaction(this, jtxnHandle);
|
||||||
|
|
||||||
|
// this instance doesn't own the underlying C++ object
|
||||||
|
txn.disOwnNativeHandle();
|
||||||
|
|
||||||
|
txns.add(txn);
|
||||||
|
}
|
||||||
|
return txns;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class KeyLockInfo {
|
||||||
|
private final String key;
|
||||||
|
private final long[] transactionIDs;
|
||||||
|
private final boolean exclusive;
|
||||||
|
|
||||||
|
public KeyLockInfo(final String key, final long transactionIDs[],
|
||||||
|
final boolean exclusive) {
|
||||||
|
this.key = key;
|
||||||
|
this.transactionIDs = transactionIDs;
|
||||||
|
this.exclusive = exclusive;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the key.
|
||||||
|
*
|
||||||
|
* @return the key
|
||||||
|
*/
|
||||||
|
public String getKey() {
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the Transaction IDs.
|
||||||
|
*
|
||||||
|
* @return the Transaction IDs.
|
||||||
|
*/
|
||||||
|
public long[] getTransactionIDs() {
|
||||||
|
return transactionIDs;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the Lock status.
|
||||||
|
*
|
||||||
|
* @return true if the lock is exclusive, false if the lock is shared.
|
||||||
|
*/
|
||||||
|
public boolean isExclusive() {
|
||||||
|
return exclusive;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns map of all locks held.
|
||||||
|
*
|
||||||
|
* @return a map of all the locks held.
|
||||||
|
*/
|
||||||
|
public Map<Long, KeyLockInfo> getLockStatusData() {
|
||||||
|
return getLockStatusData(nativeHandle_);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called from C++ native method {@link #getDeadlockInfoBuffer(long)}
|
||||||
|
* to construct a DeadlockInfo object.
|
||||||
|
*
|
||||||
|
* @param transactionID The transaction id
|
||||||
|
* @param columnFamilyId The id of the {@link ColumnFamilyHandle}
|
||||||
|
* @param waitingKey the key that we are waiting on
|
||||||
|
* @param exclusive true if the lock is exclusive, false if the lock is shared
|
||||||
|
*
|
||||||
|
* @return The waiting transactions
|
||||||
|
*/
|
||||||
|
private DeadlockInfo newDeadlockInfo(
|
||||||
|
final long transactionID, final long columnFamilyId,
|
||||||
|
final String waitingKey, final boolean exclusive) {
|
||||||
|
return new DeadlockInfo(transactionID, columnFamilyId,
|
||||||
|
waitingKey, exclusive);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class DeadlockInfo {
|
||||||
|
private final long transactionID;
|
||||||
|
private final long columnFamilyId;
|
||||||
|
private final String waitingKey;
|
||||||
|
private final boolean exclusive;
|
||||||
|
|
||||||
|
private DeadlockInfo(final long transactionID, final long columnFamilyId,
|
||||||
|
final String waitingKey, final boolean exclusive) {
|
||||||
|
this.transactionID = transactionID;
|
||||||
|
this.columnFamilyId = columnFamilyId;
|
||||||
|
this.waitingKey = waitingKey;
|
||||||
|
this.exclusive = exclusive;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the Transaction ID.
|
||||||
|
*
|
||||||
|
* @return the transaction ID
|
||||||
|
*/
|
||||||
|
public long getTransactionID() {
|
||||||
|
return transactionID;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the Column Family ID.
|
||||||
|
*
|
||||||
|
* @return The column family ID
|
||||||
|
*/
|
||||||
|
public long getColumnFamilyId() {
|
||||||
|
return columnFamilyId;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the key that we are waiting on.
|
||||||
|
*
|
||||||
|
* @return the key that we are waiting on
|
||||||
|
*/
|
||||||
|
public String getWaitingKey() {
|
||||||
|
return waitingKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the Lock status.
|
||||||
|
*
|
||||||
|
* @return true if the lock is exclusive, false if the lock is shared.
|
||||||
|
*/
|
||||||
|
public boolean isExclusive() {
|
||||||
|
return exclusive;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class DeadlockPath {
|
||||||
|
final DeadlockInfo[] path;
|
||||||
|
final boolean limitExceeded;
|
||||||
|
|
||||||
|
public DeadlockPath(final DeadlockInfo[] path, final boolean limitExceeded) {
|
||||||
|
this.path = path;
|
||||||
|
this.limitExceeded = limitExceeded;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isEmpty() {
|
||||||
|
return path.length == 0 && !limitExceeded;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public DeadlockPath[] getDeadlockInfoBuffer() {
|
||||||
|
return getDeadlockInfoBuffer(nativeHandle_);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDeadlockInfoBufferSize(final int targetSize) {
|
||||||
|
setDeadlockInfoBufferSize(nativeHandle_, targetSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void storeTransactionDbOptions(
|
||||||
|
final TransactionDBOptions transactionDbOptions) {
|
||||||
|
this.transactionDbOptions_ = transactionDbOptions;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static native long open(final long optionsHandle,
|
||||||
|
final long transactionDbOptionsHandle, final String path)
|
||||||
|
throws RocksDBException;
|
||||||
|
private static native long[] open(final long dbOptionsHandle,
|
||||||
|
final long transactionDbOptionsHandle, final String path,
|
||||||
|
final byte[][] columnFamilyNames, final long[] columnFamilyOptions);
|
||||||
|
private native long beginTransaction(final long handle,
|
||||||
|
final long writeOptionsHandle);
|
||||||
|
private native long beginTransaction(final long handle,
|
||||||
|
final long writeOptionsHandle, final long transactionOptionsHandle);
|
||||||
|
private native long beginTransaction_withOld(final long handle,
|
||||||
|
final long writeOptionsHandle, final long oldTransactionHandle);
|
||||||
|
private native long beginTransaction_withOld(final long handle,
|
||||||
|
final long writeOptionsHandle, final long transactionOptionsHandle,
|
||||||
|
final long oldTransactionHandle);
|
||||||
|
private native long getTransactionByName(final long handle,
|
||||||
|
final String name);
|
||||||
|
private native long[] getAllPreparedTransactions(final long handle);
|
||||||
|
private native Map<Long, KeyLockInfo> getLockStatusData(
|
||||||
|
final long handle);
|
||||||
|
private native DeadlockPath[] getDeadlockInfoBuffer(final long handle);
|
||||||
|
private native void setDeadlockInfoBufferSize(final long handle,
|
||||||
|
final int targetSize);
|
||||||
|
@Override protected final native void disposeInternal(final long handle);
|
||||||
|
}
|
217
java/src/main/java/org/rocksdb/TransactionDBOptions.java
Normal file
217
java/src/main/java/org/rocksdb/TransactionDBOptions.java
Normal file
@ -0,0 +1,217 @@
|
|||||||
|
// 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;
|
||||||
|
|
||||||
|
public class TransactionDBOptions extends RocksObject {
|
||||||
|
|
||||||
|
public TransactionDBOptions() {
|
||||||
|
super(newTransactionDBOptions());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specifies the maximum number of keys that can be locked at the same time
|
||||||
|
* per column family.
|
||||||
|
*
|
||||||
|
* If the number of locked keys is greater than {@link #getMaxNumLocks()},
|
||||||
|
* transaction writes (or GetForUpdate) will return an error.
|
||||||
|
*
|
||||||
|
* @return The maximum number of keys that can be locked
|
||||||
|
*/
|
||||||
|
public long getMaxNumLocks() {
|
||||||
|
assert(isOwningHandle());
|
||||||
|
return getMaxNumLocks(nativeHandle_);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specifies the maximum number of keys that can be locked at the same time
|
||||||
|
* per column family.
|
||||||
|
*
|
||||||
|
* If the number of locked keys is greater than {@link #getMaxNumLocks()},
|
||||||
|
* transaction writes (or GetForUpdate) will return an error.
|
||||||
|
*
|
||||||
|
* @param maxNumLocks The maximum number of keys that can be locked;
|
||||||
|
* If this value is not positive, no limit will be enforced.
|
||||||
|
*
|
||||||
|
* @return this TransactionDBOptions instance
|
||||||
|
*/
|
||||||
|
public TransactionDBOptions setMaxNumLocks(final long maxNumLocks) {
|
||||||
|
assert(isOwningHandle());
|
||||||
|
setMaxNumLocks(nativeHandle_, maxNumLocks);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The number of sub-tables per lock table (per column family)
|
||||||
|
*
|
||||||
|
* @return The number of sub-tables
|
||||||
|
*/
|
||||||
|
public long getNumStripes() {
|
||||||
|
assert(isOwningHandle());
|
||||||
|
return getNumStripes(nativeHandle_);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Increasing this value will increase the concurrency by dividing the lock
|
||||||
|
* table (per column family) into more sub-tables, each with their own
|
||||||
|
* separate mutex.
|
||||||
|
*
|
||||||
|
* Default: 16
|
||||||
|
*
|
||||||
|
* @param numStripes The number of sub-tables
|
||||||
|
*
|
||||||
|
* @return this TransactionDBOptions instance
|
||||||
|
*/
|
||||||
|
public TransactionDBOptions setNumStripes(final long numStripes) {
|
||||||
|
assert(isOwningHandle());
|
||||||
|
setNumStripes(nativeHandle_, numStripes);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The default wait timeout in milliseconds when
|
||||||
|
* a transaction attempts to lock a key if not specified by
|
||||||
|
* {@link TransactionOptions#setLockTimeout(long)}
|
||||||
|
*
|
||||||
|
* If 0, no waiting is done if a lock cannot instantly be acquired.
|
||||||
|
* If negative, there is no timeout.
|
||||||
|
*
|
||||||
|
* @return the default wait timeout in milliseconds
|
||||||
|
*/
|
||||||
|
public long getTransactionLockTimeout() {
|
||||||
|
assert(isOwningHandle());
|
||||||
|
return getTransactionLockTimeout(nativeHandle_);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If positive, specifies the default wait timeout in milliseconds when
|
||||||
|
* a transaction attempts to lock a key if not specified by
|
||||||
|
* {@link TransactionOptions#setLockTimeout(long)}
|
||||||
|
*
|
||||||
|
* If 0, no waiting is done if a lock cannot instantly be acquired.
|
||||||
|
* If negative, there is no timeout. Not using a timeout is not recommended
|
||||||
|
* as it can lead to deadlocks. Currently, there is no deadlock-detection to
|
||||||
|
* recover from a deadlock.
|
||||||
|
*
|
||||||
|
* Default: 1000
|
||||||
|
*
|
||||||
|
* @param transactionLockTimeout the default wait timeout in milliseconds
|
||||||
|
*
|
||||||
|
* @return this TransactionDBOptions instance
|
||||||
|
*/
|
||||||
|
public TransactionDBOptions setTransactionLockTimeout(
|
||||||
|
final long transactionLockTimeout) {
|
||||||
|
assert(isOwningHandle());
|
||||||
|
setTransactionLockTimeout(nativeHandle_, transactionLockTimeout);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The wait timeout in milliseconds when writing a key
|
||||||
|
* OUTSIDE of a transaction (ie by calling {@link RocksDB#put},
|
||||||
|
* {@link RocksDB#merge}, {@link RocksDB#remove} or {@link RocksDB#write}
|
||||||
|
* directly).
|
||||||
|
*
|
||||||
|
* If 0, no waiting is done if a lock cannot instantly be acquired.
|
||||||
|
* If negative, there is no timeout and will block indefinitely when acquiring
|
||||||
|
* a lock.
|
||||||
|
*
|
||||||
|
* @return the timeout in milliseconds when writing a key OUTSIDE of a
|
||||||
|
* transaction
|
||||||
|
*/
|
||||||
|
public long getDefaultLockTimeout() {
|
||||||
|
assert(isOwningHandle());
|
||||||
|
return getDefaultLockTimeout(nativeHandle_);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If positive, specifies the wait timeout in milliseconds when writing a key
|
||||||
|
* OUTSIDE of a transaction (ie by calling {@link RocksDB#put},
|
||||||
|
* {@link RocksDB#merge}, {@link RocksDB#remove} or {@link RocksDB#write}
|
||||||
|
* directly).
|
||||||
|
*
|
||||||
|
* If 0, no waiting is done if a lock cannot instantly be acquired.
|
||||||
|
* If negative, there is no timeout and will block indefinitely when acquiring
|
||||||
|
* a lock.
|
||||||
|
*
|
||||||
|
* Not using a timeout can lead to deadlocks. Currently, there
|
||||||
|
* is no deadlock-detection to recover from a deadlock. While DB writes
|
||||||
|
* cannot deadlock with other DB writes, they can deadlock with a transaction.
|
||||||
|
* A negative timeout should only be used if all transactions have a small
|
||||||
|
* expiration set.
|
||||||
|
*
|
||||||
|
* Default: 1000
|
||||||
|
*
|
||||||
|
* @param defaultLockTimeout the timeout in milliseconds when writing a key
|
||||||
|
* OUTSIDE of a transaction
|
||||||
|
* @return this TransactionDBOptions instance
|
||||||
|
*/
|
||||||
|
public TransactionDBOptions setDefaultLockTimeout(
|
||||||
|
final long defaultLockTimeout) {
|
||||||
|
assert(isOwningHandle());
|
||||||
|
setDefaultLockTimeout(nativeHandle_, defaultLockTimeout);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
// /**
|
||||||
|
// * If set, the {@link TransactionDB} will use this implementation of a mutex
|
||||||
|
// * and condition variable for all transaction locking instead of the default
|
||||||
|
// * mutex/condvar implementation.
|
||||||
|
// *
|
||||||
|
// * @param transactionDbMutexFactory the mutex factory for the transactions
|
||||||
|
// *
|
||||||
|
// * @return this TransactionDBOptions instance
|
||||||
|
// */
|
||||||
|
// public TransactionDBOptions setCustomMutexFactory(
|
||||||
|
// final TransactionDBMutexFactory transactionDbMutexFactory) {
|
||||||
|
//
|
||||||
|
// }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The policy for when to write the data into the DB. The default policy is to
|
||||||
|
* write only the committed data {@link TxnDBWritePolicy#WRITE_COMMITTED}.
|
||||||
|
* The data could be written before the commit phase. The DB then needs to
|
||||||
|
* provide the mechanisms to tell apart committed from uncommitted data.
|
||||||
|
*
|
||||||
|
* @return The write policy.
|
||||||
|
*/
|
||||||
|
public TxnDBWritePolicy getWritePolicy() {
|
||||||
|
assert(isOwningHandle());
|
||||||
|
return TxnDBWritePolicy.getTxnDBWritePolicy(getWritePolicy(nativeHandle_));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The policy for when to write the data into the DB. The default policy is to
|
||||||
|
* write only the committed data {@link TxnDBWritePolicy#WRITE_COMMITTED}.
|
||||||
|
* The data could be written before the commit phase. The DB then needs to
|
||||||
|
* provide the mechanisms to tell apart committed from uncommitted data.
|
||||||
|
*
|
||||||
|
* @param writePolicy The write policy.
|
||||||
|
*
|
||||||
|
* @return this TransactionDBOptions instance
|
||||||
|
*/
|
||||||
|
public TransactionDBOptions setWritePolicy(
|
||||||
|
final TxnDBWritePolicy writePolicy) {
|
||||||
|
assert(isOwningHandle());
|
||||||
|
setWritePolicy(nativeHandle_, writePolicy.getValue());
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
private native static long newTransactionDBOptions();
|
||||||
|
private native long getMaxNumLocks(final long handle);
|
||||||
|
private native void setMaxNumLocks(final long handle,
|
||||||
|
final long maxNumLocks);
|
||||||
|
private native long getNumStripes(final long handle);
|
||||||
|
private native void setNumStripes(final long handle, final long numStripes);
|
||||||
|
private native long getTransactionLockTimeout(final long handle);
|
||||||
|
private native void setTransactionLockTimeout(final long handle,
|
||||||
|
final long transactionLockTimeout);
|
||||||
|
private native long getDefaultLockTimeout(final long handle);
|
||||||
|
private native void setDefaultLockTimeout(final long handle,
|
||||||
|
final long transactionLockTimeout);
|
||||||
|
private native byte getWritePolicy(final long handle);
|
||||||
|
private native void setWritePolicy(final long handle, final byte writePolicy);
|
||||||
|
@Override protected final native void disposeInternal(final long handle);
|
||||||
|
}
|
189
java/src/main/java/org/rocksdb/TransactionOptions.java
Normal file
189
java/src/main/java/org/rocksdb/TransactionOptions.java
Normal file
@ -0,0 +1,189 @@
|
|||||||
|
// 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;
|
||||||
|
|
||||||
|
public class TransactionOptions extends RocksObject
|
||||||
|
implements TransactionalOptions {
|
||||||
|
|
||||||
|
public TransactionOptions() {
|
||||||
|
super(newTransactionOptions());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isSetSnapshot() {
|
||||||
|
assert(isOwningHandle());
|
||||||
|
return isSetSnapshot(nativeHandle_);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TransactionOptions setSetSnapshot(final boolean setSnapshot) {
|
||||||
|
assert(isOwningHandle());
|
||||||
|
setSetSnapshot(nativeHandle_, setSnapshot);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* True means that before acquiring locks, this transaction will
|
||||||
|
* check if doing so will cause a deadlock. If so, it will return with
|
||||||
|
* {@link Status.Code#Busy}. The user should retry their transaction.
|
||||||
|
*
|
||||||
|
* @return true if a deadlock is detected.
|
||||||
|
*/
|
||||||
|
public boolean isDeadlockDetect() {
|
||||||
|
assert(isOwningHandle());
|
||||||
|
return isDeadlockDetect(nativeHandle_);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Setting to true means that before acquiring locks, this transaction will
|
||||||
|
* check if doing so will cause a deadlock. If so, it will return with
|
||||||
|
* {@link Status.Code#Busy}. The user should retry their transaction.
|
||||||
|
*
|
||||||
|
* @param deadlockDetect true if we should detect deadlocks.
|
||||||
|
*
|
||||||
|
* @return this TransactionOptions instance
|
||||||
|
*/
|
||||||
|
public TransactionOptions setDeadlockDetect(final boolean deadlockDetect) {
|
||||||
|
assert(isOwningHandle());
|
||||||
|
setDeadlockDetect(nativeHandle_, deadlockDetect);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The wait timeout in milliseconds when a transaction attempts to lock a key.
|
||||||
|
*
|
||||||
|
* If 0, no waiting is done if a lock cannot instantly be acquired.
|
||||||
|
* If negative, {@link TransactionDBOptions#getTransactionLockTimeout(long)}
|
||||||
|
* will be used
|
||||||
|
*
|
||||||
|
* @return the lock tiemout in milliseconds
|
||||||
|
*/
|
||||||
|
public long getLockTimeout() {
|
||||||
|
assert(isOwningHandle());
|
||||||
|
return getLockTimeout(nativeHandle_);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If positive, specifies the wait timeout in milliseconds when
|
||||||
|
* a transaction attempts to lock a key.
|
||||||
|
*
|
||||||
|
* If 0, no waiting is done if a lock cannot instantly be acquired.
|
||||||
|
* If negative, {@link TransactionDBOptions#getTransactionLockTimeout(long)}
|
||||||
|
* will be used
|
||||||
|
*
|
||||||
|
* Default: -1
|
||||||
|
*
|
||||||
|
* @param lockTimeout the lock tiemout in milliseconds
|
||||||
|
*
|
||||||
|
* @return this TransactionOptions instance
|
||||||
|
*/
|
||||||
|
public TransactionOptions setLockTimeout(final long lockTimeout) {
|
||||||
|
assert(isOwningHandle());
|
||||||
|
setLockTimeout(nativeHandle_, lockTimeout);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Expiration duration in milliseconds.
|
||||||
|
*
|
||||||
|
* If non-negative, transactions that last longer than this many milliseconds
|
||||||
|
* will fail to commit. If not set, a forgotten transaction that is never
|
||||||
|
* committed, rolled back, or deleted will never relinquish any locks it
|
||||||
|
* holds. This could prevent keys from being written by other writers.
|
||||||
|
*
|
||||||
|
* @return expiration the expiration duration in milliseconds
|
||||||
|
*/
|
||||||
|
public long getExpiration() {
|
||||||
|
assert(isOwningHandle());
|
||||||
|
return getExpiration(nativeHandle_);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Expiration duration in milliseconds.
|
||||||
|
*
|
||||||
|
* If non-negative, transactions that last longer than this many milliseconds
|
||||||
|
* will fail to commit. If not set, a forgotten transaction that is never
|
||||||
|
* committed, rolled back, or deleted will never relinquish any locks it
|
||||||
|
* holds. This could prevent keys from being written by other writers.
|
||||||
|
*
|
||||||
|
* Default: -1
|
||||||
|
*
|
||||||
|
* @param expiration the expiration duration in milliseconds
|
||||||
|
*
|
||||||
|
* @return this TransactionOptions instance
|
||||||
|
*/
|
||||||
|
public TransactionOptions setExpiration(final long expiration) {
|
||||||
|
assert(isOwningHandle());
|
||||||
|
setExpiration(nativeHandle_, expiration);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the number of traversals to make during deadlock detection.
|
||||||
|
*
|
||||||
|
* @return the number of traversals to make during
|
||||||
|
* deadlock detection
|
||||||
|
*/
|
||||||
|
public long getDeadlockDetectDepth() {
|
||||||
|
return getDeadlockDetectDepth(nativeHandle_);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the number of traversals to make during deadlock detection.
|
||||||
|
*
|
||||||
|
* Default: 50
|
||||||
|
*
|
||||||
|
* @param deadlockDetectDepth the the number of traversals to make during
|
||||||
|
* deadlock detection
|
||||||
|
*
|
||||||
|
* @return this TransactionOptions instance
|
||||||
|
*/
|
||||||
|
public TransactionOptions setDeadlockDetectDepth(
|
||||||
|
final long deadlockDetectDepth) {
|
||||||
|
setDeadlockDetectDepth(nativeHandle_, deadlockDetectDepth);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the maximum number of bytes that may be used for the write batch.
|
||||||
|
*
|
||||||
|
* @return the maximum number of bytes, 0 means no limit.
|
||||||
|
*/
|
||||||
|
public long getMaxWriteBatchSize() {
|
||||||
|
return getMaxWriteBatchSize(nativeHandle_);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the maximum number of bytes that may be used for the write batch.
|
||||||
|
*
|
||||||
|
* @param maxWriteBatchSize the maximum number of bytes, 0 means no limit.
|
||||||
|
*
|
||||||
|
* @return this TransactionOptions instance
|
||||||
|
*/
|
||||||
|
public TransactionOptions setMaxWriteBatchSize(final long maxWriteBatchSize) {
|
||||||
|
setMaxWriteBatchSize(nativeHandle_, maxWriteBatchSize);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
private native static long newTransactionOptions();
|
||||||
|
private native boolean isSetSnapshot(final long handle);
|
||||||
|
private native void setSetSnapshot(final long handle,
|
||||||
|
final boolean setSnapshot);
|
||||||
|
private native boolean isDeadlockDetect(final long handle);
|
||||||
|
private native void setDeadlockDetect(final long handle,
|
||||||
|
final boolean deadlockDetect);
|
||||||
|
private native long getLockTimeout(final long handle);
|
||||||
|
private native void setLockTimeout(final long handle, final long lockTimeout);
|
||||||
|
private native long getExpiration(final long handle);
|
||||||
|
private native void setExpiration(final long handle, final long expiration);
|
||||||
|
private native long getDeadlockDetectDepth(final long handle);
|
||||||
|
private native void setDeadlockDetectDepth(final long handle,
|
||||||
|
final long deadlockDetectDepth);
|
||||||
|
private native long getMaxWriteBatchSize(final long handle);
|
||||||
|
private native void setMaxWriteBatchSize(final long handle,
|
||||||
|
final long maxWriteBatchSize);
|
||||||
|
@Override protected final native void disposeInternal(final long handle);
|
||||||
|
}
|
68
java/src/main/java/org/rocksdb/TransactionalDB.java
Normal file
68
java/src/main/java/org/rocksdb/TransactionalDB.java
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
// 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;
|
||||||
|
|
||||||
|
|
||||||
|
interface TransactionalDB<T extends TransactionalOptions>
|
||||||
|
extends AutoCloseable {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Starts a new Transaction.
|
||||||
|
*
|
||||||
|
* Caller is responsible for calling {@link #close()} on the returned
|
||||||
|
* transaction when it is no longer needed.
|
||||||
|
*
|
||||||
|
* @param writeOptions Any write options for the transaction
|
||||||
|
* @return a new transaction
|
||||||
|
*/
|
||||||
|
Transaction beginTransaction(final WriteOptions writeOptions);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Starts a new Transaction.
|
||||||
|
*
|
||||||
|
* Caller is responsible for calling {@link #close()} on the returned
|
||||||
|
* transaction when it is no longer needed.
|
||||||
|
*
|
||||||
|
* @param writeOptions Any write options for the transaction
|
||||||
|
* @param transactionOptions Any options for the transaction
|
||||||
|
* @return a new transaction
|
||||||
|
*/
|
||||||
|
Transaction beginTransaction(final WriteOptions writeOptions,
|
||||||
|
final T transactionOptions);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Starts a new Transaction.
|
||||||
|
*
|
||||||
|
* Caller is responsible for calling {@link #close()} on the returned
|
||||||
|
* transaction when it is no longer needed.
|
||||||
|
*
|
||||||
|
* @param writeOptions Any write options for the transaction
|
||||||
|
* @param oldTransaction this Transaction will be reused instead of allocating
|
||||||
|
* a new one. This is an optimization to avoid extra allocations
|
||||||
|
* when repeatedly creating transactions.
|
||||||
|
* @return The oldTransaction which has been reinitialized as a new
|
||||||
|
* transaction
|
||||||
|
*/
|
||||||
|
Transaction beginTransaction(final WriteOptions writeOptions,
|
||||||
|
final Transaction oldTransaction);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Starts a new Transaction.
|
||||||
|
*
|
||||||
|
* Caller is responsible for calling {@link #close()} on the returned
|
||||||
|
* transaction when it is no longer needed.
|
||||||
|
*
|
||||||
|
* @param writeOptions Any write options for the transaction
|
||||||
|
* @param transactionOptions Any options for the transaction
|
||||||
|
* @param oldTransaction this Transaction will be reused instead of allocating
|
||||||
|
* a new one. This is an optimization to avoid extra allocations
|
||||||
|
* when repeatedly creating transactions.
|
||||||
|
* @return The oldTransaction which has been reinitialized as a new
|
||||||
|
* transaction
|
||||||
|
*/
|
||||||
|
Transaction beginTransaction(final WriteOptions writeOptions,
|
||||||
|
final T transactionOptions, final Transaction oldTransaction);
|
||||||
|
}
|
31
java/src/main/java/org/rocksdb/TransactionalOptions.java
Normal file
31
java/src/main/java/org/rocksdb/TransactionalOptions.java
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
// 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;
|
||||||
|
|
||||||
|
|
||||||
|
interface TransactionalOptions extends AutoCloseable {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* True indicates snapshots will be set, just like if
|
||||||
|
* {@link Transaction#setSnapshot()} had been called
|
||||||
|
*
|
||||||
|
* @return whether a snapshot will be set
|
||||||
|
*/
|
||||||
|
boolean isSetSnapshot();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Setting the setSnapshot to true is the same as calling
|
||||||
|
* {@link Transaction#setSnapshot()}.
|
||||||
|
*
|
||||||
|
* Default: false
|
||||||
|
*
|
||||||
|
* @param <T> The type of transactional options.
|
||||||
|
* @param setSnapshot Whether to set a snapshot
|
||||||
|
*
|
||||||
|
* @return this TransactionalOptions instance
|
||||||
|
*/
|
||||||
|
<T extends TransactionalOptions> T setSetSnapshot(final boolean setSnapshot);
|
||||||
|
}
|
62
java/src/main/java/org/rocksdb/TxnDBWritePolicy.java
Normal file
62
java/src/main/java/org/rocksdb/TxnDBWritePolicy.java
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
// 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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The transaction db write policy.
|
||||||
|
*/
|
||||||
|
public enum TxnDBWritePolicy {
|
||||||
|
/**
|
||||||
|
* Write only the committed data.
|
||||||
|
*/
|
||||||
|
WRITE_COMMITTED((byte)0x00),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write data after the prepare phase of 2pc.
|
||||||
|
*/
|
||||||
|
WRITE_PREPARED((byte)0x1),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write data before the prepare phase of 2pc.
|
||||||
|
*/
|
||||||
|
WRITE_UNPREPARED((byte)0x2);
|
||||||
|
|
||||||
|
private byte value;
|
||||||
|
|
||||||
|
TxnDBWritePolicy(final byte value) {
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Returns the byte value of the enumerations value.</p>
|
||||||
|
*
|
||||||
|
* @return byte representation
|
||||||
|
*/
|
||||||
|
public byte getValue() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Get the TxnDBWritePolicy enumeration value by
|
||||||
|
* passing the byte identifier to this method.</p>
|
||||||
|
*
|
||||||
|
* @param byteIdentifier of TxnDBWritePolicy.
|
||||||
|
*
|
||||||
|
* @return TxnDBWritePolicy instance.
|
||||||
|
*
|
||||||
|
* @throws IllegalArgumentException If TxnDBWritePolicy cannot be found for
|
||||||
|
* the provided byteIdentifier
|
||||||
|
*/
|
||||||
|
public static TxnDBWritePolicy getTxnDBWritePolicy(final byte byteIdentifier) {
|
||||||
|
for (final TxnDBWritePolicy txnDBWritePolicy : TxnDBWritePolicy.values()) {
|
||||||
|
if (txnDBWritePolicy.getValue() == byteIdentifier) {
|
||||||
|
return txnDBWritePolicy;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
"Illegal value provided for TxnDBWritePolicy.");
|
||||||
|
}
|
||||||
|
}
|
@ -65,7 +65,7 @@ public enum WALRecoveryMode {
|
|||||||
*
|
*
|
||||||
* @param byteIdentifier of WALRecoveryMode.
|
* @param byteIdentifier of WALRecoveryMode.
|
||||||
*
|
*
|
||||||
* @return CompressionType instance.
|
* @return WALRecoveryMode instance.
|
||||||
*
|
*
|
||||||
* @throws IllegalArgumentException If WALRecoveryMode cannot be found for the
|
* @throws IllegalArgumentException If WALRecoveryMode cannot be found for the
|
||||||
* provided byteIdentifier
|
* provided byteIdentifier
|
||||||
|
@ -64,6 +64,18 @@ public class WriteBatchWithIndex extends AbstractWriteBatch {
|
|||||||
fallbackIndexComparator instanceof DirectComparator, reservedBytes, overwriteKey));
|
fallbackIndexComparator instanceof DirectComparator, reservedBytes, overwriteKey));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Private WriteBatchWithIndex constructor which is used to construct
|
||||||
|
* WriteBatchWithIndex instances from C++ side. As the reference to this
|
||||||
|
* object is also managed from C++ side the handle will be disowned.</p>
|
||||||
|
*
|
||||||
|
* @param nativeHandle address of native instance.
|
||||||
|
*/
|
||||||
|
WriteBatchWithIndex(final long nativeHandle) {
|
||||||
|
super(nativeHandle);
|
||||||
|
disOwnNativeHandle();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create an iterator of a column family. User can call
|
* Create an iterator of a column family. User can call
|
||||||
* {@link org.rocksdb.RocksIteratorInterface#seek(byte[])} to
|
* {@link org.rocksdb.RocksIteratorInterface#seek(byte[])} to
|
||||||
|
@ -20,6 +20,12 @@ public class WriteOptions extends RocksObject {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO(AR) consider ownership
|
||||||
|
WriteOptions(final long nativeHandle) {
|
||||||
|
super(nativeHandle);
|
||||||
|
disOwnNativeHandle();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Copy constructor for WriteOptions.
|
* Copy constructor for WriteOptions.
|
||||||
*
|
*
|
||||||
|
903
java/src/test/java/org/rocksdb/AbstractTransactionTest.java
Normal file
903
java/src/test/java/org/rocksdb/AbstractTransactionTest.java
Normal file
@ -0,0 +1,903 @@
|
|||||||
|
// Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
|
||||||
|
// This source code is licensed under both the GPLv2 (found in the
|
||||||
|
// COPYING file in the root directory) and Apache 2.0 License
|
||||||
|
// (found in the LICENSE.Apache file in the root directory).
|
||||||
|
|
||||||
|
package org.rocksdb;
|
||||||
|
|
||||||
|
import org.junit.Rule;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.rules.TemporaryFolder;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Random;
|
||||||
|
|
||||||
|
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
import static org.assertj.core.api.Assertions.fail;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Base class of {@link TransactionTest} and {@link OptimisticTransactionTest}
|
||||||
|
*/
|
||||||
|
public abstract class AbstractTransactionTest {
|
||||||
|
|
||||||
|
protected final static byte[] TXN_TEST_COLUMN_FAMILY = "txn_test_cf"
|
||||||
|
.getBytes();
|
||||||
|
|
||||||
|
protected static final Random rand = PlatformRandomHelper.
|
||||||
|
getPlatformSpecificRandomFactory();
|
||||||
|
|
||||||
|
@Rule
|
||||||
|
public TemporaryFolder dbFolder = new TemporaryFolder();
|
||||||
|
|
||||||
|
public abstract DBContainer startDb()
|
||||||
|
throws RocksDBException;
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void setSnapshot() throws RocksDBException {
|
||||||
|
try(final DBContainer dbContainer = startDb();
|
||||||
|
final Transaction txn = dbContainer.beginTransaction()) {
|
||||||
|
txn.setSnapshot();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void setSnapshotOnNextOperation() throws RocksDBException {
|
||||||
|
try(final DBContainer dbContainer = startDb();
|
||||||
|
final Transaction txn = dbContainer.beginTransaction()) {
|
||||||
|
txn.setSnapshotOnNextOperation();
|
||||||
|
txn.put("key1".getBytes(), "value1".getBytes());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void setSnapshotOnNextOperation_transactionNotifier() throws RocksDBException {
|
||||||
|
try(final DBContainer dbContainer = startDb();
|
||||||
|
final Transaction txn = dbContainer.beginTransaction()) {
|
||||||
|
|
||||||
|
try(final TestTransactionNotifier notifier = new TestTransactionNotifier()) {
|
||||||
|
txn.setSnapshotOnNextOperation(notifier);
|
||||||
|
txn.put("key1".getBytes(), "value1".getBytes());
|
||||||
|
|
||||||
|
txn.setSnapshotOnNextOperation(notifier);
|
||||||
|
txn.put("key2".getBytes(), "value2".getBytes());
|
||||||
|
|
||||||
|
assertThat(notifier.getCreatedSnapshots().size()).isEqualTo(2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getSnapshot() throws RocksDBException {
|
||||||
|
try(final DBContainer dbContainer = startDb();
|
||||||
|
final Transaction txn = dbContainer.beginTransaction()) {
|
||||||
|
txn.setSnapshot();
|
||||||
|
final Snapshot snapshot = txn.getSnapshot();
|
||||||
|
assertThat(snapshot.isOwningHandle()).isFalse();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getSnapshot_null() throws RocksDBException {
|
||||||
|
try(final DBContainer dbContainer = startDb();
|
||||||
|
final Transaction txn = dbContainer.beginTransaction()) {
|
||||||
|
final Snapshot snapshot = txn.getSnapshot();
|
||||||
|
assertThat(snapshot).isNull();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void clearSnapshot() throws RocksDBException {
|
||||||
|
try(final DBContainer dbContainer = startDb();
|
||||||
|
final Transaction txn = dbContainer.beginTransaction()) {
|
||||||
|
txn.setSnapshot();
|
||||||
|
txn.clearSnapshot();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void clearSnapshot_none() throws RocksDBException {
|
||||||
|
try(final DBContainer dbContainer = startDb();
|
||||||
|
final Transaction txn = dbContainer.beginTransaction()) {
|
||||||
|
txn.clearSnapshot();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void commit() throws RocksDBException {
|
||||||
|
final byte k1[] = "rollback-key1".getBytes(UTF_8);
|
||||||
|
final byte v1[] = "rollback-value1".getBytes(UTF_8);
|
||||||
|
try(final DBContainer dbContainer = startDb()) {
|
||||||
|
try(final Transaction txn = dbContainer.beginTransaction()) {
|
||||||
|
txn.put(k1, v1);
|
||||||
|
txn.commit();
|
||||||
|
}
|
||||||
|
|
||||||
|
try(final ReadOptions readOptions = new ReadOptions();
|
||||||
|
final Transaction txn2 = dbContainer.beginTransaction()) {
|
||||||
|
assertThat(txn2.get(readOptions, k1)).isEqualTo(v1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void rollback() throws RocksDBException {
|
||||||
|
final byte k1[] = "rollback-key1".getBytes(UTF_8);
|
||||||
|
final byte v1[] = "rollback-value1".getBytes(UTF_8);
|
||||||
|
try(final DBContainer dbContainer = startDb()) {
|
||||||
|
try(final Transaction txn = dbContainer.beginTransaction()) {
|
||||||
|
txn.put(k1, v1);
|
||||||
|
txn.rollback();
|
||||||
|
}
|
||||||
|
|
||||||
|
try(final ReadOptions readOptions = new ReadOptions();
|
||||||
|
final Transaction txn2 = dbContainer.beginTransaction()) {
|
||||||
|
assertThat(txn2.get(readOptions, k1)).isNull();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void savePoint() throws RocksDBException {
|
||||||
|
final byte k1[] = "savePoint-key1".getBytes(UTF_8);
|
||||||
|
final byte v1[] = "savePoint-value1".getBytes(UTF_8);
|
||||||
|
final byte k2[] = "savePoint-key2".getBytes(UTF_8);
|
||||||
|
final byte v2[] = "savePoint-value2".getBytes(UTF_8);
|
||||||
|
|
||||||
|
try(final DBContainer dbContainer = startDb();
|
||||||
|
final ReadOptions readOptions = new ReadOptions()) {
|
||||||
|
|
||||||
|
|
||||||
|
try(final Transaction txn = dbContainer.beginTransaction()) {
|
||||||
|
txn.put(k1, v1);
|
||||||
|
|
||||||
|
assertThat(txn.get(readOptions, k1)).isEqualTo(v1);
|
||||||
|
|
||||||
|
txn.setSavePoint();
|
||||||
|
|
||||||
|
txn.put(k2, v2);
|
||||||
|
|
||||||
|
assertThat(txn.get(readOptions, k1)).isEqualTo(v1);
|
||||||
|
assertThat(txn.get(readOptions, k2)).isEqualTo(v2);
|
||||||
|
|
||||||
|
txn.rollbackToSavePoint();
|
||||||
|
|
||||||
|
assertThat(txn.get(readOptions, k1)).isEqualTo(v1);
|
||||||
|
assertThat(txn.get(readOptions, k2)).isNull();
|
||||||
|
|
||||||
|
txn.commit();
|
||||||
|
}
|
||||||
|
|
||||||
|
try(final Transaction txn2 = dbContainer.beginTransaction()) {
|
||||||
|
assertThat(txn2.get(readOptions, k1)).isEqualTo(v1);
|
||||||
|
assertThat(txn2.get(readOptions, k2)).isNull();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getPut_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();
|
||||||
|
assertThat(txn.get(testCf, readOptions, k1)).isNull();
|
||||||
|
txn.put(testCf, k1, v1);
|
||||||
|
assertThat(txn.get(testCf, readOptions, k1)).isEqualTo(v1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getPut() 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()) {
|
||||||
|
assertThat(txn.get(readOptions, k1)).isNull();
|
||||||
|
txn.put(k1, v1);
|
||||||
|
assertThat(txn.get(readOptions, k1)).isEqualTo(v1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void multiGetPut_cf() throws RocksDBException {
|
||||||
|
final byte keys[][] = new byte[][] {
|
||||||
|
"key1".getBytes(UTF_8),
|
||||||
|
"key2".getBytes(UTF_8)};
|
||||||
|
final byte values[][] = new byte[][] {
|
||||||
|
"value1".getBytes(UTF_8),
|
||||||
|
"value2".getBytes(UTF_8)};
|
||||||
|
|
||||||
|
try(final DBContainer dbContainer = startDb();
|
||||||
|
final ReadOptions readOptions = new ReadOptions();
|
||||||
|
final Transaction txn = dbContainer.beginTransaction()) {
|
||||||
|
final ColumnFamilyHandle testCf = dbContainer.getTestColumnFamily();
|
||||||
|
final List<ColumnFamilyHandle> cfList = Arrays.asList(testCf, testCf);
|
||||||
|
|
||||||
|
assertThat(txn.multiGet(readOptions, cfList, keys)).isEqualTo(new byte[][] { null, null });
|
||||||
|
|
||||||
|
txn.put(testCf, keys[0], values[0]);
|
||||||
|
txn.put(testCf, keys[1], values[1]);
|
||||||
|
assertThat(txn.multiGet(readOptions, cfList, keys)).isEqualTo(values);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void multiGetPut() throws RocksDBException {
|
||||||
|
final byte keys[][] = new byte[][] {
|
||||||
|
"key1".getBytes(UTF_8),
|
||||||
|
"key2".getBytes(UTF_8)};
|
||||||
|
final byte values[][] = new byte[][] {
|
||||||
|
"value1".getBytes(UTF_8),
|
||||||
|
"value2".getBytes(UTF_8)};
|
||||||
|
|
||||||
|
try(final DBContainer dbContainer = startDb();
|
||||||
|
final ReadOptions readOptions = new ReadOptions();
|
||||||
|
final Transaction txn = dbContainer.beginTransaction()) {
|
||||||
|
|
||||||
|
assertThat(txn.multiGet(readOptions, keys)).isEqualTo(new byte[][] { null, null });
|
||||||
|
|
||||||
|
txn.put(keys[0], values[0]);
|
||||||
|
txn.put(keys[1], values[1]);
|
||||||
|
assertThat(txn.multiGet(readOptions, keys)).isEqualTo(values);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getForUpdate_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();
|
||||||
|
assertThat(txn.getForUpdate(readOptions, testCf, k1, true)).isNull();
|
||||||
|
txn.put(testCf, k1, v1);
|
||||||
|
assertThat(txn.getForUpdate(readOptions, testCf, k1, true)).isEqualTo(v1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getForUpdate() 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()) {
|
||||||
|
assertThat(txn.getForUpdate(readOptions, k1, true)).isNull();
|
||||||
|
txn.put(k1, v1);
|
||||||
|
assertThat(txn.getForUpdate(readOptions, k1, true)).isEqualTo(v1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void multiGetForUpdate_cf() throws RocksDBException {
|
||||||
|
final byte keys[][] = new byte[][] {
|
||||||
|
"key1".getBytes(UTF_8),
|
||||||
|
"key2".getBytes(UTF_8)};
|
||||||
|
final byte values[][] = new byte[][] {
|
||||||
|
"value1".getBytes(UTF_8),
|
||||||
|
"value2".getBytes(UTF_8)};
|
||||||
|
|
||||||
|
try(final DBContainer dbContainer = startDb();
|
||||||
|
final ReadOptions readOptions = new ReadOptions();
|
||||||
|
final Transaction txn = dbContainer.beginTransaction()) {
|
||||||
|
final ColumnFamilyHandle testCf = dbContainer.getTestColumnFamily();
|
||||||
|
final List<ColumnFamilyHandle> cfList = Arrays.asList(testCf, testCf);
|
||||||
|
|
||||||
|
assertThat(txn.multiGetForUpdate(readOptions, cfList, keys))
|
||||||
|
.isEqualTo(new byte[][] { null, null });
|
||||||
|
|
||||||
|
txn.put(testCf, keys[0], values[0]);
|
||||||
|
txn.put(testCf, keys[1], values[1]);
|
||||||
|
assertThat(txn.multiGetForUpdate(readOptions, cfList, keys))
|
||||||
|
.isEqualTo(values);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void multiGetForUpdate() throws RocksDBException {
|
||||||
|
final byte keys[][] = new byte[][]{
|
||||||
|
"key1".getBytes(UTF_8),
|
||||||
|
"key2".getBytes(UTF_8)};
|
||||||
|
final byte values[][] = new byte[][]{
|
||||||
|
"value1".getBytes(UTF_8),
|
||||||
|
"value2".getBytes(UTF_8)};
|
||||||
|
|
||||||
|
try (final DBContainer dbContainer = startDb();
|
||||||
|
final ReadOptions readOptions = new ReadOptions();
|
||||||
|
final Transaction txn = dbContainer.beginTransaction()) {
|
||||||
|
assertThat(txn.multiGetForUpdate(readOptions, keys)).isEqualTo(new byte[][]{null, null});
|
||||||
|
|
||||||
|
txn.put(keys[0], values[0]);
|
||||||
|
txn.put(keys[1], values[1]);
|
||||||
|
assertThat(txn.multiGetForUpdate(readOptions, keys)).isEqualTo(values);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getIterator() throws RocksDBException {
|
||||||
|
try(final DBContainer dbContainer = startDb();
|
||||||
|
final ReadOptions readOptions = new ReadOptions();
|
||||||
|
final Transaction txn = dbContainer.beginTransaction()) {
|
||||||
|
|
||||||
|
final byte[] k1 = "key1".getBytes(UTF_8);
|
||||||
|
final byte[] v1 = "value1".getBytes(UTF_8);
|
||||||
|
|
||||||
|
txn.put(k1, v1);
|
||||||
|
|
||||||
|
try(final RocksIterator iterator = txn.getIterator(readOptions)) {
|
||||||
|
iterator.seek(k1);
|
||||||
|
assertThat(iterator.isValid()).isTrue();
|
||||||
|
assertThat(iterator.key()).isEqualTo(k1);
|
||||||
|
assertThat(iterator.value()).isEqualTo(v1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getIterator_cf() throws RocksDBException {
|
||||||
|
try(final DBContainer dbContainer = startDb();
|
||||||
|
final ReadOptions readOptions = new ReadOptions();
|
||||||
|
final Transaction txn = dbContainer.beginTransaction()) {
|
||||||
|
final ColumnFamilyHandle testCf = dbContainer.getTestColumnFamily();
|
||||||
|
|
||||||
|
final byte[] k1 = "key1".getBytes(UTF_8);
|
||||||
|
final byte[] v1 = "value1".getBytes(UTF_8);
|
||||||
|
|
||||||
|
txn.put(testCf, k1, v1);
|
||||||
|
|
||||||
|
try(final RocksIterator iterator = txn.getIterator(readOptions, testCf)) {
|
||||||
|
iterator.seek(k1);
|
||||||
|
assertThat(iterator.isValid()).isTrue();
|
||||||
|
assertThat(iterator.key()).isEqualTo(k1);
|
||||||
|
assertThat(iterator.value()).isEqualTo(v1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void merge_cf() throws RocksDBException {
|
||||||
|
final byte[] k1 = "key1".getBytes(UTF_8);
|
||||||
|
final byte[] v1 = "value1".getBytes(UTF_8);
|
||||||
|
|
||||||
|
try(final DBContainer dbContainer = startDb();
|
||||||
|
final Transaction txn = dbContainer.beginTransaction()) {
|
||||||
|
final ColumnFamilyHandle testCf = dbContainer.getTestColumnFamily();
|
||||||
|
txn.merge(testCf, k1, v1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void merge() throws RocksDBException {
|
||||||
|
final byte[] k1 = "key1".getBytes(UTF_8);
|
||||||
|
final byte[] v1 = "value1".getBytes(UTF_8);
|
||||||
|
|
||||||
|
try(final DBContainer dbContainer = startDb();
|
||||||
|
final Transaction txn = dbContainer.beginTransaction()) {
|
||||||
|
txn.merge(k1, v1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void delete_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();
|
||||||
|
txn.put(testCf, k1, v1);
|
||||||
|
assertThat(txn.get(testCf, readOptions, k1)).isEqualTo(v1);
|
||||||
|
|
||||||
|
txn.delete(testCf, k1);
|
||||||
|
assertThat(txn.get(testCf, readOptions, k1)).isNull();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void delete() 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()) {
|
||||||
|
txn.put(k1, v1);
|
||||||
|
assertThat(txn.get(readOptions, k1)).isEqualTo(v1);
|
||||||
|
|
||||||
|
txn.delete(k1);
|
||||||
|
assertThat(txn.get(readOptions, k1)).isNull();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void delete_parts_cf() throws RocksDBException {
|
||||||
|
final byte keyParts[][] = new byte[][] {
|
||||||
|
"ke".getBytes(UTF_8),
|
||||||
|
"y1".getBytes(UTF_8)};
|
||||||
|
final byte valueParts[][] = new byte[][] {
|
||||||
|
"val".getBytes(UTF_8),
|
||||||
|
"ue1".getBytes(UTF_8)};
|
||||||
|
final byte[] key = concat(keyParts);
|
||||||
|
final byte[] value = concat(valueParts);
|
||||||
|
|
||||||
|
try(final DBContainer dbContainer = startDb();
|
||||||
|
final ReadOptions readOptions = new ReadOptions();
|
||||||
|
final Transaction txn = dbContainer.beginTransaction()) {
|
||||||
|
final ColumnFamilyHandle testCf = dbContainer.getTestColumnFamily();
|
||||||
|
txn.put(testCf, keyParts, valueParts);
|
||||||
|
assertThat(txn.get(testCf, readOptions, key)).isEqualTo(value);
|
||||||
|
|
||||||
|
txn.delete(testCf, keyParts);
|
||||||
|
|
||||||
|
assertThat(txn.get(testCf, readOptions, key))
|
||||||
|
.isNull();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void delete_parts() throws RocksDBException {
|
||||||
|
final byte keyParts[][] = new byte[][] {
|
||||||
|
"ke".getBytes(UTF_8),
|
||||||
|
"y1".getBytes(UTF_8)};
|
||||||
|
final byte valueParts[][] = new byte[][] {
|
||||||
|
"val".getBytes(UTF_8),
|
||||||
|
"ue1".getBytes(UTF_8)};
|
||||||
|
final byte[] key = concat(keyParts);
|
||||||
|
final byte[] value = concat(valueParts);
|
||||||
|
|
||||||
|
try(final DBContainer dbContainer = startDb();
|
||||||
|
final ReadOptions readOptions = new ReadOptions();
|
||||||
|
final Transaction txn = dbContainer.beginTransaction()) {
|
||||||
|
|
||||||
|
txn.put(keyParts, valueParts);
|
||||||
|
|
||||||
|
assertThat(txn.get(readOptions, key)).isEqualTo(value);
|
||||||
|
|
||||||
|
txn.delete(keyParts);
|
||||||
|
|
||||||
|
assertThat(txn.get(readOptions, key)).isNull();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getPutUntracked_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();
|
||||||
|
assertThat(txn.get(testCf, readOptions, k1)).isNull();
|
||||||
|
txn.putUntracked(testCf, k1, v1);
|
||||||
|
assertThat(txn.get(testCf, readOptions, k1)).isEqualTo(v1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getPutUntracked() 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()) {
|
||||||
|
assertThat(txn.get(readOptions, k1)).isNull();
|
||||||
|
txn.putUntracked(k1, v1);
|
||||||
|
assertThat(txn.get(readOptions, k1)).isEqualTo(v1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void multiGetPutUntracked_cf() throws RocksDBException {
|
||||||
|
final byte keys[][] = new byte[][] {
|
||||||
|
"key1".getBytes(UTF_8),
|
||||||
|
"key2".getBytes(UTF_8)};
|
||||||
|
final byte values[][] = new byte[][] {
|
||||||
|
"value1".getBytes(UTF_8),
|
||||||
|
"value2".getBytes(UTF_8)};
|
||||||
|
|
||||||
|
try(final DBContainer dbContainer = startDb();
|
||||||
|
final ReadOptions readOptions = new ReadOptions();
|
||||||
|
final Transaction txn = dbContainer.beginTransaction()) {
|
||||||
|
final ColumnFamilyHandle testCf = dbContainer.getTestColumnFamily();
|
||||||
|
|
||||||
|
final List<ColumnFamilyHandle> cfList = Arrays.asList(testCf, testCf);
|
||||||
|
|
||||||
|
assertThat(txn.multiGet(readOptions, cfList, keys)).isEqualTo(new byte[][] { null, null });
|
||||||
|
txn.putUntracked(testCf, keys[0], values[0]);
|
||||||
|
txn.putUntracked(testCf, keys[1], values[1]);
|
||||||
|
assertThat(txn.multiGet(readOptions, cfList, keys)).isEqualTo(values);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void multiGetPutUntracked() throws RocksDBException {
|
||||||
|
final byte keys[][] = new byte[][] {
|
||||||
|
"key1".getBytes(UTF_8),
|
||||||
|
"key2".getBytes(UTF_8)};
|
||||||
|
final byte values[][] = new byte[][] {
|
||||||
|
"value1".getBytes(UTF_8),
|
||||||
|
"value2".getBytes(UTF_8)};
|
||||||
|
|
||||||
|
try(final DBContainer dbContainer = startDb();
|
||||||
|
final ReadOptions readOptions = new ReadOptions();
|
||||||
|
final Transaction txn = dbContainer.beginTransaction()) {
|
||||||
|
|
||||||
|
assertThat(txn.multiGet(readOptions, keys)).isEqualTo(new byte[][] { null, null });
|
||||||
|
txn.putUntracked(keys[0], values[0]);
|
||||||
|
txn.putUntracked(keys[1], values[1]);
|
||||||
|
assertThat(txn.multiGet(readOptions, keys)).isEqualTo(values);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void mergeUntracked_cf() throws RocksDBException {
|
||||||
|
final byte[] k1 = "key1".getBytes(UTF_8);
|
||||||
|
final byte[] v1 = "value1".getBytes(UTF_8);
|
||||||
|
|
||||||
|
try(final DBContainer dbContainer = startDb();
|
||||||
|
final Transaction txn = dbContainer.beginTransaction()) {
|
||||||
|
final ColumnFamilyHandle testCf = dbContainer.getTestColumnFamily();
|
||||||
|
txn.mergeUntracked(testCf, k1, v1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void mergeUntracked() throws RocksDBException {
|
||||||
|
final byte[] k1 = "key1".getBytes(UTF_8);
|
||||||
|
final byte[] v1 = "value1".getBytes(UTF_8);
|
||||||
|
|
||||||
|
try(final DBContainer dbContainer = startDb();
|
||||||
|
final Transaction txn = dbContainer.beginTransaction()) {
|
||||||
|
txn.mergeUntracked(k1, v1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void deleteUntracked_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();
|
||||||
|
txn.put(testCf, k1, v1);
|
||||||
|
assertThat(txn.get(testCf, readOptions, k1)).isEqualTo(v1);
|
||||||
|
|
||||||
|
txn.deleteUntracked(testCf, k1);
|
||||||
|
assertThat(txn.get(testCf, readOptions, k1)).isNull();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void deleteUntracked() 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()) {
|
||||||
|
txn.put(k1, v1);
|
||||||
|
assertThat(txn.get(readOptions, k1)).isEqualTo(v1);
|
||||||
|
|
||||||
|
txn.deleteUntracked(k1);
|
||||||
|
assertThat(txn.get(readOptions, k1)).isNull();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void deleteUntracked_parts_cf() throws RocksDBException {
|
||||||
|
final byte keyParts[][] = new byte[][] {
|
||||||
|
"ke".getBytes(UTF_8),
|
||||||
|
"y1".getBytes(UTF_8)};
|
||||||
|
final byte valueParts[][] = new byte[][] {
|
||||||
|
"val".getBytes(UTF_8),
|
||||||
|
"ue1".getBytes(UTF_8)};
|
||||||
|
final byte[] key = concat(keyParts);
|
||||||
|
final byte[] value = concat(valueParts);
|
||||||
|
|
||||||
|
try(final DBContainer dbContainer = startDb();
|
||||||
|
final ReadOptions readOptions = new ReadOptions();
|
||||||
|
final Transaction txn = dbContainer.beginTransaction()) {
|
||||||
|
final ColumnFamilyHandle testCf = dbContainer.getTestColumnFamily();
|
||||||
|
txn.put(testCf, keyParts, valueParts);
|
||||||
|
assertThat(txn.get(testCf, readOptions, key)).isEqualTo(value);
|
||||||
|
|
||||||
|
txn.deleteUntracked(testCf, keyParts);
|
||||||
|
assertThat(txn.get(testCf, readOptions, key)).isNull();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void deleteUntracked_parts() throws RocksDBException {
|
||||||
|
final byte keyParts[][] = new byte[][] {
|
||||||
|
"ke".getBytes(UTF_8),
|
||||||
|
"y1".getBytes(UTF_8)};
|
||||||
|
final byte valueParts[][] = new byte[][] {
|
||||||
|
"val".getBytes(UTF_8),
|
||||||
|
"ue1".getBytes(UTF_8)};
|
||||||
|
final byte[] key = concat(keyParts);
|
||||||
|
final byte[] value = concat(valueParts);
|
||||||
|
|
||||||
|
try(final DBContainer dbContainer = startDb();
|
||||||
|
final ReadOptions readOptions = new ReadOptions();
|
||||||
|
final Transaction txn = dbContainer.beginTransaction()) {
|
||||||
|
txn.put(keyParts, valueParts);
|
||||||
|
assertThat(txn.get(readOptions, key)).isEqualTo(value);
|
||||||
|
|
||||||
|
txn.deleteUntracked(keyParts);
|
||||||
|
assertThat(txn.get(readOptions, key)).isNull();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void putLogData() throws RocksDBException {
|
||||||
|
final byte[] blob = "blobby".getBytes(UTF_8);
|
||||||
|
try(final DBContainer dbContainer = startDb();
|
||||||
|
final Transaction txn = dbContainer.beginTransaction()) {
|
||||||
|
txn.putLogData(blob);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void enabledDisableIndexing() throws RocksDBException {
|
||||||
|
try(final DBContainer dbContainer = startDb();
|
||||||
|
final Transaction txn = dbContainer.beginTransaction()) {
|
||||||
|
txn.disableIndexing();
|
||||||
|
txn.enableIndexing();
|
||||||
|
txn.disableIndexing();
|
||||||
|
txn.enableIndexing();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void numKeys() throws RocksDBException {
|
||||||
|
final byte k1[] = "key1".getBytes(UTF_8);
|
||||||
|
final byte v1[] = "value1".getBytes(UTF_8);
|
||||||
|
final byte k2[] = "key2".getBytes(UTF_8);
|
||||||
|
final byte v2[] = "value2".getBytes(UTF_8);
|
||||||
|
final byte k3[] = "key3".getBytes(UTF_8);
|
||||||
|
final byte v3[] = "value3".getBytes(UTF_8);
|
||||||
|
|
||||||
|
try(final DBContainer dbContainer = startDb();
|
||||||
|
final Transaction txn = dbContainer.beginTransaction()) {
|
||||||
|
final ColumnFamilyHandle testCf = dbContainer.getTestColumnFamily();
|
||||||
|
txn.put(k1, v1);
|
||||||
|
txn.put(testCf, k2, v2);
|
||||||
|
txn.merge(k3, v3);
|
||||||
|
txn.delete(testCf, k2);
|
||||||
|
|
||||||
|
assertThat(txn.getNumKeys()).isEqualTo(3);
|
||||||
|
assertThat(txn.getNumPuts()).isEqualTo(2);
|
||||||
|
assertThat(txn.getNumMerges()).isEqualTo(1);
|
||||||
|
assertThat(txn.getNumDeletes()).isEqualTo(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void elapsedTime() throws RocksDBException, InterruptedException {
|
||||||
|
final long preStartTxnTime = System.currentTimeMillis();
|
||||||
|
try(final DBContainer dbContainer = startDb();
|
||||||
|
final Transaction txn = dbContainer.beginTransaction()) {
|
||||||
|
Thread.sleep(1);
|
||||||
|
|
||||||
|
final long txnElapsedTime = txn.getElapsedTime();
|
||||||
|
assertThat(txnElapsedTime).isLessThan(System.currentTimeMillis()
|
||||||
|
- preStartTxnTime);
|
||||||
|
assertThat(txnElapsedTime).isGreaterThan(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getWriteBatch() throws RocksDBException {
|
||||||
|
final byte k1[] = "key1".getBytes(UTF_8);
|
||||||
|
final byte v1[] = "value1".getBytes(UTF_8);
|
||||||
|
|
||||||
|
try(final DBContainer dbContainer = startDb();
|
||||||
|
final Transaction txn = dbContainer.beginTransaction()) {
|
||||||
|
|
||||||
|
txn.put(k1, v1);
|
||||||
|
|
||||||
|
final WriteBatchWithIndex writeBatch = txn.getWriteBatch();
|
||||||
|
assertThat(writeBatch).isNotNull();
|
||||||
|
assertThat(writeBatch.isOwningHandle()).isFalse();
|
||||||
|
assertThat(writeBatch.count()).isEqualTo(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void setLockTimeout() throws RocksDBException {
|
||||||
|
try(final DBContainer dbContainer = startDb();
|
||||||
|
final Transaction txn = dbContainer.beginTransaction()) {
|
||||||
|
txn.setLockTimeout(1000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void writeOptions() throws RocksDBException {
|
||||||
|
final byte k1[] = "key1".getBytes(UTF_8);
|
||||||
|
final byte v1[] = "value1".getBytes(UTF_8);
|
||||||
|
|
||||||
|
try(final DBContainer dbContainer = startDb();
|
||||||
|
final WriteOptions writeOptions = new WriteOptions()
|
||||||
|
.setDisableWAL(true)
|
||||||
|
.setSync(true);
|
||||||
|
final Transaction txn = dbContainer.beginTransaction(writeOptions)) {
|
||||||
|
|
||||||
|
txn.put(k1, v1);
|
||||||
|
|
||||||
|
WriteOptions txnWriteOptions = txn.getWriteOptions();
|
||||||
|
assertThat(txnWriteOptions).isNotNull();
|
||||||
|
assertThat(txnWriteOptions.isOwningHandle()).isFalse();
|
||||||
|
assertThat(txnWriteOptions).isNotSameAs(writeOptions);
|
||||||
|
assertThat(txnWriteOptions.disableWAL()).isTrue();
|
||||||
|
assertThat(txnWriteOptions.sync()).isTrue();
|
||||||
|
|
||||||
|
txn.setWriteOptions(txnWriteOptions.setSync(false));
|
||||||
|
txnWriteOptions = txn.getWriteOptions();
|
||||||
|
assertThat(txnWriteOptions).isNotNull();
|
||||||
|
assertThat(txnWriteOptions.isOwningHandle()).isFalse();
|
||||||
|
assertThat(txnWriteOptions).isNotSameAs(writeOptions);
|
||||||
|
assertThat(txnWriteOptions.disableWAL()).isTrue();
|
||||||
|
assertThat(txnWriteOptions.sync()).isFalse();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void undoGetForUpdate_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();
|
||||||
|
assertThat(txn.getForUpdate(readOptions, testCf, k1, true)).isNull();
|
||||||
|
txn.put(testCf, k1, v1);
|
||||||
|
assertThat(txn.getForUpdate(readOptions, testCf, k1, true)).isEqualTo(v1);
|
||||||
|
txn.undoGetForUpdate(testCf, k1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void undoGetForUpdate() 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()) {
|
||||||
|
assertThat(txn.getForUpdate(readOptions, k1, true)).isNull();
|
||||||
|
txn.put(k1, v1);
|
||||||
|
assertThat(txn.getForUpdate(readOptions, k1, true)).isEqualTo(v1);
|
||||||
|
txn.undoGetForUpdate(k1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void rebuildFromWriteBatch() throws RocksDBException {
|
||||||
|
final byte k1[] = "key1".getBytes(UTF_8);
|
||||||
|
final byte v1[] = "value1".getBytes(UTF_8);
|
||||||
|
final byte k2[] = "key2".getBytes(UTF_8);
|
||||||
|
final byte v2[] = "value2".getBytes(UTF_8);
|
||||||
|
final byte k3[] = "key3".getBytes(UTF_8);
|
||||||
|
final byte v3[] = "value3".getBytes(UTF_8);
|
||||||
|
|
||||||
|
try(final DBContainer dbContainer = startDb();
|
||||||
|
final ReadOptions readOptions = new ReadOptions();
|
||||||
|
final Transaction txn = dbContainer.beginTransaction()) {
|
||||||
|
|
||||||
|
txn.put(k1, v1);
|
||||||
|
|
||||||
|
assertThat(txn.get(readOptions, k1)).isEqualTo(v1);
|
||||||
|
assertThat(txn.getNumKeys()).isEqualTo(1);
|
||||||
|
|
||||||
|
try(final WriteBatch writeBatch = new WriteBatch()) {
|
||||||
|
writeBatch.put(k2, v2);
|
||||||
|
writeBatch.put(k3, v3);
|
||||||
|
txn.rebuildFromWriteBatch(writeBatch);
|
||||||
|
|
||||||
|
assertThat(txn.get(readOptions, k1)).isEqualTo(v1);
|
||||||
|
assertThat(txn.get(readOptions, k2)).isEqualTo(v2);
|
||||||
|
assertThat(txn.get(readOptions, k3)).isEqualTo(v3);
|
||||||
|
assertThat(txn.getNumKeys()).isEqualTo(3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getCommitTimeWriteBatch() throws RocksDBException {
|
||||||
|
final byte k1[] = "key1".getBytes(UTF_8);
|
||||||
|
final byte v1[] = "value1".getBytes(UTF_8);
|
||||||
|
|
||||||
|
try(final DBContainer dbContainer = startDb();
|
||||||
|
final Transaction txn = dbContainer.beginTransaction()) {
|
||||||
|
|
||||||
|
txn.put(k1, v1);
|
||||||
|
final WriteBatch writeBatch = txn.getCommitTimeWriteBatch();
|
||||||
|
|
||||||
|
assertThat(writeBatch).isNotNull();
|
||||||
|
assertThat(writeBatch.isOwningHandle()).isFalse();
|
||||||
|
assertThat(writeBatch.count()).isEqualTo(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void logNumber() throws RocksDBException {
|
||||||
|
try(final DBContainer dbContainer = startDb();
|
||||||
|
final Transaction txn = dbContainer.beginTransaction()) {
|
||||||
|
assertThat(txn.getLogNumber()).isEqualTo(0);
|
||||||
|
final long logNumber = rand.nextLong();
|
||||||
|
txn.setLogNumber(logNumber);
|
||||||
|
assertThat(txn.getLogNumber()).isEqualTo(logNumber);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static byte[] concat(final byte[][] bufs) {
|
||||||
|
int resultLength = 0;
|
||||||
|
for(final byte[] buf : bufs) {
|
||||||
|
resultLength += buf.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
final byte[] result = new byte[resultLength];
|
||||||
|
int resultOffset = 0;
|
||||||
|
for(final byte[] buf : bufs) {
|
||||||
|
final int srcLength = buf.length;
|
||||||
|
System.arraycopy(buf, 0, result, resultOffset, srcLength);
|
||||||
|
resultOffset += srcLength;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class TestTransactionNotifier
|
||||||
|
extends AbstractTransactionNotifier {
|
||||||
|
private final List<Snapshot> createdSnapshots = new ArrayList<>();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void snapshotCreated(final Snapshot newSnapshot) {
|
||||||
|
createdSnapshots.add(newSnapshot);
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Snapshot> getCreatedSnapshots() {
|
||||||
|
return createdSnapshots;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static abstract class DBContainer
|
||||||
|
implements AutoCloseable {
|
||||||
|
protected final WriteOptions writeOptions;
|
||||||
|
protected final List<ColumnFamilyHandle> columnFamilyHandles;
|
||||||
|
protected final ColumnFamilyOptions columnFamilyOptions;
|
||||||
|
protected final DBOptions options;
|
||||||
|
|
||||||
|
public DBContainer(final WriteOptions writeOptions,
|
||||||
|
final List<ColumnFamilyHandle> columnFamilyHandles,
|
||||||
|
final ColumnFamilyOptions columnFamilyOptions,
|
||||||
|
final DBOptions options) {
|
||||||
|
this.writeOptions = writeOptions;
|
||||||
|
this.columnFamilyHandles = columnFamilyHandles;
|
||||||
|
this.columnFamilyOptions = columnFamilyOptions;
|
||||||
|
this.options = options;
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract Transaction beginTransaction();
|
||||||
|
|
||||||
|
public abstract Transaction beginTransaction(
|
||||||
|
final WriteOptions writeOptions);
|
||||||
|
|
||||||
|
public ColumnFamilyHandle getTestColumnFamily() {
|
||||||
|
return columnFamilyHandles.get(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public abstract void close();
|
||||||
|
}
|
||||||
|
}
|
@ -12,6 +12,7 @@ import org.junit.Rule;
|
|||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.rules.TemporaryFolder;
|
import org.junit.rules.TemporaryFolder;
|
||||||
|
|
||||||
|
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
public class ColumnFamilyTest {
|
public class ColumnFamilyTest {
|
||||||
@ -23,6 +24,31 @@ public class ColumnFamilyTest {
|
|||||||
@Rule
|
@Rule
|
||||||
public TemporaryFolder dbFolder = new TemporaryFolder();
|
public TemporaryFolder dbFolder = new TemporaryFolder();
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void columnFamilyDescriptorName() throws RocksDBException {
|
||||||
|
final byte[] cfName = "some_name".getBytes(UTF_8);
|
||||||
|
|
||||||
|
try(final ColumnFamilyOptions cfOptions = new ColumnFamilyOptions()) {
|
||||||
|
final ColumnFamilyDescriptor cfDescriptor =
|
||||||
|
new ColumnFamilyDescriptor(cfName, cfOptions);
|
||||||
|
assertThat(cfDescriptor.getName()).isEqualTo(cfName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void columnFamilyDescriptorOptions() throws RocksDBException {
|
||||||
|
final byte[] cfName = "some_name".getBytes(UTF_8);
|
||||||
|
|
||||||
|
try(final ColumnFamilyOptions cfOptions = new ColumnFamilyOptions()
|
||||||
|
.setCompressionType(CompressionType.BZLIB2_COMPRESSION)) {
|
||||||
|
final ColumnFamilyDescriptor cfDescriptor =
|
||||||
|
new ColumnFamilyDescriptor(cfName, cfOptions);
|
||||||
|
|
||||||
|
assertThat(cfDescriptor.getOptions().compressionType())
|
||||||
|
.isEqualTo(CompressionType.BZLIB2_COMPRESSION);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void listColumnFamilies() throws RocksDBException {
|
public void listColumnFamilies() throws RocksDBException {
|
||||||
try (final Options options = new Options().setCreateIfMissing(true);
|
try (final Options options = new Options().setCreateIfMissing(true);
|
||||||
@ -47,6 +73,9 @@ public class ColumnFamilyTest {
|
|||||||
try {
|
try {
|
||||||
assertThat(cfh).isNotNull();
|
assertThat(cfh).isNotNull();
|
||||||
|
|
||||||
|
assertThat(cfh.getName()).isEqualTo("default".getBytes(UTF_8));
|
||||||
|
assertThat(cfh.getID()).isEqualTo(0);
|
||||||
|
|
||||||
final byte[] key = "key".getBytes();
|
final byte[] key = "key".getBytes();
|
||||||
final byte[] value = "value".getBytes();
|
final byte[] value = "value".getBytes();
|
||||||
|
|
||||||
@ -64,13 +93,23 @@ public class ColumnFamilyTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void createColumnFamily() throws RocksDBException {
|
public void createColumnFamily() throws RocksDBException {
|
||||||
|
final byte[] cfName = "new_cf".getBytes(UTF_8);
|
||||||
|
final ColumnFamilyDescriptor cfDescriptor = new ColumnFamilyDescriptor(cfName,
|
||||||
|
new ColumnFamilyOptions());
|
||||||
|
|
||||||
try (final Options options = new Options().setCreateIfMissing(true);
|
try (final Options options = new Options().setCreateIfMissing(true);
|
||||||
final RocksDB db = RocksDB.open(options,
|
final RocksDB db = RocksDB.open(options,
|
||||||
dbFolder.getRoot().getAbsolutePath())) {
|
dbFolder.getRoot().getAbsolutePath())) {
|
||||||
final ColumnFamilyHandle columnFamilyHandle = db.createColumnFamily(
|
|
||||||
new ColumnFamilyDescriptor("new_cf".getBytes(),
|
final ColumnFamilyHandle columnFamilyHandle = db.createColumnFamily(cfDescriptor);
|
||||||
new ColumnFamilyOptions()));
|
|
||||||
try {
|
try {
|
||||||
|
assertThat(columnFamilyHandle.getName()).isEqualTo(cfName);
|
||||||
|
assertThat(columnFamilyHandle.getID()).isEqualTo(1);
|
||||||
|
|
||||||
|
final ColumnFamilyDescriptor latestDescriptor = columnFamilyHandle.getDescriptor();
|
||||||
|
assertThat(latestDescriptor.getName()).isEqualTo(cfName);
|
||||||
|
|
||||||
final List<byte[]> columnFamilyNames = RocksDB.listColumnFamilies(
|
final List<byte[]> columnFamilyNames = RocksDB.listColumnFamilies(
|
||||||
options, dbFolder.getRoot().getAbsolutePath());
|
options, dbFolder.getRoot().getAbsolutePath());
|
||||||
assertThat(columnFamilyNames).isNotNull();
|
assertThat(columnFamilyNames).isNotNull();
|
||||||
|
131
java/src/test/java/org/rocksdb/OptimisticTransactionDBTest.java
Normal file
131
java/src/test/java/org/rocksdb/OptimisticTransactionDBTest.java
Normal file
@ -0,0 +1,131 @@
|
|||||||
|
// Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
|
||||||
|
// This source code is licensed under both the GPLv2 (found in the
|
||||||
|
// COPYING file in the root directory) and Apache 2.0 License
|
||||||
|
// (found in the LICENSE.Apache file in the root directory).
|
||||||
|
|
||||||
|
package org.rocksdb;
|
||||||
|
|
||||||
|
import org.junit.Rule;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.rules.TemporaryFolder;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
|
public class OptimisticTransactionDBTest {
|
||||||
|
|
||||||
|
@Rule
|
||||||
|
public TemporaryFolder dbFolder = new TemporaryFolder();
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void open() throws RocksDBException {
|
||||||
|
try (final Options options = new Options().setCreateIfMissing(true);
|
||||||
|
final OptimisticTransactionDB otdb = OptimisticTransactionDB.open(options,
|
||||||
|
dbFolder.getRoot().getAbsolutePath())) {
|
||||||
|
assertThat(otdb).isNotNull();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void open_columnFamilies() throws RocksDBException {
|
||||||
|
try(final DBOptions dbOptions = new DBOptions().setCreateIfMissing(true)
|
||||||
|
.setCreateMissingColumnFamilies(true);
|
||||||
|
final ColumnFamilyOptions myCfOpts = new ColumnFamilyOptions()) {
|
||||||
|
|
||||||
|
final List<ColumnFamilyDescriptor> columnFamilyDescriptors =
|
||||||
|
Arrays.asList(
|
||||||
|
new ColumnFamilyDescriptor(RocksDB.DEFAULT_COLUMN_FAMILY),
|
||||||
|
new ColumnFamilyDescriptor("myCf".getBytes(), myCfOpts));
|
||||||
|
|
||||||
|
final List<ColumnFamilyHandle> columnFamilyHandles = new ArrayList<>();
|
||||||
|
|
||||||
|
try (final OptimisticTransactionDB otdb = OptimisticTransactionDB.open(dbOptions,
|
||||||
|
dbFolder.getRoot().getAbsolutePath(),
|
||||||
|
columnFamilyDescriptors, columnFamilyHandles)) {
|
||||||
|
try {
|
||||||
|
assertThat(otdb).isNotNull();
|
||||||
|
} finally {
|
||||||
|
for (final ColumnFamilyHandle handle : columnFamilyHandles) {
|
||||||
|
handle.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void beginTransaction() throws RocksDBException {
|
||||||
|
try (final Options options = new Options().setCreateIfMissing(true);
|
||||||
|
final OptimisticTransactionDB otdb = OptimisticTransactionDB.open(
|
||||||
|
options, dbFolder.getRoot().getAbsolutePath());
|
||||||
|
final WriteOptions writeOptions = new WriteOptions()) {
|
||||||
|
|
||||||
|
try(final Transaction txn = otdb.beginTransaction(writeOptions)) {
|
||||||
|
assertThat(txn).isNotNull();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void beginTransaction_transactionOptions() throws RocksDBException {
|
||||||
|
try (final Options options = new Options().setCreateIfMissing(true);
|
||||||
|
final OptimisticTransactionDB otdb = OptimisticTransactionDB.open(
|
||||||
|
options, dbFolder.getRoot().getAbsolutePath());
|
||||||
|
final WriteOptions writeOptions = new WriteOptions();
|
||||||
|
final OptimisticTransactionOptions optimisticTxnOptions =
|
||||||
|
new OptimisticTransactionOptions()) {
|
||||||
|
|
||||||
|
try(final Transaction txn = otdb.beginTransaction(writeOptions,
|
||||||
|
optimisticTxnOptions)) {
|
||||||
|
assertThat(txn).isNotNull();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void beginTransaction_withOld() throws RocksDBException {
|
||||||
|
try (final Options options = new Options().setCreateIfMissing(true);
|
||||||
|
final OptimisticTransactionDB otdb = OptimisticTransactionDB.open(
|
||||||
|
options, dbFolder.getRoot().getAbsolutePath());
|
||||||
|
final WriteOptions writeOptions = new WriteOptions()) {
|
||||||
|
|
||||||
|
try(final Transaction txn = otdb.beginTransaction(writeOptions)) {
|
||||||
|
final Transaction txnReused = otdb.beginTransaction(writeOptions, txn);
|
||||||
|
assertThat(txnReused).isSameAs(txn);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void beginTransaction_withOld_transactionOptions()
|
||||||
|
throws RocksDBException {
|
||||||
|
try (final Options options = new Options().setCreateIfMissing(true);
|
||||||
|
final OptimisticTransactionDB otdb = OptimisticTransactionDB.open(
|
||||||
|
options, dbFolder.getRoot().getAbsolutePath());
|
||||||
|
final WriteOptions writeOptions = new WriteOptions();
|
||||||
|
final OptimisticTransactionOptions optimisticTxnOptions =
|
||||||
|
new OptimisticTransactionOptions()) {
|
||||||
|
|
||||||
|
try(final Transaction txn = otdb.beginTransaction(writeOptions)) {
|
||||||
|
final Transaction txnReused = otdb.beginTransaction(writeOptions,
|
||||||
|
optimisticTxnOptions, txn);
|
||||||
|
assertThat(txnReused).isSameAs(txn);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void baseDB() throws RocksDBException {
|
||||||
|
try (final Options options = new Options().setCreateIfMissing(true);
|
||||||
|
final OptimisticTransactionDB otdb = OptimisticTransactionDB.open(options,
|
||||||
|
dbFolder.getRoot().getAbsolutePath())) {
|
||||||
|
assertThat(otdb).isNotNull();
|
||||||
|
final RocksDB db = otdb.getBaseDB();
|
||||||
|
assertThat(db).isNotNull();
|
||||||
|
assertThat(db.isOwningHandle()).isFalse();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,37 @@
|
|||||||
|
// Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
|
||||||
|
// This source code is licensed under both the GPLv2 (found in the
|
||||||
|
// COPYING file in the root directory) and Apache 2.0 License
|
||||||
|
// (found in the LICENSE.Apache file in the root directory).
|
||||||
|
|
||||||
|
package org.rocksdb;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.rocksdb.util.DirectBytewiseComparator;
|
||||||
|
|
||||||
|
import java.util.Random;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
|
public class OptimisticTransactionOptionsTest {
|
||||||
|
|
||||||
|
private static final Random rand = PlatformRandomHelper.
|
||||||
|
getPlatformSpecificRandomFactory();
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void setSnapshot() {
|
||||||
|
try (final OptimisticTransactionOptions opt = new OptimisticTransactionOptions()) {
|
||||||
|
final boolean boolValue = rand.nextBoolean();
|
||||||
|
opt.setSetSnapshot(boolValue);
|
||||||
|
assertThat(opt.isSetSnapshot()).isEqualTo(boolValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void comparator() {
|
||||||
|
try (final OptimisticTransactionOptions opt = new OptimisticTransactionOptions();
|
||||||
|
final ComparatorOptions copt = new ComparatorOptions();
|
||||||
|
final DirectComparator comparator = new DirectBytewiseComparator(copt)) {
|
||||||
|
opt.setComparator(comparator);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
350
java/src/test/java/org/rocksdb/OptimisticTransactionTest.java
Normal file
350
java/src/test/java/org/rocksdb/OptimisticTransactionTest.java
Normal file
@ -0,0 +1,350 @@
|
|||||||
|
// Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
|
||||||
|
// This source code is licensed under both the GPLv2 (found in the
|
||||||
|
// COPYING file in the root directory) and Apache 2.0 License
|
||||||
|
// (found in the LICENSE.Apache file in the root directory).
|
||||||
|
|
||||||
|
package org.rocksdb;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
import static org.assertj.core.api.Assertions.fail;
|
||||||
|
|
||||||
|
public class OptimisticTransactionTest extends AbstractTransactionTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getForUpdate_cf_conflict() throws RocksDBException {
|
||||||
|
final byte k1[] = "key1".getBytes(UTF_8);
|
||||||
|
final byte v1[] = "value1".getBytes(UTF_8);
|
||||||
|
final byte v12[] = "value12".getBytes(UTF_8);
|
||||||
|
try(final DBContainer dbContainer = startDb();
|
||||||
|
final ReadOptions readOptions = new ReadOptions()) {
|
||||||
|
final ColumnFamilyHandle testCf = dbContainer.getTestColumnFamily();
|
||||||
|
|
||||||
|
try(final Transaction txn = dbContainer.beginTransaction()) {
|
||||||
|
txn.put(testCf, k1, v1);
|
||||||
|
assertThat(txn.get(testCf, readOptions, k1)).isEqualTo(v1);
|
||||||
|
txn.commit();
|
||||||
|
}
|
||||||
|
|
||||||
|
try(final Transaction txn2 = dbContainer.beginTransaction()) {
|
||||||
|
try(final Transaction txn3 = dbContainer.beginTransaction()) {
|
||||||
|
assertThat(txn3.getForUpdate(readOptions, testCf, k1, true)).isEqualTo(v1);
|
||||||
|
|
||||||
|
// NOTE: txn2 updates k1, during txn3
|
||||||
|
txn2.put(testCf, k1, v12);
|
||||||
|
assertThat(txn2.get(testCf, readOptions, k1)).isEqualTo(v12);
|
||||||
|
txn2.commit();
|
||||||
|
|
||||||
|
try {
|
||||||
|
txn3.commit(); // should cause an exception!
|
||||||
|
} catch(final RocksDBException e) {
|
||||||
|
assertThat(e.getStatus().getCode()).isSameAs(Status.Code.Busy);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fail("Expected an exception for put after getForUpdate from conflicting" +
|
||||||
|
"transactions");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getForUpdate_conflict() throws RocksDBException {
|
||||||
|
final byte k1[] = "key1".getBytes(UTF_8);
|
||||||
|
final byte v1[] = "value1".getBytes(UTF_8);
|
||||||
|
final byte v12[] = "value12".getBytes(UTF_8);
|
||||||
|
try(final DBContainer dbContainer = startDb();
|
||||||
|
final ReadOptions readOptions = new ReadOptions()) {
|
||||||
|
|
||||||
|
try(final Transaction txn = dbContainer.beginTransaction()) {
|
||||||
|
txn.put(k1, v1);
|
||||||
|
assertThat(txn.get(readOptions, k1)).isEqualTo(v1);
|
||||||
|
txn.commit();
|
||||||
|
}
|
||||||
|
|
||||||
|
try(final Transaction txn2 = dbContainer.beginTransaction()) {
|
||||||
|
try(final Transaction txn3 = dbContainer.beginTransaction()) {
|
||||||
|
assertThat(txn3.getForUpdate(readOptions, k1, true)).isEqualTo(v1);
|
||||||
|
|
||||||
|
// NOTE: txn2 updates k1, during txn3
|
||||||
|
txn2.put(k1, v12);
|
||||||
|
assertThat(txn2.get(readOptions, k1)).isEqualTo(v12);
|
||||||
|
txn2.commit();
|
||||||
|
|
||||||
|
try {
|
||||||
|
txn3.commit(); // should cause an exception!
|
||||||
|
} catch(final RocksDBException e) {
|
||||||
|
assertThat(e.getStatus().getCode()).isSameAs(Status.Code.Busy);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fail("Expected an exception for put after getForUpdate from conflicting" +
|
||||||
|
"transactions");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void multiGetForUpdate_cf_conflict() throws RocksDBException {
|
||||||
|
final byte keys[][] = new byte[][] {
|
||||||
|
"key1".getBytes(UTF_8),
|
||||||
|
"key2".getBytes(UTF_8)};
|
||||||
|
final byte values[][] = new byte[][] {
|
||||||
|
"value1".getBytes(UTF_8),
|
||||||
|
"value2".getBytes(UTF_8)};
|
||||||
|
final byte[] otherValue = "otherValue".getBytes(UTF_8);
|
||||||
|
|
||||||
|
try(final DBContainer dbContainer = startDb();
|
||||||
|
final ReadOptions readOptions = new ReadOptions()) {
|
||||||
|
final ColumnFamilyHandle testCf = dbContainer.getTestColumnFamily();
|
||||||
|
final List<ColumnFamilyHandle> cfList = Arrays.asList(testCf, testCf);
|
||||||
|
|
||||||
|
try(final Transaction txn = dbContainer.beginTransaction()) {
|
||||||
|
txn.put(testCf, keys[0], values[0]);
|
||||||
|
txn.put(testCf, keys[1], values[1]);
|
||||||
|
assertThat(txn.multiGet(readOptions, cfList, keys)).isEqualTo(values);
|
||||||
|
txn.commit();
|
||||||
|
}
|
||||||
|
|
||||||
|
try(final Transaction txn2 = dbContainer.beginTransaction()) {
|
||||||
|
try(final Transaction txn3 = dbContainer.beginTransaction()) {
|
||||||
|
assertThat(txn3.multiGetForUpdate(readOptions, cfList, keys))
|
||||||
|
.isEqualTo(values);
|
||||||
|
|
||||||
|
// NOTE: txn2 updates k1, during txn3
|
||||||
|
txn2.put(testCf, keys[0], otherValue);
|
||||||
|
assertThat(txn2.get(testCf, readOptions, keys[0]))
|
||||||
|
.isEqualTo(otherValue);
|
||||||
|
txn2.commit();
|
||||||
|
|
||||||
|
try {
|
||||||
|
txn3.commit(); // should cause an exception!
|
||||||
|
} catch(final RocksDBException e) {
|
||||||
|
assertThat(e.getStatus().getCode()).isSameAs(Status.Code.Busy);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fail("Expected an exception for put after getForUpdate from conflicting" +
|
||||||
|
"transactions");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void multiGetForUpdate_conflict() throws RocksDBException {
|
||||||
|
final byte keys[][] = new byte[][] {
|
||||||
|
"key1".getBytes(UTF_8),
|
||||||
|
"key2".getBytes(UTF_8)};
|
||||||
|
final byte values[][] = new byte[][] {
|
||||||
|
"value1".getBytes(UTF_8),
|
||||||
|
"value2".getBytes(UTF_8)};
|
||||||
|
final byte[] otherValue = "otherValue".getBytes(UTF_8);
|
||||||
|
|
||||||
|
try(final DBContainer dbContainer = startDb();
|
||||||
|
final ReadOptions readOptions = new ReadOptions()) {
|
||||||
|
try(final Transaction txn = dbContainer.beginTransaction()) {
|
||||||
|
txn.put(keys[0], values[0]);
|
||||||
|
txn.put(keys[1], values[1]);
|
||||||
|
assertThat(txn.multiGet(readOptions, keys)).isEqualTo(values);
|
||||||
|
txn.commit();
|
||||||
|
}
|
||||||
|
|
||||||
|
try(final Transaction txn2 = dbContainer.beginTransaction()) {
|
||||||
|
try(final Transaction txn3 = dbContainer.beginTransaction()) {
|
||||||
|
assertThat(txn3.multiGetForUpdate(readOptions, keys))
|
||||||
|
.isEqualTo(values);
|
||||||
|
|
||||||
|
// NOTE: txn2 updates k1, during txn3
|
||||||
|
txn2.put(keys[0], otherValue);
|
||||||
|
assertThat(txn2.get(readOptions, keys[0]))
|
||||||
|
.isEqualTo(otherValue);
|
||||||
|
txn2.commit();
|
||||||
|
|
||||||
|
try {
|
||||||
|
txn3.commit(); // should cause an exception!
|
||||||
|
} catch(final RocksDBException e) {
|
||||||
|
assertThat(e.getStatus().getCode()).isSameAs(Status.Code.Busy);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fail("Expected an exception for put after getForUpdate from conflicting" +
|
||||||
|
"transactions");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void undoGetForUpdate_cf_conflict() throws RocksDBException {
|
||||||
|
final byte k1[] = "key1".getBytes(UTF_8);
|
||||||
|
final byte v1[] = "value1".getBytes(UTF_8);
|
||||||
|
final byte v12[] = "value12".getBytes(UTF_8);
|
||||||
|
try(final DBContainer dbContainer = startDb();
|
||||||
|
final ReadOptions readOptions = new ReadOptions()) {
|
||||||
|
final ColumnFamilyHandle testCf = dbContainer.getTestColumnFamily();
|
||||||
|
|
||||||
|
try(final Transaction txn = dbContainer.beginTransaction()) {
|
||||||
|
txn.put(testCf, k1, v1);
|
||||||
|
assertThat(txn.get(testCf, readOptions, k1)).isEqualTo(v1);
|
||||||
|
txn.commit();
|
||||||
|
}
|
||||||
|
|
||||||
|
try(final Transaction txn2 = dbContainer.beginTransaction()) {
|
||||||
|
try(final Transaction txn3 = dbContainer.beginTransaction()) {
|
||||||
|
assertThat(txn3.getForUpdate(readOptions, testCf, k1, true)).isEqualTo(v1);
|
||||||
|
|
||||||
|
// undo the getForUpdate
|
||||||
|
txn3.undoGetForUpdate(testCf, k1);
|
||||||
|
|
||||||
|
// NOTE: txn2 updates k1, during txn3
|
||||||
|
txn2.put(testCf, k1, v12);
|
||||||
|
assertThat(txn2.get(testCf, readOptions, k1)).isEqualTo(v12);
|
||||||
|
txn2.commit();
|
||||||
|
|
||||||
|
// should not cause an exception
|
||||||
|
// because we undid the getForUpdate above!
|
||||||
|
txn3.commit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void undoGetForUpdate_conflict() throws RocksDBException {
|
||||||
|
final byte k1[] = "key1".getBytes(UTF_8);
|
||||||
|
final byte v1[] = "value1".getBytes(UTF_8);
|
||||||
|
final byte v12[] = "value12".getBytes(UTF_8);
|
||||||
|
try(final DBContainer dbContainer = startDb();
|
||||||
|
final ReadOptions readOptions = new ReadOptions()) {
|
||||||
|
|
||||||
|
try(final Transaction txn = dbContainer.beginTransaction()) {
|
||||||
|
txn.put(k1, v1);
|
||||||
|
assertThat(txn.get(readOptions, k1)).isEqualTo(v1);
|
||||||
|
txn.commit();
|
||||||
|
}
|
||||||
|
|
||||||
|
try(final Transaction txn2 = dbContainer.beginTransaction()) {
|
||||||
|
try(final Transaction txn3 = dbContainer.beginTransaction()) {
|
||||||
|
assertThat(txn3.getForUpdate(readOptions, k1, true)).isEqualTo(v1);
|
||||||
|
|
||||||
|
// undo the getForUpdate
|
||||||
|
txn3.undoGetForUpdate(k1);
|
||||||
|
|
||||||
|
// NOTE: txn2 updates k1, during txn3
|
||||||
|
txn2.put(k1, v12);
|
||||||
|
assertThat(txn2.get(readOptions, k1)).isEqualTo(v12);
|
||||||
|
txn2.commit();
|
||||||
|
|
||||||
|
// should not cause an exception
|
||||||
|
// because we undid the getForUpdate above!
|
||||||
|
txn3.commit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void name() throws RocksDBException {
|
||||||
|
try(final DBContainer dbContainer = startDb();
|
||||||
|
final Transaction txn = dbContainer.beginTransaction()) {
|
||||||
|
assertThat(txn.getName()).isEmpty();
|
||||||
|
final String name = "my-transaction-" + rand.nextLong();
|
||||||
|
|
||||||
|
try {
|
||||||
|
txn.setName(name);
|
||||||
|
} catch(final RocksDBException e) {
|
||||||
|
assertThat(e.getStatus().getCode() == Status.Code.InvalidArgument);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
fail("Optimistic transactions cannot be named.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public OptimisticTransactionDBContainer startDb()
|
||||||
|
throws RocksDBException {
|
||||||
|
final DBOptions options = new DBOptions()
|
||||||
|
.setCreateIfMissing(true)
|
||||||
|
.setCreateMissingColumnFamilies(true);
|
||||||
|
|
||||||
|
final ColumnFamilyOptions columnFamilyOptions = new ColumnFamilyOptions();
|
||||||
|
final List<ColumnFamilyDescriptor> columnFamilyDescriptors =
|
||||||
|
Arrays.asList(
|
||||||
|
new ColumnFamilyDescriptor(RocksDB.DEFAULT_COLUMN_FAMILY),
|
||||||
|
new ColumnFamilyDescriptor(TXN_TEST_COLUMN_FAMILY,
|
||||||
|
columnFamilyOptions));
|
||||||
|
final List<ColumnFamilyHandle> columnFamilyHandles = new ArrayList<>();
|
||||||
|
|
||||||
|
final OptimisticTransactionDB optimisticTxnDb;
|
||||||
|
try {
|
||||||
|
optimisticTxnDb = OptimisticTransactionDB.open(
|
||||||
|
options, dbFolder.getRoot().getAbsolutePath(),
|
||||||
|
columnFamilyDescriptors, columnFamilyHandles);
|
||||||
|
} catch(final RocksDBException e) {
|
||||||
|
columnFamilyOptions.close();
|
||||||
|
options.close();
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
|
||||||
|
final WriteOptions writeOptions = new WriteOptions();
|
||||||
|
final OptimisticTransactionOptions optimisticTxnOptions =
|
||||||
|
new OptimisticTransactionOptions();
|
||||||
|
|
||||||
|
return new OptimisticTransactionDBContainer(optimisticTxnOptions,
|
||||||
|
writeOptions, columnFamilyHandles, optimisticTxnDb, columnFamilyOptions,
|
||||||
|
options);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class OptimisticTransactionDBContainer
|
||||||
|
extends DBContainer {
|
||||||
|
|
||||||
|
private final OptimisticTransactionOptions optimisticTxnOptions;
|
||||||
|
private final OptimisticTransactionDB optimisticTxnDb;
|
||||||
|
|
||||||
|
public OptimisticTransactionDBContainer(
|
||||||
|
final OptimisticTransactionOptions optimisticTxnOptions,
|
||||||
|
final WriteOptions writeOptions,
|
||||||
|
final List<ColumnFamilyHandle> columnFamilyHandles,
|
||||||
|
final OptimisticTransactionDB optimisticTxnDb,
|
||||||
|
final ColumnFamilyOptions columnFamilyOptions,
|
||||||
|
final DBOptions options) {
|
||||||
|
super(writeOptions, columnFamilyHandles, columnFamilyOptions,
|
||||||
|
options);
|
||||||
|
this.optimisticTxnOptions = optimisticTxnOptions;
|
||||||
|
this.optimisticTxnDb = optimisticTxnDb;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Transaction beginTransaction() {
|
||||||
|
return optimisticTxnDb.beginTransaction(writeOptions,
|
||||||
|
optimisticTxnOptions);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Transaction beginTransaction(final WriteOptions writeOptions) {
|
||||||
|
return optimisticTxnDb.beginTransaction(writeOptions,
|
||||||
|
optimisticTxnOptions);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() {
|
||||||
|
optimisticTxnOptions.close();
|
||||||
|
writeOptions.close();
|
||||||
|
for(final ColumnFamilyHandle columnFamilyHandle : columnFamilyHandles) {
|
||||||
|
columnFamilyHandle.close();
|
||||||
|
}
|
||||||
|
optimisticTxnDb.close();
|
||||||
|
options.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
64
java/src/test/java/org/rocksdb/TransactionDBOptionsTest.java
Normal file
64
java/src/test/java/org/rocksdb/TransactionDBOptionsTest.java
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
// Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
|
||||||
|
// This source code is licensed under both the GPLv2 (found in the
|
||||||
|
// COPYING file in the root directory) and Apache 2.0 License
|
||||||
|
// (found in the LICENSE.Apache file in the root directory).
|
||||||
|
|
||||||
|
package org.rocksdb;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.util.Random;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
|
public class TransactionDBOptionsTest {
|
||||||
|
|
||||||
|
private static final Random rand = PlatformRandomHelper.
|
||||||
|
getPlatformSpecificRandomFactory();
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void maxNumLocks() {
|
||||||
|
try (final TransactionDBOptions opt = new TransactionDBOptions()) {
|
||||||
|
final long longValue = rand.nextLong();
|
||||||
|
opt.setMaxNumLocks(longValue);
|
||||||
|
assertThat(opt.getMaxNumLocks()).isEqualTo(longValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void maxNumStripes() {
|
||||||
|
try (final TransactionDBOptions opt = new TransactionDBOptions()) {
|
||||||
|
final long longValue = rand.nextLong();
|
||||||
|
opt.setNumStripes(longValue);
|
||||||
|
assertThat(opt.getNumStripes()).isEqualTo(longValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void transactionLockTimeout() {
|
||||||
|
try (final TransactionDBOptions opt = new TransactionDBOptions()) {
|
||||||
|
final long longValue = rand.nextLong();
|
||||||
|
opt.setTransactionLockTimeout(longValue);
|
||||||
|
assertThat(opt.getTransactionLockTimeout()).isEqualTo(longValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void defaultLockTimeout() {
|
||||||
|
try (final TransactionDBOptions opt = new TransactionDBOptions()) {
|
||||||
|
final long longValue = rand.nextLong();
|
||||||
|
opt.setDefaultLockTimeout(longValue);
|
||||||
|
assertThat(opt.getDefaultLockTimeout()).isEqualTo(longValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void writePolicy() {
|
||||||
|
try (final TransactionDBOptions opt = new TransactionDBOptions()) {
|
||||||
|
final TxnDBWritePolicy writePolicy = TxnDBWritePolicy.WRITE_UNPREPARED; // non-default
|
||||||
|
opt.setWritePolicy(writePolicy);
|
||||||
|
assertThat(opt.getWritePolicy()).isEqualTo(writePolicy);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
178
java/src/test/java/org/rocksdb/TransactionDBTest.java
Normal file
178
java/src/test/java/org/rocksdb/TransactionDBTest.java
Normal file
@ -0,0 +1,178 @@
|
|||||||
|
// Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
|
||||||
|
// This source code is licensed under both the GPLv2 (found in the
|
||||||
|
// COPYING file in the root directory) and Apache 2.0 License
|
||||||
|
// (found in the LICENSE.Apache file in the root directory).
|
||||||
|
|
||||||
|
package org.rocksdb;
|
||||||
|
|
||||||
|
import org.junit.Rule;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.rules.TemporaryFolder;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||||
|
|
||||||
|
public class TransactionDBTest {
|
||||||
|
|
||||||
|
@Rule
|
||||||
|
public TemporaryFolder dbFolder = new TemporaryFolder();
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void open() throws RocksDBException {
|
||||||
|
try (final Options options = new Options().setCreateIfMissing(true);
|
||||||
|
final TransactionDBOptions txnDbOptions = new TransactionDBOptions();
|
||||||
|
final TransactionDB tdb = TransactionDB.open(options, txnDbOptions,
|
||||||
|
dbFolder.getRoot().getAbsolutePath())) {
|
||||||
|
assertThat(tdb).isNotNull();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void open_columnFamilies() throws RocksDBException {
|
||||||
|
try(final DBOptions dbOptions = new DBOptions().setCreateIfMissing(true)
|
||||||
|
.setCreateMissingColumnFamilies(true);
|
||||||
|
final ColumnFamilyOptions myCfOpts = new ColumnFamilyOptions()) {
|
||||||
|
|
||||||
|
final List<ColumnFamilyDescriptor> columnFamilyDescriptors =
|
||||||
|
Arrays.asList(
|
||||||
|
new ColumnFamilyDescriptor(RocksDB.DEFAULT_COLUMN_FAMILY),
|
||||||
|
new ColumnFamilyDescriptor("myCf".getBytes(), myCfOpts));
|
||||||
|
|
||||||
|
final List<ColumnFamilyHandle> columnFamilyHandles = new ArrayList<>();
|
||||||
|
|
||||||
|
try (final TransactionDBOptions txnDbOptions = new TransactionDBOptions();
|
||||||
|
final TransactionDB tdb = TransactionDB.open(dbOptions, txnDbOptions,
|
||||||
|
dbFolder.getRoot().getAbsolutePath(),
|
||||||
|
columnFamilyDescriptors, columnFamilyHandles)) {
|
||||||
|
try {
|
||||||
|
assertThat(tdb).isNotNull();
|
||||||
|
} finally {
|
||||||
|
for (final ColumnFamilyHandle handle : columnFamilyHandles) {
|
||||||
|
handle.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void beginTransaction() throws RocksDBException {
|
||||||
|
try (final Options options = new Options().setCreateIfMissing(true);
|
||||||
|
final TransactionDBOptions txnDbOptions = new TransactionDBOptions();
|
||||||
|
final TransactionDB tdb = TransactionDB.open(options, txnDbOptions,
|
||||||
|
dbFolder.getRoot().getAbsolutePath());
|
||||||
|
final WriteOptions writeOptions = new WriteOptions()) {
|
||||||
|
|
||||||
|
try(final Transaction txn = tdb.beginTransaction(writeOptions)) {
|
||||||
|
assertThat(txn).isNotNull();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void beginTransaction_transactionOptions() throws RocksDBException {
|
||||||
|
try (final Options options = new Options().setCreateIfMissing(true);
|
||||||
|
final TransactionDBOptions txnDbOptions = new TransactionDBOptions();
|
||||||
|
final TransactionDB tdb = TransactionDB.open(options, txnDbOptions,
|
||||||
|
dbFolder.getRoot().getAbsolutePath());
|
||||||
|
final WriteOptions writeOptions = new WriteOptions();
|
||||||
|
final TransactionOptions txnOptions = new TransactionOptions()) {
|
||||||
|
|
||||||
|
try(final Transaction txn = tdb.beginTransaction(writeOptions,
|
||||||
|
txnOptions)) {
|
||||||
|
assertThat(txn).isNotNull();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void beginTransaction_withOld() throws RocksDBException {
|
||||||
|
try (final Options options = new Options().setCreateIfMissing(true);
|
||||||
|
final TransactionDBOptions txnDbOptions = new TransactionDBOptions();
|
||||||
|
final TransactionDB tdb = TransactionDB.open(options, txnDbOptions,
|
||||||
|
dbFolder.getRoot().getAbsolutePath());
|
||||||
|
final WriteOptions writeOptions = new WriteOptions()) {
|
||||||
|
|
||||||
|
try(final Transaction txn = tdb.beginTransaction(writeOptions)) {
|
||||||
|
final Transaction txnReused = tdb.beginTransaction(writeOptions, txn);
|
||||||
|
assertThat(txnReused).isSameAs(txn);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void beginTransaction_withOld_transactionOptions()
|
||||||
|
throws RocksDBException {
|
||||||
|
try (final Options options = new Options().setCreateIfMissing(true);
|
||||||
|
final TransactionDBOptions txnDbOptions = new TransactionDBOptions();
|
||||||
|
final TransactionDB tdb = TransactionDB.open(options, txnDbOptions,
|
||||||
|
dbFolder.getRoot().getAbsolutePath());
|
||||||
|
final WriteOptions writeOptions = new WriteOptions();
|
||||||
|
final TransactionOptions txnOptions = new TransactionOptions()) {
|
||||||
|
|
||||||
|
try(final Transaction txn = tdb.beginTransaction(writeOptions)) {
|
||||||
|
final Transaction txnReused = tdb.beginTransaction(writeOptions,
|
||||||
|
txnOptions, txn);
|
||||||
|
assertThat(txnReused).isSameAs(txn);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void lockStatusData() throws RocksDBException {
|
||||||
|
try (final Options options = new Options().setCreateIfMissing(true);
|
||||||
|
final TransactionDBOptions txnDbOptions = new TransactionDBOptions();
|
||||||
|
final TransactionDB tdb = TransactionDB.open(options, txnDbOptions,
|
||||||
|
dbFolder.getRoot().getAbsolutePath());
|
||||||
|
final WriteOptions writeOptions = new WriteOptions();
|
||||||
|
final ReadOptions readOptions = new ReadOptions()) {
|
||||||
|
|
||||||
|
try (final Transaction txn = tdb.beginTransaction(writeOptions)) {
|
||||||
|
|
||||||
|
final byte key[] = "key".getBytes(UTF_8);
|
||||||
|
final byte value[] = "value".getBytes(UTF_8);
|
||||||
|
|
||||||
|
txn.put(key, value);
|
||||||
|
assertThat(txn.getForUpdate(readOptions, key, true)).isEqualTo(value);
|
||||||
|
|
||||||
|
final Map<Long, TransactionDB.KeyLockInfo> lockStatus =
|
||||||
|
tdb.getLockStatusData();
|
||||||
|
|
||||||
|
assertThat(lockStatus.size()).isEqualTo(1);
|
||||||
|
final Set<Map.Entry<Long, TransactionDB.KeyLockInfo>> entrySet = lockStatus.entrySet();
|
||||||
|
final Map.Entry<Long, TransactionDB.KeyLockInfo> entry = entrySet.iterator().next();
|
||||||
|
final long columnFamilyId = entry.getKey();
|
||||||
|
assertThat(columnFamilyId).isEqualTo(0);
|
||||||
|
final TransactionDB.KeyLockInfo keyLockInfo = entry.getValue();
|
||||||
|
assertThat(keyLockInfo.getKey()).isEqualTo(new String(key, UTF_8));
|
||||||
|
assertThat(keyLockInfo.getTransactionIDs().length).isEqualTo(1);
|
||||||
|
assertThat(keyLockInfo.getTransactionIDs()[0]).isEqualTo(txn.getId());
|
||||||
|
assertThat(keyLockInfo.isExclusive()).isTrue();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void deadlockInfoBuffer() throws RocksDBException {
|
||||||
|
try (final Options options = new Options().setCreateIfMissing(true);
|
||||||
|
final TransactionDBOptions txnDbOptions = new TransactionDBOptions();
|
||||||
|
final TransactionDB tdb = TransactionDB.open(options, txnDbOptions,
|
||||||
|
dbFolder.getRoot().getAbsolutePath())) {
|
||||||
|
|
||||||
|
// TODO(AR) can we cause a deadlock so that we can test the output here?
|
||||||
|
assertThat(tdb.getDeadlockInfoBuffer()).isEmpty();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void setDeadlockInfoBufferSize() throws RocksDBException {
|
||||||
|
try (final Options options = new Options().setCreateIfMissing(true);
|
||||||
|
final TransactionDBOptions txnDbOptions = new TransactionDBOptions();
|
||||||
|
final TransactionDB tdb = TransactionDB.open(options, txnDbOptions,
|
||||||
|
dbFolder.getRoot().getAbsolutePath())) {
|
||||||
|
tdb.setDeadlockInfoBufferSize(123);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
72
java/src/test/java/org/rocksdb/TransactionOptionsTest.java
Normal file
72
java/src/test/java/org/rocksdb/TransactionOptionsTest.java
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
// Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
|
||||||
|
// This source code is licensed under both the GPLv2 (found in the
|
||||||
|
// COPYING file in the root directory) and Apache 2.0 License
|
||||||
|
// (found in the LICENSE.Apache file in the root directory).
|
||||||
|
|
||||||
|
package org.rocksdb;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.util.Random;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
|
public class TransactionOptionsTest {
|
||||||
|
|
||||||
|
private static final Random rand = PlatformRandomHelper.
|
||||||
|
getPlatformSpecificRandomFactory();
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void snapshot() {
|
||||||
|
try (final TransactionOptions opt = new TransactionOptions()) {
|
||||||
|
final boolean boolValue = rand.nextBoolean();
|
||||||
|
opt.setSetSnapshot(boolValue);
|
||||||
|
assertThat(opt.isSetSnapshot()).isEqualTo(boolValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void deadlockDetect() {
|
||||||
|
try (final TransactionOptions opt = new TransactionOptions()) {
|
||||||
|
final boolean boolValue = rand.nextBoolean();
|
||||||
|
opt.setDeadlockDetect(boolValue);
|
||||||
|
assertThat(opt.isDeadlockDetect()).isEqualTo(boolValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void lockTimeout() {
|
||||||
|
try (final TransactionOptions opt = new TransactionOptions()) {
|
||||||
|
final long longValue = rand.nextLong();
|
||||||
|
opt.setLockTimeout(longValue);
|
||||||
|
assertThat(opt.getLockTimeout()).isEqualTo(longValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void expiration() {
|
||||||
|
try (final TransactionOptions opt = new TransactionOptions()) {
|
||||||
|
final long longValue = rand.nextLong();
|
||||||
|
opt.setExpiration(longValue);
|
||||||
|
assertThat(opt.getExpiration()).isEqualTo(longValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void deadlockDetectDepth() {
|
||||||
|
try (final TransactionOptions opt = new TransactionOptions()) {
|
||||||
|
final long longValue = rand.nextLong();
|
||||||
|
opt.setDeadlockDetectDepth(longValue);
|
||||||
|
assertThat(opt.getDeadlockDetectDepth()).isEqualTo(longValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void maxWriteBatchSize() {
|
||||||
|
try (final TransactionOptions opt = new TransactionOptions()) {
|
||||||
|
final long longValue = rand.nextLong();
|
||||||
|
opt.setMaxWriteBatchSize(longValue);
|
||||||
|
assertThat(opt.getMaxWriteBatchSize()).isEqualTo(longValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
308
java/src/test/java/org/rocksdb/TransactionTest.java
Normal file
308
java/src/test/java/org/rocksdb/TransactionTest.java
Normal file
@ -0,0 +1,308 @@
|
|||||||
|
// Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
|
||||||
|
// This source code is licensed under both the GPLv2 (found in the
|
||||||
|
// COPYING file in the root directory) and Apache 2.0 License
|
||||||
|
// (found in the LICENSE.Apache file in the root directory).
|
||||||
|
|
||||||
|
package org.rocksdb;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
import static org.assertj.core.api.Assertions.fail;
|
||||||
|
|
||||||
|
public class TransactionTest extends AbstractTransactionTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getForUpdate_cf_conflict() throws RocksDBException {
|
||||||
|
final byte k1[] = "key1".getBytes(UTF_8);
|
||||||
|
final byte v1[] = "value1".getBytes(UTF_8);
|
||||||
|
final byte v12[] = "value12".getBytes(UTF_8);
|
||||||
|
try(final DBContainer dbContainer = startDb();
|
||||||
|
final ReadOptions readOptions = new ReadOptions()) {
|
||||||
|
final ColumnFamilyHandle testCf = dbContainer.getTestColumnFamily();
|
||||||
|
|
||||||
|
try(final Transaction txn = dbContainer.beginTransaction()) {
|
||||||
|
txn.put(testCf, k1, v1);
|
||||||
|
assertThat(txn.getForUpdate(readOptions, testCf, k1, true)).isEqualTo(v1);
|
||||||
|
txn.commit();
|
||||||
|
}
|
||||||
|
|
||||||
|
try(final Transaction txn2 = dbContainer.beginTransaction()) {
|
||||||
|
try(final Transaction txn3 = dbContainer.beginTransaction()) {
|
||||||
|
assertThat(txn3.getForUpdate(readOptions, testCf, k1, true)).isEqualTo(v1);
|
||||||
|
|
||||||
|
// NOTE: txn2 updates k1, during txn3
|
||||||
|
try {
|
||||||
|
txn2.put(testCf, k1, v12); // should cause an exception!
|
||||||
|
} catch(final RocksDBException e) {
|
||||||
|
assertThat(e.getStatus().getCode()).isSameAs(Status.Code.TimedOut);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fail("Expected an exception for put after getForUpdate from conflicting" +
|
||||||
|
"transactions");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getForUpdate_conflict() throws RocksDBException {
|
||||||
|
final byte k1[] = "key1".getBytes(UTF_8);
|
||||||
|
final byte v1[] = "value1".getBytes(UTF_8);
|
||||||
|
final byte v12[] = "value12".getBytes(UTF_8);
|
||||||
|
try(final DBContainer dbContainer = startDb();
|
||||||
|
final ReadOptions readOptions = new ReadOptions()) {
|
||||||
|
|
||||||
|
try(final Transaction txn = dbContainer.beginTransaction()) {
|
||||||
|
txn.put(k1, v1);
|
||||||
|
assertThat(txn.getForUpdate(readOptions, k1, true)).isEqualTo(v1);
|
||||||
|
txn.commit();
|
||||||
|
}
|
||||||
|
|
||||||
|
try(final Transaction txn2 = dbContainer.beginTransaction()) {
|
||||||
|
try(final Transaction txn3 = dbContainer.beginTransaction()) {
|
||||||
|
assertThat(txn3.getForUpdate(readOptions, k1, true)).isEqualTo(v1);
|
||||||
|
|
||||||
|
// NOTE: txn2 updates k1, during txn3
|
||||||
|
try {
|
||||||
|
txn2.put(k1, v12); // should cause an exception!
|
||||||
|
} catch(final RocksDBException e) {
|
||||||
|
assertThat(e.getStatus().getCode()).isSameAs(Status.Code.TimedOut);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fail("Expected an exception for put after getForUpdate from conflicting" +
|
||||||
|
"transactions");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void multiGetForUpdate_cf_conflict() throws RocksDBException {
|
||||||
|
final byte keys[][] = new byte[][] {
|
||||||
|
"key1".getBytes(UTF_8),
|
||||||
|
"key2".getBytes(UTF_8)};
|
||||||
|
final byte values[][] = new byte[][] {
|
||||||
|
"value1".getBytes(UTF_8),
|
||||||
|
"value2".getBytes(UTF_8)};
|
||||||
|
final byte[] otherValue = "otherValue".getBytes(UTF_8);
|
||||||
|
|
||||||
|
try(final DBContainer dbContainer = startDb();
|
||||||
|
final ReadOptions readOptions = new ReadOptions()) {
|
||||||
|
final ColumnFamilyHandle testCf = dbContainer.getTestColumnFamily();
|
||||||
|
final List<ColumnFamilyHandle> cfList = Arrays.asList(testCf, testCf);
|
||||||
|
|
||||||
|
try(final Transaction txn = dbContainer.beginTransaction()) {
|
||||||
|
txn.put(testCf, keys[0], values[0]);
|
||||||
|
txn.put(testCf, keys[1], values[1]);
|
||||||
|
assertThat(txn.multiGet(readOptions, cfList, keys)).isEqualTo(values);
|
||||||
|
txn.commit();
|
||||||
|
}
|
||||||
|
|
||||||
|
try(final Transaction txn2 = dbContainer.beginTransaction()) {
|
||||||
|
try(final Transaction txn3 = dbContainer.beginTransaction()) {
|
||||||
|
assertThat(txn3.multiGetForUpdate(readOptions, cfList, keys))
|
||||||
|
.isEqualTo(values);
|
||||||
|
|
||||||
|
// NOTE: txn2 updates k1, during txn3
|
||||||
|
try {
|
||||||
|
txn2.put(testCf, keys[0], otherValue); // should cause an exception!
|
||||||
|
} catch(final RocksDBException e) {
|
||||||
|
assertThat(e.getStatus().getCode()).isSameAs(Status.Code.TimedOut);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fail("Expected an exception for put after getForUpdate from conflicting" +
|
||||||
|
"transactions");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void multiGetForUpdate_conflict() throws RocksDBException {
|
||||||
|
final byte keys[][] = new byte[][] {
|
||||||
|
"key1".getBytes(UTF_8),
|
||||||
|
"key2".getBytes(UTF_8)};
|
||||||
|
final byte values[][] = new byte[][] {
|
||||||
|
"value1".getBytes(UTF_8),
|
||||||
|
"value2".getBytes(UTF_8)};
|
||||||
|
final byte[] otherValue = "otherValue".getBytes(UTF_8);
|
||||||
|
|
||||||
|
try(final DBContainer dbContainer = startDb();
|
||||||
|
final ReadOptions readOptions = new ReadOptions()) {
|
||||||
|
try(final Transaction txn = dbContainer.beginTransaction()) {
|
||||||
|
txn.put(keys[0], values[0]);
|
||||||
|
txn.put(keys[1], values[1]);
|
||||||
|
assertThat(txn.multiGet(readOptions, keys)).isEqualTo(values);
|
||||||
|
txn.commit();
|
||||||
|
}
|
||||||
|
|
||||||
|
try(final Transaction txn2 = dbContainer.beginTransaction()) {
|
||||||
|
try(final Transaction txn3 = dbContainer.beginTransaction()) {
|
||||||
|
assertThat(txn3.multiGetForUpdate(readOptions, keys))
|
||||||
|
.isEqualTo(values);
|
||||||
|
|
||||||
|
// NOTE: txn2 updates k1, during txn3
|
||||||
|
try {
|
||||||
|
txn2.put(keys[0], otherValue); // should cause an exception!
|
||||||
|
} catch(final RocksDBException e) {
|
||||||
|
assertThat(e.getStatus().getCode()).isSameAs(Status.Code.TimedOut);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fail("Expected an exception for put after getForUpdate from conflicting" +
|
||||||
|
"transactions");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void name() throws RocksDBException {
|
||||||
|
try(final DBContainer dbContainer = startDb();
|
||||||
|
final Transaction txn = dbContainer.beginTransaction()) {
|
||||||
|
assertThat(txn.getName()).isEmpty();
|
||||||
|
final String name = "my-transaction-" + rand.nextLong();
|
||||||
|
txn.setName(name);
|
||||||
|
assertThat(txn.getName()).isEqualTo(name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void ID() throws RocksDBException {
|
||||||
|
try(final DBContainer dbContainer = startDb();
|
||||||
|
final Transaction txn = dbContainer.beginTransaction()) {
|
||||||
|
assertThat(txn.getID()).isGreaterThan(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void deadlockDetect() throws RocksDBException {
|
||||||
|
try(final DBContainer dbContainer = startDb();
|
||||||
|
final Transaction txn = dbContainer.beginTransaction()) {
|
||||||
|
assertThat(txn.isDeadlockDetect()).isFalse();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void waitingTxns() throws RocksDBException {
|
||||||
|
try(final DBContainer dbContainer = startDb();
|
||||||
|
final Transaction txn = dbContainer.beginTransaction()) {
|
||||||
|
assertThat(txn.getWaitingTxns().getTransactionIds().length).isEqualTo(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void state() throws RocksDBException {
|
||||||
|
try(final DBContainer dbContainer = startDb()) {
|
||||||
|
|
||||||
|
try(final Transaction txn = dbContainer.beginTransaction()) {
|
||||||
|
assertThat(txn.getState())
|
||||||
|
.isSameAs(Transaction.TransactionState.STARTED);
|
||||||
|
txn.commit();
|
||||||
|
assertThat(txn.getState())
|
||||||
|
.isSameAs(Transaction.TransactionState.COMMITED);
|
||||||
|
}
|
||||||
|
|
||||||
|
try(final Transaction txn = dbContainer.beginTransaction()) {
|
||||||
|
assertThat(txn.getState())
|
||||||
|
.isSameAs(Transaction.TransactionState.STARTED);
|
||||||
|
txn.rollback();
|
||||||
|
assertThat(txn.getState())
|
||||||
|
.isSameAs(Transaction.TransactionState.STARTED);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void Id() throws RocksDBException {
|
||||||
|
try(final DBContainer dbContainer = startDb();
|
||||||
|
final Transaction txn = dbContainer.beginTransaction()) {
|
||||||
|
assertThat(txn.getId()).isNotNull();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TransactionDBContainer startDb() throws RocksDBException {
|
||||||
|
final DBOptions options = new DBOptions()
|
||||||
|
.setCreateIfMissing(true)
|
||||||
|
.setCreateMissingColumnFamilies(true);
|
||||||
|
final TransactionDBOptions txnDbOptions = new TransactionDBOptions();
|
||||||
|
final ColumnFamilyOptions columnFamilyOptions = new ColumnFamilyOptions();
|
||||||
|
final List<ColumnFamilyDescriptor> columnFamilyDescriptors =
|
||||||
|
Arrays.asList(
|
||||||
|
new ColumnFamilyDescriptor(RocksDB.DEFAULT_COLUMN_FAMILY),
|
||||||
|
new ColumnFamilyDescriptor(TXN_TEST_COLUMN_FAMILY,
|
||||||
|
columnFamilyOptions));
|
||||||
|
final List<ColumnFamilyHandle> columnFamilyHandles = new ArrayList<>();
|
||||||
|
|
||||||
|
final TransactionDB txnDb;
|
||||||
|
try {
|
||||||
|
txnDb = TransactionDB.open(options, txnDbOptions,
|
||||||
|
dbFolder.getRoot().getAbsolutePath(), columnFamilyDescriptors,
|
||||||
|
columnFamilyHandles);
|
||||||
|
} catch(final RocksDBException e) {
|
||||||
|
columnFamilyOptions.close();
|
||||||
|
txnDbOptions.close();
|
||||||
|
options.close();
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
|
||||||
|
final WriteOptions writeOptions = new WriteOptions();
|
||||||
|
final TransactionOptions txnOptions = new TransactionOptions();
|
||||||
|
|
||||||
|
return new TransactionDBContainer(txnOptions, writeOptions,
|
||||||
|
columnFamilyHandles, txnDb, txnDbOptions, columnFamilyOptions, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class TransactionDBContainer
|
||||||
|
extends DBContainer {
|
||||||
|
private final TransactionOptions txnOptions;
|
||||||
|
private final TransactionDB txnDb;
|
||||||
|
private final TransactionDBOptions txnDbOptions;
|
||||||
|
|
||||||
|
public TransactionDBContainer(
|
||||||
|
final TransactionOptions txnOptions, final WriteOptions writeOptions,
|
||||||
|
final List<ColumnFamilyHandle> columnFamilyHandles,
|
||||||
|
final TransactionDB txnDb, final TransactionDBOptions txnDbOptions,
|
||||||
|
final ColumnFamilyOptions columnFamilyOptions,
|
||||||
|
final DBOptions options) {
|
||||||
|
super(writeOptions, columnFamilyHandles, columnFamilyOptions,
|
||||||
|
options);
|
||||||
|
this.txnOptions = txnOptions;
|
||||||
|
this.txnDb = txnDb;
|
||||||
|
this.txnDbOptions = txnDbOptions;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Transaction beginTransaction() {
|
||||||
|
return txnDb.beginTransaction(writeOptions, txnOptions);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Transaction beginTransaction(final WriteOptions writeOptions) {
|
||||||
|
return txnDb.beginTransaction(writeOptions, txnOptions);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() {
|
||||||
|
txnOptions.close();
|
||||||
|
writeOptions.close();
|
||||||
|
for(final ColumnFamilyHandle columnFamilyHandle : columnFamilyHandles) {
|
||||||
|
columnFamilyHandle.close();
|
||||||
|
}
|
||||||
|
txnDb.close();
|
||||||
|
txnDbOptions.close();
|
||||||
|
options.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
8
src.mk
8
src.mk
@ -396,6 +396,8 @@ JNI_NATIVE_SOURCES = \
|
|||||||
java/rocksjni/lru_cache.cc \
|
java/rocksjni/lru_cache.cc \
|
||||||
java/rocksjni/memtablejni.cc \
|
java/rocksjni/memtablejni.cc \
|
||||||
java/rocksjni/merge_operator.cc \
|
java/rocksjni/merge_operator.cc \
|
||||||
|
java/rocksjni/optimistic_transaction_db.cc \
|
||||||
|
java/rocksjni/optimistic_transaction_options.cc \
|
||||||
java/rocksjni/options.cc \
|
java/rocksjni/options.cc \
|
||||||
java/rocksjni/options_util.cc \
|
java/rocksjni/options_util.cc \
|
||||||
java/rocksjni/ratelimiterjni.cc \
|
java/rocksjni/ratelimiterjni.cc \
|
||||||
@ -412,7 +414,13 @@ JNI_NATIVE_SOURCES = \
|
|||||||
java/rocksjni/statistics.cc \
|
java/rocksjni/statistics.cc \
|
||||||
java/rocksjni/statisticsjni.cc \
|
java/rocksjni/statisticsjni.cc \
|
||||||
java/rocksjni/table.cc \
|
java/rocksjni/table.cc \
|
||||||
|
java/rocksjni/transaction.cc \
|
||||||
|
java/rocksjni/transaction_db.cc \
|
||||||
|
java/rocksjni/transaction_options.cc \
|
||||||
|
java/rocksjni/transaction_db_options.cc \
|
||||||
java/rocksjni/transaction_log.cc \
|
java/rocksjni/transaction_log.cc \
|
||||||
|
java/rocksjni/transaction_notifier.cc \
|
||||||
|
java/rocksjni/transaction_notifier_jnicallback.cc \
|
||||||
java/rocksjni/ttl.cc \
|
java/rocksjni/ttl.cc \
|
||||||
java/rocksjni/write_batch.cc \
|
java/rocksjni/write_batch.cc \
|
||||||
java/rocksjni/writebatchhandlerjnicallback.cc \
|
java/rocksjni/writebatchhandlerjnicallback.cc \
|
||||||
|
Loading…
Reference in New Issue
Block a user