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: bcd34ce95ed88cc641786089ff4232df7b2f089fmain
parent
d060421c77
commit
2ac988c67e
@ -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); |
||||||
|
} |
@ -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); |
||||||
|
} |
File diff suppressed because it is too large
Load Diff
@ -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); |
||||||
|
} |
@ -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); |
||||||
|
} |
@ -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; |
||||||
|
} |
@ -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
|
@ -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_
|
@ -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); |
||||||
|
} |
@ -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); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -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); |
||||||
|
} |
@ -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); |
||||||
|
} |
File diff suppressed because it is too large
Load Diff
@ -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); |
||||||
|
} |
@ -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); |
||||||
|
} |
@ -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); |
||||||
|
} |
@ -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); |
||||||
|
} |
@ -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); |
||||||
|
} |
@ -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."); |
||||||
|
} |
||||||
|
} |
@ -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(); |
||||||
|
} |
||||||
|
} |
@ -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); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -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(); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -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); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -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); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -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); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -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(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} |
Loading…
Reference in new issue