forked from mongodb-developer/nodejs-quickstart
-
Notifications
You must be signed in to change notification settings - Fork 0
/
transaction-bankingexample.js
128 lines (108 loc) · 5.3 KB
/
transaction-bankingexample.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
const { MongoClient } = require('mongodb');
// In MongoDB 4.2 and earlier, CRUD operations in transactions must be on existing collections
// See https://docs.mongodb.com/manual/core/transactions/#transactions-api for more information
// Before running this script...
// 1. Create a database named 'banking'
// 2. Create a collection named 'accounts' in the database
// 3. Create two documents in the 'accounts' collection:
// {"_id":"account1", "balance":500}
// {"_id":"account2", "balance":0}
// 4: Optional: add schema validation to ensure an account balance cannot drop below 0.
// See https://docs.mongodb.com/manual/core/schema-validation/ for details on how to
// enable schema validation. Configuring schema validation in MongoDB Compass is an
// easy way to add schema validation to an existing database: https://docs.mongodb.com/compass/current/validation/
//
// {
// $jsonSchema: {
// properties: {
// balance: {
// minimum: 0,
// description: 'account balance cannot be negative'
// }
// }
// }
// }
async function main() {
/**
* Connection URI. Update <username>, <password>, and <your-cluster-url> to reflect your cluster.
* See https://docs.mongodb.com/drivers/node/ for more details
*/
const uri = "mongodb+srv://<username>:<password>@<your-cluster-url>/banking?retryWrites=true&w=majority";
/**
* The Mongo Client you will use to interact with your database
* See https://mongodb.github.io/node-mongodb-native/3.6/api/MongoClient.html for more details
*/
const client = new MongoClient(uri);
try {
// Connect to the MongoDB cluster
await client.connect();
// Transfer $100 from "account1" to "account2"
await transferMoney(client, "account1", "account2", 100);
} finally {
// Close the connection to the MongoDB cluster
await client.close();
}
}
main().catch(console.error);
/**
* Transfer money from one bank account to another using
* @param {MongoClient} client A MongoClient that is connected to a cluster with the banking database
* @param {String} account1 The _id of the account where money should be subtracted
* @param {String} account2 The _id of the account where money should be added
* @param {Number} amount The amount of money to be transferred
*/
async function transferMoney(client, account1, account2, amount) {
/**
* The accounts collection in the banking database
*/
const accountsCollection = client.db("banking").collection("accounts");
// Step 1: Start a Client Session
// See https://mongodb.github.io/node-mongodb-native/3.6/api/MongoClient.html#startSession for the startSession() docs
const session = client.startSession();
// Step 2: Optional. Define options for the transaction
const transactionOptions = {
readPreference: 'primary',
readConcern: { level: 'local' },
writeConcern: { w: 'majority' }
};
try {
// 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.
// See https://mongodb.github.io/node-mongodb-native/3.6/api/ClientSession.html#withTransaction for the withTransaction() docs
const transactionResults = await session.withTransaction(async () => {
// Important:: You must pass the session to each of the operations
// Remove the money from the first account
const subtractMoneyResults = await accountsCollection.updateOne(
{ _id: account1 },
{ $inc: { balance: amount * -1 } },
{ session });
console.log(`${subtractMoneyResults.matchedCount} document(s) found in the accounts collection with _id ${account1}.`);
console.log(`${subtractMoneyResults.modifiedCount} document(s) was/were updated to remove the money.`);
if (subtractMoneyResults.modifiedCount !== 1) {
await session.abortTransaction();
return;
}
// Add the money to the second account
const addMoneyResults = await accountsCollection.updateOne(
{ _id: account2 },
{ $inc: { balance: amount } },
{ session });
console.log(`${addMoneyResults.matchedCount} document(s) found in the accounts collection with _id ${account2}.`);
console.log(`${addMoneyResults.modifiedCount} document(s) was/were updated to add the money.`);
if (addMoneyResults.modifiedCount !== 1) {
await session.abortTransaction();
return;
}
}, transactionOptions);
if (transactionResults) {
console.log("The money was successfully transferred. Database operations from the transaction are now visible outside the transaction.");
} else {
console.log("The money was not transferred. The transaction was intentionally aborted.");
}
} catch (e) {
console.log("The money was not transferred. The transaction was aborted due to an unexpected error: " + e);
} finally {
// Step 4: End the session
await session.endSession();
}
}