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 <z.bessarab@victoriametrics.com>
This commit is contained in:
Zakhar Bessarab 2023-04-12 20:51:27 +04:00 committed by Aliaksandr Valialkin
parent 61839d4807
commit 217eea6e15
No known key found for this signature in database
GPG key ID: A72BEC6CD3D0DED1
2 changed files with 42 additions and 1 deletions

View file

@ -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

View file

@ -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
}