Skip to content

Add snapshot command - #219

Open
peterjan wants to merge 5 commits into
pj/snapshot-orphan-guardfrom
pj/backup-commands
Open

peterjan wants to merge 5 commits into
pj/snapshot-orphan-guardfrom
pj/backup-commands

Conversation

@peterjan

@peterjan peterjan commented Jun 24, 2026

Copy link
Copy Markdown
Member

This PR adds a CLI command to create, list, delete, and restore snapshots.

s3d snapshots create/list/delete wrap the admin API. list --remote enumerates the snapshots stored on the Sia network, reading the app key from the local database rather than the admin API, so it works against a stopped daemon. restore fetches a backup from Sia and writes it to the data directory, which is what makes a lost database recoverable from the network alone.

References #202

@peterjan peterjan self-assigned this Jun 24, 2026
@peterjan
peterjan requested a lite review from Copilot June 24, 2026 14:00
@github-project-automation github-project-automation Bot moved this to In Progress in Sia Jun 24, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds backup management capabilities to s3d by exposing snapshot list/delete operations through the admin API and wiring corresponding s3d backup CLI subcommands, building on the existing SQLite snapshot/backup implementation.

Changes:

  • Extend the backend interface and Sia implementation to support listing and deleting recorded snapshot backups.
  • Add new admin API endpoints to list backups and delete a backup snapshot; update OpenAPI accordingly.
  • Add s3d backup create|list|delete CLI commands and a changeset entry documenting the feature.

Reviewed changes

Copilot reviewed 8 out of 8 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
sia/sia.go Adds backend methods for listing/deleting snapshots and maps store snapshot records to admin API DTOs.
s3/s3.go Extends admin backend interface and registers new admin API routes for backup listing/deletion.
s3/s3_test.go Updates admin API tests to validate list/delete snapshot endpoints via HTTP.
s3/admin.go Introduces Snapshot response type and handlers for listing/deleting snapshots.
openapi.yml Documents the new /system/sqlite3/backups list and delete endpoints plus Snapshot schema.
cmd/s3d/main.go Wires the new backup command and its subcommands into the CLI command tree.
cmd/s3d/backup.go Implements the `s3d backup create
.changeset/backup_commands.md Records the user-facing change for release tooling.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread sia/sia.go Outdated
Comment thread s3/admin.go Outdated
Comment thread cmd/s3d/backup.go Outdated
Comment thread cmd/s3d/backup.go Outdated
Comment thread openapi.yml Outdated
Comment thread cmd/s3d/main.go Outdated
@peterjan
peterjan force-pushed the pj/backup-commands branch 2 times, most recently from cfd57b9 to 9569274 Compare June 25, 2026 13:23
@peterjan
peterjan marked this pull request as ready for review June 26, 2026 07:17
@chris124567 chris124567 mentioned this pull request Jun 26, 2026
@chris124567
chris124567 requested a review from Copilot June 26, 2026 18:26

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 8 out of 8 changed files in this pull request and generated 3 comments.

Comment thread s3/s3.go Outdated
Comment thread sia/sia.go Outdated
Comment thread openapi.yml Outdated

@chris124567 chris124567 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM except for what Copilot said about NewAdmin docstring

@peterjan
peterjan marked this pull request as draft July 9, 2026 08:12
@peterjan

peterjan commented Jul 9, 2026

Copy link
Copy Markdown
Member Author

moved back into DRAFT until snapshot design is consolidated - then I'll update this accordingly

@peterjan
peterjan force-pushed the pj/backup-commands branch 4 times, most recently from 192d7a8 to 8f9de4c Compare July 15, 2026 12:56
@peterjan
peterjan marked this pull request as ready for review July 15, 2026 13:13
@peterjan
peterjan force-pushed the pj/backup-commands branch from 8f9de4c to 8bb8684 Compare July 16, 2026 10:11

@chris124567 chris124567 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

more conflicts

@peterjan

Copy link
Copy Markdown
Member Author

more conflicts

Yeah I'm tired of rebasing it so I'll wait until the base PR gets merged :)

@peterjan
peterjan force-pushed the pj/backup-commands branch from 8bb8684 to e21bfc9 Compare July 22, 2026 08:54
@peterjan

Copy link
Copy Markdown
Member Author

Should we consider calling the command snapshot? It's bothering me a bit that we kind of use backup/snapshot interchangeably now, even though it's consistent but renaming it saves backup for when we add plain sqlite backups maybe...

@peterjan
peterjan marked this pull request as ready for review September 8, 2026 14:55
Copilot AI review requested due to automatic review settings September 8, 2026 14:55

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

s3d snapshots restore --force can fail on Windows because os.Rename won’t overwrite an existing database file unless it is removed first.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Review details
  • Files reviewed: 24/24 changed files
  • Comments generated: 1
  • Review effort level: Lite

Comment thread cmd/s3d/snapshots.go
Copilot AI review requested due to automatic review settings September 10, 2026 09:03

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

The restore/remote-list flows currently require opening the local SQLite DB to obtain the app key, which undermines the stated “lost database” recovery story and should be addressed (e.g., alternate key source) before approval.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Review details
  • Files reviewed: 24/24 changed files
  • Comments generated: 1
  • Review effort level: Lite

Comment thread cmd/s3d/snapshots.go
Copilot AI review requested due to automatic review settings September 14, 2026 18:38

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Unresolved critical snapshot safety and consistency issues remain, along with API compatibility and response-ordering fixes.

Get a fresh assessment by requesting another Copilot review.

Review details

Suppressed comments (2)

s3/admin.go:23

  • id was part of the existing /snapshots response and is removed from JSON here, silently breaking clients of the already-published POST endpoint. The new Sia object ID can be the cross-database address without removing the local ID; keep this field for wire compatibility (and update the OpenAPI schema), or explicitly version this breaking response change.
	// ID is the local database row, needed because a snapshot row is inserted
	// before its Sia object exists. It is deliberately not serialized:
	// snapshots are addressed by their Sia object ID, and a row ID is
	// meaningless outside the one database that issued it.
	ID          int64         `json:"-"`

s3/admin.go:125

  • The empty result from Store.ListSnapshots is a nil slice, so encoding it here produces JSON null, while this endpoint is documented as returning an array. Consumers that iterate the response must special-case null; normalize an empty result to [] before encoding.
	jc.Encode(snapshots)
  • Files reviewed: 24/24 changed files
  • Comments generated: 3
  • Review effort level: Lite

Comment thread cmd/s3d/snapshots.go Outdated
Comment thread sia/sia.go
Comment thread sia/snapshots.go Outdated
Copilot AI review requested due to automatic review settings September 16, 2026 13:33

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

Copilot AI review requested due to automatic review settings September 16, 2026 15:06

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

Comment thread cmd/s3d/snapshots.go
// truncated database behind
tmp, err := os.CreateTemp(destDir, "restore-*.tmp")
checkFatalError("failed to create temporary file", err)
defer os.Remove(tmp.Name())

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: checkFatalError calls os.Exit which will result in the defer calls being skipped so to be thorough here you should make a helper that removes it then calls checkFatalError

Copilot AI review requested due to automatic review settings September 18, 2026 14:02

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Unresolved snapshot consistency, restore-safety, and deletion-race issues include one critical finding.

Get a fresh assessment by requesting another Copilot review.

Review details

Suppressed comments (3)

cmd/s3d/snapshots.go:240

  • The existing s3d.db-wal is removed before the replacement database is renamed into place. If the second sidecar removal or the later os.Rename fails, the old database remains but its WAL may already be gone, losing committed transactions and undermining the promise that a failed restore cannot damage the existing database. Stage or preserve the sidecars and only remove them after a replacement-safe commit.
	for _, sidecar := range []string{dbPath + "-wal", dbPath + "-shm"} {
		if err := os.Remove(sidecar); err != nil && !errors.Is(err, os.ErrNotExist) {
			checkFatalRestoreError("failed to remove stale sidecar", err)
		}

sia/sia.go:396

  • Flushing before creating the snapshot does not make the backup self-contained because store.Backup runs while writes remain allowed (the API docs explicitly promise this). A PUT that starts after this flush can leave a pending object row in the SQLite image whose filename points into uploads/; the snapshot only contains SQLite, so restoring it into a fresh directory has no file to upload and cannot recover that object. The snapshot path needs to quiesce/coordinate new writes or otherwise exclude and handle pending rows.
	if err := s.FlushObjects(ctx); err != nil {
		return s3.Snapshot{}, fmt.Errorf("failed to flush objects before snapshot: %w", err)
	}

sia/sia.go:562

  • A concurrent metadata sync can delete this row after DeleteObject succeeds and before DeleteSnapshotsBySiaObject runs. In that race, deleted == 0 makes an already successful delete return ErrSnapshotNotFound (and skips the orphan-loop wake), so the CLI reports a failure. After the initial HasSnapshotObject check, treat the local delete as idempotent instead of converting this race into 404.
	} else if deleted == 0 {
		return s3.ErrSnapshotNotFound
  • Files reviewed: 24/24 changed files
  • Comments generated: 1
  • Review effort level: Lite

Comment thread sia/sia.go
// snapshot object, pins it, and marks the record pinned. On failure the
// snapshot is rolled back.
func (s *Sia) CreateSnapshot(ctx context.Context) (_ s3.Snapshot, err error) {
if err := s.FlushObjects(ctx); err != nil {
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: In Progress

Development

Successfully merging this pull request may close these issues.

4 participants