Skip to content

Instantly share code, notes, and snippets.

@mbroadst
Last active May 26, 2018 21:27
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mbroadst/eff2260e668b3fe2fc14d4892717184e to your computer and use it in GitHub Desktop.
Save mbroadst/eff2260e668b3fe2fc14d4892717184e to your computer and use it in GitHub Desktop.
const mongodb = require('mongodb');
const MongoClient = mongodb.MongoClient;
const expect = require('chai').expect;
const MongoNetworkError = mongodb.MongoNetworkError;
const testContext = {};
describe('Beta Examples (transactions)', function() {
before(function() {
testContext.url = 'mongodb://localhost:27017/test?replicaSet=rs0';
return MongoClient.connect(testContext.url)
.then(client =>
client.db('test').createCollection('shipment')
.then(() => client.db('test').createCollection('inventory'))
.then(() => client.db('test').collection('inventory').insertOne({ sku: 'abc123', qty: 500 }))
.then(() => client.close())
);
});
afterEach(() => testContext.client.close());
beforeEach(function() {
testContext.client = new MongoClient(testContext.url);
return testContext.client.connect();
});
it('example with explicit start, commit, and abort (promises)', function() {
const client = testContext.client;
// Start Beta Transaction Example 1 (promises)
const db = client.db('test');
const promise = client.withSession(session => {
session.startTransaction();
return db
.collection('inventory')
.updateOne({ sku: 'abc123' }, { $inc: { qty: -100 } }, { session })
.then(() =>
db.collection('shipment').insertOne({ sku: 'abc123', qty: 100 }, { session })
)
.then(() => session.commitTransaction())
.catch(err => {
if (err) console.dir(err);
session.abortTransaction();
});
});
// End Beta Transaction Example 1 (promises)
return promise.then(() => client.close());
});
it('example with explicit start, commit, and abort (async-await)', async function() {
const client = testContext.client;
// Start Beta Transaction Example 1 (async-await)
const db = client.db('test');
const promise = client.withSession(
session =>
async function() {
session.startTransaction();
try {
await db
.collection('inventory')
.updateOne({ sku: 'abc123' }, { $inc: { qty: -100 } }, { session });
await db.collection('shipment').insertOne({ sku: 'abc123', qty: 100 }, { session });
} catch (err) {
if (err) console.dir(err);
session.abortTransaction();
throw err;
}
await session.commitTransaction();
}
);
// End Beta Transaction Example 1 (async-await)
return promise.then(() => client.close());
});
it('example with write concern override (promises)', function() {
const client = testContext.client;
// Start Beta Transaction Example 2 (promises)
// In the following block, the following write concerns are used:
// the updateOne and insertOne operations uses w = 1, the transaction commit/abort uses w = "majority".
// Only abort and commit respect the writeConcern. Uncommitted writes are not replicated.
const db = client.db('test');
const promise = client.withSession(session => {
session.startTransaction({ writeConcern: { w: 'majority' } });
return db
.collection('inventory')
.updateOne({ sku: 'abc123' }, { $inc: { qty: -100 } }, { session })
.then(() =>
// The write concern specified here will be ignored
db
.collection('shipment')
.insertOne({ sku: 'abc123', qty: 100 }, { session, writeConcern: { w: 1 } })
)
.then(() => session.commitTransaction())
.catch(err => {
if (err) console.dir(err);
session.abortTransaction();
});
});
// End Beta Transaction Example 2 (promises)
return promise.then(() => client.close());
});
it('example with write concern override (async-await)', function() {
const client = testContext.client;
// Start Beta Transaction Example 2 (async-await)
// In the following block, the following write concerns are used:
// the updateOne and insertOne operations uses w = 1, the transaction commit/abort uses w = "majority".
// Only abort and commit respect the writeConcern. Uncommitted writes are not replicated.
const db = client.db('test');
const promise = client.withSession(
session =>
async function() {
session.startTransaction({ writeConcern: { w: 'majority' } });
try {
await db
.collection('inventory')
.updateOne({ sku: 'abc123' }, { $inc: { qty: -100 } }, { session });
await db
.collection('shipment')
.insertOne({ sku: 'abc123', qty: 100 }, { session, writeConcern: { w: 1 } });
} catch (err) {
if (err) console.dir(err);
session.abortTransaction();
throw err;
}
await session.commitTransaction();
}
);
// End Beta Transaction Example 2 (async-await)
return promise.then(() => client.close());
});
it('example with retryability (promises)', function() {
const client = testContext.client;
// Start Beta Transaction Example 3 (promises)
function runShipmentTransaction(db, session) {
session.startTransaction();
return db
.collection('inventory')
.updateOne({ sku: 'abc123' }, { $inc: { qty: -100 } }, { session })
.then(() =>
// The write concern specified here will be ignored
db
.collection('shipment')
.insertOne({ sku: 'abc123', qty: 100 }, { session, writeConcern: { w: 1 } })
)
.then(() => session.commitTransaction())
.catch(err => {
if (err) console.dir(err);
session.abortTransaction();
});
}
const db = client.db('test');
const promise = client.withSession(session => {
return runShipmentTransaction(db, session).catch(err => {
if (err instanceof MongoNetworkError || [112, 244, 251].includes(err.code)) {
return runShipmentTransaction(db, session);
}
// otherwise rethrow
throw err;
});
});
// End Beta Transaction Example 3 (promises)
return promise.then(() => client.close());
});
it('example with retryability (async-await)', function() {
const client = testContext.client;
// Start Beta Transaction Example 3 (async-await)
async function runShipmentTransaction(db, session) {
session.startTransaction({ writeConcern: { w: 'majority' } });
try {
await db
.collection('inventory')
.updateOne({ sku: 'abc123' }, { $inc: { qty: -100 } }, { session });
await db
.collection('shipment')
.insertOne({ sku: 'abc123', qty: 100 }, { session, writeConcern: { w: 1 } });
} catch (err) {
session.abortTransaction();
throw err;
}
await session.commitTransaction();
}
const db = client.db('test');
const promise = client.withSession(
session =>
async function() {
try {
await runShipmentTransaction(db, session);
} catch (err) {
if (err instanceof MongoNetworkError || [112, 244, 251].includes(err.code)) {
await runShipmentTransaction(db, session);
} else {
throw err;
}
}
}
);
// End Beta Transaction Example 3 (async-await)
return promise.then(() => client.close());
});
});
{
"name": "beta-examples",
"version": "0.0.1",
"main": "index.js",
"scripts": {
"test": "mocha beta-examples.js"
},
"dependencies": {
"chai": "^4.1.2",
"mocha": "^5.1.1",
"mongodb": "^3.1.0-beta2"
}
}
@davidsonsns
Copy link

In email from Alyson Cabral the config for initialize mongo beta is: mongod --dbpath /data/db --replSet "rs0" --port 27017, so in line 9 the correct is mongodb://localhost:27017/test?replicaSet=rs0, correct?

Tks for this code 😁

@yuki-takeichi
Copy link

I've fixed this code to pass the tests.

(my env:

  "dependencies": {
    "chai": "^4.1.2",
    "mocha": "^5.1.1",
    "mongodb": "^3.1.0-beta2"
  }
$ node -v
v8.9.3

)

--- beta-examples.js	2018-04-23 12:32:52.000000000 +0900
+++ beta-examples-fixed.js	2018-04-23 12:36:44.000000000 +0900
@@ -6,18 +6,18 @@
 const testContext = {};
 describe('Beta Examples (transactions)', function() {
   before(function() {
-    testContext.url = `mongodb://localhost:27017/test?replicaSet=rs`;
+    testContext.url = `mongodb://localhost:27017/test?replicaSet=rs0`;
     return MongoClient.connect(testContext.url).then(client =>
-      Promise.all([
-        client.db('test').createCollection('shipment'),
-        client.db('test').createCollection('inventory'),
-        client
-          .db('test')
-          .collection('inventory')
-          .insertOne({ sku: 'abc123', qty: 500 }),
-        client.close()
-      ])
-    );
+        client.db('test').createCollection('shipment').then(() =>
+          client.db('test').createCollection('inventory').then(() =>
+            client.db('test').collection('inventory').insertOne({ sku: 'abc123', qty: 500 }).then(() =>
+              client.close()
+              ))));
+  });
+  after(() => {
+    new MongoClient
+      .connect(testContext.url)
+      .then(client => client.db('test').dropDatabase().then(() => client.close()));
   });
 
   afterEach(() => testContext.client.close());

@mbroadst
Copy link
Author

mbroadst commented May 1, 2018

@yuki-takeichi thanks very much for the fix! I hadn't seen your update, and just came here to update with similar changes 😄

@mbroadst
Copy link
Author

mbroadst commented May 1, 2018

@davidsonsns yes, you're correct. Thanks for pointing this out - updated

@henryefranks
Copy link

For the async/await examples, I've found that instead of session => async function() { you need async session => { for the transaction to complete.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment