MongoDB & Node.js: Create an ACID Transaction (Part 3 of 4)
Rate this video
00:00:00Introduction to MongoDB Transactions
00:02:15Understanding ACID Properties
00:03:22Debunking the Myth: MongoDB Supports Transactions
00:04:17When to Use Transactions in MongoDB
00:05:14Creating a Use Case for Transactions
00:06:10Setting Up for Transaction Implementation
00:08:15Implementing Transactions in Node.js
00:10:38Creating Helper Functions for Reservations
00:12:16Building the Transaction Logic
00:14:42Executing and Verifying the Transaction
00:16:41Handling Transaction Success and Abortion
00:18:15Testing the Transaction with Different Scenarios
00:20:38Conclusion and What's Next
The primary focus of the video is to demonstrate how to implement and use transactions in MongoDB, particularly with a Node.js application, to maintain data integrity across multiple documents.
🔑 Key Points
- MongoDB supports multi-document ACID transactions.
- Transactions ensure operations are atomic, consistent, isolated, and durable.
- The video provides a practical example of using transactions in a Node.js script.
- Data modeling in MongoDB often reduces the need for transactions, but they are available when necessary.
- The session includes a step-by-step guide on setting up and executing transactions.
🔗 Related Links
- https://mdb.link/free-bdS03tgD2QQ
- https://mdb.link/community-bdS03tgD2QQ
- https://youtu.be/fbYExfeFsI0
- https://youtu.be/iz37fDe1XoM
- https://developer.mongodb.com/quickstart/node-transactions
- https://github.com/mongodb-developer/nodejs-quickstart/blob/master/transaction.js
- https://github.com/mongodb-developer/nodejs-quickstart/blob/master/usersCollection.js
- https://github.com/mongodb-developer/nodejs-quickstart/blob/master/template.js
- https://docs.mongodb.com/manual/tutorial/convert-standalone-to-replica-set
- https://twitter.com/lauren_schaefer
- https://tiktok.com/@lauren_schaefer
- https://www.linkedin.com/in/laurenjan...
- https://developer.mongodb.com/community/forums/t/hey-friends-im-lauren/168/
- https://bit.ly/3bpg1Z1
- https://bit.ly/2LjtNBZ
- https://bit.ly/3fH87gR
- https://bit.ly/3fEaIsd
- https://bit.ly/2SY9w90
- https://bit.ly/3bn9bDv
- https://bit.ly/2I8VCi5
- https://bit.ly/3fHoqdJ
Full Video Transcript
have you ever thought to yourself i'd love to use mongodb but i really need transactions if that's you i have good news mongodb supports transactions today i'm going to talk about what transactions are and then create a transaction in a node.js script now if you prefer to read instead of watching videos i've got you covered i've created a blog series that covers exactly the same content you'll see me cover today and i will drop a link for you in the description of this video now this is the third video in the mongodb and node.js quickstart video series in the first video i showed you how to get connected to a mongodb database and then how to execute each of the crud that's create read update and delete operations if you have any questions about how to get started with mongodb and node.js i recommend hopping back to that first video in the second video i discussed the basics of the aggregation pipeline now i'll drop links to both of those in the description below before we go any further let's talk about what acid transactions are acid stands for atomicity consistency isolation and durability when a database supports acid properties operations are guaranteed to either completely happen or not happen end with consistent data return the same results when run concurrently with other operations as they would if run sequentially and be permanently written meaning that they won't be lost in the event of a shutdown or something crazy happening now there's a myth floating around that mongodb does not support transactions and that's simply not true beginning in 4.0 mongodb added support for multi-document asset transactions and beginning in 4.2 mongodb added support for distributed asset transactions so if you need a transaction fear not mongodb supports transactions let's talk about how to use transactions in mongodb as you may have experienced while working with mongodb most use cases don't actually require you to use multi-document transactions when you model your data using that rule of thumb data that is accessed together should be stored together you'll find that you rarely need to use a transaction in fact i struggled a bit to come up with a use case for the airbnb dataset that would actually require a multi-document transaction did a bit of brainstorming and i've come up with a somewhat plausible example so let's say we want to allow users to create reservations in the sample airbnb database i've begun by creating a new collection named users i want users to be able to easily view their reservations when they look at their user profile pages so i want to store their reservations as embedded documents in the users collection for example let's say that a user named leslie creates two reservations her document in the user's collection will look like this when browsing airbnb listings users need to know if the listing they're viewing is already booked for their travel dates as a result we want to store the dates the listing is reserved in the listings and reviews collection for example the infinite views listing that leslie reserved should be updated to list her reservation dates keeping these two collections in sync is imperative if we were to create a reservation in a document in the user's collection without updating the associated document in the listings and reviews collection our data would be inconsistent so we can use a multi-document transaction to ensure both updates either succeed or fail together so let's write a node.js script that uses transactions to create reservations for users let's get set up first up let's talk about your database to utilize transactions mongodb must be configured as a replica set or a sharded cluster transactions are not supported on standalone deployments if you're using a database hosted on atlas which is what i'm going to be doing today you don't need to worry about this as every atlas cluster is either a replica set or a shirted cluster by default if you are hosting your own standalone deployment check out the link in the description for instructions on how to convert your instance to a replica set let's talk about the data i'm going to be using a database that is hosted in mongodb atlas and i've already loaded this sample data set there today i'm going to continue working in the sample airbnb database i'm going to use the infiniteviews airbnb listing that i created in the first video in this series hop back to the first video if your database does not currently have the infinite views listing the airbnb sample dataset only has the listings in review collection by default i'm also going to need a collection to store my users i wrote a script that you can use to quickly create this collection so check out the description for a link to that script all right now that we're set up let's implement the functionality to create airbnb reservations i'm going to head over to vs code now i'm starting here with a template file this template has the same structure as the code you saw me write in the last two videos if you have questions about how this template file is structured or what it's doing head back to that first video i'm going to begin by creating a helper function that will generate a reservation document you'll see how this document is used just a little later so let's call this function create reservation document it will have three params so we'll say name of listing the reservation dates and the reservation details i want every reservation document to have a name and dates so i'm going to add those to a variable named reservation so i'll say let reservation equals a new object we'll say name is set to name of listing and dates is set to reservation dates now reservations can have a variety of other properties and we may not know what those are in advance so what i'm going to do is just loop through the reservation details param and pull out all of the properties in it so i'll say for let detail in reservation details and then i'm going to pull the property and assign it to the reservation variable so i'll say reservation the detail equals reservation details detail so after that's done looping the reservation is ready i'm just going to return it so to give you an idea of what this function is doing let me just show you an example i'm going to go up to main and i'm just going to paste in a call to this function so this is saying create a reservation document where the name of the listing is infinite views the reservation will begin new year's eve and end on new year's day and then several other details that are specific to the reservation so the price per night's 180 for special requests the guest has selected late checkout and this reservation comes with breakfast included so let me run this and we can see the function is returning a nicely formatted document that represents this reservation now that we have our helper function ready let's create a function whose job is to actually create the reservation in the database so let's create an asynchronous function named create reservation now let's talk parameters the function should accept a [ __ ] client the user's email address the name of the airbnb listing the reservation dates and any other reservation details now we need to get the collections that we will use in this function so first let's create a constant named user's collection and we'll get that by saying client.db sample airbnb dot collection users and let's do the same for the listings and reviews collections so we'll say const listings and reviews collection equals client.db sample airbnb dot collection listings and reviews now let's create a document for this reservation so i'm going to call that helper function i created a moment ago and pass those function arguments so i'll say create reservation document name of listing reservation dates reservation details and let's assign that to a constant named reservation all right now we're ready to start creating a transaction every transaction and its operations must be associated with a session creating a session is fairly straightforward so i'll say const session equals client dot start session not too tricky right so i can choose to define options for the transaction and i'm not going to get into the details of those here you can learn more about those options in the driver documentation for this example i'll say const transaction options equals and let's set the read preference to primary the read concern level to local and the right concern to majority all right so my transaction code could throw errors so i'm going to create a try catch block uh so let's do a try catch and then let's follow that up with a finally block all right let's talk about with transaction you can use with transaction to start a transaction run a lambda and either commit or abort the transaction i'm going to call session.with transaction and i need to pass a lambda for now i'm going to pass an anonymous asynchronous function that doesn't do anything i'm going to add more to that in just a moment i'm also going to pass the transaction options i created a moment ago with transaction is going to return a promise so i'm going to choose to await the results and i'm going to assign the results to a constant named transaction result this anonymous function i'm passing to with transaction doesn't currently do anything so i'm going to incrementally build the database operations that i want to call from inside of that function all right so recall that i want to make updates to two collections the users collection and the listings and reviews collection let's start with making an update to the appropriate document in the users collection i want to add a reservation to the user's reservations array so i'll say await users collection dot update 1 and i want to search for the user document where the email is set to the user email argument for the update operation i want to use add to set to add the reservation to the reservations array now i need to associate this operation with the transaction so i'm going to pass the session let's store the results of this update in a constant named users update results to help us see what's happening in each stage of the transaction i'm just going to add some logging here let's log how many documents this query found so i'll say console.log usersupdateresults.matchedcount that's how many documents found in the user's collection with the email address user email and then let's also log how many documents were actually updated so i'll say console.log usersupdateresults.modifiedcount documents was were updated to include the reservation next i want to make sure that an airbnb listing is not double booked for any given date so i'm going to check if the registration date is already listed in the listings dates reserved array so i'll say await listings and reviews collection dot find one and i'm going to search for a document where the name is set to name of listing and where any of the reservation dates are already in the dates reserved field so i'll say dates reserved dollar in reservation dates just like i did with the last query i need to pass the session let's store the results of this query in a constant named is listing reserved results so if we get results meaning if the airbnb listing is already reserved for the given dates we want to stop here and abort the transaction so i'll say if is listing reserved results await session dot abort transaction aborting the transaction is going to roll back the update to the user document we made in the previous query now i'm going to add some logging just for clarity so i'll say console dot air this listing is already reserved for at least one of the given dates the reservation could not be created now to really drive home this point about aborting transactions i'm going to say console.air any operations that already occurred as part of this transaction will be rolled back and then i'm going to add a return here the final thing i want to do inside of this transaction is update the document in the listings and reviews collection i want to add the reservation dates to the dates reserved array so i'll say await listings and reviews collection dot update 1 and i want to update the document where the name is the name of listing argument for the update operation i'll use add to set i want to update the dates reserved array and i want to add each date from the reservation dates argument now i can't forget to pass the session of course let's set the result of calling update 1 to a constant named listings and reviews update results it's a little wordy but hopefully you're following along with me let's add a little logging here just so we can trace what's happening so first let's log how many documents matched our query so i'll say console.log listings and reviews update results dot matched count documents found in the listings and reviews collection with the name name of listing and then let's log how many documents were actually updated so i'll say console.log listings and reviews update results dot modified count documents was or were updated to include the reservation dates so now i've done everything i want to do in this transaction i've updated documents in both the users collection and the listings and reviews collection now i'm going to scroll up a bit recall that i stored my transaction results here in a constant named transaction results to check if the transaction succeeded i can say if transaction results now if transaction results is defined i know the transaction succeeded so i'll just say console.log the reservation was successfully created else meaning transaction results is undefined i know that the code intentionally aborted the transaction so i can say console.log the transaction was intentionally aborted if something goes wrong in my transaction an error will be thrown so inside of my catch i'll say console.log the transaction was aborted due to an unexpected error and we'll stick that error in there regardless of what happens i need to end the session so inside of the finally block i'll say await session dot end session i've written a lot of code here let's actually try this out i'm going to create a reservation for leslie at the infinite views listing for new year's eve and new year's day so i'm going to scroll up to maine and i'll say await create reservation now i need to pass several arguments first up the [ __ ] client next is the user email address so i'll say leslie example.com next i need to pass the name of the listing so i'll say infinite views and then i need to pass an array of dates so i'll create a new date for 2021 12 31 and a new date for 2022 0101 and then finally i need to pass any other reservation details so i'll say the price per night is 180. there is a special request for late checkout and for breakfast included i'll say true okay i'm going to save my file and let's run it okay we got a lot of output here let's look at what's happening we can see one document was found in the user's collection with the email address leslie example.com so our user exists that's good one document was updated to include the reservation that's good then one document was found in the listings and reviews collection with the name infinite views so we found that listing and one document was updated to include the reservation dates the reservation was successfully created our transaction worked let's take a look at leslie's document here we can see she has the reservation we just created for her let's also take a look at the infinite views listing in the listings and reviews collection we can see this document also shows that it has been reserved on the dates we requested now let's say a different user tom tries to create a reservation at the same airbnb location on the same dates i'm just going to update the email address here to say tom example.com and i'm going to run this script again here we can see the transaction was intentionally aborted because the listing was already booked we can take a look at tom's document and see he has no reservations we can look at the infinite views listing and see the dates reserved array remains unchanged so the transaction successfully aborted and neither of these documents were updated so there you go to see an example of a transaction that succeeded as well as a transaction that was aborted all right let's wrap this up today we talked all about transactions we discussed what transactions are and wrote code to use a transaction in a node.js script when you use relational databases related data is commonly split between different tables in an effort to normalize the data as a result transaction usage is fairly common when you use mongodb data that is accessed together should be stored together when you model your data this way you will likely find that you rarely need to use transactions but if you need them don't worry mongodb supports them one thing to note as you begin using transactions be sure you are using the correct read and write concerns when creating a transaction i'm not going to get into the details here so check out the mongodb documentation for more information if you want to reference the code you saw me write today check out the quick start blog series i wrote that covers the exact same content i also have a github repo that shows just the code with no explanation so i will drop links to both of those in the description below all right we have only one video left in the series i know don't be too sad it'll be okay that last video is all about change streams and triggers in that video i'll be explaining how you can automatically take actions based on changes in your database that video is going to be releasing soon so be sure to just subscribe so you do not miss it if you have any questions about transactions or mongodb in general i encourage you to ask them in the mongodb community my teammates and i are there every day answering questions and discussing best practices with members of our developer community i hope to see you there you