When attempting to create multiple document inside a mongoose transaction, encountering a 11000 duplicate key error auto abort’s the transaction, even if the error is swallowed in a try / catch block. Any other error does not abort.
I would like for mongoose to attempt to create all the documents regardless of duplicate error, so that I can provide the user with all the possible errors rather than one error per request.
I have tried creating the documents in a for of loop like so:
try {
session.startTransaction();
for (const document of documents) {
try {
const newDocument = await Document.create([document], { session });
} catch (error) {
switch (error.code) {
case 11000:
// I would like to swallow 11000 and push error to array
const { keyValue } = error;
const duplicateError = createDuplicateError(keyValue);
duplicateErrors.push(duplicateError);
break;
case 251:
break;
default:
throw error;
}
}
}
await session.commitTransaction();
} catch (error) {
// Transaction failed
await session.abortTransaction();
throw error;
} finally {
await session.endSession();
}
// Here I can throw error for user with all duplicate errors
if (duplicateErrors.length > 0) throw new Error("Duplicate duplicateErrors");
If there is an 11000 error in the loop the next iteration will throw a 251 ‘NoSuchTransaction’ error when attempting the next create.
I have also tried unordered bulkWrite operation, however according to the docs
Inside a transaction, the first error in a bulk write causes the entire bulk write to fail and aborts the transaction, even if the bulk write is unordered.
Mongodb Bulk Write Transaction Docs
Is it possible to change the default behavior of bulkWrite inside a transaction, to ignore errors, or else swallow the error when looping over separate create operations?
I get that in write operations of millions of docs, it would not be ideal to continue attempting to create docs if the transaction was guaranteed to fail, but in my case the number of docs will be few, and be more beneficial to provide the user with all errors in one request rather tha requiring multiple requests for each error
2
Answers
Eventually got this working by creating the below function. Maybe it will help someone
I had the same issue. Searched for a built-in answer to no avail. My solution was to add a custom validator to ensure that those duplicate docs are not actually sent to the DB, and are caught on the server side.
Then in your
insertMany
result, you should get a list of validation errors, wherefrom you can grab the docs that were duplicates.