From 9d47a15f5c3bae7392ac9fc8fe4ec1ca7905ac77 Mon Sep 17 00:00:00 2001 From: Anurag Mittal <1321012+anurag4DSB@users.noreply.github.com> Date: Tue, 8 Sep 2026 08:14:59 +0200 Subject: [PATCH] CLDSRV-992: Bound and settle the 1000-object writes in Multi-Object Delete setup The "Multi-Object Delete Success" beforeEach uploads 1000 objects behind what looks like a concurrency limiter of 20. It is not one. objects.map() invokes all 1000 async calls synchronously; the first 20 push and the other 980 all park on the same `await Promise.race(queued)`. When the first put settles they all wake, and each splices one entry off the head of the queue regardless of whether that entry had settled, so the queue empties, every waiter's `while` test goes false, and the remaining puts fire together. Running the limiter against an instrumented stub gives a peak of 999 concurrent puts against an intended 20. That is the ServiceUnavailable and the "Socket timed out without establishing a connection within 5000 ms" seen in CI: a thousand simultaneous puts, not twenty. It also explains why one failure becomes seven. Promise.all rejects on the first error while the other puts are still in flight, so the hook throws with writes outstanding. Teardown then empties the bucket, the stragglers land, and deleteBucket fails with BucketNotEmpty. All four suites in this file share the bucket name multi-object-delete-234-634, so once it is left behind with objects in it, every later suite's cleanup fails the same way. Replace the limiter with a fixed pool of 20 workers draining a shared queue. Concurrency is genuinely bounded, and every put is settled before the hook returns, on the failure path too, so teardown can no longer race them. Errors are collected and the first is rethrown, which keeps the diagnostic the hook previously reported. Clears CLDSRV-992 rows F1 (its two Multi-Object Delete hooks, run 33734792029) and F10 (all seven failures, run 33681270087, fixed for the 9.2 line separately). Issue: CLDSRV-992 --- .../test/object/multiObjectDelete.js | 35 +++++++++++-------- 1 file changed, 20 insertions(+), 15 deletions(-) diff --git a/tests/functional/aws-node-sdk/test/object/multiObjectDelete.js b/tests/functional/aws-node-sdk/test/object/multiObjectDelete.js index 7aa6d9babf..8481bb37d7 100644 --- a/tests/functional/aws-node-sdk/test/object/multiObjectDelete.js +++ b/tests/functional/aws-node-sdk/test/object/multiObjectDelete.js @@ -74,22 +74,27 @@ describe('Multi-Object Delete Success', function success() { objects.push(`${key}${i}`); } const parallel = 20; - const queued = []; - const putObjectWithLimit = async key => { - while (queued.length >= parallel) { - await Promise.race(queued); - queued.splice(0, queued.findIndex(p => p === queued[0]) + 1); + // Fixed pool draining a shared queue. The previous limiter peaked + // at 999 concurrent puts and abandoned the rest on first failure. + const pending = objects.slice(); + const putErrors = []; + await Promise.all(Array.from({ length: parallel }, async () => { + while (pending.length > 0) { + const objectKey = pending.shift(); + try { + await s3.send(new PutObjectCommand({ + Bucket: bucketName, + Key: objectKey, + Body: 'somebody', + })); + } catch (err) { + putErrors.push(err); + } } - const result = s3.send(new PutObjectCommand({ - Bucket: bucketName, - Key: key, - Body: 'somebody', - })); - queued.push(result); - return result; - }; - const putPromises = objects.map(key => putObjectWithLimit(key)); - await Promise.all(putPromises); + })); + if (putErrors.length > 0) { + throw putErrors[0]; + } } catch (err) { process.stdout.write(`Error creating objects: ${err}\n`); throw err;