dropbox: don't retry chunked upload requests when the upload has been cancelled

The append loop retries everything once the upload session has
started, so a cancelled context error was retried through all the low
level retries with exponential backoff before the upload gave up.
This commit is contained in:
Nick Craig-Wood
2026-08-03 20:58:14 +01:00
parent bff17664ad
commit e0701daea0
2 changed files with 38 additions and 0 deletions
+5
View File
@@ -2106,6 +2106,11 @@ func (o *Object) uploadChunked(ctx context.Context, in0 io.Reader, commitInfo *f
}
}
}
// Don't waste the low level retries if the context has
// been cancelled
if fserrors.ContextError(ctx, &err) {
return false, err
}
return err != nil, err
})
if err != nil {
+33
View File
@@ -151,6 +151,7 @@ type uploadSessionClient struct {
maxAppends int // fail the append after this many calls to stop runaway loops
bytesWritten int64 // bytes received by UploadSessionAppendV2Context
finishCalled bool // set if UploadSessionFinishContext was called
appended func() // if set, called after each successful append
}
var errTooManyAppends = errors.New("too many appends - upload looping?")
@@ -173,6 +174,9 @@ func (c *uploadSessionClient) UploadSessionAppendV2Context(ctx context.Context,
return err
}
c.bytesWritten += n
if c.appended != nil {
c.appended()
}
return nil
}
@@ -197,6 +201,16 @@ func newUploadTestFs(t *testing.T, srv files.ContextClient, chunkSize fs.SizeSuf
return f
}
// endlessReader supplies bytes forever
type endlessReader struct{}
func (endlessReader) Read(p []byte) (int, error) {
for i := range p {
p[i] = 'x'
}
return len(p), nil
}
func TestUploadChunkedEarlyEOF(t *testing.T) {
ctx := context.Background()
@@ -235,6 +249,25 @@ func TestUploadChunkedEarlyEOF(t *testing.T) {
})
}
func TestUploadChunkedCancel(t *testing.T) {
// Cancelling the context must stop the upload even though every
// append is succeeding
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
client := &uploadSessionClient{maxAppends: 8}
client.appended = func() {
if client.appends == 2 {
cancel()
}
}
f := newUploadTestFs(t, client, 100)
o := &Object{fs: f, remote: "test.bin"}
_, err := o.uploadChunked(ctx, endlessReader{}, files.NewCommitInfo("/test.bin"), -1)
require.Error(t, err)
assert.ErrorIs(t, err, context.Canceled)
assert.False(t, client.finishCalled)
}
func (f *Fs) importPaperForTest(t *testing.T) {
content := `# test doc