@@ -119,28 +119,54 @@ protected function writeSingle(string $urn, StreamInterface $stream, array $meta
119119 protected function writeMultiPart (string $ urn , StreamInterface $ stream , array $ metaData ): void {
120120 $ mimetype = $ metaData ['mimetype ' ] ?? null ;
121121 unset($ metaData ['mimetype ' ]);
122- $ uploader = new MultipartUploader ($ this ->getConnection (), $ stream , [
123- 'bucket ' => $ this ->bucket ,
124- 'concurrency ' => $ this ->concurrency ,
125- 'key ' => $ urn ,
126- 'part_size ' => $ this ->uploadPartSize ,
127- 'params ' => [
128- 'ContentType ' => $ mimetype ,
129- 'Metadata ' => $ this ->buildS3Metadata ($ metaData ),
130- 'StorageClass ' => $ this ->storageClass ,
131- ] + $ this ->getSSECParameters (),
132- ]);
133122
134- try {
135- $ uploader ->upload ();
136- } catch (S3MultipartUploadException $ e ) {
123+ $ attempts = 0 ;
124+ $ uploaded = false ;
125+ $ concurrency = $ this ->concurrency ;
126+ $ exception = null ;
127+ $ state = null ;
128+
129+ // retry multipart upload once with concurrency at half on failure
130+ while (!$ uploaded && $ attempts <= 1 ) {
131+ $ uploader = new MultipartUploader ($ this ->getConnection (), $ stream , [
132+ 'bucket ' => $ this ->bucket ,
133+ 'concurrency ' => $ concurrency ,
134+ 'key ' => $ urn ,
135+ 'part_size ' => $ this ->uploadPartSize ,
136+ 'state ' => $ state ,
137+ 'params ' => [
138+ 'ContentType ' => $ mimetype ,
139+ 'Metadata ' => $ this ->buildS3Metadata ($ metaData ),
140+ 'StorageClass ' => $ this ->storageClass ,
141+ ] + $ this ->getSSECParameters (),
142+ ]);
143+
144+ try {
145+ $ uploader ->upload ();
146+ $ uploaded = true ;
147+ } catch (S3MultipartUploadException $ e ) {
148+ $ exception = $ e ;
149+ $ attempts ++;
150+
151+ if ($ concurrency > 1 ) {
152+ $ concurrency = round ($ concurrency / 2 );
153+ }
154+
155+ if ($ stream ->isSeekable ()) {
156+ $ stream ->rewind ();
157+ }
158+ }
159+ }
160+
161+ if (!$ uploaded ) {
137162 // if anything goes wrong with multipart, make sure that you don´t poison and
138163 // slow down s3 bucket with orphaned fragments
139- $ uploadInfo = $ e ->getState ()->getId ();
140- if ($ e ->getState ()->isInitiated () && (array_key_exists ('UploadId ' , $ uploadInfo ))) {
164+ $ uploadInfo = $ exception ->getState ()->getId ();
165+ if ($ exception ->getState ()->isInitiated () && (array_key_exists ('UploadId ' , $ uploadInfo ))) {
141166 $ this ->getConnection ()->abortMultipartUpload ($ uploadInfo );
142167 }
143- throw new \OCA \DAV \Connector \Sabre \Exception \BadGateway ('Error while uploading to S3 bucket ' , 0 , $ e );
168+
169+ throw new \OCA \DAV \Connector \Sabre \Exception \BadGateway ('Error while uploading to S3 bucket ' , 0 , $ exception );
144170 }
145171 }
146172
0 commit comments