Skip to main content
RocksDB provides transaction support with snapshot isolation, enabling atomic, consistent, isolated, and durable (ACID) operations. This guide covers both pessimistic and optimistic transaction implementations.

Transaction Types

RocksDB offers two transaction implementations:

TransactionDB (Pessimistic)

  • Uses locks to prevent conflicts
  • Better for write-heavy workloads
  • Detects conflicts immediately

OptimisticTransactionDB

  • Lock-free, checks conflicts at commit
  • Better for low-contention workloads
  • Lower overhead

Pessimistic Transactions

Opening a TransactionDB

#include "rocksdb/db.h"
#include "rocksdb/options.h"
#include "rocksdb/utilities/transaction.h"
#include "rocksdb/utilities/transaction_db.h"

using ROCKSDB_NAMESPACE::Options;
using ROCKSDB_NAMESPACE::TransactionDB;
using ROCKSDB_NAMESPACE::TransactionDBOptions;
using ROCKSDB_NAMESPACE::Status;

Options options;
TransactionDBOptions txn_db_options;
options.create_if_missing = true;

TransactionDB* txn_db;
Status s = TransactionDB::Open(options, txn_db_options, "/path/to/db", &txn_db);
assert(s.ok());

Basic Transaction Operations

1

Begin transaction

Create a new transaction object.
2

Perform operations

Execute reads and writes within the transaction.
3

Commit or rollback

Apply changes atomically or discard them.
Example:
using ROCKSDB_NAMESPACE::Transaction;
using ROCKSDB_NAMESPACE::WriteOptions;
using ROCKSDB_NAMESPACE::ReadOptions;

WriteOptions write_options;
ReadOptions read_options;
std::string value;

// Start a transaction
Transaction* txn = txn_db->BeginTransaction(write_options);
assert(txn);

// Read a key in this transaction
Status s = txn->Get(read_options, "abc", &value);
assert(s.IsNotFound());

// Write a key in this transaction
s = txn->Put("abc", "def");
assert(s.ok());

// Commit transaction
s = txn->Commit();
assert(s.ok());
delete txn;

// Value is committed, can be read now
s = txn_db->Get(read_options, "abc", &value);
assert(s.ok());
assert(value == "def");

Conflict Detection

Pessimistic transactions use locks to prevent conflicts:
// Transaction 1
Transaction* txn1 = txn_db->BeginTransaction(write_options);
txn1->Put("abc", "def");

// Transaction 2 tries to write the same key
// This will fail with kLockTimeout
Status s = txn_db->Put(write_options, "abc", "xyz");
assert(s.subcode() == Status::kLockTimeout);

// Transaction 1 commits
txn1->Commit();
delete txn1;

// Now transaction 2 can proceed
s = txn_db->Put(write_options, "abc", "xyz");
assert(s.ok());
Writes outside of a transaction will block if a transaction holds a lock on the same key.

Snapshot Isolation

Read Committed

By default, transactions use “Read Committed” isolation:
Transaction* txn = txn_db->BeginTransaction(write_options);

// Write outside of transaction
txn_db->Put(write_options, "xyz", "zzz");

// Transaction can read the committed value
std::string value;
Status s = txn->Get(read_options, "xyz", &value);
assert(s.ok());
assert(value == "zzz");

txn->Commit();
delete txn;

Repeatable Read (Snapshot Isolation)

Use snapshots for stronger isolation guarantees:
using ROCKSDB_NAMESPACE::TransactionOptions;
using ROCKSDB_NAMESPACE::Snapshot;

// Set a snapshot at start of transaction
TransactionOptions txn_options;
txn_options.set_snapshot = true;

Transaction* txn = txn_db->BeginTransaction(write_options, txn_options);
const Snapshot* snapshot = txn->GetSnapshot();

// Write OUTSIDE of transaction
Status s = txn_db->Put(write_options, "abc", "xyz");
assert(s.ok());

// Read the latest committed value (without snapshot)
std::string value;
s = txn->Get(read_options, "abc", &value);
assert(s.ok());
assert(value == "xyz");

// Read the snapshotted value
read_options.snapshot = snapshot;
s = txn->Get(read_options, "abc", &value);
assert(s.ok());
assert(value == "def");  // Old value from snapshot

// Attempt to write will fail due to conflict
s = txn->GetForUpdate(read_options, "abc", &value);
assert(s.IsBusy());

txn->Rollback();
delete txn;
Use set_snapshot = true when you need consistent reads across multiple operations in a transaction.

Advanced Features

Save Points

Save points allow partial rollback within a transaction:
Transaction* txn = txn_db->BeginTransaction(write_options);

// Do some writes
txn->Put("x", "x");

// Set a save point
txn->SetSavePoint();

// Do more writes
txn->Put("y", "y2");

// Rollback to save point
txn->RollbackToSavePoint();

// Commit - only "x" is written, "y" is not
Status s = txn->Commit();
assert(s.ok());
delete txn;

GetForUpdate

Read and lock a key in a single operation:
Transaction* txn = txn_db->BeginTransaction(write_options);

std::string value;
// Read and acquire lock
Status s = txn->GetForUpdate(read_options, "key", &value);

if (s.ok()) {
    // Modify the value
    value += "_modified";
    txn->Put("key", value);
}

txn->Commit();
delete txn;

Optimistic Transactions

Opening an OptimisticTransactionDB

#include "rocksdb/utilities/optimistic_transaction_db.h"

using ROCKSDB_NAMESPACE::OptimisticTransactionDB;
using ROCKSDB_NAMESPACE::OptimisticTransactionOptions;

Options options;
options.create_if_missing = true;

OptimisticTransactionDB* txn_db;
Status s = OptimisticTransactionDB::Open(options, "/path/to/db", &txn_db);
assert(s.ok());

Optimistic Transaction Example

OptimisticTransactionOptions txn_options;
Transaction* txn = txn_db->BeginTransaction(write_options, txn_options);

// Read a key
std::string value;
Status s = txn->Get(read_options, "abc", &value);

// Write a key
s = txn->Put("abc", "xyz");
assert(s.ok());

// Write OUTSIDE the transaction to the same key
// This does NOT block (unlike pessimistic transactions)
DB* db = txn_db->GetBaseDB();
s = db->Put(write_options, "abc", "def");
assert(s.ok());

// Commit will fail because of the conflict
s = txn->Commit();
assert(s.IsBusy());  // Conflict detected at commit time
delete txn;
Optimistic transactions detect conflicts only at commit time. The commit will fail with Status::Busy() if a conflict is detected.

When to Use Optimistic Transactions

  • Low contention on keys
  • Read-heavy workloads
  • Short transactions
  • Want lower overhead

Complete Examples

#include "rocksdb/db.h"
#include "rocksdb/utilities/transaction.h"
#include "rocksdb/utilities/transaction_db.h"

using ROCKSDB_NAMESPACE::Options;
using ROCKSDB_NAMESPACE::ReadOptions;
using ROCKSDB_NAMESPACE::Transaction;
using ROCKSDB_NAMESPACE::TransactionDB;
using ROCKSDB_NAMESPACE::TransactionDBOptions;
using ROCKSDB_NAMESPACE::WriteOptions;

int main() {
  Options options;
  TransactionDBOptions txn_db_options;
  options.create_if_missing = true;
  TransactionDB* txn_db;

  Status s = TransactionDB::Open(options, txn_db_options,
                                 "/tmp/rocksdb_transaction_example", &txn_db);
  assert(s.ok());

  WriteOptions write_options;
  ReadOptions read_options;
  std::string value;

  // Simple Transaction Example
  Transaction* txn = txn_db->BeginTransaction(write_options);
  
  s = txn->Put("abc", "def");
  assert(s.ok());
  
  s = txn->Commit();
  assert(s.ok());
  delete txn;

  // Cleanup
  delete txn_db;
  return 0;
}

Transaction Options

TransactionOptions txn_options;

// Set snapshot at transaction start
txn_options.set_snapshot = true;

// Lock timeout (milliseconds)
txn_options.lock_timeout = 1000;

// Deadlock detection timeout (milliseconds)
txn_options.deadlock_detect_depth = 50;

Transaction* txn = txn_db->BeginTransaction(write_options, txn_options);

Best Practices

1

Keep transactions short

Long-running transactions can block other operations and increase memory usage.
2

Use appropriate isolation level

Choose between Read Committed and Snapshot Isolation based on your consistency requirements.
3

Handle conflicts gracefully

Always check return status and retry transactions on conflicts when appropriate.
4

Clean up transaction objects

Always delete transaction objects after commit or rollback to free resources.

Next Steps

Basic Operations

Learn fundamental CRUD operations

Iterators

Scan and iterate through transactional data

Build docs developers (and LLMs) love