Documentation
¶
Overview ¶
Package dbbackup creates and restores hot backups of the cloop SQLite state database (Task 20115).
Why a dedicated package:
Operators need a safe way to capture the current project state without stopping the running cloop daemon. SQLite WAL mode (Task 20084) makes this possible — readers and writers do not block each other — but a naive `cp` of state.db is unsafe because uncommitted frames may still be in the WAL file. We use `VACUUM INTO 'path'` which the SQLite engine guarantees is a transactionally-consistent copy at the read transaction's snapshot point; it is the recommended hot-backup primitive when the C-level Online Backup API is not available, as is the case for the pure-Go modernc.org/sqlite driver we ship.
Backup flow:
- Open the source database (this triggers Migrate, but that is idempotent and does not write user data).
- PRAGMA wal_checkpoint(TRUNCATE) flushes pending WAL frames into the main database file so the snapshot is as current as possible.
- VACUUM INTO 'output.db' produces a self-contained, defragmented copy. The copy has no -wal/-shm sidecars; it is a single file.
- Compute SHA-256 of the copy and write a sidecar metadata JSON describing source path, size, schema version, and duration.
Restore flow:
- Validate the backup with PRAGMA integrity_check (read-only handle).
- Verify the SHA-256 against the metadata sidecar (if present).
- Refuse to overwrite an existing destination unless --force is set.
- Atomically swap the file in via tmp+rename.
Why VACUUM INTO instead of OS-level copy:
- Atomically consistent against in-flight writes.
- Skips freelist pages, so the backup is smaller and quicker to verify.
- Single self-contained file (no WAL/SHM sidecars to ship together).
Index ¶
Constants ¶
const MetadataSuffix = ".meta.json"
MetadataSuffix is appended to the backup file path to form the sidecar metadata path: "/path/foo.db" → "/path/foo.db.meta.json".
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type BackupReport ¶
type BackupReport struct {
Source string
Output string
MetadataPath string
SizeBytes int64
Duration time.Duration
SHA256 string
WALCheckpoint string // "PASSED" or human-readable diagnostic
SchemaVersion int
}
BackupReport summarises a successful backup run.
func Backup ¶
func Backup(srcPath, outPath string) (*BackupReport, error)
Backup creates a hot backup of the SQLite database at srcPath and writes it to outPath. The destination directory must exist. A sibling "<outPath>.meta.json" file is written alongside containing the SHA-256 checksum, source size, and other metadata used by Restore for validation.
If outPath already exists, it is overwritten — operators are expected to rotate backup filenames (timestamp, sequence, etc.) externally.
type Metadata ¶
type Metadata struct {
Source string `json:"source"`
Backup string `json:"backup"`
CreatedAt time.Time `json:"created_at"`
SizeBytes int64 `json:"size_bytes"`
SHA256 string `json:"sha256"`
DurationMS int64 `json:"duration_ms"`
SchemaVersion int `json:"schema_version"`
CloopVersion string `json:"cloop_version,omitempty"`
}
Metadata is persisted next to the backup file. Restore consults it to verify the backup before overwriting the target. All fields are optional for forward-compat — Restore tolerates an old metadata blob that is missing newer fields.
func LoadMetadata ¶
LoadMetadata reads the sidecar metadata file written next to a backup. Returns (nil, nil) if the file is missing — backups without metadata are allowed (e.g. when SkipChecksum is later passed to Restore).
type RestoreOptions ¶
type RestoreOptions struct {
// Force allows overwriting an existing destination database. Without
// this flag, Restore refuses to overwrite if the destination file or
// any of its WAL/SHM sidecars exist (heuristic for "active database").
Force bool
// SkipChecksum disables the SHA-256 verification step. Useful only for
// recovering from a backup whose metadata sidecar has been lost.
SkipChecksum bool
}
RestoreOptions configures Restore.
type RestoreReport ¶
type RestoreReport struct {
Backup string
Destination string
SizeBytes int64
Duration time.Duration
BackedUpPath string // pre-restore copy of the destination, if any
}
RestoreReport describes a successful restore.
func Restore ¶
func Restore(backupPath, dstPath string, opts RestoreOptions) (*RestoreReport, error)
Restore validates the backup at backupPath and atomically swaps it into dstPath. Validation steps:
- PRAGMA integrity_check on the backup file (read-only).
- SHA-256 verification against the metadata sidecar, if present.
- Schema version sanity (any positive integer is accepted).
If dstPath already exists and Force is false, Restore returns an error. When Force is true, the existing destination is moved to a sibling .pre-restore backup before the swap, so a botched restore is recoverable.