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/pessimistic_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/transaction_db.h"
#include "atoms.h"
#include "refobjects.h"
#include "util.h"
#include "erocksdb_db.h"
#include "erocksdb_iter.h"
#include "pessimistic_transaction.h"
namespace erocksdb {
ERL_NIF_TERM
parse_txn_option(ErlNifEnv* env, ERL_NIF_TERM item, rocksdb::TransactionOptions& opts)
{
int arity;
const ERL_NIF_TERM* option;
if (enif_get_tuple(env, item, &arity, &option) && 2==arity)
{
if (option[0] == ATOM_SET_SNAPSHOT)
{
opts.set_snapshot = (option[1] == ATOM_TRUE);
}
else if (option[0] == ATOM_DEADLOCK_DETECT)
{
opts.deadlock_detect = (option[1] == ATOM_TRUE);
}
else if (option[0] == ATOM_LOCK_TIMEOUT)
{
ErlNifSInt64 lock_timeout;
if (enif_get_int64(env, option[1], &lock_timeout))
opts.lock_timeout = lock_timeout;
}
}
return ATOM_OK;
}
ERL_NIF_TERM
NewPessimisticTransaction(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);
// Verify this is a pessimistic transaction DB
if(!db_ptr->m_IsPessimistic)
return error_tuple(env, ATOM_ERROR, "not a pessimistic transaction db");
// Initialize options
rocksdb::TransactionOptions txn_options;
rocksdb::WriteOptions write_options;
fold(env, argv[1], parse_write_option, write_options);
// Parse transaction-specific options from the same list
if (argc >= 3)
{
fold(env, argv[2], parse_txn_option, txn_options);
}
rocksdb::TransactionDB* txn_db =
reinterpret_cast<rocksdb::TransactionDB*>(db_ptr->m_Db);
rocksdb::Transaction* tx = txn_db->BeginTransaction(write_options, txn_options);
TransactionObject* tx_ptr = TransactionObject::CreateTransactionObject(db_ptr.get(), tx);
ERL_NIF_TERM result = enif_make_resource(env, tx_ptr);
enif_release_resource(tx_ptr);
return enif_make_tuple2(env, ATOM_OK, result);
}
ERL_NIF_TERM
PessimisticTransactionPut(ErlNifEnv* env,
int argc,
const ERL_NIF_TERM argv[])
{
ReferencePtr<TransactionObject> tx_ptr;
ReferencePtr<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)
{
// With column family: tx, cf, key, value
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);
status = tx_ptr->m_Tx->Put(cf_ptr->m_ColumnFamily, key_slice, value_slice);
}
else
{
// Default column family: tx, key, value
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())
{
if(status.IsBusy())
return error_tuple(env, ATOM_BUSY, status);
if(status.IsTimedOut())
return error_tuple(env, ATOM_TIMED_OUT, status);
return error_tuple(env, ATOM_ERROR, status);
}
return ATOM_OK;
}
ERL_NIF_TERM
PessimisticTransactionGet(ErlNifEnv* env,
int argc,
const ERL_NIF_TERM argv[])
{
ReferencePtr<TransactionObject> tx_ptr;
ReferencePtr<ColumnFamilyObject> cf_ptr;
if(!enif_get_transaction(env, argv[0], &tx_ptr))
return enif_make_badarg(env);
int key_idx = 1;
int opts_idx = 2;
if(argc == 4)
{
key_idx = 2;
opts_idx = 3;
}
rocksdb::Slice key;
if(!binary_to_slice(env, argv[key_idx], &key))
return enif_make_badarg(env);
rocksdb::ReadOptions opts;
fold(env, argv[opts_idx], parse_read_option, opts);
rocksdb::PinnableSlice pvalue;
rocksdb::Status status;
if(argc == 4)
{
if(!enif_get_cf(env, argv[1], &cf_ptr))
return enif_make_badarg(env);
// Use Get (not GetForUpdate) - reads without locking
status = tx_ptr->m_Tx->Get(opts, cf_ptr->m_ColumnFamily, key, &pvalue);
}
else
{
status = tx_ptr->m_Tx->Get(opts, tx_ptr->m_DbPtr->m_Db->DefaultColumnFamily(), key, &pvalue);
}
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
PessimisticTransactionGetForUpdate(ErlNifEnv* env,
int argc,
const ERL_NIF_TERM argv[])
{
ReferencePtr<TransactionObject> tx_ptr;
ReferencePtr<ColumnFamilyObject> cf_ptr;
if(!enif_get_transaction(env, argv[0], &tx_ptr))
return enif_make_badarg(env);
int key_idx = 1;
int opts_idx = 2;
if(argc == 4)
{
key_idx = 2;
opts_idx = 3;
}
rocksdb::Slice key;
if(!binary_to_slice(env, argv[key_idx], &key))
return enif_make_badarg(env);
rocksdb::ReadOptions opts;
fold(env, argv[opts_idx], parse_read_option, opts);
rocksdb::PinnableSlice pvalue;
rocksdb::Status status;
if(argc == 4)
{
if(!enif_get_cf(env, argv[1], &cf_ptr))
return enif_make_badarg(env);
// Use GetForUpdate - acquires exclusive lock
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.IsTimedOut())
return error_tuple(env, ATOM_TIMED_OUT, status);
if(status.IsCorruption())
return error_tuple(env, ATOM_CORRUPTION, status);
if(status.IsTryAgain())
return error_tuple(env, ATOM_TRY_AGAIN, 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
PessimisticTransactionMultiGet(ErlNifEnv* env,
int argc,
const ERL_NIF_TERM argv[])
{
ReferencePtr<TransactionObject> tx_ptr;
ReferencePtr<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: pessimistic_transaction_multi_get(Txn, Keys, ReadOpts)
// argc == 4: pessimistic_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 (without locking)
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
PessimisticTransactionMultiGetForUpdate(ErlNifEnv* env,
int argc,
const ERL_NIF_TERM argv[])
{
ReferencePtr<TransactionObject> tx_ptr;
ReferencePtr<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: pessimistic_transaction_multi_get_for_update(Txn, Keys, ReadOpts)
// argc == 4: pessimistic_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 - acquires exclusive locks
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].IsTimedOut())
{
item = error_tuple(env, ATOM_TIMED_OUT, 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
PessimisticTransactionDelete(ErlNifEnv* env,
int argc,
const ERL_NIF_TERM argv[])
{
ReferencePtr<TransactionObject> tx_ptr;
ReferencePtr<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)
{
// With column family: tx, cf, key
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
{
// Default column family: tx, key
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())
{
if(status.IsBusy())
return error_tuple(env, ATOM_BUSY, status);
if(status.IsTimedOut())
return error_tuple(env, ATOM_TIMED_OUT, status);
return error_tuple(env, ATOM_ERROR, status);
}
return ATOM_OK;
}
ERL_NIF_TERM
PessimisticTransactionCommit(ErlNifEnv* env,
int /*argc*/,
const ERL_NIF_TERM argv[])
{
ReferencePtr<TransactionObject> tx_ptr;
if(!enif_get_transaction(env, argv[0], &tx_ptr))
return enif_make_badarg(env);
rocksdb::Status status = tx_ptr->m_Tx->Commit();
if(!status.ok())
{
if(status.IsBusy())
return error_tuple(env, ATOM_BUSY, status);
if(status.IsExpired())
return error_tuple(env, ATOM_EXPIRED, status);
return error_tuple(env, ATOM_ERROR, status);
}
return ATOM_OK;
}
ERL_NIF_TERM
PessimisticTransactionRollback(ErlNifEnv* env,
int /*argc*/,
const ERL_NIF_TERM argv[])
{
ReferencePtr<TransactionObject> tx_ptr;
if(!enif_get_transaction(env, argv[0], &tx_ptr))
return enif_make_badarg(env);
rocksdb::Status status = tx_ptr->m_Tx->Rollback();
if(!status.ok())
return error_tuple(env, ATOM_ERROR, status);
return ATOM_OK;
}
ERL_NIF_TERM
ReleasePessimisticTransaction(ErlNifEnv* env,
int /*argc*/,
const ERL_NIF_TERM argv[])
{
const ERL_NIF_TERM& handle_ref = argv[0];
ReferencePtr<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;
}
ERL_NIF_TERM
PessimisticTransactionIterator(ErlNifEnv* env,
int argc,
const ERL_NIF_TERM argv[])
{
ReferencePtr<TransactionObject> tx_ptr;
if(!enif_get_transaction(env, argv[0], &tx_ptr))
return enif_make_badarg(env);
int opts_idx = argc - 1;
if(!enif_is_list(env, argv[opts_idx]))
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[opts_idx], *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);
enif_release_resource(itr_ptr);
delete opts;
iterator = NULL;
return enif_make_tuple2(env, ATOM_OK, result);
}
// Savepoint operations
ERL_NIF_TERM
PessimisticTransactionSetSavepoint(ErlNifEnv* env,
int /*argc*/,
const ERL_NIF_TERM argv[])
{
ReferencePtr<TransactionObject> tx_ptr;
if(!enif_get_transaction(env, argv[0], &tx_ptr))
return enif_make_badarg(env);
tx_ptr->m_Tx->SetSavePoint();
return ATOM_OK;
}
ERL_NIF_TERM
PessimisticTransactionRollbackToSavepoint(ErlNifEnv* env,
int /*argc*/,
const ERL_NIF_TERM argv[])
{
ReferencePtr<TransactionObject> tx_ptr;
if(!enif_get_transaction(env, argv[0], &tx_ptr))
return enif_make_badarg(env);
rocksdb::Status status = tx_ptr->m_Tx->RollbackToSavePoint();
if(!status.ok())
{
if(status.IsNotFound())
return error_tuple(env, ATOM_ERROR, "no savepoint set");
return error_tuple(env, ATOM_ERROR, status);
}
return ATOM_OK;
}
ERL_NIF_TERM
PessimisticTransactionPopSavepoint(ErlNifEnv* env,
int /*argc*/,
const ERL_NIF_TERM argv[])
{
ReferencePtr<TransactionObject> tx_ptr;
if(!enif_get_transaction(env, argv[0], &tx_ptr))
return enif_make_badarg(env);
rocksdb::Status status = tx_ptr->m_Tx->PopSavePoint();
if(!status.ok())
{
if(status.IsNotFound())
return error_tuple(env, ATOM_ERROR, "no savepoint set");
return error_tuple(env, ATOM_ERROR, status);
}
return ATOM_OK;
}
// Transaction information
ERL_NIF_TERM
PessimisticTransactionGetId(ErlNifEnv* env,
int /*argc*/,
const ERL_NIF_TERM argv[])
{
ReferencePtr<TransactionObject> tx_ptr;
if(!enif_get_transaction(env, argv[0], &tx_ptr))
return enif_make_badarg(env);
uint64_t id = tx_ptr->m_Tx->GetID();
return enif_make_tuple2(env, ATOM_OK, enif_make_uint64(env, id));
}
ERL_NIF_TERM
PessimisticTransactionGetWaitingTxns(ErlNifEnv* env,
int /*argc*/,
const ERL_NIF_TERM argv[])
{
ReferencePtr<TransactionObject> tx_ptr;
if(!enif_get_transaction(env, argv[0], &tx_ptr))
return enif_make_badarg(env);
uint32_t column_family_id;
std::string key;
std::vector<rocksdb::TransactionID> waiting_txns =
tx_ptr->m_Tx->GetWaitingTxns(&column_family_id, &key);
// Build list of waiting transaction IDs
ERL_NIF_TERM txn_list = enif_make_list(env, 0);
for (auto it = waiting_txns.rbegin(); it != waiting_txns.rend(); ++it)
{
txn_list = enif_make_list_cell(env, enif_make_uint64(env, *it), txn_list);
}
// Build key binary
ERL_NIF_TERM key_bin;
memcpy(enif_make_new_binary(env, key.size(), &key_bin), key.data(), key.size());
// Return {ok, #{column_family_id => CfId, key => Key, waiting_txns => TxnList}}
ERL_NIF_TERM keys[3];
ERL_NIF_TERM values[3];
keys[0] = enif_make_atom(env, "column_family_id");
values[0] = enif_make_uint(env, column_family_id);
keys[1] = enif_make_atom(env, "key");
values[1] = key_bin;
keys[2] = enif_make_atom(env, "waiting_txns");
values[2] = txn_list;
ERL_NIF_TERM result_map;
enif_make_map_from_arrays(env, keys, values, 3, &result_map);
return enif_make_tuple2(env, ATOM_OK, result_map);
}
} // namespace erocksdb