I am trying to use transactions to update multiple documents.
One being a loading sheet document [await sheet.save({ session });]
and other being an array of stock reservation records [await Stock.bulkWrite()].
const session = await mongoose.startSession();
session.startTransaction({
readPreference: 'primary',
readConcern: { level: 'local' },
writeConcern: { w: 'majority' },
});
let sheetAfterSave: any = null;
try {
sheetAfterSave = await sheet.save({ session });
records.forEach(async (el: any) => {
let updatedStockRecord = await Stock.bulkWrite(
[
{
updateOne: {
filter: {
index: el.index,
product: el.product,
batchNo: el.batchNo,
agency,
totalQuantity: { $gte: el.loadingTotal },
},
update: {
$push: {
reservations: {
loadingSheetId: sheetAfterSave._id,
reservedCaseQuantity: el.loadingCaseCount,
reservedUnitQuantity: el.loadingUnitCount,
reservedTotalQuantity: el.loadingTotal,
},
},
},
},
},
],
{
session: session,
}
);
});
await session.commitTransaction();
session.endSession();
} catch (error) {
console.log(error);
await session.abortTransaction();
session.endSession();
throw new Error(
`Error occured while trying to create a new loading sheet. ${error}`
);
}
In this operation loading sheet document gets saved in the database, but not the stock reservation records array.
It gives the error
[distribution] MongoError: Transaction 1 has been committed.
[distribution] at MessageStream.messageHandler (/app/node_modules/mongoose/node_modules/mongodb/lib/cmap/connection.js:263:20)
[distribution] at MessageStream.emit (node:events:376:20)
[distribution] at processIncomingData (/app/node_modules/mongoose/node_modules/mongodb/lib/cmap/message_stream.js:144:12)
[distribution] at MessageStream._write (/app/node_modules/mongoose/node_modules/mongodb/lib/cmap/message_stream.js:42:5)
[distribution] at writeOrBuffer (node:internal/streams/writable:388:12)
[distribution] at MessageStream.Writable.write (node:internal/streams/writable:333:10)
[distribution] at TLSSocket.ondata (node:internal/streams/readable:716:22)
[distribution] at TLSSocket.emit (node:events:376:20)
[distribution] at addChunk (node:internal/streams/readable:305:12)
[distribution] at readableAddChunk (node:internal/streams/readable:280:9)
[distribution] at TLSSocket.Readable.push (node:internal/streams/readable:219:10)
[distribution] at TLSWrap.onStreamRead (node:internal/stream_base_commons:192:23)
[distribution] [ERROR] 14:56:43 MongoError: Transaction 1 has been committed.
As I use the session in a failure like this the saved documents must be rolled back but its not happening here.
Am I missing something here?? Appreciate your help
Cheers
Do you see anything wrong with this code. I am trying to create a dynamic error inside the loop to make sure that all the the transactions are rolled back if any error occurs inside the loop?? Dynamic error outside the loop rolls back all the transactions perfectly but not the one inside the loop.
const session = await mongoose.startSession();
try {
await session.withTransaction(
async () => {
sheetAfterSave = await sheet.save({ session });
records.forEach(async (el: any) => {
let updatedStockRecord = await Stock.bulkWrite(
[
{
updateOne: {
filter: {
index: el.index,
product: el.product,
batchNo: el.batchNo,
agency,
totalQuantity: { $gte: el.loadingTotal },
},
update: {
$push: {
reservations: {
loadingSheetId: sheetAfterSave._id,
reservedCaseQuantity: el.loadingCaseCount,
reservedUnitQuantity: el.loadingUnitCount,
reservedTotalQuantity: el.loadingTotal,
},
},
},
},
},
],
{
session: session,
}
);
console.log('******************');
throw new Error('12/24/2020 ERROR INSIDE LOOP'); // MongoError: Transaction 1 has been committed.
});
throw new Error('12/24/2020 ERROR OUTSIDE LOOP'); // All transactions are rolled back perfectly
},
{
readPreference: 'primary',
readConcern: { level: 'local' },
writeConcern: { w: 'majority' },
}
);
} catch (error) {
console.log('ERROR BLOCK', error);
throw new Error(
`Error occured while trying to create a new loading sheet. ${error}`
);
} finally {
session.endSession();
await mongoose.connection.close();
console.log('************ FINALLY *****************');
}