Backups
The backups plugin copies committed transactions to local folders and S3-compatible storage. It can restore a missing or older database from a backup target.
Enable backups in the plugins option and provide its settings when you open the database.
An absent plugin selection enables no plugins.
Release packages include backups as a plugin library and in the static archive.
The core library does not contain it.
The history and backups plugins cannot share a database.
History retention changes the transaction history that backups must preserve.
Settings
backups {targets {local {file {path backups/main}}
offsite {s3 {endpoint https://s3.eu-central-1.amazonaws.com
region eu-central-1
bucket team-backups
prefix main
accessKeyIdEnv BACKUP_KEY_ID
secretAccessKeyEnv BACKUP_SECRET}
compression {algorithm zstd
level 3}
encryptionKeyEnv BACKUP_ENCRYPTION_KEY
maximumLag {transactions 1000
duration {#dur PT5M}}}}
bypassUnreachable false}
| Setting | Value | Effect |
|---|---|---|
targets | Object | Each member defines a named target. |
bypassUnreachable | Boolean, default false | Permit writes during a target outage. Lag limits and recovery checks still apply. |
Each target requires exactly one storage member: file or s3.
These members are optional:
| Member | Value | Effect |
|---|---|---|
compression | Object | Select algorithm: none or zstd. Zstandard accepts an optional integer level. Compression applies to transaction runs. |
encryptionKeyEnv | Text | Name an environment variable containing a 64-digit hexadecimal AES-256 key. Runs, payload chunks and payload manifests use AES-256-GCM encryption. |
maximumLag | Object | Refuse further data writes when the target exceeds any specified lag limit. |
| Lag limit | Value | Measurement |
|---|---|---|
transactions | Positive integer | Transactions the target has not confirmed |
logicalBytes | Positive integer | Stored transaction-row bytes the target has not confirmed |
binaryBytes | Positive integer | Payload bytes the target has not confirmed |
duration | Positive #dur | Age of the oldest unconfirmed transaction |
The file member requires path.
A relative path resolves against the working directory when the database opens.
| S3 member | Required | Value |
|---|---|---|
endpoint | Yes | S3 service URL |
region | Yes | Signing region |
bucket | Yes | Bucket name |
accessKeyIdEnv | Yes | Environment variable containing the access key ID |
secretAccessKeyEnv | Yes | Environment variable containing the secret access key |
prefix | No | Object key prefix |
sessionTokenEnv | No | Environment variable containing the session token |
addressingStyle | No | path or virtual |
On Linux and macOS, S3 requires libcurl 7.84.0 or newer with thread-safe initialization and asynchronous DNS.
The plugin loads libcurl on the first S3 connection.
Windows uses WinHTTP.
If libcurl is unavailable, S3 targets report Backup_Operating_System. Local file targets remain usable.
Unknown members, invalid values and duplicate storage locations make the database open fail. The plugin reads secret values from the environment when needed. It does not store them in the database.
Stored options
Startup settings are authoritative on every open.
The plugin records them as read-only definitions of the backup type:
| Definition | Contents |
|---|---|
backups/targets/<name> | Settings for the named target |
backups/settings | Plugin-wide settings |
Different startup settings replace these definitions in one transaction. Definitions for removed targets are deleted. Identical settings produce no transaction.
Attempts to write, edit, move or delete these definitions fail with definition_read_only.
To change the settings, close the database and open it with the new settings.
Format
Each target stores immutable runs and a mutable head file, manifest.json.
A run contains one JSON line per transaction. Each line is exactly the transaction's merge patch.
It includes the transaction ID, stardust/committedAt and stardust/transaction.
A SHA-256 chain links the transaction lines. The head identifies the runs, newest transaction and chain digest. The plugin merges small runs into larger runs to limit the number of stored files.
Open and recovery
On open, the plugin compares the local history with each readable target. It selects the newest history only if every other compared history is an exact prefix of it. Missing local transactions are restored with their original transaction IDs, commit times, entity IDs and field names. Older targets receive the transactions they lack.
Data writes remain held until recovery and target catch-up finish. This also applies with bypassUnreachable true.
A source target reports restoring while the database restores from it. A source failure holds writes until recovery can continue.
After a crash or database close, the next open continues after the last restored transaction.
Reactors exclude restored transactions because the history already contains their live-mutation results.
These conditions make the open fail without changing the database:
| Condition | Status | Code |
|---|---|---|
| Histories diverge after a common transaction | conflict | backup_history_divergent |
| The newest target cannot supply history needed to compare another copy | unavailable | backup_target_unreadable |
| The database is new or empty and every target is unreachable | unavailable | backup_targets_unreachable |
Recovery needs object payloads but objects is disabled | unavailable | backup_objects_required |
Error messages name the affected targets. Divergence errors use local for the database.
Unreachable storage includes network or operating-system failures and a target held by another database.
A new database cannot safely start a history while unreachable targets might contain an existing one.
A missing storage location or unavailable encryption key does not itself make the open fail. The target reports the failure in its status.
The plugin retries targets it cannot compare during open. A later divergence reports Backup_History_Divergent in the target status.
After an empty open or a restore, an unexamined target might contain newer history.
It reports comparisonPending, and data writes remain held until comparison and any further recovery finish.
bypassUnreachable true does not bypass this check.
Write admission
Initial comparison and recovery hold data writes and the stored-options transaction.
After recovery, a target that exceeds its maximumLag holds further data writes.
A held data write returns conflict with code write_held.
Retry the write later, or call sync first.
Maintenance, restore transactions and plugin progress records remain permitted.
An unreachable target or Backup_Memory_Exhausted failure triggers retries with increasing delays, up to five minutes.
bypassUnreachable true permits writes during retries except when recovery or comparison requires that target.
Lag limits still apply.
Each commit immediately increases the measured lag. A target within its limits permits the next write.
Runtime status
Call status to inspect backup progress without contacting a target.
| Field | Contents |
|---|---|
enabled | Whether backups are enabled |
head | Newest committed transaction |
writesAdmitted | Whether data writes are permitted |
optionsStored | Whether stored definitions match startup settings |
optionsCode | Last stored-options commit failure, or "" |
targets | Per-target status entries |
| Target field | Contents |
|---|---|
name | Target name |
phase | Current phase, listed below |
cursor | Newest confirmed transaction, or null |
synchronized | Whether the target has confirmed the current history |
restoring | Whether the database is restoring history from this target |
comparisonPending | Whether comparison is still required after an open |
code | Last failure, or "" |
lag | Current transactions, logicalBytes and binaryBytes, when measured |
| Phase | Meaning |
|---|---|
starting | Opening the target |
validating | Reading and checking stored history |
reconciling | Restoring or comparing transactions |
synchronizing | Storing new transactions |
synchronized | All committed transactions confirmed |
retrying | Waiting to retry after a failure |
failed | Stopped until the database opens again |
stopped | The database is closing |
When to call sync
Backups run automatically after commits. A successful write can return before a target stores the transaction. Call sync when an operation requires every target to confirm the current history, such as a recovery test.
Sync waits for the transaction current at the call, or the stored-options transaction if that commits later.
It succeeds after the options are stored and every target confirms that transaction. Targets with maximumLag must also report current lag measurements.
A failed target or refused stored-options commit makes sync report failed.
Use maxWait to set the wait duration. Omitting it, or using zero, checks once without waiting.
Object payloads
The objects plugin identifies file payloads through stardust/object/digest.
Backups store referenced payloads before the transaction run that names them:
| Location | Contents |
|---|---|
binaries/chunks/ | Shared payload chunks |
binaries/manifests/ | Payload manifests |
A shared chunk is stored once. Targets retain stored payloads permanently.
encryptionKeyEnv encrypts chunks and manifests. Payloads are not compressed.
Recovery copies missing payloads into the object store. Before each referencing transaction commits, recovery checks every chunk and the whole payload against their digests. These checks also apply to payloads already present locally. Each payload digest is published with its referencing transaction.
A missing or corrupt payload stops recovery before that transaction and reports Backup_Corrupted.
Earlier restored transactions remain committed, and data writes remain held.
Recovery of payload references requires objects.
If this requirement is discovered during open, it fails with backup_objects_required, as listed under open and recovery.
A target examined later reports Backup_Objects_Required before restoring any transactions.
The same status applies when a target must store payloads that the database cannot read.
Each pending payload contributes its size to the binaryBytes lag.
Operations
| Operation | Returns | Summary |
|---|---|---|
| Backups.Status | document | backups.status() returns an owned document describing backup progress. |
| Backups.Sync | none | backups.sync(options) waits for every target to confirm the current transaction history. |
Backups.Status
func (v Backups) Status() (*Document, error)Errors
| Status | When |
|---|---|
StatusUnavailable | the database does not enable the backups plugin |
StatusClosed | the database is closed |
StatusOutOfMemory | native memory is exhausted |
Notes
See runtime status for the result fields. This call does not contact targets.
Examples
Describe the backup runtime
failed, err := db.Backups().Status()
if failed != nil {
defer failed.Close()
}
var result *stardust.Error
if !errors.As(err, &result) {
return err
}{code unavailable
message the plugin is not enabled
path null
field null
entity null
offset null
status unavailable}C exports: stardust_db_plugin_call, stardust_result_take_document, stardust_result_error, stardust_result_release
Backups.Sync
func (v Backups) Sync(options *Document) errorParameters
| Parameter | Type | Ownership | Default | Meaning |
|---|---|---|---|---|
options | *Document | Borrowed for the call | Absent selects operation defaults | The borrowed call options, or none. Optional maxWait is a nonnegative native duration, such as {maxWait {#dur PT5S}}. Without it, or with zero, the call checks once. |
Errors
| Status | When |
|---|---|
StatusInvalidArgument | options is not an object, or an option is invalid, duplicated or unsupported |
StatusFailed | the plugin's work failed; status describes the failure |
StatusTimeout | maxWait passed before every target confirmed |
StatusUnavailable | the database does not enable the backups plugin |
StatusClosed | the database is closed |
Notes
Database close interrupts an active wait. See when to call sync.
Examples
Wait until every target stored the history
syncOptions, err := stardust.Parse([]byte(`{maxWait {#dur PT5S}}`), stardust.DUST)
if err != nil {
return err
}
defer syncOptions.Close()
err = db.Backups().Sync(syncOptions)
var result *stardust.Error
if !errors.As(err, &result) {
return err
}{code unavailable
message the plugin is not enabled
path null
field null
entity null
offset null
status unavailable}C exports: stardust_db_plugin_call, stardust_result_take_document, stardust_result_error, stardust_result_release