# Transactions

> UStore for C

### Transactions and Views

A transaction groups reads and writes across collections and modalities, and commits all of them or none.
Writes take the transaction itself, while reads take a view of it, which also chooses whether the read registers its keys so the commit fails if another writer changed them since:

```c
ustore_transaction_t transaction = NULL;
ustore_transaction_init(&(ustore_transaction_init_t){
    .transaction = &transaction,
    .database = database,
    .outcome = &outcome,
});

ustore_blobs_write(&(ustore_blobs_write_t){.database = database, .outcome = &outcome, .transaction = transaction, /* ... */});
ustore_blobs_read(&(ustore_blobs_read_t){
    .database = database, .outcome = &outcome,
    .view = ustore_transaction_view(transaction, ustore_watch_keys_k), /* ... */});

ustore_transaction_commit(&(ustore_transaction_commit_t){
    .database = database,
    .outcome = &outcome,
    .transaction = transaction,
});
ustore_transaction_free(transaction);
```

`ustore_transaction_init` begins a new transaction into a `NULL` handle, left `NULL` on failure, and resets a transaction of the same database in place, keeping its handle whatever the outcome, so a reset that fails leaves it fit only to be freed or initialized again.
A failed commit keeps the contents of the transaction for a retry.
`ustore_transaction_rollback` withdraws a stage, releasing whatever it claimed, while keeping the reads and writes, so the transaction can stage again or be reset through `ustore_transaction_init`.
`ustore_transaction_stage` stages the transaction for a two-phase commit and can export its `read_stamp`, the `ustore_stamp_t` the transaction reads at and its commit validates against.
A commit can export its `commit_stamp`, its position in commit order, which every snapshot at or above it sees, and is never below the stage's `read_stamp`.

`#include <ustore/db.h>`

## ustore_transaction_t

An opaque handle to an optimistic transaction that groups operations across collections and modalities.

```c
typedef struct ustore_transaction_handle_t * ustore_transaction_t;
```

It serves one call at a time, so a call on a transaction that serves an unfinished one finishes with [`ustore_status_contended_k`](https://www.unum.cloud/docs/ustore/c/errors.md#ustore_status_contended_k), and holds its database open until [`ustore_transaction_free`](#ustore_transaction_free).

## ustore_view_t

An opaque handle to what a read sees: the head of the database, a transaction or a snapshot.

```c
typedef struct ustore_view_handle_t * ustore_view_t;
```

`NULL` is the head. Others come from [`ustore_transaction_view`](#ustore_transaction_view) and [`ustore_snapshot_view`](https://www.unum.cloud/docs/ustore/c/snapshots.md#ustore_snapshot_view), which tag the parent's pointer and allocate nothing, so a view needs no free and dies with its parent, which a cursor reading through it holds until the cursor is freed. A view names its parent, not a moment: after a commit, a transaction's view reads the transaction's next generation. Writes take a transaction instead, so nothing can write through a snapshot.

## ustore_stamp_t

A position in the order commits apply in, which grows with every commit: where a commit landed, or the newest commit a transaction or a snapshot reads.

```c
typedef uint64_t ustore_stamp_t;
```

## ustore_watch_t

Whether a transaction records the keys a call reads or writes, to refuse its commit when another writer changes them first.

```c
enum ustore_watch_t
```

- `ustore_watch_keys_k` = `0`: Records the keys, so a concurrent change to any of them fails the commit with a conflict.
- `ustore_watch_none_k` = `1`: Records nothing, so concurrent changes to these keys never fail the commit.

Spellings: ```
char const * ustore_watch_name(ustore_watch_t watch);
```

```
ustore_watch_t ustore_watch_from_name(ustore_bytes_view_t name);
```

## ustore_isolation_t

How the transactions of the in-memory engine see each other's writes.

```c
enum ustore_isolation_t
```

- `ustore_isolation_strict_serializable_k` = `0`: Every transaction appears to run alone at one moment between its start and its commit.
- `ustore_isolation_snapshot_k` = `1`: Every transaction reads the state its start saw and conflicts only over what it writes.

Spellings: ```
char const * ustore_isolation_name(ustore_isolation_t isolation);
```

```
ustore_isolation_t ustore_isolation_from_name(ustore_bytes_view_t name);
```

## ustore_transaction_init

Begins a transaction, or resets an existing one.

```c
void ustore_transaction_init(ustore_transaction_init_t *arguments);
```

- `transaction`: In and out: a `NULL` handle here begins a new transaction, which the caller frees with [`ustore_transaction_free`](#ustore_transaction_free), written on success and left `NULL` on failure; a transaction of `database` is reset in place onto a fresh snapshot, dropping its reads and writes, and keeps its handle whatever the outcome, so after a failed reset it can only be freed or initialized again.
- `database`: An open database.
- `executor`: Executor that runs the call, where `NULL` runs it on the calling thread.
- `outcome`: Output for the outcome, final once its status leaves [`ustore_status_deferred_k`](https://www.unum.cloud/docs/ustore/c/errors.md#ustore_status_deferred_k).
- `completion`: Whether the call returns final, the default, or once submitted.

Statuses: - [`ustore_status_contended_k`](https://www.unum.cloud/docs/ustore/c/errors.md#ustore_status_contended_k) The transaction reset serves another call, the executor's deferred table is full, or a callback blocks on the executor running it.
- [`ustore_status_uninitialized_k`](https://www.unum.cloud/docs/ustore/c/errors.md#ustore_status_uninitialized_k) The `database` is `NULL`.
- [`ustore_status_read_only_k`](https://www.unum.cloud/docs/ustore/c/errors.md#ustore_status_read_only_k) The database was opened read-only.
- [`ustore_status_unsupported_k`](https://www.unum.cloud/docs/ustore/c/errors.md#ustore_status_unsupported_k) The executor computes on a device, or the engine has no transactions, as LevelDB has none.
- [`ustore_status_invalid_argument_k`](https://www.unum.cloud/docs/ustore/c/errors.md#ustore_status_invalid_argument_k) The `transaction` output is `NULL`, or the transaction reset belongs to another database.

## ustore_transaction_view

Names a transaction as the view a read sees, without allocating.

```c
ustore_view_t ustore_transaction_view(
    ustore_transaction_t transaction,
    ustore_watch_t watch);
```

- `transaction` (in): A transaction, which the view dies with.
- `watch` (in): Whether reads through the view record their keys for conflicts.

## ustore_transaction_stage

Stages a transaction as the first phase of a two-phase commit.

```c
void ustore_transaction_stage(ustore_transaction_stage_t *arguments);
```

- `database`: An open database.
- `executor`: Executor that runs the call, where `NULL` runs it on the calling thread.
- `outcome`: Output for the outcome, final once its status leaves [`ustore_status_deferred_k`](https://www.unum.cloud/docs/ustore/c/errors.md#ustore_status_deferred_k).
- `completion`: Whether the call returns final, the default, or once submitted.
- `transaction`: An initialized transaction.
- `read_stamp`: Optional output for the stamp the transaction reads at, which its commit validates against; zeroed on failure.

Statuses: - [`ustore_status_contended_k`](https://www.unum.cloud/docs/ustore/c/errors.md#ustore_status_contended_k) The transaction serves another call, the executor's deferred table is full, or a callback blocks on the executor running it.
- [`ustore_status_uninitialized_k`](https://www.unum.cloud/docs/ustore/c/errors.md#ustore_status_uninitialized_k) The `database` or the `transaction` is `NULL`.
- [`ustore_status_read_only_k`](https://www.unum.cloud/docs/ustore/c/errors.md#ustore_status_read_only_k) The database was opened read-only.
- [`ustore_status_unsupported_k`](https://www.unum.cloud/docs/ustore/c/errors.md#ustore_status_unsupported_k) The executor computes on a device, or the engine has no transactions, as LevelDB has none.
- [`ustore_status_invalid_argument_k`](https://www.unum.cloud/docs/ustore/c/errors.md#ustore_status_invalid_argument_k) A failed RocksDB stage left the transaction needing a reset.
- [`ustore_status_write_conflict_k`](https://www.unum.cloud/docs/ustore/c/errors.md#ustore_status_write_conflict_k) Another commit changed a key this transaction wrote, or, on the NVMe engine, any key, range or prefix it watched or a collection it touched.
- [`ustore_status_read_conflict_k`](https://www.unum.cloud/docs/ustore/c/errors.md#ustore_status_read_conflict_k) Another commit changed a key this transaction read, on the RAM and RocksDB engines.
- [`ustore_status_phantom_conflict_k`](https://www.unum.cloud/docs/ustore/c/errors.md#ustore_status_phantom_conflict_k) Another commit changed a window this transaction walked, on the strictly serializable RAM engine.
- [`ustore_status_not_found_k`](https://www.unum.cloud/docs/ustore/c/errors.md#ustore_status_not_found_k) A collection the transaction touched was dropped, on RocksDB.
- [`ustore_status_malformed_k`](https://www.unum.cloud/docs/ustore/c/errors.md#ustore_status_malformed_k) A value outgrows what its NVMe collection admits.
- [`ustore_status_capacity_exhausted_k`](https://www.unum.cloud/docs/ustore/c/errors.md#ustore_status_capacity_exhausted_k) The strings outgrow one NVMe run file.

## ustore_transaction_commit

Commits a transaction.

```c
void ustore_transaction_commit(ustore_transaction_commit_t *arguments);
```

- `database`: An open database.
- `executor`: Executor that runs the call, where `NULL` runs it on the calling thread.
- `outcome`: Output for the outcome, final once its status leaves [`ustore_status_deferred_k`](https://www.unum.cloud/docs/ustore/c/errors.md#ustore_status_deferred_k).
- `completion`: Whether the call returns final, the default, or once submitted.
- `transaction`: An initialized transaction, staged first when the caller did not.
- `commit_stamp`: Optional output for the commit's position in commit order, visible to every snapshot at or above it; zeroed on failure.
- `durability`: How durable the writes are once the commit finishes.

Statuses: - [`ustore_status_contended_k`](https://www.unum.cloud/docs/ustore/c/errors.md#ustore_status_contended_k) The transaction serves another call, the executor's deferred table is full, or a callback blocks on the executor running it.
- [`ustore_status_uninitialized_k`](https://www.unum.cloud/docs/ustore/c/errors.md#ustore_status_uninitialized_k) The `database` or the `transaction` is `NULL`.
- [`ustore_status_read_only_k`](https://www.unum.cloud/docs/ustore/c/errors.md#ustore_status_read_only_k) The database was opened read-only.
- [`ustore_status_unsupported_k`](https://www.unum.cloud/docs/ustore/c/errors.md#ustore_status_unsupported_k) The executor computes on a device, or the engine has no transactions, as LevelDB has none.
- [`ustore_status_invalid_argument_k`](https://www.unum.cloud/docs/ustore/c/errors.md#ustore_status_invalid_argument_k) The `durability` is unknown, or a failed RocksDB stage left the transaction needing a reset.
- [`ustore_status_write_conflict_k`](https://www.unum.cloud/docs/ustore/c/errors.md#ustore_status_write_conflict_k) Another commit changed a key this transaction wrote, or, on the NVMe engine, any key, range or prefix it watched or a collection it touched.
- [`ustore_status_read_conflict_k`](https://www.unum.cloud/docs/ustore/c/errors.md#ustore_status_read_conflict_k) Another commit changed a key this transaction read, on the RAM and RocksDB engines.
- [`ustore_status_phantom_conflict_k`](https://www.unum.cloud/docs/ustore/c/errors.md#ustore_status_phantom_conflict_k) Another commit changed a window this transaction walked, on the strictly serializable RAM engine.
- [`ustore_status_not_found_k`](https://www.unum.cloud/docs/ustore/c/errors.md#ustore_status_not_found_k) A collection the transaction touched was dropped, on RocksDB.
- [`ustore_status_malformed_k`](https://www.unum.cloud/docs/ustore/c/errors.md#ustore_status_malformed_k) A value outgrows what its NVMe collection admits.
- [`ustore_status_capacity_exhausted_k`](https://www.unum.cloud/docs/ustore/c/errors.md#ustore_status_capacity_exhausted_k) The strings outgrow one NVMe run file.

## ustore_transaction_rollback

Withdraws a stage, releasing whatever it claimed, while keeping the reads and writes, so the transaction can stage again or be reset through [`ustore_transaction_init`](#ustore_transaction_init).

```c
void ustore_transaction_rollback(
    ustore_transaction_rollback_t *arguments);
```

- `database`: An open database.
- `executor`: Executor that runs the call, where `NULL` runs it on the calling thread.
- `outcome`: Output for the outcome, final once its status leaves [`ustore_status_deferred_k`](https://www.unum.cloud/docs/ustore/c/errors.md#ustore_status_deferred_k).
- `completion`: Whether the call returns final, the default, or once submitted.
- `transaction`: A transaction, staged or not.

Statuses: - [`ustore_status_contended_k`](https://www.unum.cloud/docs/ustore/c/errors.md#ustore_status_contended_k) The transaction serves another call, the executor's deferred table is full, or a callback blocks on the executor running it.
- [`ustore_status_uninitialized_k`](https://www.unum.cloud/docs/ustore/c/errors.md#ustore_status_uninitialized_k) The `database` or the `transaction` is `NULL`.
- [`ustore_status_read_only_k`](https://www.unum.cloud/docs/ustore/c/errors.md#ustore_status_read_only_k) The database was opened read-only.
- [`ustore_status_unsupported_k`](https://www.unum.cloud/docs/ustore/c/errors.md#ustore_status_unsupported_k) The executor computes on a device, or the engine has no transactions, as LevelDB has none.
- [`ustore_status_invalid_argument_k`](https://www.unum.cloud/docs/ustore/c/errors.md#ustore_status_invalid_argument_k) A failed RocksDB stage left the transaction needing a reset.

## ustore_transaction_free

Waits until every deferred call naming the transaction finishes, then resets it and frees its memory with every view of it once no cursor reads through it.

```c
void ustore_transaction_free(ustore_transaction_t transaction);
```

- `transaction` (inout): The transaction to be freed; `NULL` is safe.

