guides/transactions.md

# Optimistic Transactions

Optimistic Transactions provide light-weight optimistic concurrency control for workloads that do not expect high contention between multiple transactions.

## Optimistic vs Pessimistic Transactions

| Feature | Optimistic | Pessimistic |
|---------|------------|-------------|
| Locking | Validation at commit | Lock on write/GetForUpdate |
| Conflict handling | Retry on commit failure | Block/timeout on lock acquisition |
| Best for | Low contention workloads | High contention workloads |
| Deadlock | N/A | Detection & timeout |
| Performance | Better for read-heavy | Better for write-heavy |

Choose Optimistic Transactions when:
- Write conflicts are rare
- You have many non-transactional writes alongside transactions
- You prefer retry logic over blocking

Choose [Pessimistic Transactions](pessimistic_transactions.md) when:
- Multiple transactions frequently update the same keys
- You need strict locking guarantees
- You want automatic deadlock detection

## Opening an Optimistic Transaction Database

```erlang
%% Basic open
{ok, Db, [DefaultCF]} = rocksdb:open_optimistic_transaction_db(
    "my_db",
    [{create_if_missing, true}],
    [{"default", []}]
).

%% With column families
CfOpts = [],
{ok, Db, [DefaultCF, DataCF]} = rocksdb:open_optimistic_transaction_db(
    "my_db",
    [{create_if_missing, true}],
    [{"default", CfOpts}, {"data", CfOpts}]
).
```

## Basic Operations

### Creating a Transaction

```erlang
%% Create a transaction with default options
{ok, Txn} = rocksdb:transaction(Db, []).

%% Create with write options
WriteOpts = [{sync, true}],
{ok, Txn} = rocksdb:transaction(Db, WriteOpts).
```

### Put, Get, Delete

```erlang
{ok, Txn} = rocksdb:transaction(Db, []),

%% Put a key-value pair
ok = rocksdb:transaction_put(Txn, <<"key1">>, <<"value1">>),

%% Get a value
{ok, Value} = rocksdb:transaction_get(Txn, <<"key1">>, []),

%% Delete a key
ok = rocksdb:transaction_delete(Txn, <<"key1">>),

%% Commit the transaction
ok = rocksdb:transaction_commit(Txn).
```

### Column Family Support

All operations support column families:

```erlang
{ok, Txn} = rocksdb:transaction(Db, []),

%% Operations with column family
ok = rocksdb:transaction_put(Txn, CfHandle, <<"key">>, <<"value">>),
{ok, Value} = rocksdb:transaction_get(Txn, CfHandle, <<"key">>, []),
ok = rocksdb:transaction_delete(Txn, CfHandle, <<"key">>),

ok = rocksdb:transaction_commit(Txn).
```

## Reading Uncommitted Data

Transactions can read their own uncommitted changes:

```erlang
%% Write to database first
ok = rocksdb:put(Db, <<"a">>, <<"old_a">>, []),
ok = rocksdb:put(Db, <<"b">>, <<"old_b">>, []),

%% Start transaction
{ok, Txn} = rocksdb:transaction(Db, []),

%% Modify within transaction
ok = rocksdb:transaction_put(Txn, <<"a">>, <<"new_a">>),

%% Read sees uncommitted change
{ok, <<"new_a">>} = rocksdb:transaction_get(Txn, <<"a">>, []),

%% Read sees committed value for unmodified key
{ok, <<"old_b">>} = rocksdb:transaction_get(Txn, <<"b">>, []),

ok = rocksdb:transaction_commit(Txn).
```

## Iterators

Create an iterator that sees uncommitted changes in the transaction:

```erlang
{ok, Txn} = rocksdb:transaction(Db, []),

%% Add uncommitted data
ok = rocksdb:transaction_put(Txn, <<"key1">>, <<"value1">>),
ok = rocksdb:transaction_put(Txn, <<"key2">>, <<"value2">>),

%% Create iterator - sees both committed and uncommitted data
{ok, Iter} = rocksdb:transaction_iterator(Txn, []),

%% Use standard iterator operations
{ok, Key, Value} = rocksdb:iterator_move(Iter, first),
{ok, NextKey, NextValue} = rocksdb:iterator_move(Iter, next),

ok = rocksdb:iterator_close(Iter),
ok = rocksdb:transaction_commit(Txn).
```

With column family:

```erlang
{ok, Iter} = rocksdb:transaction_iterator(Txn, CfHandle, []).
```

## Conflict Detection and Retry

Optimistic transactions validate at commit time. If another transaction modified the same keys, commit will fail:

```erlang
retry_transaction(Db, Key, UpdateFun) ->
    retry_transaction(Db, Key, UpdateFun, 3).

retry_transaction(_Db, _Key, _UpdateFun, 0) ->
    {error, max_retries};
retry_transaction(Db, Key, UpdateFun, Retries) ->
    {ok, Txn} = rocksdb:transaction(Db, []),
    try
        case rocksdb:transaction_get(Txn, Key, []) of
            {ok, Value} ->
                NewValue = UpdateFun(Value),
                ok = rocksdb:transaction_put(Txn, Key, NewValue),
                case rocksdb:transaction_commit(Txn) of
                    ok -> ok;
                    {error, {busy, _}} ->
                        %% Conflict detected, retry
                        retry_transaction(Db, Key, UpdateFun, Retries - 1)
                end;
            not_found ->
                {error, not_found}
        end
    catch
        _:Error ->
            {error, Error}
    end.
```

## Error Handling

Optimistic transactions can return these errors on commit:

- `{error, {busy, Reason}}` - Write conflict detected at commit time
- `{error, {try_again, Reason}}` - Transient error, retry the operation

```erlang
case rocksdb:transaction_commit(Txn) of
    ok ->
        %% Success
        ok;
    {error, {busy, _}} ->
        %% Another transaction modified the same keys
        %% Rollback and retry with fresh data
        handle_conflict();
    {error, Reason} ->
        %% Other error
        {error, Reason}
end.
```

## Complete Example

```erlang
increment_counter(Db, CounterKey) ->
    {ok, Txn} = rocksdb:transaction(Db, []),
    try
        Value = case rocksdb:transaction_get(Txn, CounterKey, []) of
            {ok, V} -> binary_to_integer(V);
            not_found -> 0
        end,
        NewValue = integer_to_binary(Value + 1),
        ok = rocksdb:transaction_put(Txn, CounterKey, NewValue),
        case rocksdb:transaction_commit(Txn) of
            ok ->
                {ok, Value + 1};
            {error, {busy, _}} ->
                %% Conflict - retry
                increment_counter(Db, CounterKey)
        end
    catch
        _:Error ->
            {error, Error}
    end.
```