Skip to content

Commit bce5adf

Browse files
committed
feat(mongo-client): implement MongoClient.prototype.startSession
NODE-1088
1 parent e8933bb commit bce5adf

File tree

3 files changed

+109
-11
lines changed

3 files changed

+109
-11
lines changed

lib/mongo_client.js

+42-10
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,9 @@ var parse = require('./url_parser'),
1515
f = require('util').format,
1616
assign = require('./utils').assign,
1717
shallowClone = require('./utils').shallowClone,
18-
authenticate = require('./authenticate');
18+
authenticate = require('./authenticate'),
19+
ServerSessionPool = require('mongodb-core').Sessions.ServerSessionPool,
20+
ClientSession = require('mongodb-core').Sessions.ClientSession;
1921

2022
/**
2123
* @fileOverview The **MongoClient** class is a class that allows for making Connections to MongoDB.
@@ -180,7 +182,8 @@ function MongoClient(url, options) {
180182
url: url,
181183
options: options || {},
182184
promiseLibrary: null,
183-
dbCache: {}
185+
dbCache: {},
186+
sessionPool: null
184187
};
185188

186189
// Get the promiseLibrary
@@ -228,9 +231,8 @@ MongoClient.prototype.connect = function(callback) {
228231
// Did we have a validation error
229232
if (err) return reject(err);
230233
// Attempt to connect
231-
connect(self, self.s.url, self.s.options, function(err, topology) {
234+
connect(self, self.s.url, self.s.options, function(err) {
232235
if (err) return reject(err);
233-
self.topology = topology;
234236
resolve(self);
235237
});
236238
});
@@ -239,9 +241,8 @@ MongoClient.prototype.connect = function(callback) {
239241
// Did we have a validation error
240242
if (err) return callback(err);
241243
// Fallback to callback based connect
242-
connect(self, self.s.url, self.s.options, function(err, topology) {
244+
connect(self, self.s.url, self.s.options, function(err) {
243245
if (err) return callback(err);
244-
self.topology = topology;
245246
callback(null, self);
246247
});
247248
};
@@ -454,6 +455,28 @@ MongoClient.connect = function(url, options, callback) {
454455

455456
define.staticMethod('connect', { callback: true, promise: true });
456457

458+
/**
459+
* Starts a new session on the server
460+
*
461+
* @param {object} [options] optional settings for a driver session
462+
* @param {MongoClient~sessionCallback} [callback] The callback called with a newly establish session, or an error if one occurred
463+
* @return {Promise} if no callback is specified, a promise will be returned for the newly established session
464+
*/
465+
MongoClient.prototype.startSession = function(options) {
466+
options = options || {};
467+
if (!this.topology) {
468+
throw new MongoError('Must connect to a server before calling this method');
469+
}
470+
471+
const capabilities = this.topology.capabilities();
472+
if (capabilities && !capabilities.hasSessionSupport) {
473+
throw new MongoError('Current topology does not support sessions');
474+
}
475+
476+
const session = new ClientSession(this.topology.s.coreTopology, this.s.sessionPool, options);
477+
return session;
478+
};
479+
457480
var mergeOptions = function(target, source, flatten) {
458481
for (var name in source) {
459482
if (source[name] && typeof source[name] === 'object' && flatten) {
@@ -611,6 +634,11 @@ function relayEvents(self, topology) {
611634
});
612635
}
613636

637+
function assignTopology(client, topology) {
638+
client.topology = topology;
639+
client.s.sessionPool = new ServerSessionPool(topology.s.coreTopology);
640+
}
641+
614642
function createServer(self, options, callback) {
615643
// Set default options
616644
var servers = translateOptions(options);
@@ -629,8 +657,9 @@ function createServer(self, options, callback) {
629657
addListeners(self, servers[0]);
630658
// Check if we are really speaking to a mongos
631659
var ismaster = topology.lastIsMaster();
660+
632661
// Set the topology
633-
self.topology = topology;
662+
assignTopology(self, topology);
634663

635664
// Do we actually have a mongos
636665
if (ismaster && ismaster.msg === 'isdbgrid') {
@@ -659,7 +688,8 @@ function createReplicaset(self, options, callback) {
659688
// Open the connection
660689
topology.connect(options, function(err, topology) {
661690
if (err) return callback(err);
662-
self.topology = topology;
691+
692+
assignTopology(self, topology);
663693
callback(null, topology);
664694
});
665695
}
@@ -676,7 +706,8 @@ function createMongos(self, options, callback) {
676706
// Open the connection
677707
topology.connect(options, function(err, topology) {
678708
if (err) return callback(err);
679-
self.topology = topology;
709+
710+
assignTopology(self, topology);
680711
callback(null, topology);
681712
});
682713
}
@@ -772,7 +803,8 @@ var connect = function(self, url, options, callback) {
772803
// Did we pass in a Server/ReplSet/Mongos
773804
if (url instanceof Server || url instanceof ReplSet || url instanceof Mongos) {
774805
// Set the topology
775-
self.topology = url;
806+
assignTopology(self, url);
807+
776808
// Add listeners
777809
addListeners(self, url);
778810
// Connect

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
"official"
1414
],
1515
"dependencies": {
16-
"mongodb-core": "mongodb-js/mongodb-core#3.0.0"
16+
"mongodb-core": "mongodb-js/mongodb-core#driver-sessions"
1717
},
1818
"devDependencies": {
1919
"betterbenchmarks": "^0.1.0",

test/functional/sessions_tests.js

+66
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
'use strict';
2+
3+
const MongoClient = require('../..').MongoClient;
4+
const expect = require('chai').expect;
5+
const assign = require('../../lib/utils').assign;
6+
const mock = require('../mock');
7+
8+
const test = {};
9+
describe('Sessions', function() {
10+
describe('unit', function() {
11+
afterEach(() => mock.cleanup());
12+
beforeEach(() => {
13+
return mock.createServer().then(server => {
14+
test.server = server;
15+
});
16+
});
17+
18+
it('should throw an exception if sessions are not supported', {
19+
metadata: { requires: { topology: 'single' } },
20+
test: function(done) {
21+
test.server.setMessageHandler(request => {
22+
var doc = request.document;
23+
if (doc.ismaster) {
24+
request.reply(assign({}, mock.DEFAULT_ISMASTER));
25+
}
26+
});
27+
28+
MongoClient.connect(`mongodb://${test.server.uri()}/test`, function(err, client) {
29+
expect(err).to.not.exist;
30+
expect(() => {
31+
client.startSession();
32+
}).to.throw(/Current topology does not support sessions/);
33+
34+
client.close();
35+
done();
36+
});
37+
}
38+
});
39+
40+
it('should return a client session when requested if the topology supports it', {
41+
metadata: { requires: { topology: 'single' } },
42+
43+
test: function(done) {
44+
test.server.setMessageHandler(request => {
45+
var doc = request.document;
46+
if (doc.ismaster) {
47+
request.reply(
48+
assign({}, mock.DEFAULT_ISMASTER, {
49+
logicalSessionTimeoutMinutes: 10
50+
})
51+
);
52+
}
53+
});
54+
55+
MongoClient.connect(`mongodb://${test.server.uri()}/test`, function(err, client) {
56+
expect(err).to.not.exist;
57+
let session = client.startSession();
58+
expect(session).to.exist;
59+
60+
client.close();
61+
done();
62+
});
63+
}
64+
});
65+
});
66+
});

0 commit comments

Comments
 (0)