diff --git a/backend/cloudinary/cloudinary.go b/backend/cloudinary/cloudinary.go index 5a80439b2..8e9f17e7c 100644 --- a/backend/cloudinary/cloudinary.go +++ b/backend/cloudinary/cloudinary.go @@ -34,6 +34,13 @@ import ( "github.com/zeebo/blake3" ) +// Pacer settings - tuned to match other rclone backends +const ( + minSleep = 10 * time.Millisecond + maxSleep = 2 * time.Second + decayConstant = 2 // bigger for slower decay, exponential +) + // Cloudinary shouldn't have a trailing dot if there is no path func cldPathDir(somePath string) string { if somePath == "" || somePath == "." { @@ -189,7 +196,7 @@ func NewFs(ctx context.Context, name string, root string, m configmap.Mapper) (f root: root, opt: *opt, cld: cld, - pacer: fs.NewPacer(ctx, pacer.NewDefault(pacer.MinSleep(1000), pacer.MaxSleep(10000), pacer.DecayConstant(2))), + pacer: fs.NewPacer(ctx, pacer.NewDefault(pacer.MinSleep(minSleep), pacer.MaxSleep(maxSleep), pacer.DecayConstant(decayConstant))), srv: rest.NewClient(client), } @@ -586,21 +593,18 @@ func shouldRetry(ctx context.Context, resp *http.Response, err error) (bool, err if fserrors.ContextError(ctx, &err) { return false, err } + // Cloudinary returns rate-limit windows as "Try again on YYYY-MM-DD HH:MM:SS UTC" + // embedded in the error message - honour it as a retry-after. if err != nil { tryAgain := "Try again on " if idx := strings.Index(err.Error(), tryAgain); idx != -1 { layout := "2006-01-02 15:04:05 UTC" dateStr := err.Error()[idx+len(tryAgain) : idx+len(tryAgain)+len(layout)] - timestamp, err2 := time.Parse(layout, dateStr) - if err2 == nil { + if timestamp, err2 := time.Parse(layout, dateStr); err2 == nil { return true, fserrors.NewErrorRetryAfter(time.Until(timestamp)) } } - - fs.Debugf(nil, "Retrying API error %v", err) - return true, err } - return fserrors.ShouldRetry(err) || fserrors.ShouldRetryHTTP(resp, retryErrorCodes), err }