Skip to content

Commit 0d71490

Browse files
leftybournesAndyScherzinger
authored andcommitted
fix(s3): retry failed multipart uploads with decreased concurrency
Signed-off-by: Kent Delante <[email protected]> [skip ci]
1 parent ce91153 commit 0d71490

File tree

1 file changed

+43
-6
lines changed

1 file changed

+43
-6
lines changed

lib/private/Files/ObjectStore/S3ObjectTrait.php

Lines changed: 43 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -118,16 +118,53 @@ protected function writeMultiPart(string $urn, StreamInterface $stream, ?string
118118
] + $this->getSSECParameters(),
119119
]);
120120

121-
try {
122-
$uploader->upload();
123-
} catch (S3MultipartUploadException $e) {
121+
$attempts = 0;
122+
$uploaded = false;
123+
$concurrency = $this->concurrency;
124+
$exception = null;
125+
$state = null;
126+
127+
// retry multipart upload once with concurrency at half on failure
128+
while (!$uploaded && $attempts <= 1) {
129+
$uploader = new MultipartUploader($this->getConnection(), $stream, [
130+
'bucket' => $this->bucket,
131+
'concurrency' => $concurrency,
132+
'key' => $urn,
133+
'part_size' => $this->uploadPartSize,
134+
'state' => $state,
135+
'params' => [
136+
'ContentType' => $mimetype,
137+
'Metadata' => $this->buildS3Metadata($metaData),
138+
'StorageClass' => $this->storageClass,
139+
] + $this->getSSECParameters(),
140+
]);
141+
142+
try {
143+
$uploader->upload();
144+
$uploaded = true;
145+
} catch (S3MultipartUploadException $e) {
146+
$exception = $e;
147+
$attempts++;
148+
149+
if ($concurrency > 1) {
150+
$concurrency = round($concurrency / 2);
151+
}
152+
153+
if ($stream->isSeekable()) {
154+
$stream->rewind();
155+
}
156+
}
157+
}
158+
159+
if (!$uploaded) {
124160
// if anything goes wrong with multipart, make sure that you don´t poison and
125161
// slow down s3 bucket with orphaned fragments
126-
$uploadInfo = $e->getState()->getId();
127-
if ($e->getState()->isInitiated() && (array_key_exists('UploadId', $uploadInfo))) {
162+
$uploadInfo = $exception->getState()->getId();
163+
if ($exception->getState()->isInitiated() && (array_key_exists('UploadId', $uploadInfo))) {
128164
$this->getConnection()->abortMultipartUpload($uploadInfo);
129165
}
130-
throw new \OCA\DAV\Connector\Sabre\Exception\BadGateway('Error while uploading to S3 bucket', 0, $e);
166+
167+
throw new \OCA\DAV\Connector\Sabre\Exception\BadGateway('Error while uploading to S3 bucket', 0, $exception);
131168
}
132169
}
133170

0 commit comments

Comments
 (0)