Files
backup/scheduler/scheduler.go

88 lines
2.1 KiB
Go

package scheduler
import (
"fmt"
"log"
"time"
"git.nyne.dev/x/backup/backup"
"git.nyne.dev/x/backup/config"
)
// Scheduler handles the backup scheduling
type Scheduler struct {
config *config.Config
backupManager *backup.BackupManager
ticker *time.Ticker
quit chan struct{}
}
// NewScheduler creates a new scheduler
func NewScheduler(cfg *config.Config) (*Scheduler, error) {
bm, err := backup.NewBackupManager(cfg)
if err != nil {
return nil, fmt.Errorf("failed to create backup manager: %w", err)
}
return &Scheduler{
config: cfg,
backupManager: bm,
quit: make(chan struct{}),
}, nil
}
// Start starts the scheduler
func (s *Scheduler) Start() error {
interval, err := s.config.Options.ParseInterval()
if err != nil {
return fmt.Errorf("failed to parse interval: %w", err)
}
log.Printf("Starting scheduler with interval: %s\n", interval)
// Only run an immediate backup if no recent backup exists within the configured interval.
// This prevents redundant backups when the container restarts frequently.
lastBackupTime, err := s.backupManager.GetLastBackupTime()
if err != nil {
log.Printf("Warning: could not determine last backup time, will run backup now: %v\n", err)
}
elapsed := time.Since(lastBackupTime)
if lastBackupTime.IsZero() || elapsed >= interval {
log.Println("Running initial backup...")
if err := s.backupManager.RunBackup(); err != nil {
log.Printf("Initial backup failed: %v\n", err)
}
} else {
remaining := interval - elapsed
log.Printf("Skipping initial backup: last backup was %s ago (interval: %s), next backup in %s\n",
elapsed.Round(time.Second), interval, remaining.Round(time.Second))
}
s.ticker = time.NewTicker(interval)
go func() {
for {
select {
case <-s.ticker.C:
log.Println("Starting scheduled backup...")
if err := s.backupManager.RunBackup(); err != nil {
log.Printf("Backup failed: %v\n", err)
}
case <-s.quit:
s.ticker.Stop()
return
}
}
}()
log.Println("Scheduler started successfully")
return nil
}
// Stop stops the scheduler
func (s *Scheduler) Stop() {
close(s.quit)
}