Transactions¶
Overview¶
For situations that require atomicity of reads and writes to multiple documents (in one or more collections), MongoDB supports multi-document transactions. The code snippets on this page demonstrate how to execute multiple MongoDB queries in a single transaction that will undo changes in the event of an error.
Snippet Setup¶
To use a code snippet in a function, you must first instantiate a MongoDB collection handle:
exports = function() { const mongodb = context.services.get("mongodb-atlas"); const itemsCollection = mongodb.db("store").collection("items"); const purchasesCollection = mongodb.db("store").collection("purchases"); // ... paste snippet here ... }
Example¶
To perform a transaction:
- Obtain and start a client session with
client.startSession()
. - (Optional) Define a read and write settings object for the transaction. Pass this object to the method called in the next step.
- Use the
session.withTransaction()
method to start a transaction. - Execute the MongoDB queries that you would like to include in
your transaction in the callback for this function, which needs to
either be
async
or return aPromise
. Be sure to pass thesession
to each query to ensure that it is included in the transaction. - If the callback encounters an error, you should abort the transaction
with
session.abortTransaction()
in aPromise
rejection handler withPromise.catch()
or atry ... catch
exception handler. - When the transaction is complete, call
session.endSession()
to end the session and free resources.
The following example creates two users, "henry" and "michelle", and a uses a transaction to move "browniePoints" between those users atomically:
exports = function () { const client = context.services.get("mongodb-atlas"); db = client.db("exampleDatabase"); accounts = db.collection("accounts"); browniePointsTrades = db.collection("browniePointsTrades"); // create user accounts with initial balances accounts.insertOne({ name: "henry", browniePoints: 42 }); accounts.insertOne({ name: "michelle", browniePoints: 144 }); // trade points between user accounts in a transaction tradeBrowniePoints( client, accounts, browniePointsTrades, "michelle", "henry", 5 ); return "Successfully traded brownie points."; }; async function tradeBrowniePoints(
client,
accounts,
browniePointsTrades,
userAddPoints,
userSubtractPoints,
pointVolume
) { // Step 1: Start a Client Session const session = client.startSession(); // Step 2: Optional. Define options to use for the transaction const transactionOptions = { readPreference: "primary", readConcern: { level: "local" }, writeConcern: { w: "majority" }, }; // Step 3: Use withTransaction to start a transaction, execute the callback, and commit (or abort on error) // Note: The callback for withTransaction MUST be async and/or return a Promise. try { await session.withTransaction(async () => { // Step 4: Execute the queries you would like to include in one atomic transaction // Important:: You must pass the session to the operations await accounts.updateOne( { name: userSubtractPoints }, { $inc: { browniePoints: -1 * pointVolume } }, { session } ); await accounts.updateOne( { name: userAddPoints }, { $inc: { browniePoints: pointVolume } }, { session } ); await browniePointsTrades.insertOne( { userAddPoints: userAddPoints, userSubtractPoints: userSubtractPoints, pointVolume: pointVolume, }, { session } ); }, transactionOptions); } catch (err) { // Step 5: Handle errors with a transaction abort await session.abortTransaction(); } finally { // Step 6: End the session when you complete the transaction await session.endSession(); } }
Give Feedback