Skip to content

Commit 3cf46a8

Browse files
committed
Merge remote-tracking branch 'origin/bugfix/CLDSRV-669-complete' into w/9.0/bugfix/CLDSRV-669-complete
2 parents ef5932e + 5f86015 commit 3cf46a8

File tree

3 files changed

+341
-154
lines changed

3 files changed

+341
-154
lines changed

lib/api/completeMultipartUpload.js

Lines changed: 44 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -536,35 +536,60 @@ function completeMultipartUpload(authInfo, request, log, callback) {
536536
function deletePartsMetadata(mpuBucket, keysToDelete, aggregateETag,
537537
extraPartLocations, destinationBucket, generatedVersionId, droppedMPUSize, next) {
538538
services.batchDeleteObjectMetadata(mpuBucket.getName(),
539-
keysToDelete, log, err => next(err, extraPartLocations,
540-
destinationBucket, aggregateETag, generatedVersionId, droppedMPUSize));
539+
keysToDelete, log, err => {
540+
if (err) {
541+
// Handle specific error cases according to retry strategy
542+
if (err.is?.DeleteConflict) {
543+
// DeleteConflict should trigger automatic retry
544+
// Convert to InternalError to make it retryable
545+
return next(errors.InternalError, extraPartLocations,
546+
destinationBucket, aggregateETag, generatedVersionId, droppedMPUSize);
547+
}
548+
549+
// For NoSuchKey and other errors, return them as-is
550+
// NoSuchKey is non-retryable, InternalError and others are retryable
551+
return next(err, extraPartLocations,
552+
destinationBucket, aggregateETag, generatedVersionId, droppedMPUSize);
553+
}
554+
return next(null, extraPartLocations,
555+
destinationBucket, aggregateETag, generatedVersionId, droppedMPUSize);
556+
});
541557
},
542558
function batchDeleteExtraParts(extraPartLocations, destinationBucket,
543559
aggregateETag, generatedVersionId, droppedMPUSize, next) {
544560
if (extraPartLocations && extraPartLocations.length > 0) {
545561
return data.batchDelete(extraPartLocations, request.method, null, log, err => {
546562
if (err) {
547-
return next(err);
563+
// Extra part deletion failure should not fail the operation
564+
// The S3 object was created successfully and MPU metadata was cleaned up
565+
// Orphaned extra parts are acceptable since the main operation succeeded
566+
log.warn('failed to delete extra parts, keeping orphan but returning success', {
567+
method: 'completeMultipartUpload',
568+
extraPartLocationsCount: extraPartLocations.length,
569+
error: err,
570+
});
548571
}
549-
550-
return validateQuotas(request, destinationBucket, request.accountQuotas,
551-
['objectDelete'], 'objectDelete', -droppedMPUSize, false, log, err => {
552-
if (err) {
553-
// Ignore error, as the data has been deleted already: only inflight count
554-
// has not been updated, and will be eventually consistent anyway
555-
log.warn('failed to update inflights', {
556-
method: 'completeMultipartUpload',
557-
extraPartLocations,
558-
error: err,
559-
});
560-
}
561-
return next(null, destinationBucket, aggregateETag,
562-
generatedVersionId);
563-
});
572+
return next(null, destinationBucket, aggregateETag,
573+
generatedVersionId, droppedMPUSize);
564574
});
565575
}
566576
return next(null, destinationBucket, aggregateETag,
567-
generatedVersionId);
577+
generatedVersionId, droppedMPUSize);
578+
},
579+
function updateQuotas(destinationBucket, aggregateETag, generatedVersionId, droppedMPUSize, next) {
580+
return validateQuotas(request, destinationBucket, request.accountQuotas,
581+
['objectDelete'], 'objectDelete', -droppedMPUSize, false, log, err => {
582+
if (err) {
583+
// Ignore error, as the data has been deleted already: only inflight count
584+
// has not been updated, and will be eventually consistent anyway
585+
log.warn('failed to update inflights', {
586+
method: 'completeMultipartUpload',
587+
error: err,
588+
});
589+
}
590+
return next(null, destinationBucket, aggregateETag,
591+
generatedVersionId);
592+
});
568593
},
569594
], (err, destinationBucket, aggregateETag, generatedVersionId) => {
570595
const corsHeaders =

0 commit comments

Comments
 (0)