From 21355b4208f124d28fe50cf14345ea0eb69ece35 Mon Sep 17 00:00:00 2001 From: Nick Craig-Wood Date: Thu, 24 Feb 2022 10:03:41 +0000 Subject: [PATCH] sync: Fix --max-duration so it doesn't retry when the duration is exceeded Before this change, if the --max-duration limit was reached then rclone would retry the sync as a fatal error wasn't raised. This checks the deadline and raises a fatal error if necessary at the end of the sync. Fixes #6002 --- fs/sync/sync.go | 17 ++++++++++++++--- fs/sync/sync_test.go | 2 +- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/fs/sync/sync.go b/fs/sync/sync.go index 4c9e0f959..6e71af499 100644 --- a/fs/sync/sync.go +++ b/fs/sync/sync.go @@ -73,6 +73,7 @@ type syncCopyMove struct { compareCopyDest []fs.Fs // place to check for files to server side copy backupDir fs.Fs // place to store overwrites/deletes checkFirst bool // if set run all the checkers before starting transfers + maxDurationEndTime time.Time // end time if --max-duration is set } type trackRenamesStrategy byte @@ -146,9 +147,9 @@ func newSyncCopyMove(ctx context.Context, fdst, fsrc fs.Fs, deleteMode fs.Delete } // If a max session duration has been defined add a deadline to the context if ci.MaxDuration > 0 { - endTime := time.Now().Add(ci.MaxDuration) - fs.Infof(s.fdst, "Transfer session deadline: %s", endTime.Format("2006/01/02 15:04:05")) - s.ctx, s.cancel = context.WithDeadline(ctx, endTime) + s.maxDurationEndTime = time.Now().Add(ci.MaxDuration) + fs.Infof(s.fdst, "Transfer session deadline: %s", s.maxDurationEndTime.Format("2006/01/02 15:04:05")) + s.ctx, s.cancel = context.WithDeadline(ctx, s.maxDurationEndTime) } else { s.ctx, s.cancel = context.WithCancel(ctx) } @@ -809,6 +810,10 @@ func (s *syncCopyMove) tryRename(src fs.Object) bool { return true } +// errorMaxDurationReached defines error when transfer duration is reached +// Used for checking on exit and matching to correct exit code. +var errorMaxDurationReached = fserrors.FatalError(errors.New("Max transfer duration reached as set by --max-duration")) + // Syncs fsrc into fdst // // If Delete is true then it deletes any files in fdst that aren't in fsrc @@ -902,6 +907,12 @@ func (s *syncCopyMove) run() error { // Read the error out of the context if there is one s.processError(s.ctx.Err()) + // If the duration was exceeded then add a Fatal Error so we don't retry + if !s.maxDurationEndTime.IsZero() && time.Since(s.maxDurationEndTime) > 0 { + fs.Errorf(s.fdst, "%v", errorMaxDurationReached) + s.processError(errorMaxDurationReached) + } + // Print nothing to transfer message if there were no transfers and no errors if s.deleteMode != fs.DeleteModeOnly && accounting.Stats(s.ctx).GetTransfers() == 0 && s.currentError() == nil { fs.Infof(nil, "There was nothing to transfer") diff --git a/fs/sync/sync_test.go b/fs/sync/sync_test.go index 6239cadfc..0520a2323 100644 --- a/fs/sync/sync_test.go +++ b/fs/sync/sync_test.go @@ -1029,7 +1029,7 @@ func TestSyncWithMaxDuration(t *testing.T) { accounting.GlobalStats().ResetCounters() startTime := time.Now() err := Sync(ctx, r.Fremote, r.Flocal, false) - require.True(t, errors.Is(err, context.DeadlineExceeded)) + require.True(t, errors.Is(err, errorMaxDurationReached)) elapsed := time.Since(startTime) maxTransferTime := (time.Duration(len(testFiles)) * 60 * time.Second) / time.Duration(bytesPerSecond)