Skip to content
This repository was archived by the owner on Feb 4, 2022. It is now read-only.

Commit 11865bf

Browse files
committed
feat(sessions): track logicalSessionTimeoutMinutes for sessions
NODE-1088
1 parent c9fa89c commit 11865bf

File tree

5 files changed

+293
-0
lines changed

5 files changed

+293
-0
lines changed

lib/topologies/mongos.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -250,6 +250,14 @@ Object.defineProperty(Mongos.prototype, 'parserType', {
250250
}
251251
});
252252

253+
Object.defineProperty(Mongos.prototype, 'logicalSessionTimeoutMinutes', {
254+
enumerable: true,
255+
get: function() {
256+
if (!this.ismaster) return null;
257+
return this.ismaster.logicalSessionTimeoutMinutes || null;
258+
}
259+
});
260+
253261
/**
254262
* Emit event if it exists
255263
* @method

lib/topologies/replset.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -270,6 +270,13 @@ Object.defineProperty(ReplSet.prototype, 'parserType', {
270270
}
271271
});
272272

273+
Object.defineProperty(ReplSet.prototype, 'logicalSessionTimeoutMinutes', {
274+
enumerable: true,
275+
get: function() {
276+
return this.s.replicaSetState.logicalSessionTimeoutMinutes || null;
277+
}
278+
});
279+
273280
function rexecuteOperations(self) {
274281
// If we have a primary and a disconnect handler, execute
275282
// buffered operations

lib/topologies/replset_state.js

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,8 @@ var ReplSetState = function(options) {
7272
topologyType: 'Unknown',
7373
servers: []
7474
};
75+
76+
this.logicalSessionTimeoutMinutes = undefined;
7577
};
7678

7779
inherits(ReplSetState, EventEmitter);
@@ -263,6 +265,21 @@ ReplSetState.prototype.update = function(server) {
263265
return false;
264266
}
265267

268+
// Update logicalSessionTimeoutMinutes
269+
if (ismaster.logicalSessionTimeoutMinutes !== undefined) {
270+
if (
271+
self.logicalSessionTimeoutMinutes === undefined ||
272+
ismaster.logicalSessionTimeoutMinutes === null
273+
) {
274+
self.logicalSessionTimeoutMinutes = ismaster.logicalSessionTimeoutMinutes;
275+
} else {
276+
self.logicalSessionTimeoutMinutes = Math.min(
277+
self.logicalSessionTimeoutMinutes,
278+
ismaster.logicalSessionTimeoutMinutes
279+
);
280+
}
281+
}
282+
266283
//
267284
// Is this a mongos
268285
//

lib/topologies/server.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,14 @@ Object.defineProperty(Server.prototype, 'parserType', {
195195
}
196196
});
197197

198+
Object.defineProperty(Server.prototype, 'logicalSessionTimeoutMinutes', {
199+
enumerable: true,
200+
get: function() {
201+
if (!this.ismaster) return null;
202+
return this.ismaster.logicalSessionTimeoutMinutes || null;
203+
}
204+
});
205+
198206
Server.enableServerAccounting = function() {
199207
serverAccounting = true;
200208
servers = {};

test/tests/unit/sessions_tests.js

Lines changed: 253 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,253 @@
1+
'use strict';
2+
var expect = require('chai').expect,
3+
assign = require('../../../lib/utils').assign,
4+
co = require('co'),
5+
mock = require('../../mock'),
6+
ObjectId = require('bson').ObjectId;
7+
8+
function MockReplSetState() {
9+
this.electionIds = [new ObjectId(), new ObjectId()];
10+
this.defaultFields = assign({}, mock.DEFAULT_ISMASTER, {
11+
setName: 'rs',
12+
setVersion: 1,
13+
electionId: this.electionIds[0],
14+
hosts: ['localhost:32000', 'localhost:32001', 'localhost:32002'],
15+
arbiters: ['localhost:32002']
16+
});
17+
18+
this.primary = [
19+
assign({}, this.defaultFields, {
20+
ismaster: true,
21+
secondary: false,
22+
me: 'localhost:32000',
23+
primary: 'localhost:32000',
24+
tags: { loc: 'ny' }
25+
})
26+
];
27+
28+
this.firstSecondary = [
29+
assign({}, this.defaultFields, {
30+
ismaster: false,
31+
secondary: true,
32+
me: 'localhost:32001',
33+
primary: 'localhost:32000',
34+
tags: { loc: 'sf' }
35+
})
36+
];
37+
38+
this.arbiter = [
39+
assign({}, this.defaultFields, {
40+
ismaster: false,
41+
secondary: false,
42+
arbiterOnly: true,
43+
me: 'localhost:32002',
44+
primary: 'localhost:32000'
45+
})
46+
];
47+
}
48+
49+
describe('Sessions', function() {
50+
afterEach(() => mock.cleanup());
51+
52+
it('should track `logicalSessionTimeoutMinutes` for a single topology', {
53+
metadata: { requires: { topology: 'single' } },
54+
test: function(done) {
55+
const Server = this.configuration.mongo.Server;
56+
57+
co(function*() {
58+
const mockServer = yield mock.createServer(37019, 'localhost');
59+
mockServer.setMessageHandler(request => {
60+
request.reply(
61+
assign({}, mock.DEFAULT_ISMASTER, {
62+
logicalSessionTimeoutMinutes: 10
63+
})
64+
);
65+
});
66+
67+
var client = new Server({ host: 'localhost', port: 37019 });
68+
client.on('error', done);
69+
client.once('connect', () => {
70+
expect(client.logicalSessionTimeoutMinutes).to.equal(10);
71+
client.destroy();
72+
done();
73+
});
74+
75+
client.connect();
76+
});
77+
}
78+
});
79+
80+
it('should track `logicalSessionTimeoutMinutes` for a mongos topology', {
81+
metadata: { requires: { topology: 'single' } },
82+
test: function(done) {
83+
const Mongos = this.configuration.mongo.Mongos;
84+
85+
co(function*() {
86+
const mockServer = yield mock.createServer(37019, 'localhost');
87+
mockServer.setMessageHandler(request => {
88+
request.reply(
89+
assign({}, mock.DEFAULT_ISMASTER, {
90+
msg: 'isdbgrid',
91+
logicalSessionTimeoutMinutes: 10
92+
})
93+
);
94+
});
95+
96+
var mongos = new Mongos([{ host: 'localhost', port: 37019 }], {
97+
connectionTimeout: 30000,
98+
socketTimeout: 30000,
99+
haInterval: 500,
100+
size: 1
101+
});
102+
103+
mongos.on('error', done);
104+
mongos.once('connect', () => {
105+
expect(mongos.logicalSessionTimeoutMinutes).to.equal(10);
106+
mongos.destroy();
107+
done();
108+
});
109+
110+
mongos.connect();
111+
});
112+
}
113+
});
114+
115+
it(
116+
'should track `logicalSessionTimeoutMinutes` for replset topology, choosing the lowest value',
117+
{
118+
metadata: { requires: { topology: 'single' } },
119+
test: function(done) {
120+
var ReplSet = this.configuration.mongo.ReplSet;
121+
122+
const replSetState = new MockReplSetState();
123+
replSetState.primary[0].logicalSessionTimeoutMinutes = 426;
124+
replSetState.firstSecondary[0].logicalSessionTimeoutMinutes = 1;
125+
replSetState.arbiter[0].logicalSessionTimeoutMinutes = 32;
126+
127+
co(function*() {
128+
const primaryServer = yield mock.createServer(32000, 'localhost');
129+
const firstSecondaryServer = yield mock.createServer(32001, 'localhost');
130+
const arbiterServer = yield mock.createServer(32002, 'localhost');
131+
132+
primaryServer.setMessageHandler(request => {
133+
var doc = request.document;
134+
if (doc.ismaster) {
135+
request.reply(replSetState.primary[0]);
136+
}
137+
});
138+
139+
firstSecondaryServer.setMessageHandler(request => {
140+
var doc = request.document;
141+
if (doc.ismaster) {
142+
request.reply(replSetState.firstSecondary[0]);
143+
}
144+
});
145+
146+
arbiterServer.setMessageHandler(request => {
147+
var doc = request.document;
148+
if (doc.ismaster) {
149+
request.reply(replSetState.arbiter[0]);
150+
}
151+
});
152+
153+
var replset = new ReplSet(
154+
[{ host: '127.0.0.1', port: 32000 }, { host: '127.0.0.1', port: 32001 }],
155+
{
156+
setName: 'rs',
157+
connectionTimeout: 3000,
158+
socketTimeout: 0,
159+
haInterval: 100,
160+
size: 1
161+
}
162+
);
163+
164+
replset.on('error', done);
165+
replset.once('connect', () => {
166+
expect(replset.logicalSessionTimeoutMinutes).to.equal(1);
167+
replset.destroy();
168+
done();
169+
});
170+
171+
replset.connect();
172+
});
173+
}
174+
}
175+
);
176+
177+
it('should set `logicalSessionTimeoutMinutes` to `null` if any incoming server is `null`', {
178+
metadata: { requires: { topology: 'single' } },
179+
test: function(done) {
180+
var ReplSet = this.configuration.mongo.ReplSet;
181+
182+
const replSetState = new MockReplSetState();
183+
replSetState.primary[0].logicalSessionTimeoutMinutes = 426;
184+
replSetState.firstSecondary[0].logicalSessionTimeoutMinutes = null;
185+
replSetState.arbiter[0].logicalSessionTimeoutMinutes = 32;
186+
187+
co(function*() {
188+
const primaryServer = yield mock.createServer(32000, 'localhost');
189+
const firstSecondaryServer = yield mock.createServer(32001, 'localhost');
190+
const arbiterServer = yield mock.createServer(32002, 'localhost');
191+
192+
primaryServer.setMessageHandler(request => {
193+
var doc = request.document;
194+
if (doc.ismaster) {
195+
request.reply(replSetState.primary[0]);
196+
}
197+
});
198+
199+
firstSecondaryServer.setMessageHandler(request => {
200+
var doc = request.document;
201+
if (doc.ismaster) {
202+
request.reply(replSetState.firstSecondary[0]);
203+
}
204+
});
205+
206+
arbiterServer.setMessageHandler(request => {
207+
var doc = request.document;
208+
if (doc.ismaster) {
209+
request.reply(replSetState.arbiter[0]);
210+
}
211+
});
212+
213+
var replset = new ReplSet(
214+
[{ host: '127.0.0.1', port: 32000 }, { host: '127.0.0.1', port: 32001 }],
215+
{
216+
setName: 'rs',
217+
connectionTimeout: 3000,
218+
socketTimeout: 0,
219+
haInterval: 100,
220+
size: 1
221+
}
222+
);
223+
224+
replset.on('error', done);
225+
replset.once('connect', () => {
226+
expect(replset.logicalSessionTimeoutMinutes).to.equal(null);
227+
replset.destroy();
228+
done();
229+
});
230+
231+
replset.connect();
232+
});
233+
}
234+
});
235+
236+
it('should default `logicalSessionTimeoutMinutes` to `null` for all topology types', {
237+
metadata: { requires: { topology: 'single' } },
238+
test: function() {
239+
const ReplSet = this.configuration.mongo.ReplSet,
240+
Mongos = this.configuration.mongo.Mongos,
241+
Server = this.configuration.mongo.Server;
242+
243+
const single = new Server();
244+
expect(single.logicalSessionTimeoutMinutes).to.equal(null);
245+
246+
const mongos = new Mongos();
247+
expect(mongos.logicalSessionTimeoutMinutes).to.equal(null);
248+
249+
const replset = new ReplSet([{ host: '127.0.0.1', port: 32000 }]);
250+
expect(replset.logicalSessionTimeoutMinutes).to.equal(null);
251+
}
252+
});
253+
});

0 commit comments

Comments
 (0)