From 5dd51e6149c7e6170f4618461a2d14d95adfe8b6 Mon Sep 17 00:00:00 2001 From: Nick Craig-Wood Date: Mon, 7 Mar 2022 18:21:16 +0000 Subject: [PATCH] union: fix deadlock when one part of a multi-upload fails Before this fix, rclone would deadlock when uploading two files at once, if one errored. This caused the other file to block in the multi reader and never complete. This fix drains the input buffer on error which allows the other upload to complete. See: https://forum.rclone.org/t/union-with-create-policy-all-copy-stuck-when-first-union-fails/29601 --- backend/union/entry.go | 5 +++++ backend/union/union.go | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/backend/union/entry.go b/backend/union/entry.go index 1123183d8..efd2cbaef 100644 --- a/backend/union/entry.go +++ b/backend/union/entry.go @@ -4,6 +4,7 @@ import ( "context" "fmt" "io" + "io/ioutil" "sync" "time" @@ -84,6 +85,10 @@ func (o *Object) Update(ctx context.Context, in io.Reader, src fs.ObjectInfo, op err := o.Update(ctx, readers[i], src, options...) if err != nil { errs[i] = fmt.Errorf("%s: %w", o.UpstreamFs().Name(), err) + if len(entries) > 1 { + // Drain the input buffer to allow other uploads to continue + _, _ = io.Copy(ioutil.Discard, readers[i]) + } } } else { errs[i] = fs.ErrorNotAFile diff --git a/backend/union/union.go b/backend/union/union.go index 0b01bddce..46c697a64 100644 --- a/backend/union/union.go +++ b/backend/union/union.go @@ -6,6 +6,7 @@ import ( "errors" "fmt" "io" + "io/ioutil" "path" "path/filepath" "strings" @@ -486,6 +487,10 @@ func (f *Fs) put(ctx context.Context, in io.Reader, src fs.ObjectInfo, stream bo } if err != nil { errs[i] = fmt.Errorf("%s: %w", u.Name(), err) + if len(upstreams) > 1 { + // Drain the input buffer to allow other uploads to continue + _, _ = io.Copy(ioutil.Discard, readers[i]) + } return } objs[i] = u.WrapObject(o)