Created
July 10, 2022 18:51
-
-
Save sibu-github/1fd1f880279da8df270356bc13ef951d to your computer and use it in GitHub Desktop.
Example of transaction in Mongo DB
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// transfer balance from one account to another | |
const transferBalance = async (fromAccount, toAccount, amount) => { | |
// get database client | |
const client = await database.getClient(); | |
// create a database session | |
const session = client.startSession(); | |
// define transaction options | |
const transactionOptions = { | |
readPreference: 'primary', | |
readConcern: { level: 'local' }, | |
writeConcern: { w: 'majority' } | |
}; | |
// since transaction can throw error it must be executed within a try catch bolck | |
try { | |
// withTransaction takes a function as argument, | |
// the function must be an async function | |
// if the function throws any error then all operations are rollbacked | |
// and transaction is aborted. | |
// we can also call the abortTransaction explicitly to rollback the transaction as well. | |
const transactionResult = await session.withTransaction(async () => { | |
const accountCollection = client.db('mydb').collection('accounts'); | |
const transactionLogCollection = client.db('mydb').collection('transactionLog'); | |
// update fromAccount and decrement balance | |
let updateResult = await accountCollection.updateOne({"accountNo": fromAccount}, { | |
$inc: {balance: -1 * amount}, | |
$set: {updateTimestamp: utils.getCurrentTimestamp()} | |
}, {session} | |
); | |
// NOTE: WE MUST PASS THE session object to the above updateOne statement | |
// so that it executes within the session. | |
if (updateResult.modifedCount !== 1){ | |
// update did not happen properly, abort transaction | |
await session.abortTransaction(); | |
return; | |
} | |
// update toAccount and increment balance | |
updateResult = await accountCollection.updateOne({"accountNo": toAccount}, { | |
$inc: {balance: amount}, | |
$set: {updateTimestamp: utils.getCurrentTimestamp()} | |
}, {session} | |
); | |
// NOTE: WE MUST PASS THE session object to the above updateOne statement | |
// so that it executes within the session. | |
if (updateResult.modifedCount !== 1){ | |
// update did not happen properly, abort transaction | |
await session.abortTransaction(); | |
return; | |
} | |
// insert the data into the transactionLog | |
await transactionLogCollection.insertOne({ | |
fromAccount, | |
toAccount, | |
amount, | |
createdTimestamp: utils.getCurrentTimestamp() | |
}); | |
},transactionOptions); | |
// if the transaction was aborted then transactionResult will have undefined value | |
// otherwise it will hold some value which is the return value of the last statement | |
// executed within the transaction | |
// check the transactionResult value here and return appropriate response | |
if(!transactionResult) { | |
console.error("transaction is aborted"); | |
return false; | |
} | |
return true; | |
} catch(err){ | |
// log the error or return error response from here | |
console.error(err); | |
return false; | |
} finally{ | |
// we must always call endSession | |
// thats why it is put inside finally block | |
await session.endSession(); | |
} | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment