Skip to content
Open
Show file tree
Hide file tree
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
37 changes: 22 additions & 15 deletions lib/storage/metadata/mongoclient/MongoClientInterface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

will this not make us go through the shardCollections below?
could this cause a race condition if 2 shardCollection commands are sent to mongo in parallel, and possibly let the call fail (and still return an error)?

}
throw err;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we could call cb here to satisfy the contract of createBucket and call the callback in any case, as the then clause is not followed by a catch clause. It seems like the error case was already missing in the original code, causing the exception to be thrown and caught in the outermost layer. Calling the callback in any case and not throwing an exception is probably a better design choice. We could otherwise add a final .catch(err => cb(errors.InternalError)) to catch any thrown error and turn it into an error callback call there instead of relying on the outermost layer.

})
.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);
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand Down
Loading