Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 28 additions & 6 deletions tests/functional/aws-node-sdk/test/object/abortMPU.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,26 @@ async function cleanupVersionedBucket(bucketUtil, bucketName) {
await bucketUtil.deleteOne(bucketName);
}

// Poll ListMultipartUploads until the given uploadId no longer appears.
// After a racing Complete/Abort, MPU metadata cleanup can be eventually
// consistent, so a single immediate read may still observe the upload and
// fail spuriously (CLDSRV-938). On timeout this returns the still-present
// uploads so the caller's assertion fails with the original message, which
// preserves detection of a genuine orphan-MPU leak.
async function waitForMpuCleanup(s3, bucketName, uploadId, { timeoutMs = 10000, intervalMs = 250 } = {}) {
const deadline = Date.now() + timeoutMs;
let remainingUploads = [];
do {
const listResult = await s3.send(new ListMultipartUploadsCommand({ Bucket: bucketName }));
remainingUploads = (listResult.Uploads || []).filter(upload => upload.UploadId === uploadId);
if (remainingUploads.length === 0) {
return remainingUploads;
}
await scheduler.wait(intervalMs);
} while (Date.now() < deadline);
return remainingUploads;
}

describe('Abort MPU', () => {
withV4(sigCfg => {
let bucketUtil;
Expand Down Expand Up @@ -811,9 +831,10 @@ describe('Abort MPU - Race Conditions', function testSuite() {
// If both operations encountered errors, that's also acceptable
// as long as the system remains consistent

// Verify no MPU metadata remains
const listResult = await s3.send(new ListMultipartUploadsCommand({ Bucket: bucketName }));
const remainingUploads = (listResult.Uploads || []).filter(upload => upload.UploadId === uploadId);
// Verify no MPU metadata remains. Cleanup after a racing
// Complete/Abort can be eventually consistent, so poll rather than
// reading once (CLDSRV-938).
const remainingUploads = await waitForMpuCleanup(s3, bucketName, uploadId);
assert.strictEqual(remainingUploads.length, 0, 'No MPU metadata should remain');
});

Expand Down Expand Up @@ -880,9 +901,10 @@ describe('Abort MPU - Race Conditions', function testSuite() {
}
}

// Verify no MPU metadata remains
const listResult = await s3.send(new ListMultipartUploadsCommand({ Bucket: bucketName }));
const remainingUploads = (listResult.Uploads || []).filter(upload => upload.UploadId === uploadId);
// Verify no MPU metadata remains. Cleanup after concurrent aborts
// can be eventually consistent, so poll rather than reading once
// (CLDSRV-938).
const remainingUploads = await waitForMpuCleanup(s3, bucketName, uploadId);
assert.strictEqual(remainingUploads.length, 0,
'No MPU metadata should remain after concurrent aborts');
});
Expand Down
Loading