Packages
rocksdb
2.5.0
3.1.1
3.1.0
3.0.0
2.6.2
2.6.1
retired
2.6.0
retired
2.5.0
2.4.1
2.4.0
2.3.0
2.2.0
2.1.0
2.0.0
1.9.0
1.8.0
1.7.0
1.6.0
1.5.1
1.5.0
1.4.0
1.3.2
1.3.1
1.3.0
1.2.0
1.1.1
1.1.0
1.0.0
0.26.2
0.26.1
0.26.0
0.25.0
0.24.0
0.23.3
0.23.2
0.23.1
0.23.0
0.22.0
0.21.0
0.20.1
0.20.0
0.19.0
0.18.0
0.17.0
0.16.0
0.15.0
0.14.0
0.13.1
0.13.0
0.12.0
0.11.0
0.10.0
0.9.1
0.9.0
0.8.2
0.8.1
0.8.0
0.7.1
0.7.0
0.6.4
0.6.3
0.6.2
0.6.1
0.6.0
RocksDB for Erlang
Current section
Files
Jump to
Current section
Files
c_src/transaction.cc
// -------------------------------------------------------------------
// Copyright (c) 2016-2026 Benoit Chesneau. All Rights Reserved.
//
// This file is provided to you under the Apache License,
// Version 2.0 (the "License"); you may not use this file
// except in compliance with the License. You may obtain
// a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
//
// -------------------------------------------------------------------
#include <iostream>
#include "erl_nif.h"
#include "rocksdb/db.h"
#include "rocksdb/options.h"
#include "rocksdb/slice.h"
#include "rocksdb/utilities/transaction.h"
#include "rocksdb/utilities/optimistic_transaction_db.h"
#include "atoms.h"
#include "refobjects.h"
#include "util.h"
#include "erocksdb_db.h"
#include "erocksdb_iter.h"
using namespace std;
namespace erocksdb {
ERL_NIF_TERM
NewTransaction(ErlNifEnv* env,
int argc,
const ERL_NIF_TERM argv[])
{
if(argc != 2)
return enif_make_badarg(env);
ReferencePtr<DbObject> db_ptr;
if(!enif_get_db(env, argv[0], &db_ptr))
return enif_make_badarg(env);
TransactionObject* tx_ptr;
rocksdb::Transaction* tx;
// initialize options
// not sure that we need this, since there are multiple usable arities
rocksdb::OptimisticTransactionOptions transaction_options;
rocksdb::WriteOptions write_options;
fold(env, argv[1], parse_write_option, write_options);
rocksdb::OptimisticTransactionDB* db =
reinterpret_cast<rocksdb::OptimisticTransactionDB*>(db_ptr->m_Db);
tx = db->BeginTransaction(write_options, transaction_options);
tx_ptr = TransactionObject::CreateTransactionObject(db_ptr.get(), tx);
ERL_NIF_TERM result = enif_make_resource(env, tx_ptr);
enif_release_resource(tx_ptr);
tx = NULL;
return enif_make_tuple2(env, ATOM_OK, result);
}
ERL_NIF_TERM
PutTransaction(ErlNifEnv* env,
int argc,
const ERL_NIF_TERM argv[])
{
ReferencePtr<erocksdb::TransactionObject> tx_ptr;
ReferencePtr<erocksdb::ColumnFamilyObject> cf_ptr;
if(!enif_get_transaction(env, argv[0], &tx_ptr))
return enif_make_badarg(env);
ErlNifBinary key, value;
rocksdb::Status status;
if( argc > 3 ) { // cf
if(!enif_get_cf(env, argv[1], &cf_ptr) ||
!enif_inspect_binary(env, argv[2], &key) ||
!enif_inspect_binary(env, argv[3], &value)) {
return enif_make_badarg(env);
}
rocksdb::Slice key_slice(reinterpret_cast<char*>(key.data), key.size);
rocksdb::Slice value_slice(reinterpret_cast<char*>(value.data), value.size);
erocksdb::ColumnFamilyObject* cf = cf_ptr.get();
status = tx_ptr->m_Tx->Put(cf->m_ColumnFamily, key_slice, value_slice);
}
else
{ // default
if(!enif_inspect_binary(env, argv[1], &key) ||
!enif_inspect_binary(env, argv[2], &value)) {
return enif_make_badarg(env);
}
rocksdb::Slice key_slice(reinterpret_cast<char*>(key.data), key.size);
rocksdb::Slice value_slice(reinterpret_cast<char*>(value.data), value.size);
status = tx_ptr->m_Tx->Put(key_slice, value_slice);
}
if(!status.ok())
return error_tuple(env, ATOM_ERROR, status);
return ATOM_OK;
}
ERL_NIF_TERM
GetTransaction(ErlNifEnv* env,
int argc,
const ERL_NIF_TERM argv[])
{
ReferencePtr<erocksdb::TransactionObject> tx_ptr;
ReferencePtr<erocksdb::ColumnFamilyObject> cf_ptr;
if(!enif_get_transaction(env, argv[0], &tx_ptr))
return enif_make_badarg(env);
int i = 1;
if(argc == 4)
i = 2;
rocksdb::Slice key;
if(!binary_to_slice(env, argv[i], &key)) {
return enif_make_badarg(env);
}
rocksdb::ReadOptions *opts = new rocksdb::ReadOptions();
fold(env, argv[i+1], parse_read_option, *opts);
rocksdb::Status status;
rocksdb::PinnableSlice pvalue;
if(argc == 4) {
if(!enif_get_cf(env, argv[1], &cf_ptr)) {
return enif_make_badarg(env);
}
status = tx_ptr->m_Tx->GetForUpdate(*opts, cf_ptr->m_ColumnFamily, key, &pvalue);
} else {
status = tx_ptr->m_Tx->GetForUpdate(*opts, tx_ptr->m_DbPtr->m_Db->DefaultColumnFamily(), key, &pvalue);
}
delete opts;
if (!status.ok())
{
if (status.IsNotFound())
return ATOM_NOT_FOUND;
if (status.IsCorruption())
return error_tuple(env, ATOM_CORRUPTION, status);
return error_tuple(env, ATOM_UNKNOWN_STATUS_ERROR, status);
}
ERL_NIF_TERM value_bin;
memcpy(enif_make_new_binary(env, pvalue.size(), &value_bin), pvalue.data(), pvalue.size());
pvalue.Reset();
return enif_make_tuple2(env, ATOM_OK, value_bin);
}
ERL_NIF_TERM
GetForUpdateTransaction(ErlNifEnv* env,
int argc,
const ERL_NIF_TERM argv[])
{
ReferencePtr<erocksdb::TransactionObject> tx_ptr;
ReferencePtr<erocksdb::ColumnFamilyObject> cf_ptr;
if(!enif_get_transaction(env, argv[0], &tx_ptr))
return enif_make_badarg(env);
int i = 1;
if(argc == 4)
i = 2;
rocksdb::Slice key;
if(!binary_to_slice(env, argv[i], &key)) {
return enif_make_badarg(env);
}
rocksdb::ReadOptions opts;
fold(env, argv[i+1], parse_read_option, opts);
rocksdb::Status status;
rocksdb::PinnableSlice pvalue;
if(argc == 4) {
if(!enif_get_cf(env, argv[1], &cf_ptr)) {
return enif_make_badarg(env);
}
status = tx_ptr->m_Tx->GetForUpdate(opts, cf_ptr->m_ColumnFamily, key, &pvalue);
} else {
status = tx_ptr->m_Tx->GetForUpdate(opts, tx_ptr->m_DbPtr->m_Db->DefaultColumnFamily(), key, &pvalue);
}
if (!status.ok())
{
if (status.IsNotFound())
return ATOM_NOT_FOUND;
if (status.IsBusy())
return error_tuple(env, ATOM_BUSY, status);
if (status.IsCorruption())
return error_tuple(env, ATOM_CORRUPTION, status);
return error_tuple(env, ATOM_UNKNOWN_STATUS_ERROR, status);
}
ERL_NIF_TERM value_bin;
memcpy(enif_make_new_binary(env, pvalue.size(), &value_bin), pvalue.data(), pvalue.size());
pvalue.Reset();
return enif_make_tuple2(env, ATOM_OK, value_bin);
}
ERL_NIF_TERM
MultiGetTransaction(ErlNifEnv* env,
int argc,
const ERL_NIF_TERM argv[])
{
ReferencePtr<erocksdb::TransactionObject> tx_ptr;
ReferencePtr<erocksdb::ColumnFamilyObject> cf_ptr;
if(!enif_get_transaction(env, argv[0], &tx_ptr))
return enif_make_badarg(env);
// Determine argument positions based on arity
// argc == 3: transaction_multi_get(Txn, Keys, ReadOpts)
// argc == 4: transaction_multi_get(Txn, CF, Keys, ReadOpts)
int keys_idx = (argc == 4) ? 2 : 1;
int opts_idx = keys_idx + 1;
// Get column family handle if provided
rocksdb::ColumnFamilyHandle* cfh = tx_ptr->m_DbPtr->m_Db->DefaultColumnFamily();
if (argc == 4)
{
if(!enif_get_cf(env, argv[1], &cf_ptr))
return enif_make_badarg(env);
cfh = cf_ptr->m_ColumnFamily;
}
// Parse keys list
ERL_NIF_TERM keys_list = argv[keys_idx];
unsigned int num_keys;
if (!enif_get_list_length(env, keys_list, &num_keys))
return enif_make_badarg(env);
// Handle empty list case
if (num_keys == 0)
return enif_make_list(env, 0);
// Allocate arrays for keys and values
std::vector<rocksdb::Slice> keys(num_keys);
std::vector<ErlNifBinary> key_binaries(num_keys);
std::vector<std::string> values(num_keys);
// Convert Erlang binaries to Slices
ERL_NIF_TERM head, tail = keys_list;
for (unsigned int i = 0; i < num_keys; i++)
{
if (!enif_get_list_cell(env, tail, &head, &tail))
return enif_make_badarg(env);
if (!enif_inspect_binary(env, head, &key_binaries[i]))
return enif_make_badarg(env);
keys[i] = rocksdb::Slice(reinterpret_cast<const char*>(key_binaries[i].data),
key_binaries[i].size);
}
// Parse read options
rocksdb::ReadOptions opts;
fold(env, argv[opts_idx], parse_read_option, opts);
// Call MultiGet on transaction
std::vector<rocksdb::ColumnFamilyHandle*> cfs(num_keys, cfh);
std::vector<rocksdb::Status> statuses = tx_ptr->m_Tx->MultiGet(opts, cfs, keys, &values);
// Build result list (from tail to head for efficiency)
ERL_NIF_TERM result = enif_make_list(env, 0);
for (int i = num_keys - 1; i >= 0; i--)
{
ERL_NIF_TERM item;
if (statuses[i].ok())
{
ERL_NIF_TERM value_bin;
memcpy(enif_make_new_binary(env, values[i].size(), &value_bin),
values[i].data(), values[i].size());
item = enif_make_tuple2(env, ATOM_OK, value_bin);
}
else if (statuses[i].IsNotFound())
{
item = ATOM_NOT_FOUND;
}
else if (statuses[i].IsCorruption())
{
item = error_tuple(env, ATOM_CORRUPTION, statuses[i]);
}
else
{
item = error_tuple(env, ATOM_UNKNOWN_STATUS_ERROR, statuses[i]);
}
result = enif_make_list_cell(env, item, result);
}
return result;
}
ERL_NIF_TERM
MultiGetForUpdateTransaction(ErlNifEnv* env,
int argc,
const ERL_NIF_TERM argv[])
{
ReferencePtr<erocksdb::TransactionObject> tx_ptr;
ReferencePtr<erocksdb::ColumnFamilyObject> cf_ptr;
if(!enif_get_transaction(env, argv[0], &tx_ptr))
return enif_make_badarg(env);
// Determine argument positions based on arity
// argc == 3: transaction_multi_get_for_update(Txn, Keys, ReadOpts)
// argc == 4: transaction_multi_get_for_update(Txn, CF, Keys, ReadOpts)
int keys_idx = (argc == 4) ? 2 : 1;
int opts_idx = keys_idx + 1;
// Get column family handle if provided
rocksdb::ColumnFamilyHandle* cfh = tx_ptr->m_DbPtr->m_Db->DefaultColumnFamily();
if (argc == 4)
{
if(!enif_get_cf(env, argv[1], &cf_ptr))
return enif_make_badarg(env);
cfh = cf_ptr->m_ColumnFamily;
}
// Parse keys list
ERL_NIF_TERM keys_list = argv[keys_idx];
unsigned int num_keys;
if (!enif_get_list_length(env, keys_list, &num_keys))
return enif_make_badarg(env);
// Handle empty list case
if (num_keys == 0)
return enif_make_list(env, 0);
// Allocate arrays for keys and values
std::vector<rocksdb::Slice> keys(num_keys);
std::vector<ErlNifBinary> key_binaries(num_keys);
std::vector<std::string> values(num_keys);
// Convert Erlang binaries to Slices
ERL_NIF_TERM head, tail = keys_list;
for (unsigned int i = 0; i < num_keys; i++)
{
if (!enif_get_list_cell(env, tail, &head, &tail))
return enif_make_badarg(env);
if (!enif_inspect_binary(env, head, &key_binaries[i]))
return enif_make_badarg(env);
keys[i] = rocksdb::Slice(reinterpret_cast<const char*>(key_binaries[i].data),
key_binaries[i].size);
}
// Parse read options
rocksdb::ReadOptions opts;
fold(env, argv[opts_idx], parse_read_option, opts);
// Call MultiGetForUpdate on transaction - tracks keys for conflict detection
std::vector<rocksdb::ColumnFamilyHandle*> cfs(num_keys, cfh);
std::vector<rocksdb::Status> statuses = tx_ptr->m_Tx->MultiGetForUpdate(opts, cfs, keys, &values);
// Build result list (from tail to head for efficiency)
ERL_NIF_TERM result = enif_make_list(env, 0);
for (int i = num_keys - 1; i >= 0; i--)
{
ERL_NIF_TERM item;
if (statuses[i].ok())
{
ERL_NIF_TERM value_bin;
memcpy(enif_make_new_binary(env, values[i].size(), &value_bin),
values[i].data(), values[i].size());
item = enif_make_tuple2(env, ATOM_OK, value_bin);
}
else if (statuses[i].IsNotFound())
{
item = ATOM_NOT_FOUND;
}
else if (statuses[i].IsBusy())
{
item = error_tuple(env, ATOM_BUSY, statuses[i]);
}
else if (statuses[i].IsCorruption())
{
item = error_tuple(env, ATOM_CORRUPTION, statuses[i]);
}
else
{
item = error_tuple(env, ATOM_UNKNOWN_STATUS_ERROR, statuses[i]);
}
result = enif_make_list_cell(env, item, result);
}
return result;
}
ERL_NIF_TERM
DelTransaction(ErlNifEnv* env,
int argc,
const ERL_NIF_TERM argv[])
{
ReferencePtr<erocksdb::TransactionObject> tx_ptr;
ReferencePtr<erocksdb::ColumnFamilyObject> cf_ptr;
if(!enif_get_transaction(env, argv[0], &tx_ptr))
return enif_make_badarg(env);
ErlNifBinary key;
rocksdb::Status status;
rocksdb::ColumnFamilyHandle *cfh;
if (argc > 2)
{
if(!enif_get_cf(env, argv[1], &cf_ptr) ||
!enif_inspect_binary(env, argv[2], &key))
return enif_make_badarg(env);
cfh = cf_ptr->m_ColumnFamily;
}
else
{
if(!enif_inspect_binary(env, argv[1], &key))
return enif_make_badarg(env);
cfh = tx_ptr->m_DbPtr->m_Db->DefaultColumnFamily();
}
rocksdb::Slice key_slice(reinterpret_cast<char*>(key.data), key.size);
status = tx_ptr->m_Tx->Delete(cfh, key_slice);
if(status.ok()) {
return ATOM_OK;
} else {
return error_tuple(env, ATOM_ERROR, status);
}
}
ERL_NIF_TERM
IteratorTransaction(ErlNifEnv* env,
int argc,
const ERL_NIF_TERM argv[]) {
ReferencePtr<erocksdb::TransactionObject> tx_ptr;
if(!enif_get_transaction(env, argv[0], &tx_ptr))
return enif_make_badarg(env);
int i = argc - 1;
if(!enif_is_list(env, argv[i])) {
return enif_make_badarg(env);
}
rocksdb::ReadOptions *opts = new rocksdb::ReadOptions;
ItrBounds bounds;
auto itr_env = std::make_shared<ErlEnvCtr>();
if (!parse_iterator_options(env, itr_env->env, argv[i], *opts, bounds)) {
delete bounds.upper_bound_slice;
delete bounds.lower_bound_slice;
delete opts;
return enif_make_badarg(env);
}
ItrObject * itr_ptr;
rocksdb::Iterator * iterator;
if (argc == 3) {
ReferencePtr<ColumnFamilyObject> cf_ptr;
if(!enif_get_cf(env, argv[1], &cf_ptr)) {
delete bounds.upper_bound_slice;
delete bounds.lower_bound_slice;
delete opts;
return enif_make_badarg(env);
}
iterator = tx_ptr->m_Tx->GetIterator(*opts, cf_ptr->m_ColumnFamily);
} else {
iterator = tx_ptr->m_Tx->GetIterator(*opts);
}
itr_ptr = ItrObject::CreateItrObject(tx_ptr->m_DbPtr.get(), itr_env, iterator);
if(bounds.upper_bound_slice != nullptr) {
itr_ptr->SetUpperBoundSlice(bounds.upper_bound_slice);
}
if(bounds.lower_bound_slice != nullptr) {
itr_ptr->SetLowerBoundSlice(bounds.lower_bound_slice);
}
ERL_NIF_TERM result = enif_make_resource(env, itr_ptr);
// release reference created during CreateItrObject()
enif_release_resource(itr_ptr);
delete opts;
iterator = NULL;
return enif_make_tuple2(env, ATOM_OK, result);
}
ERL_NIF_TERM
CommitTransaction(ErlNifEnv* env,
int /*argc*/,
const ERL_NIF_TERM argv[])
{
ReferencePtr<erocksdb::TransactionObject> tx_ptr;
if(!enif_get_transaction(env, argv[0], &tx_ptr))
return enif_make_badarg(env);
rocksdb::Status s = tx_ptr->m_Tx->Commit();
if(s.ok() ) {
return ATOM_OK;
} else {
return error_tuple(env, ATOM_ERROR, s);
}
}
ERL_NIF_TERM
RollbackTransaction(ErlNifEnv* env,
int /*argc*/,
const ERL_NIF_TERM argv[])
{
ReferencePtr<erocksdb::TransactionObject> tx_ptr;
if(!enif_get_transaction(env, argv[0], &tx_ptr))
return enif_make_badarg(env);
rocksdb::Status s = tx_ptr->m_Tx->Rollback();
if(s.ok() ) {
return ATOM_OK;
} else {
return error_tuple(env, ATOM_ERROR, s);
}
}
ERL_NIF_TERM
ReleaseTransaction(
ErlNifEnv* env,
int /*argc*/,
const ERL_NIF_TERM argv[])
{
const ERL_NIF_TERM& handle_ref = argv[0];
ReferencePtr<erocksdb::TransactionObject> tx_ptr;
tx_ptr.assign(TransactionObject::RetrieveTransactionObject(env, handle_ref));
if(NULL==tx_ptr.get())
return ATOM_OK;
TransactionObject* tx = tx_ptr.get();
ErlRefObject::InitiateCloseRequest(tx);
return ATOM_OK;
} // erocksdb::releaseTransaction
}