i'm trying to save several documents in a transaction and if i'm using Promise.all to run operations in parallel, and one of them fails and the promise rejects, then the other operations should rollback, but it doesn't happen if running operations one by one, then it's working as expected
so here's the working script:
import mongoose from "mongoose";
main()
.then(() => console.log('done'))
.catch(err => console.log(err));
const userSchema = new mongoose.Schema({
email: {
type: String,
required: true,
},
password: {
type: String,
},
});
const User = mongoose.model('User', userSchema);
async function main() {
await mongoose.connect('mongodb://127.0.0.1:27017/transaction-test?replicaSet=myReplicaSet');
const users = [
{
email: 'cred@mail.ru',
password: '123',
},
{
email: 'buba@mail.ru',
password: '123',
},
{
password: '1234', // no 'email' property so trying to save it will result in validation error
},
];
await User.db.transaction(async session => {
const promise1 = User.create([users[0]], { session });
const promise2 = User.create([users[1]], { session });
const promise3 = User.create([users[2]], { session });
await promise1;
await promise2;
await promise3;
});
};
the above code will work as intended and no docs will end up getting saved in the db, but if i replace the transaction part with:
await User.db.transaction(async session => {
const promise1 = User.create([users[0]], { session });
const promise2 = User.create([users[1]], { session });
const promise3 = User.create([users[2]], { session });
await Promise.all([promise1, promise2, promise3]);
});
};
then the first two docs will not rollback and will actually get saved to the db although the function will throw a validation error
mongoose version is 6.1.7 mongod version v5.0.4
Try with startTransaction and commitTransaction.
Something like this:
const session = await mongoose.startSession();
session.startTransaction();
try {
const promise1 = User.create([users[0]], { session });
const promise2 = User.create([users[1]], { session });
const promise3 = User.create([users[2]], { session });
await Promise.all([promise1, promise2, promise3]);
await session.commitTransaction();
} catch(error){
// Rollback any changes made in the database
await session.abortTransaction();
// logging the error
console.error(error);
// Rethrow the error
throw error;
} finally {
// Ending the session
session.endSession();
}