From cc8538e0d11c1adcf053f428b89f8e5be6cbea33 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 9d6717a0e..a41bb0d09 100644 --- a/backend/googlecloudstorage/googlecloudstorage.go +++ b/backend/googlecloudstorage/googlecloudstorage.go @@ -840,20 +840,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 }