From 81f28f0f1f679fca2c638c7a437f4c1e8987cb28 Mon Sep 17 00:00:00 2001 From: Zakhar Bessarab Date: Wed, 12 Apr 2023 20:51:27 +0400 Subject: [PATCH] lib/backup/actions: store metadata(creation and completion time) in backup files (#4117) This makes it easier to understand exact point in time which is included in this backup. Signed-off-by: Zakhar Bessarab --- docs/CHANGELOG.md | 2 ++ lib/backup/actions/backup.go | 41 +++++++++++++++++++++++++++++++++++- 2 files changed, 42 insertions(+), 1 deletion(-) diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 8e4b2b392..52ed8f345 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -15,6 +15,8 @@ The following tip changes can be tested by building VictoriaMetrics components f ## tip +* FEATURE: [vmbackup](https://docs.victoriametrics.com/vmbackup.html): store backup creation and completion time in `backup_complete.ignore` file of backup contents. This is useful to determine point in time when backup was created and completed. + ## [v1.90.0](https://github.com/VictoriaMetrics/VictoriaMetrics/releases/tag/v1.90.0) Released at 2023-04-06 diff --git a/lib/backup/actions/backup.go b/lib/backup/actions/backup.go index 984d68f5f..f01d5b127 100644 --- a/lib/backup/actions/backup.go +++ b/lib/backup/actions/backup.go @@ -1,8 +1,10 @@ package actions import ( + "encoding/json" "fmt" "io" + "strings" "sync/atomic" "time" @@ -11,6 +13,7 @@ import ( "github.com/VictoriaMetrics/VictoriaMetrics/lib/backup/fslocal" "github.com/VictoriaMetrics/VictoriaMetrics/lib/backup/fsnil" "github.com/VictoriaMetrics/VictoriaMetrics/lib/logger" + "github.com/VictoriaMetrics/VictoriaMetrics/lib/snapshot" "github.com/VictoriaMetrics/metrics" ) @@ -46,6 +49,13 @@ type Backup struct { Origin common.OriginFS } +// BackupMetadata contains metadata about the backup. +// Note that CreatedAt and CompletedAt are in RFC3339 format. +type BackupMetadata struct { + CreatedAt string `json:"created_at"` + CompletedAt string `json:"completed_at"` +} + // Run runs b with the provided settings. func (b *Backup) Run() error { concurrency := b.Concurrency @@ -66,9 +76,38 @@ func (b *Backup) Run() error { if err := runBackup(src, dst, origin, concurrency); err != nil { return err } - if err := dst.CreateFile(fscommon.BackupCompleteFilename, []byte("ok")); err != nil { + if err := storeMetadata(src, dst); err != nil { + return fmt.Errorf("cannot store backup metadata: %w", err) + } + return nil +} + +func storeMetadata(src *fslocal.FS, dst common.RemoteFS) error { + last := strings.LastIndex(src.Dir, "/") + if last < 0 { + return fmt.Errorf("cannot decode snapshot location %q", src.Dir) + } + + snapshotName := src.Dir[last+1:] + snapshotTime, err := snapshot.Time(snapshotName) + if err != nil { + return fmt.Errorf("cannot decode snapshot name %q: %w", snapshotName, err) + } + + d := BackupMetadata{ + CreatedAt: snapshotTime.Format(time.RFC3339), + CompletedAt: time.Now().Format(time.RFC3339), + } + + metadata, err := json.Marshal(d) + if err != nil { + return fmt.Errorf("cannot marshal metadata: %w", err) + } + + if err := dst.CreateFile(fscommon.BackupCompleteFilename, metadata); err != nil { return fmt.Errorf("cannot create `backup complete` file at %s: %w", dst, err) } + return nil }