# Snapshots

> UStore for C

A snapshot pins a point-in-time view of the whole database, and reads select it through `ustore_snapshot_view`, so a read can name a transaction or a snapshot but never both:

```c
ustore_snapshot_t snapshot = NULL;
ustore_snapshot_init(&(ustore_snapshot_init_t){
    .snapshot = &snapshot,
    .database = database,
    .outcome = &outcome,
});

ustore_blobs_read(&(ustore_blobs_read_t){
    .database = database, .outcome = &outcome, .view = ustore_snapshot_view(snapshot), /* ... */});

ustore_snapshot_free(snapshot);
```

`ustore_snapshot_init` can export its `read_stamp`, the stamp the snapshot reads at, which LevelDB, keeping its sequence numbers private, reports as zero.
A snapshot lives no longer than the process that took it, and holds its database open until freed, as a transaction does, so `ustore_database_free` closes the database only once the last of them is freed.
`ustore_snapshot_save` writes one out as a new database of the same engine, holding exactly the collections and names the snapshot records, which that engine's open then serves, so a save is how a view outlives the process.
RocksDB and LevelDB create its `path`, the `ram` engine writes its image into the directory `path` names, and NVMe takes `path` as the manifest of the new database.
Persistent snapshots, recorded in a system collection under UUIDv7 identifiers and kept across restarts until dropped, are planned.

`#include <ustore/db.h>`

## ustore_snapshot_t

An opaque handle to a point-in-time state of the whole database, which reads address through [`ustore_snapshot_view`](#ustore_snapshot_view).

```c
typedef struct ustore_snapshot_handle_t * ustore_snapshot_t;
```

Short-lived: it pins the versions its reads need and holds its database open until [`ustore_snapshot_free`](#ustore_snapshot_free).

## ustore_snapshot_init

Pins a point-in-time state of the database until [`ustore_snapshot_free`](#ustore_snapshot_free) releases it.

```c
void ustore_snapshot_init(ustore_snapshot_init_t *arguments);
```

- `snapshot`: Output for the new snapshot, which the caller frees with [`ustore_snapshot_free`](#ustore_snapshot_free); `NULL` on failure.
- `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.
- `read_stamp`: Optional output for the stamp the snapshot reads at; zeroed on failure.

Statuses: - [`ustore_status_contended_k`](https://www.unum.cloud/docs/ustore/c/errors.md#ustore_status_contended_k) 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_unsupported_k`](https://www.unum.cloud/docs/ustore/c/errors.md#ustore_status_unsupported_k) The executor computes on a device.
- [`ustore_status_invalid_argument_k`](https://www.unum.cloud/docs/ustore/c/errors.md#ustore_status_invalid_argument_k) The `snapshot` output is `NULL`.

## ustore_snapshot_save

Writes a snapshot out as a new database of the same engine, which that engine's open then serves.

```c
void ustore_snapshot_save(ustore_snapshot_save_t *arguments);
```

The copy holds exactly the collections and names the snapshot records, whatever changed since. RocksDB and LevelDB create `path`, while the RAM engine writes its image into the directory `path` names. Its progress counts the bytes of the keys and values saved, and a save its progress callback cancels removes what it wrote, as a failed one does. The NVMe engine names the SHA-256 of every run in the saved manifest, which the first open of the copy checks before it reads and then drops from every manifest it publishes.

- `snapshot`: The snapshot to save, which holds its database open.
- `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.
- `path`: Directory that receives the saved database, or on NVMe its manifest, `<database>.ustore.json`, or the raw address of its first slot.
- `progress`: Optional callback reporting the bytes saved.
- `progress_state`: Opaque pointer passed to `progress`.

Statuses: - [`ustore_status_contended_k`](https://www.unum.cloud/docs/ustore/c/errors.md#ustore_status_contended_k) 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 `snapshot` is `NULL`.
- [`ustore_status_unsupported_k`](https://www.unum.cloud/docs/ustore/c/errors.md#ustore_status_unsupported_k) The executor computes on a device.
- [`ustore_status_invalid_argument_k`](https://www.unum.cloud/docs/ustore/c/errors.md#ustore_status_invalid_argument_k) The `path` is empty on the NVMe engine, or RocksDB finds a database there already.
- [`ustore_status_not_found_k`](https://www.unum.cloud/docs/ustore/c/errors.md#ustore_status_not_found_k) The `path` is no directory on the RAM engine, or a collection was dropped meanwhile on the NVMe engine.

## ustore_snapshot_view

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

```c
ustore_view_t ustore_snapshot_view(ustore_snapshot_t snapshot);
```

- `snapshot` (in): A snapshot, which the view dies with.

## ustore_snapshot_free

Waits until every deferred call naming the snapshot finishes, then releases it, unpinning the versions only it kept, and frees its memory with every view of it once no cursor reads through it.

```c
void ustore_snapshot_free(ustore_snapshot_t snapshot);
```

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

