diff --git a/server/server.go b/server/server.go index 080d3f7415df4..c0e8fe853471a 100644 --- a/server/server.go +++ b/server/server.go @@ -52,8 +52,11 @@ func NewServer(ctx context.Context, profile *profile.Profile, store *store.Store Profile: profile, // Asynchronous runners. - backupRunner: backup.NewBackupRunner(store), - telegramBot: telegram.NewBotWithHandler(integration.NewTelegramHandler(store)), + telegramBot: telegram.NewBotWithHandler(integration.NewTelegramHandler(store)), + } + + if profile.Driver == "sqlite" { + s.backupRunner = backup.NewBackupRunner(store) } e.Use(middleware.LoggerWithConfig(middleware.LoggerConfig{ @@ -112,7 +115,10 @@ func NewServer(ctx context.Context, profile *profile.Profile, store *store.Store func (s *Server) Start(ctx context.Context) error { go versionchecker.NewVersionChecker(s.Store, s.Profile).Start(ctx) go s.telegramBot.Start(ctx) - go s.backupRunner.Run(ctx) + + if s.backupRunner != nil { + go s.backupRunner.Run(ctx) + } metric.Enqueue("server start") return s.e.Start(fmt.Sprintf("%s:%d", s.Profile.Addr, s.Profile.Port)) diff --git a/server/service/backup/backup.go b/server/service/backup/backup.go index 399d586dd79f3..b979aa38ec850 100644 --- a/server/service/backup/backup.go +++ b/server/service/backup/backup.go @@ -3,6 +3,7 @@ package backup import ( "context" "fmt" + "os" "strconv" "time" @@ -24,6 +25,8 @@ func NewBackupRunner(store *store.Store) *BackupRunner { } } +const MaxBackupFiles = 5 + func (r *BackupRunner) Run(ctx context.Context) { intervalStr := r.Store.GetSystemSettingValueWithDefault(ctx, apiv1.SystemSettingAutoBackupIntervalName.String(), "") if intervalStr == "" { @@ -46,20 +49,46 @@ func (r *BackupRunner) Run(ctx context.Context) { ticker := time.NewTicker(time.Duration(interval) * time.Second) defer ticker.Stop() - var t time.Time for { select { case <-ctx.Done(): log.Info("stop auto backup graceful.") return - case t = <-ticker.C: + case <-ticker.C: + } + + filename := r.Store.Profile.DSN + ".bak" + + if err := rotateFiles(filename, MaxBackupFiles); err != nil { + log.Error("fail to rotate backup files", zap.Error(err)) + continue } - filename := r.Store.Profile.DSN + t.Format("-20060102-150405.bak") log.Info(fmt.Sprintf("create backup to %s", filename)) - err := r.Store.BackupTo(ctx, filename) - if err != nil { + if err := r.Store.BackupTo(ctx, filename); err != nil { log.Error("fail to create backup", zap.Error(err)) } } } + +func rotateFiles(filename string, cnt int) error { + // Generate suffix slices of history files like "",".1",".2",".3"... + ss := make([]string, cnt-1) + for i := 1; i < len(ss); i++ { + ss[i] = fmt.Sprintf(".%d", i) + } + + // Iterate through the suffix slices and rename the files + for i := len(ss) - 1; i >= 0; i-- { + from := filename + ss[i] + to := filename + "." + strconv.Itoa(i+1) + + log.Info("rotate file", zap.String("from", from), zap.String("to", to)) + err := os.Rename(from, to) + if err != nil && !os.IsNotExist(err) { + return err + } + } + + return nil +}