Skip to content

Maintenance Operations

Maintenance Operations

TidesDB compacts and flushes on its own. Everything here forces work that would otherwise happen when a trigger fired, or establishes a durability barrier stronger than the configured sync mode.

These calls exist for operators and for tests. A well-configured database under normal load should not need them on a schedule — reach for them to take a backup, to make a barrier before something risky, or to compact during a known-quiet window rather than letting it land during a busy one.

Thread safety

Every call in this chapter is safe from any thread, and the database stays open to readers and writers while one runs. What they do not do is queue behind each other: each can report TDB_ERR_LOCKED, which always means the work was not done because something else held what it needed, never that anything was lost or corrupted. Asking again later is the whole remedy.

What is held differs by call. tidesdb_compact and tidesdb_compact_range take one family exclusively and report locked immediately if a compaction already holds it, rather than parking the caller behind a merge that may run for minutes — and in that case the work asked for is usually already under way. tidesdb_backup claims every family the same way and reports locked if any one of them will not come free. tidesdb_flush_memtable, and tidesdb_checkpoint through it, instead wait a bounded time for the immutable queue to drain and report locked only if it has not, which says flush is not keeping up rather than that anything is wrong with the call.

tidesdb_compact

Force one compaction pass on a column family.

Synopsis

int tidesdb_compact(tidesdb_t *db, tidesdb_column_family_t *cf);

Description

Runs one compaction pass synchronously, merging even when no trigger is due. Returns when the pass is finished.

This is I/O-heavy in proportion to how much data the pass merges, and it competes with the background compaction pool for the same device. Forcing it during peak load makes latency worse, not better.

Fails fast rather than queueing. If the family is already being compacted — by the background scheduler or another caller — it returns TDB_ERR_LOCKED immediately.

Errors

CodeCause
TDB_ERR_INVALID_ARGSdb or cf is NULL
TDB_ERR_LOCKEDA compaction is already running on this family
TDB_ERR_IO / TDB_ERR_CORRUPTIONReading inputs or writing outputs failed
TDB_ERR_MEMORYAllocation failed

See Also

tidesdb_compact_range, tidesdb_is_compacting

tidesdb_compact_range

Compact only the sstables overlapping a key range.

Synopsis

int tidesdb_compact_range(tidesdb_t *db, tidesdb_column_family_t *cf,
const uint8_t *start_key, size_t start_key_size,
const uint8_t *end_key, size_t end_key_size);

Description

Compacts every sstable overlapping [start_key, end_key), merging toward the largest level affected. Useful after deleting a contiguous span of keys, where a full compaction would do far more work than the space being reclaimed justifies.

Either endpoint may be NULL for unbounded. Both NULL is rejected with TDB_ERR_INVALID_ARGS rather than silently becoming a full compaction — use tidesdb_compact when that is what you mean.

The range is half-open: start_key is included, end_key is not.

Errors

CodeCause
TDB_ERR_INVALID_ARGSdb or cf is NULL, or both endpoints are NULL
TDB_ERR_LOCKEDA compaction is already running on this family
TDB_ERR_IO / TDB_ERR_CORRUPTIONReading inputs or writing outputs failed
TDB_ERR_MEMORYAllocation failed

tidesdb_flush_memtable

Rotate and flush the memtable.

Synopsis

int tidesdb_flush_memtable(tidesdb_t *db);

Description

Seals the active memtable, installs a fresh one, and writes the sealed one out to each column family’s L1, returning when that is done.

The memtable is shared by every column family, so this flushes all of them — there is no per-family flush. This is why tidesdb_rename_column_family and tidesdb_clone_column_family are database-wide in their cost.

Writers are not blocked for the whole call: the rotation installs a new active memtable immediately, and writes continue into it while the sealed one is written out.

The call waits for the immutable queue to drain, so on return every generation sealed before it is in L1 — which is what backup and clone need, since they copy files and anything still only in memory would be missed.

Under sustained writes from other threads the queue may never be observed empty, and the call then reports TDB_ERR_LOCKED even though this caller’s own data has landed. That is deliberate: the alternative — returning as soon as the caller’s own generation lands — lets a copy silently omit data that was still queued.

Errors

CodeCause
TDB_ERR_INVALID_ARGSdb is NULL
TDB_ERR_IOWriting an sstable or committing the manifest failed
TDB_ERR_NO_SPACEThe device is full; the data stays in the memtable and its log until space is freed
TDB_ERR_LOCKEDThe immutable queue did not drain within the wait — either the flush pool is stuck, or writes are arriving faster than it drains
TDB_ERR_MEMORYAllocation failed

See Also

tidesdb_is_flushing, tidesdb_checkpoint

tidesdb_is_flushing

Report whether a flush or rotation is in progress.

Synopsis

int tidesdb_is_flushing(tidesdb_t *db);

Description

Returns 1 while the memtable is rotating or a sealed memtable is being written out, 0 otherwise. A sample of a moving value — it may be stale the instant it returns, so it is for monitoring, not for synchronisation. Waiting on it in a loop is a bug.

Return Value

1 if flushing, 0 otherwise, including for a NULL handle.

tidesdb_is_compacting

Report whether a family is being compacted.

Synopsis

int tidesdb_is_compacting(tidesdb_column_family_t *cf);

Description

Returns 1 while a compaction is running on cf. The same caveat applies: a sample, not a lock. Checking it before tidesdb_compact does not prevent TDB_ERR_LOCKED, since a background compaction can start in between — handle the error instead.

Return Value

1 if compacting, 0 otherwise, including for a NULL handle.

tidesdb_sync_wal

Force the write-ahead log to disk.

Synopsis

int tidesdb_sync_wal(tidesdb_t *db);

Description

Forces an fsync of the write-ahead log, making every commit acknowledged before this call durable on the device regardless of the configured sync mode.

This is the cheap barrier. Under TDB_SYNC_NONE it converts “staged in the log’s ring, still inside this process” into “on the device” for the WAL alone — it waits for the ring to drain before the fsync, because syncing the descriptor alone would report as durable a record still held in memory. Under TDB_SYNC_INTERVAL it makes the barrier now instead of at the next tick. Under TDB_SYNC_FULL it is redundant, since each commit is already durable when it returns.

It does not cover the sstables, the value log, or the manifest. For a barrier across all of those use tidesdb_checkpoint.

Errors

CodeCause
TDB_ERR_INVALID_ARGSdb is NULL
TDB_ERR_IOThe fsync failed

tidesdb_backup

Write a consistent, directly-openable copy of the database.

Synopsis

int tidesdb_backup(tidesdb_t *db, const char *dir);

Description

Flushes the memtable, then copies the manifest, the shared value log, and every sstable the manifest references — all at a single manifest snapshot, with compaction held off, so the copy can never reference a file a merge deleted mid-copy.

The result is a database directory, not an archive. Point tidesdb_open at it and it opens.

The database stays writable throughout. Writes committed during the backup are not included: the copy is consistent as of the flush at the start.

Cost and free space required are proportional to the live on-disk size.

Parameters

ParameterDescription
dbDatabase handle
dirDestination directory, created if absent. Must not be the live database directory.

Errors

CodeCause
TDB_ERR_INVALID_ARGSdb or dir is NULL
TDB_ERR_LOCKEDA column family stayed under compaction for the whole freeze window, so it could not be copied at a consistent point. Retryable
TDB_ERR_MEMORYAllocation failed
TDB_ERR_IOThe directory could not be created, or a file could not be read or written

See Also

tidesdb_checkpoint, tidesdb_clone_column_family

tidesdb_checkpoint

Establish a full durability barrier.

Synopsis

int tidesdb_checkpoint(tidesdb_t *db);

Description

Flushes the memtable to L1, then forces the value log, the write-ahead log, and the manifest to disk regardless of the configured sync mode. When it returns, everything committed beforehand is on the device.

This is the strong barrier, and the one to use before anything risky — a host reboot, a storage migration, a filesystem-level snapshot. It differs from tidesdb_sync_wal in covering the whole durable base rather than the log alone, and from tidesdb_backup in making the live database durable rather than producing a second copy.

Errors

CodeCause
TDB_ERR_INVALID_ARGSdb is NULL
TDB_ERR_LOCKEDThe flush it begins with could not drain the immutable queue inside its bounded wait, which says flush is behind rather than that anything is held. Retryable
TDB_ERR_IOThe flush or one of the fsyncs failed

Examples

/* make everything durable before a planned restart */
if (tidesdb_checkpoint(db) != TDB_SUCCESS)
fprintf(stderr, "checkpoint failed; do not assume the data is on disk\n");
tidesdb_close(db); /* close cannot report a failure -- see its entry */