1
+ /* Performs multiple write operations in a transaction */
2
+
1
3
const { MongoError, MongoClient } = require ( 'mongodb' ) ;
2
4
3
- // drop collections
5
+ // Drop the "customers", "inventory", and "orders" collections from the "testdb" database
4
6
async function cleanUp ( client ) {
5
7
await Promise . all ( [ 'customers' , 'inventory' , 'orders' ] . map ( async c => {
6
8
try {
7
9
const coll = client . db ( 'testdb' ) . collection ( c ) ;
8
10
await coll . drop ( ) ;
9
- } catch ( e ) { }
11
+ } catch ( e ) { } // Ignore any exceptions
10
12
} ) ) ;
11
13
}
12
14
@@ -15,17 +17,21 @@ async function setup(client) {
15
17
const customerColl = client . db ( 'testdb' ) . collection ( 'customers' ) ;
16
18
const inventoryColl = client . db ( 'testdb' ) . collection ( 'inventory' ) ;
17
19
20
+ // Insert order data for customer "98765" in the customers collection
18
21
await customerColl . insertOne ( { _id : 98765 , orders : [ ] } ) ;
19
22
23
+ // Insert inventory data for "sunblock" and "beach towel"
20
24
await inventoryColl . insertMany ( [
21
25
{ name : 'sunblock' , sku : 5432 , qty : 85 } ,
22
26
{ name : 'beach towel' , sku : 7865 , qty : 41 } ,
23
27
] ) ;
24
28
} catch ( e ) {
29
+ // Print the exception if one was thrown
25
30
console . log ( 'Unable to insert test data: ' + e ) ;
26
31
}
27
32
}
28
33
34
+ // Print all documents in the "customers", "inventory", and "orders" collections
29
35
async function queryData ( ) {
30
36
const uri = process . env . MONGODB_URI ;
31
37
const client = new MongoClient ( uri ) ;
@@ -36,23 +42,29 @@ async function queryData() {
36
42
} , client ) ) ;
37
43
38
44
} finally {
45
+ // Close the database connection
39
46
client . close ( ) ;
40
47
}
41
48
}
42
49
43
50
// start placeOrder
44
51
async function placeOrder ( client , cart , payment ) {
52
+ // Specify readConcern, writeConcern, and readPreference transaction options
45
53
const transactionOptions = {
46
54
readConcern : { level : 'snapshot' } ,
47
55
writeConcern : { w : 'majority' } ,
48
56
readPreference : 'primary'
49
57
} ;
50
58
59
+ // Start the session
51
60
const session = client . startSession ( ) ;
52
61
try {
62
+ // Start the transaction in the session, specifying the transaction options
53
63
session . startTransaction ( transactionOptions ) ;
54
64
55
65
const ordersCollection = client . db ( 'testdb' ) . collection ( 'orders' ) ;
66
+ // Within the session, insert an order that contains information about the
67
+ // customer, items purchased, and the total payment.
56
68
const orderResult = await ordersCollection . insertOne (
57
69
{
58
70
customer : payment . customer ,
@@ -63,21 +75,26 @@ async function placeOrder(client, cart, payment) {
63
75
) ;
64
76
65
77
const inventoryCollection = client . db ( 'testdb' ) . collection ( 'inventory' ) ;
78
+
79
+ // Within the session, for each item purchased, decrement the purchased quantity in the "inventory" collection.
80
+ // Cancel the transaction when you have insufficient inventory or if the item SKU does not exist.
66
81
for ( let i = 0 ; i < cart . length ; i ++ ) {
67
82
const item = cart [ i ] ;
68
83
69
- // Cancel the transaction when you have insufficient inventory
84
+ // Retrieve the inventory information for the item
70
85
const checkInventory = await inventoryCollection . findOne (
71
86
{
72
87
sku : item . sku ,
73
88
qty : { $gte : item . qty }
74
89
} ,
75
90
{ session }
76
91
)
92
+ // Throw an exception if the item lacks sufficient quantity or SKU does not exist.
77
93
if ( checkInventory === null ) {
78
94
throw new Error ( 'Insufficient quantity or SKU not found.' ) ;
79
95
}
80
96
97
+ // Decrement the inventory of the item by the amount specified in the order.
81
98
await inventoryCollection . updateOne (
82
99
{ sku : item . sku } ,
83
100
{ $inc : { 'qty' : - item . qty } } ,
@@ -86,15 +103,23 @@ async function placeOrder(client, cart, payment) {
86
103
}
87
104
88
105
const customerCollection = client . db ( 'testdb' ) . collection ( 'customers' ) ;
106
+
107
+ // Within the session, add the order details to the "orders" array of the customer document.
89
108
await customerCollection . updateOne (
90
109
{ _id : payment . customer } ,
91
110
{ $push : { orders : orderResult . insertedId } } ,
92
111
{ session }
93
112
) ;
113
+
114
+ // Commit the transaction to apply all updates performed within it
94
115
await session . commitTransaction ( ) ;
95
116
console . log ( 'Transaction successfully committed.' ) ;
96
117
97
118
} catch ( error ) {
119
+ /*
120
+ Handle any exceptions thrown during the transaction and end the
121
+ transaction. Roll back all the updates performed in the transaction.
122
+ */
98
123
if ( error instanceof MongoError && error . hasErrorLabel ( 'UnknownTransactionCommitResult' ) ) {
99
124
// add your logic to retry or handle the error
100
125
}
@@ -105,28 +130,41 @@ async function placeOrder(client, cart, payment) {
105
130
}
106
131
await session . abortTransaction ( ) ;
107
132
} finally {
133
+ // End the session so that no further calls can be made on it
108
134
await session . endSession ( ) ;
109
135
}
110
136
}
111
137
// end placeOrder
112
138
139
+
140
+ // Run the full transaction example
113
141
async function run ( ) {
114
142
const uri = process . env . MONGODB_URI ;
115
143
const client = new MongoClient ( uri ) ;
116
144
145
+ // Call a method that removes data from prior runs of this example
117
146
await cleanUp ( client ) ;
147
+
148
+ // Call a method that creates sample inventory data for this example
118
149
await setup ( client ) ;
119
150
151
+ // Create sample data for a customer's shopping cart that includes "sunblock" and "beach towel" items
120
152
const cart = [
121
153
{ name : 'sunblock' , sku : 5432 , qty : 1 , price : 5.19 } ,
122
154
{ name : 'beach towel' , sku : 7865 , qty : 2 , price : 15.99 }
123
155
] ;
156
+
157
+ // Create sample data for a customer's payment, calculated from the contents of their cart
124
158
const payment = { customer : 98765 , total : 37.17 } ;
125
159
126
160
try {
161
+ // Call the method that updates the customer and inventory in a transaction
127
162
await placeOrder ( client , cart , payment ) ;
128
163
} finally {
164
+ // Call a method that removes data from prior runs of this example
129
165
await cleanUp ( client ) ;
166
+
167
+ // Close the database connection
130
168
await client . close ( ) ;
131
169
}
132
170
}
0 commit comments