From a96539eeecf4e857d3cd92f8f72ac368a93438d8 Mon Sep 17 00:00:00 2001 From: Nick Craig-Wood Date: Mon, 30 Nov 2020 16:18:41 +0000 Subject: [PATCH] gcs: fix server side copy of large objects - fixes #3724 Before this change rclone was using the copy endpoint to copy large objects. This can fail for large objects with this error: Error 413: Copy spanning locations and/or storage classes could not complete within 30 seconds. Please use the Rewrite method This change makes Copy use the Rewrite method as suggested by the error message which should be good for any size of copy. --- .../googlecloudstorage/googlecloudstorage.go | 29 ++++++++++++------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/backend/googlecloudstorage/googlecloudstorage.go b/backend/googlecloudstorage/googlecloudstorage.go index 52eee6484..60176bdce 100644 --- a/backend/googlecloudstorage/googlecloudstorage.go +++ b/backend/googlecloudstorage/googlecloudstorage.go @@ -841,20 +841,27 @@ func (f *Fs) Copy(ctx context.Context, src fs.Object, remote string) (fs.Object, remote: remote, } - var newObject *storage.Object - err = f.pacer.Call(func() (bool, error) { - copyObject := f.svc.Objects.Copy(srcBucket, srcPath, dstBucket, dstPath, nil) - if !f.opt.BucketPolicyOnly { - copyObject.DestinationPredefinedAcl(f.opt.ObjectACL) + rewriteRequest := f.svc.Objects.Rewrite(srcBucket, srcPath, dstBucket, dstPath, nil) + if !f.opt.BucketPolicyOnly { + rewriteRequest.DestinationPredefinedAcl(f.opt.ObjectACL) + } + var rewriteResponse *storage.RewriteResponse + for { + err = f.pacer.Call(func() (bool, error) { + rewriteResponse, err = rewriteRequest.Context(ctx).Do() + return shouldRetry(err) + }) + if err != nil { + return nil, err } - newObject, err = copyObject.Context(ctx).Do() - return shouldRetry(err) - }) - if err != nil { - return nil, err + if rewriteResponse.Done { + break + } + rewriteRequest.RewriteToken(rewriteResponse.RewriteToken) + fs.Debugf(dstObj, "Continuing rewrite %d bytes done", rewriteResponse.TotalBytesRewritten) } // Set the metadata for the new object while we have it - dstObj.setMetaData(newObject) + dstObj.setMetaData(rewriteResponse.Resource) return dstObj, nil }