From 4a7a6b06afbb776328353740959765c50a0d84ee Mon Sep 17 00:00:00 2001 From: Michael Eischer Date: Thu, 29 Dec 2022 12:31:20 +0100 Subject: [PATCH] ui/backup: Use progress.Updater for progress updates --- cmd/restic/cmd_backup.go | 10 ++-- internal/ui/backup/progress.go | 80 +++++++++-------------------- internal/ui/backup/progress_test.go | 5 -- 3 files changed, 30 insertions(+), 65 deletions(-) diff --git a/cmd/restic/cmd_backup.go b/cmd/restic/cmd_backup.go index 683ce9268..e59f503db 100644 --- a/cmd/restic/cmd_backup.go +++ b/cmd/restic/cmd_backup.go @@ -483,16 +483,12 @@ func runBackup(ctx context.Context, opts BackupOptions, gopts GlobalOptions, ter } progressReporter := backup.NewProgress(progressPrinter, calculateProgressInterval(!gopts.Quiet, gopts.JSON)) + defer progressReporter.Done() if opts.DryRun { repo.SetDryRun() } - wg, wgCtx := errgroup.WithContext(ctx) - cancelCtx, cancel := context.WithCancel(wgCtx) - defer cancel() - wg.Go(func() error { progressReporter.Run(cancelCtx); return nil }) - if !gopts.JSON { progressPrinter.V("lock repository") } @@ -590,6 +586,10 @@ func runBackup(ctx context.Context, opts BackupOptions, gopts GlobalOptions, ter targets = []string{filename} } + wg, wgCtx := errgroup.WithContext(ctx) + cancelCtx, cancel := context.WithCancel(wgCtx) + defer cancel() + if !opts.NoScan { sc := archiver.NewScanner(targetFS) sc.SelectByName = selectByNameFilter diff --git a/internal/ui/backup/progress.go b/internal/ui/backup/progress.go index 746fe7a49..71facda4c 100644 --- a/internal/ui/backup/progress.go +++ b/internal/ui/backup/progress.go @@ -1,13 +1,12 @@ package backup import ( - "context" "sync" "time" "github.com/restic/restic/internal/archiver" "github.com/restic/restic/internal/restic" - "github.com/restic/restic/internal/ui/signals" + "github.com/restic/restic/internal/ui/progress" ) // A ProgressPrinter can print various progress messages. @@ -41,10 +40,10 @@ type Summary struct { // Progress reports progress for the `backup` command. type Progress struct { + progress.Updater mu sync.Mutex - interval time.Duration - start time.Time + start time.Time scanStarted, scanFinished bool @@ -52,66 +51,37 @@ type Progress struct { processed, total Counter errors uint - closed chan struct{} - summary Summary printer ProgressPrinter } func NewProgress(printer ProgressPrinter, interval time.Duration) *Progress { - return &Progress{ - interval: interval, - start: time.Now(), - + p := &Progress{ + start: time.Now(), currentFiles: make(map[string]struct{}), - closed: make(chan struct{}), - - printer: printer, + printer: printer, } -} + p.Updater = *progress.NewUpdater(interval, func(runtime time.Duration, final bool) { + if final { + p.printer.Reset() + } else { + p.mu.Lock() + defer p.mu.Unlock() + if !p.scanStarted { + return + } -// Run regularly updates the status lines. It should be called in a separate -// goroutine. -func (p *Progress) Run(ctx context.Context) { - defer close(p.closed) - // Reset status when finished - defer p.printer.Reset() + var secondsRemaining uint64 + if p.scanFinished { + secs := float64(runtime / time.Second) + todo := float64(p.total.Bytes - p.processed.Bytes) + secondsRemaining = uint64(secs / float64(p.processed.Bytes) * todo) + } - var tick <-chan time.Time - if p.interval != 0 { - t := time.NewTicker(p.interval) - defer t.Stop() - tick = t.C - } - - signalsCh := signals.GetProgressChannel() - - for { - var now time.Time - select { - case <-ctx.Done(): - return - case now = <-tick: - case <-signalsCh: - now = time.Now() + p.printer.Update(p.total, p.processed, p.errors, p.currentFiles, p.start, secondsRemaining) } - - p.mu.Lock() - if !p.scanStarted { - p.mu.Unlock() - continue - } - - var secondsRemaining uint64 - if p.scanFinished { - secs := float64(now.Sub(p.start) / time.Second) - todo := float64(p.total.Bytes - p.processed.Bytes) - secondsRemaining = uint64(secs / float64(p.processed.Bytes) * todo) - } - - p.printer.Update(p.total, p.processed, p.errors, p.currentFiles, p.start, secondsRemaining) - p.mu.Unlock() - } + }) + return p } // Error is the error callback function for the archiver, it prints the error and returns nil. @@ -236,6 +206,6 @@ func (p *Progress) ReportTotal(item string, s archiver.ScanStats) { // Finish prints the finishing messages. func (p *Progress) Finish(snapshotID restic.ID, dryrun bool) { // wait for the status update goroutine to shut down - <-p.closed + p.Updater.Done() p.printer.Finish(snapshotID, p.start, &p.summary, dryrun) } diff --git a/internal/ui/backup/progress_test.go b/internal/ui/backup/progress_test.go index e0dc093d2..a7282c7da 100644 --- a/internal/ui/backup/progress_test.go +++ b/internal/ui/backup/progress_test.go @@ -1,7 +1,6 @@ package backup import ( - "context" "sync" "testing" "time" @@ -53,9 +52,6 @@ func TestProgress(t *testing.T) { prnt := &mockPrinter{} prog := NewProgress(prnt, time.Millisecond) - ctx, cancel := context.WithCancel(context.Background()) - go prog.Run(ctx) - prog.StartFile("foo") prog.CompleteBlob(1024) @@ -67,7 +63,6 @@ func TestProgress(t *testing.T) { prog.CompleteItem("foo", nil, &node, archiver.ItemStats{}, 0) time.Sleep(10 * time.Millisecond) - cancel() id := restic.NewRandomID() prog.Finish(id, false)