From 03bc5787f2ad64c05a8edc4c160779baa0f56819 Mon Sep 17 00:00:00 2001 From: Thomas Carmet <8408330+tcarmet@users.noreply.github.com> Date: Tue, 16 Jun 2026 13:40:16 -0700 Subject: [PATCH] ARSN-596: treat NamespaceExists as idempotent success in createBucket MongoDB returns error code 48 (NamespaceExists) when createCollection is called on an already-existing collection. This happens under concurrent create/drop/create sequences on MPU shadow buckets. The outer catch was converting this benign collision into InternalError, causing HTTP 500 responses in the afterEach cleanup hooks of functional tests. Treat NamespaceExists the same as a successful collection creation. --- .../mongoclient/MongoClientInterface.ts | 37 +++++++++++-------- .../mongoclient/MongoClientInterface.spec.js | 20 ++++++++++ 2 files changed, 42 insertions(+), 15 deletions(-) diff --git a/lib/storage/metadata/mongoclient/MongoClientInterface.ts b/lib/storage/metadata/mongoclient/MongoClientInterface.ts index 34bcd192b..0a15e4939 100644 --- a/lib/storage/metadata/mongoclient/MongoClientInterface.ts +++ b/lib/storage/metadata/mongoclient/MongoClientInterface.ts @@ -466,21 +466,28 @@ class MongoClientInterface { // "constants.usersBucket" and "PENSIEVE" since it has already // been created if (bucketName !== constants.usersBucket && bucketName !== PENSIEVE) { - return this.db!.createCollection(bucketName).then(() => { - if (this.shardCollections) { - const cmd = { - shardCollection: `${this.database}.${bucketName}`, - key: { _id: 1 }, - }; - return this.adminDb!.command(cmd, {}) - .then(() => cb(null)) - .catch(err => { - log.error('createBucket: enabling sharding', { error: err }); - return cb(errors.InternalError); - }); - } - return cb(null); - }); + return this.db!.createCollection(bucketName) + .catch(err => { + if (err.codeName === 'NamespaceExists') { + return; + } + throw err; + }) + .then(() => { + if (this.shardCollections) { + const cmd = { + shardCollection: `${this.database}.${bucketName}`, + key: { _id: 1 }, + }; + return this.adminDb!.command(cmd, {}) + .then(() => cb(null)) + .catch(err => { + log.error('createBucket: enabling sharding', { error: err }); + return cb(errors.InternalError); + }); + } + return cb(null); + }); } return cb(null); }) diff --git a/tests/unit/storage/metadata/mongoclient/MongoClientInterface.spec.js b/tests/unit/storage/metadata/mongoclient/MongoClientInterface.spec.js index 0323e3647..720d1a71a 100644 --- a/tests/unit/storage/metadata/mongoclient/MongoClientInterface.spec.js +++ b/tests/unit/storage/metadata/mongoclient/MongoClientInterface.spec.js @@ -1900,6 +1900,26 @@ describe('MongoClientInterface, createBucket', () => { }); }); + it('should succeed when createCollection returns NamespaceExists', done => { + const bucketName = 'test-bucket-namespace-exists'; + const nsExistsError = Object.assign( + new Error('Collection already exists. NS: metadata.mpuShadowBucketbucket1'), + { codeName: 'NamespaceExists', code: 48 }, + ); + const originalCreateCollection = client.db.createCollection; + client.db.createCollection = () => Promise.reject(nsExistsError); + + client.createBucket(bucketName, baseBucket, logger, err => { + client.db.createCollection = originalCreateCollection; + try { + assert.ifError(err); + done(); + } catch (assertionError) { + done(assertionError); + } + }); + }); + it('should handle modifiedCount 0 and upsertedCount 0 in createBucket', done => { const bucketName = 'test-bucket-createbucket';