From d975196cfad23062dd360fad91a5d2bb2e9fb68c Mon Sep 17 00:00:00 2001 From: Nick Craig-Wood Date: Mon, 21 Mar 2022 11:34:42 +0000 Subject: [PATCH] dropbox: fix retries of multipart uploads with incorrect_offset error Before this fix, rclone retries chunks of multipart uploads. However if they had been partially received dropbox would reply with an incorrect_offset error which rclone was ignoring. This patch parses the new offset from the error response and uses it to adjust the data that rclone sends so it is the same as what dropbox is expecting. See: https://forum.rclone.org/t/dropbox-rate-limiting-for-upload/29779 --- backend/dropbox/dropbox.go | 28 ++++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/backend/dropbox/dropbox.go b/backend/dropbox/dropbox.go index 4fc5829e2..60cecc104 100644 --- a/backend/dropbox/dropbox.go +++ b/backend/dropbox/dropbox.go @@ -1650,13 +1650,37 @@ func (o *Object) uploadChunked(ctx context.Context, in0 io.Reader, commitInfo *f } chunk := readers.NewRepeatableLimitReaderBuffer(in, buf, chunkSize) + skip := int64(0) err = o.fs.pacer.Call(func() (bool, error) { // seek to the start in case this is a retry - if _, err = chunk.Seek(0, io.SeekStart); err != nil { - return false, nil + if _, err = chunk.Seek(skip, io.SeekStart); err != nil { + return false, err } err = o.fs.srv.UploadSessionAppendV2(&appendArg, chunk) // after session is started, we retry everything + if err != nil { + // Check for incorrect offset error and retry with new offset + if uErr, ok := err.(files.UploadSessionAppendV2APIError); ok { + if uErr.EndpointError != nil && uErr.EndpointError.IncorrectOffset != nil { + correctOffset := uErr.EndpointError.IncorrectOffset.CorrectOffset + delta := int64(correctOffset) - int64(cursor.Offset) + skip += delta + what := fmt.Sprintf("incorrect offset error receved: sent %d, need %d, skip %d", cursor.Offset, correctOffset, skip) + if skip < 0 { + return false, fmt.Errorf("can't seek backwards to correct offset: %s", what) + } else if skip == chunkSize { + fs.Debugf(o, "%s: chunk received OK - continuing", what) + return false, nil + } else if skip > chunkSize { + // This error should never happen + return false, fmt.Errorf("can't seek forwards by more than a chunk to correct offset: %s", what) + } + // Skip the sent data on next retry + cursor.Offset = uint64(int64(cursor.Offset) + delta) + fs.Debugf(o, "%s: skipping bytes on retry to fix offset", what) + } + } + } return err != nil, err }) if err != nil {