seafile: fix corrupted uploads after a retried upload error
When an upload failed with a 500 error the upload was retried with a new upload link but the same input stream. The stream had already been consumed by the first attempt so the retry uploaded an empty file. This fixes it by returning a RetryError instead so the caller retries the upload with a fresh stream, which will fetch a new upload link.
This commit is contained in:
@@ -88,19 +88,15 @@ func (o *Object) Open(ctx context.Context, options ...fs.OpenOption) (io.ReadClo
|
|||||||
// But for unknown-sized objects (indicated by src.Size() == -1), Upload should either
|
// But for unknown-sized objects (indicated by src.Size() == -1), Upload should either
|
||||||
// return an error or update the object properly (rather than e.g. calling panic).
|
// return an error or update the object properly (rather than e.g. calling panic).
|
||||||
func (o *Object) Update(ctx context.Context, in io.Reader, src fs.ObjectInfo, options ...fs.OpenOption) error {
|
func (o *Object) Update(ctx context.Context, in io.Reader, src fs.ObjectInfo, options ...fs.OpenOption) error {
|
||||||
// The upload sometimes return a temporary 500 error
|
|
||||||
// We cannot use the pacer to retry uploading the file as the upload link is single use only
|
|
||||||
for retry := 0; retry <= 3; retry++ {
|
|
||||||
uploadLink, err := o.fs.getUploadLink(ctx, o.libraryID)
|
uploadLink, err := o.fs.getUploadLink(ctx, o.libraryID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The upload can't be retried here as the input stream can't be re-read and
|
||||||
|
// the upload link is single use, so upload returns a retry
|
||||||
|
// error for the caller to retry with a fresh stream.
|
||||||
uploaded, err := o.fs.upload(ctx, in, uploadLink, o.pathInLibrary)
|
uploaded, err := o.fs.upload(ctx, in, uploadLink, o.pathInLibrary)
|
||||||
if err == ErrorInternalDuringUpload {
|
|
||||||
// This is a temporary error, try again with a new upload link
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -108,8 +104,6 @@ func (o *Object) Update(ctx context.Context, in io.Reader, src fs.ObjectInfo, op
|
|||||||
o.size = uploaded.Size
|
o.size = uploaded.Size
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
|
||||||
return ErrorInternalDuringUpload
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove this object
|
// Remove this object
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import (
|
|||||||
|
|
||||||
"github.com/rclone/rclone/backend/seafile/api"
|
"github.com/rclone/rclone/backend/seafile/api"
|
||||||
"github.com/rclone/rclone/fs"
|
"github.com/rclone/rclone/fs"
|
||||||
|
"github.com/rclone/rclone/fs/fserrors"
|
||||||
"github.com/rclone/rclone/lib/readers"
|
"github.com/rclone/rclone/lib/readers"
|
||||||
"github.com/rclone/rclone/lib/rest"
|
"github.com/rclone/rclone/lib/rest"
|
||||||
)
|
)
|
||||||
@@ -721,8 +722,8 @@ func (f *Fs) upload(ctx context.Context, in io.Reader, uploadLink, filePath stri
|
|||||||
return nil, fs.ErrorPermissionDenied
|
return nil, fs.ErrorPermissionDenied
|
||||||
}
|
}
|
||||||
if resp.StatusCode == 500 {
|
if resp.StatusCode == 500 {
|
||||||
// This is a temporary error - we will get a new upload link before retrying
|
// This is a temporary error - the caller will get a new upload link when it retries
|
||||||
return nil, ErrorInternalDuringUpload
|
return nil, fserrors.RetryError(ErrorInternalDuringUpload)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil, fmt.Errorf("failed to upload file: %w", err)
|
return nil, fmt.Errorf("failed to upload file: %w", err)
|
||||||
|
|||||||
Reference in New Issue
Block a user