dbbackup

package
v0.0.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 17, 2026 License: MIT Imports: 14 Imported by: 0

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:

  1. Open the source database (this triggers Migrate, but that is idempotent and does not write user data).
  2. PRAGMA wal_checkpoint(TRUNCATE) flushes pending WAL frames into the main database file so the snapshot is as current as possible.
  3. VACUUM INTO 'output.db' produces a self-contained, defragmented copy. The copy has no -wal/-shm sidecars; it is a single file.
  4. Compute SHA-256 of the copy and write a sidecar metadata JSON describing source path, size, schema version, and duration.

Restore flow:

  1. Validate the backup with PRAGMA integrity_check (read-only handle).
  2. Verify the SHA-256 against the metadata sidecar (if present).
  3. Refuse to overwrite an existing destination unless --force is set.
  4. 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

View Source
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

func LoadMetadata(backupPath string) (*Metadata, error)

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.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL