mongodb 3.2.5 → 3.3.0-beta2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/HISTORY.md +0 -10
- package/index.js +4 -4
- package/lib/admin.js +56 -56
- package/lib/aggregation_cursor.js +7 -3
- package/lib/bulk/common.js +18 -13
- package/lib/change_stream.js +196 -89
- package/lib/collection.js +217 -169
- package/lib/command_cursor.js +17 -7
- package/lib/core/auth/auth_provider.js +158 -0
- package/lib/core/auth/defaultAuthProviders.js +29 -0
- package/lib/core/auth/gssapi.js +241 -0
- package/lib/core/auth/mongo_credentials.js +81 -0
- package/lib/core/auth/mongocr.js +51 -0
- package/lib/core/auth/plain.js +35 -0
- package/lib/core/auth/scram.js +293 -0
- package/lib/core/auth/sspi.js +131 -0
- package/lib/core/auth/x509.js +26 -0
- package/lib/core/connection/apm.js +236 -0
- package/lib/core/connection/command_result.js +36 -0
- package/lib/core/connection/commands.js +507 -0
- package/lib/core/connection/connect.js +370 -0
- package/lib/core/connection/connection.js +624 -0
- package/lib/core/connection/logger.js +246 -0
- package/lib/core/connection/msg.js +219 -0
- package/lib/core/connection/pool.js +1285 -0
- package/lib/core/connection/utils.js +57 -0
- package/lib/core/cursor.js +752 -0
- package/lib/core/error.js +186 -0
- package/lib/core/index.js +50 -0
- package/lib/core/sdam/monitoring.js +228 -0
- package/lib/core/sdam/server.js +467 -0
- package/lib/core/sdam/server_description.js +163 -0
- package/lib/core/sdam/server_selectors.js +244 -0
- package/lib/core/sdam/srv_polling.js +135 -0
- package/lib/core/sdam/topology.js +1151 -0
- package/lib/core/sdam/topology_description.js +408 -0
- package/lib/core/sessions.js +711 -0
- package/lib/core/tools/smoke_plugin.js +61 -0
- package/lib/core/topologies/mongos.js +1337 -0
- package/lib/core/topologies/read_preference.js +202 -0
- package/lib/core/topologies/replset.js +1507 -0
- package/lib/core/topologies/replset_state.js +1121 -0
- package/lib/core/topologies/server.js +984 -0
- package/lib/core/topologies/shared.js +453 -0
- package/lib/core/transactions.js +167 -0
- package/lib/core/uri_parser.js +631 -0
- package/lib/core/utils.js +165 -0
- package/lib/core/wireprotocol/command.js +170 -0
- package/lib/core/wireprotocol/compression.js +73 -0
- package/lib/core/wireprotocol/constants.js +13 -0
- package/lib/core/wireprotocol/get_more.js +86 -0
- package/lib/core/wireprotocol/index.js +18 -0
- package/lib/core/wireprotocol/kill_cursors.js +70 -0
- package/lib/core/wireprotocol/query.js +224 -0
- package/lib/core/wireprotocol/shared.js +115 -0
- package/lib/core/wireprotocol/write_command.js +50 -0
- package/lib/cursor.js +40 -46
- package/lib/db.js +141 -95
- package/lib/dynamic_loaders.js +32 -0
- package/lib/error.js +12 -10
- package/lib/gridfs/chunk.js +2 -2
- package/lib/gridfs/grid_store.js +31 -25
- package/lib/gridfs-stream/index.js +4 -4
- package/lib/gridfs-stream/upload.js +1 -1
- package/lib/mongo_client.js +37 -15
- package/lib/operations/add_user.js +96 -0
- package/lib/operations/aggregate.js +24 -13
- package/lib/operations/aggregate_operation.js +127 -0
- package/lib/operations/bulk_write.js +104 -0
- package/lib/operations/close.js +47 -0
- package/lib/operations/collection_ops.js +28 -287
- package/lib/operations/collections.js +55 -0
- package/lib/operations/command.js +120 -0
- package/lib/operations/command_v2.js +43 -0
- package/lib/operations/common_functions.js +372 -0
- package/lib/operations/{mongo_client_ops.js → connect.js} +185 -157
- package/lib/operations/count.js +72 -0
- package/lib/operations/count_documents.js +46 -0
- package/lib/operations/create_collection.js +118 -0
- package/lib/operations/create_index.js +92 -0
- package/lib/operations/create_indexes.js +61 -0
- package/lib/operations/cursor_ops.js +3 -4
- package/lib/operations/db_ops.js +15 -12
- package/lib/operations/delete_many.js +25 -0
- package/lib/operations/delete_one.js +25 -0
- package/lib/operations/distinct.js +85 -0
- package/lib/operations/drop.js +53 -0
- package/lib/operations/drop_index.js +42 -0
- package/lib/operations/drop_indexes.js +23 -0
- package/lib/operations/estimated_document_count.js +33 -0
- package/lib/operations/execute_db_admin_command.js +34 -0
- package/lib/operations/execute_operation.js +165 -0
- package/lib/operations/explain.js +23 -0
- package/lib/operations/find_and_modify.js +98 -0
- package/lib/operations/find_one.js +33 -0
- package/lib/operations/find_one_and_delete.js +16 -0
- package/lib/operations/find_one_and_replace.js +18 -0
- package/lib/operations/find_one_and_update.js +19 -0
- package/lib/operations/geo_haystack_search.js +79 -0
- package/lib/operations/has_next.js +40 -0
- package/lib/operations/index_exists.js +39 -0
- package/lib/operations/index_information.js +23 -0
- package/lib/operations/indexes.js +22 -0
- package/lib/operations/insert_many.js +63 -0
- package/lib/operations/insert_one.js +75 -0
- package/lib/operations/is_capped.js +19 -0
- package/lib/operations/list_indexes.js +66 -0
- package/lib/operations/map_reduce.js +189 -0
- package/lib/operations/next.js +32 -0
- package/lib/operations/operation.js +63 -0
- package/lib/operations/options_operation.js +32 -0
- package/lib/operations/profiling_level.js +31 -0
- package/lib/operations/re_index.js +28 -0
- package/lib/operations/remove_user.js +52 -0
- package/lib/operations/rename.js +61 -0
- package/lib/operations/replace_one.js +47 -0
- package/lib/operations/set_profiling_level.js +48 -0
- package/lib/operations/stats.js +45 -0
- package/lib/operations/to_array.js +68 -0
- package/lib/operations/update_many.js +29 -0
- package/lib/operations/update_one.js +44 -0
- package/lib/operations/validate_collection.js +40 -0
- package/lib/read_concern.js +55 -0
- package/lib/topologies/mongos.js +3 -3
- package/lib/topologies/native_topology.js +22 -2
- package/lib/topologies/replset.js +3 -3
- package/lib/topologies/server.js +4 -4
- package/lib/topologies/topology_base.js +6 -6
- package/lib/url_parser.js +4 -3
- package/lib/utils.js +46 -59
- package/lib/write_concern.js +66 -0
- package/package.json +15 -6
- package/lib/.DS_Store +0 -0
|
@@ -0,0 +1,453 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const os = require('os');
|
|
4
|
+
const f = require('util').format;
|
|
5
|
+
const ReadPreference = require('./read_preference');
|
|
6
|
+
const Buffer = require('safe-buffer').Buffer;
|
|
7
|
+
const TopologyType = require('../sdam/topology_description').TopologyType;
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Emit event if it exists
|
|
11
|
+
* @method
|
|
12
|
+
*/
|
|
13
|
+
function emitSDAMEvent(self, event, description) {
|
|
14
|
+
if (self.listeners(event).length > 0) {
|
|
15
|
+
self.emit(event, description);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// Get package.json variable
|
|
20
|
+
var driverVersion = require('../../../package.json').version;
|
|
21
|
+
var nodejsversion = f('Node.js %s, %s', process.version, os.endianness());
|
|
22
|
+
var type = os.type();
|
|
23
|
+
var name = process.platform;
|
|
24
|
+
var architecture = process.arch;
|
|
25
|
+
var release = os.release();
|
|
26
|
+
|
|
27
|
+
function createClientInfo(options) {
|
|
28
|
+
// Build default client information
|
|
29
|
+
var clientInfo = options.clientInfo
|
|
30
|
+
? clone(options.clientInfo)
|
|
31
|
+
: {
|
|
32
|
+
driver: {
|
|
33
|
+
name: 'nodejs-core',
|
|
34
|
+
version: driverVersion
|
|
35
|
+
},
|
|
36
|
+
os: {
|
|
37
|
+
type: type,
|
|
38
|
+
name: name,
|
|
39
|
+
architecture: architecture,
|
|
40
|
+
version: release
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
// Is platform specified
|
|
45
|
+
if (clientInfo.platform && clientInfo.platform.indexOf('mongodb-core') === -1) {
|
|
46
|
+
clientInfo.platform = f('%s, mongodb-core: %s', clientInfo.platform, driverVersion);
|
|
47
|
+
} else if (!clientInfo.platform) {
|
|
48
|
+
clientInfo.platform = nodejsversion;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Do we have an application specific string
|
|
52
|
+
if (options.appname) {
|
|
53
|
+
// Cut at 128 bytes
|
|
54
|
+
var buffer = Buffer.from(options.appname);
|
|
55
|
+
// Return the truncated appname
|
|
56
|
+
var appname = buffer.length > 128 ? buffer.slice(0, 128).toString('utf8') : options.appname;
|
|
57
|
+
// Add to the clientInfo
|
|
58
|
+
clientInfo.application = { name: appname };
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return clientInfo;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function createCompressionInfo(options) {
|
|
65
|
+
if (!options.compression || !options.compression.compressors) {
|
|
66
|
+
return [];
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// Check that all supplied compressors are valid
|
|
70
|
+
options.compression.compressors.forEach(function(compressor) {
|
|
71
|
+
if (compressor !== 'snappy' && compressor !== 'zlib') {
|
|
72
|
+
throw new Error('compressors must be at least one of snappy or zlib');
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
return options.compression.compressors;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function clone(object) {
|
|
80
|
+
return JSON.parse(JSON.stringify(object));
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
var getPreviousDescription = function(self) {
|
|
84
|
+
if (!self.s.serverDescription) {
|
|
85
|
+
self.s.serverDescription = {
|
|
86
|
+
address: self.name,
|
|
87
|
+
arbiters: [],
|
|
88
|
+
hosts: [],
|
|
89
|
+
passives: [],
|
|
90
|
+
type: 'Unknown'
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
return self.s.serverDescription;
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
var emitServerDescriptionChanged = function(self, description) {
|
|
98
|
+
if (self.listeners('serverDescriptionChanged').length > 0) {
|
|
99
|
+
// Emit the server description changed events
|
|
100
|
+
self.emit('serverDescriptionChanged', {
|
|
101
|
+
topologyId: self.s.topologyId !== -1 ? self.s.topologyId : self.id,
|
|
102
|
+
address: self.name,
|
|
103
|
+
previousDescription: getPreviousDescription(self),
|
|
104
|
+
newDescription: description
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
self.s.serverDescription = description;
|
|
108
|
+
}
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
var getPreviousTopologyDescription = function(self) {
|
|
112
|
+
if (!self.s.topologyDescription) {
|
|
113
|
+
self.s.topologyDescription = {
|
|
114
|
+
topologyType: 'Unknown',
|
|
115
|
+
servers: [
|
|
116
|
+
{
|
|
117
|
+
address: self.name,
|
|
118
|
+
arbiters: [],
|
|
119
|
+
hosts: [],
|
|
120
|
+
passives: [],
|
|
121
|
+
type: 'Unknown'
|
|
122
|
+
}
|
|
123
|
+
]
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
return self.s.topologyDescription;
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
var emitTopologyDescriptionChanged = function(self, description) {
|
|
131
|
+
if (self.listeners('topologyDescriptionChanged').length > 0) {
|
|
132
|
+
// Emit the server description changed events
|
|
133
|
+
self.emit('topologyDescriptionChanged', {
|
|
134
|
+
topologyId: self.s.topologyId !== -1 ? self.s.topologyId : self.id,
|
|
135
|
+
address: self.name,
|
|
136
|
+
previousDescription: getPreviousTopologyDescription(self),
|
|
137
|
+
newDescription: description
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
self.s.serverDescription = description;
|
|
141
|
+
}
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
var changedIsMaster = function(self, currentIsmaster, ismaster) {
|
|
145
|
+
var currentType = getTopologyType(self, currentIsmaster);
|
|
146
|
+
var newType = getTopologyType(self, ismaster);
|
|
147
|
+
if (newType !== currentType) return true;
|
|
148
|
+
return false;
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
var getTopologyType = function(self, ismaster) {
|
|
152
|
+
if (!ismaster) {
|
|
153
|
+
ismaster = self.ismaster;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
if (!ismaster) return 'Unknown';
|
|
157
|
+
if (ismaster.ismaster && ismaster.msg === 'isdbgrid') return 'Mongos';
|
|
158
|
+
if (ismaster.ismaster && !ismaster.hosts) return 'Standalone';
|
|
159
|
+
if (ismaster.ismaster) return 'RSPrimary';
|
|
160
|
+
if (ismaster.secondary) return 'RSSecondary';
|
|
161
|
+
if (ismaster.arbiterOnly) return 'RSArbiter';
|
|
162
|
+
return 'Unknown';
|
|
163
|
+
};
|
|
164
|
+
|
|
165
|
+
var inquireServerState = function(self) {
|
|
166
|
+
return function(callback) {
|
|
167
|
+
if (self.s.state === 'destroyed') return;
|
|
168
|
+
// Record response time
|
|
169
|
+
var start = new Date().getTime();
|
|
170
|
+
|
|
171
|
+
// emitSDAMEvent
|
|
172
|
+
emitSDAMEvent(self, 'serverHeartbeatStarted', { connectionId: self.name });
|
|
173
|
+
|
|
174
|
+
// Attempt to execute ismaster command
|
|
175
|
+
self.command('admin.$cmd', { ismaster: true }, { monitoring: true }, function(err, r) {
|
|
176
|
+
if (!err) {
|
|
177
|
+
// Legacy event sender
|
|
178
|
+
self.emit('ismaster', r, self);
|
|
179
|
+
|
|
180
|
+
// Calculate latencyMS
|
|
181
|
+
var latencyMS = new Date().getTime() - start;
|
|
182
|
+
|
|
183
|
+
// Server heart beat event
|
|
184
|
+
emitSDAMEvent(self, 'serverHeartbeatSucceeded', {
|
|
185
|
+
durationMS: latencyMS,
|
|
186
|
+
reply: r.result,
|
|
187
|
+
connectionId: self.name
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
// Did the server change
|
|
191
|
+
if (changedIsMaster(self, self.s.ismaster, r.result)) {
|
|
192
|
+
// Emit server description changed if something listening
|
|
193
|
+
emitServerDescriptionChanged(self, {
|
|
194
|
+
address: self.name,
|
|
195
|
+
arbiters: [],
|
|
196
|
+
hosts: [],
|
|
197
|
+
passives: [],
|
|
198
|
+
type: !self.s.inTopology ? 'Standalone' : getTopologyType(self)
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// Updat ismaster view
|
|
203
|
+
self.s.ismaster = r.result;
|
|
204
|
+
|
|
205
|
+
// Set server response time
|
|
206
|
+
self.s.isMasterLatencyMS = latencyMS;
|
|
207
|
+
} else {
|
|
208
|
+
emitSDAMEvent(self, 'serverHeartbeatFailed', {
|
|
209
|
+
durationMS: latencyMS,
|
|
210
|
+
failure: err,
|
|
211
|
+
connectionId: self.name
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// Peforming an ismaster monitoring callback operation
|
|
216
|
+
if (typeof callback === 'function') {
|
|
217
|
+
return callback(err, r);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
// Perform another sweep
|
|
221
|
+
self.s.inquireServerStateTimeout = setTimeout(inquireServerState(self), self.s.haInterval);
|
|
222
|
+
});
|
|
223
|
+
};
|
|
224
|
+
};
|
|
225
|
+
|
|
226
|
+
//
|
|
227
|
+
// Clone the options
|
|
228
|
+
var cloneOptions = function(options) {
|
|
229
|
+
var opts = {};
|
|
230
|
+
for (var name in options) {
|
|
231
|
+
opts[name] = options[name];
|
|
232
|
+
}
|
|
233
|
+
return opts;
|
|
234
|
+
};
|
|
235
|
+
|
|
236
|
+
function Interval(fn, time) {
|
|
237
|
+
var timer = false;
|
|
238
|
+
|
|
239
|
+
this.start = function() {
|
|
240
|
+
if (!this.isRunning()) {
|
|
241
|
+
timer = setInterval(fn, time);
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
return this;
|
|
245
|
+
};
|
|
246
|
+
|
|
247
|
+
this.stop = function() {
|
|
248
|
+
clearInterval(timer);
|
|
249
|
+
timer = false;
|
|
250
|
+
return this;
|
|
251
|
+
};
|
|
252
|
+
|
|
253
|
+
this.isRunning = function() {
|
|
254
|
+
return timer !== false;
|
|
255
|
+
};
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
function Timeout(fn, time) {
|
|
259
|
+
var timer = false;
|
|
260
|
+
|
|
261
|
+
this.start = function() {
|
|
262
|
+
if (!this.isRunning()) {
|
|
263
|
+
timer = setTimeout(fn, time);
|
|
264
|
+
}
|
|
265
|
+
return this;
|
|
266
|
+
};
|
|
267
|
+
|
|
268
|
+
this.stop = function() {
|
|
269
|
+
clearTimeout(timer);
|
|
270
|
+
timer = false;
|
|
271
|
+
return this;
|
|
272
|
+
};
|
|
273
|
+
|
|
274
|
+
this.isRunning = function() {
|
|
275
|
+
if (timer && timer._called) return false;
|
|
276
|
+
return timer !== false;
|
|
277
|
+
};
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
function diff(previous, current) {
|
|
281
|
+
// Difference document
|
|
282
|
+
var diff = {
|
|
283
|
+
servers: []
|
|
284
|
+
};
|
|
285
|
+
|
|
286
|
+
// Previous entry
|
|
287
|
+
if (!previous) {
|
|
288
|
+
previous = { servers: [] };
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
// Check if we have any previous servers missing in the current ones
|
|
292
|
+
for (var i = 0; i < previous.servers.length; i++) {
|
|
293
|
+
var found = false;
|
|
294
|
+
|
|
295
|
+
for (var j = 0; j < current.servers.length; j++) {
|
|
296
|
+
if (current.servers[j].address.toLowerCase() === previous.servers[i].address.toLowerCase()) {
|
|
297
|
+
found = true;
|
|
298
|
+
break;
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
if (!found) {
|
|
303
|
+
// Add to the diff
|
|
304
|
+
diff.servers.push({
|
|
305
|
+
address: previous.servers[i].address,
|
|
306
|
+
from: previous.servers[i].type,
|
|
307
|
+
to: 'Unknown'
|
|
308
|
+
});
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
// Check if there are any severs that don't exist
|
|
313
|
+
for (j = 0; j < current.servers.length; j++) {
|
|
314
|
+
found = false;
|
|
315
|
+
|
|
316
|
+
// Go over all the previous servers
|
|
317
|
+
for (i = 0; i < previous.servers.length; i++) {
|
|
318
|
+
if (previous.servers[i].address.toLowerCase() === current.servers[j].address.toLowerCase()) {
|
|
319
|
+
found = true;
|
|
320
|
+
break;
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
// Add the server to the diff
|
|
325
|
+
if (!found) {
|
|
326
|
+
diff.servers.push({
|
|
327
|
+
address: current.servers[j].address,
|
|
328
|
+
from: 'Unknown',
|
|
329
|
+
to: current.servers[j].type
|
|
330
|
+
});
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
// Got through all the servers
|
|
335
|
+
for (i = 0; i < previous.servers.length; i++) {
|
|
336
|
+
var prevServer = previous.servers[i];
|
|
337
|
+
|
|
338
|
+
// Go through all current servers
|
|
339
|
+
for (j = 0; j < current.servers.length; j++) {
|
|
340
|
+
var currServer = current.servers[j];
|
|
341
|
+
|
|
342
|
+
// Matching server
|
|
343
|
+
if (prevServer.address.toLowerCase() === currServer.address.toLowerCase()) {
|
|
344
|
+
// We had a change in state
|
|
345
|
+
if (prevServer.type !== currServer.type) {
|
|
346
|
+
diff.servers.push({
|
|
347
|
+
address: prevServer.address,
|
|
348
|
+
from: prevServer.type,
|
|
349
|
+
to: currServer.type
|
|
350
|
+
});
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
// Return difference
|
|
357
|
+
return diff;
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
/**
|
|
361
|
+
* Shared function to determine clusterTime for a given topology
|
|
362
|
+
*
|
|
363
|
+
* @param {*} topology
|
|
364
|
+
* @param {*} clusterTime
|
|
365
|
+
*/
|
|
366
|
+
function resolveClusterTime(topology, $clusterTime) {
|
|
367
|
+
if (topology.clusterTime == null) {
|
|
368
|
+
topology.clusterTime = $clusterTime;
|
|
369
|
+
} else {
|
|
370
|
+
if ($clusterTime.clusterTime.greaterThan(topology.clusterTime.clusterTime)) {
|
|
371
|
+
topology.clusterTime = $clusterTime;
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
// NOTE: this is a temporary move until the topologies can be more formally refactored
|
|
377
|
+
// to share code.
|
|
378
|
+
const SessionMixins = {
|
|
379
|
+
endSessions: function(sessions, callback) {
|
|
380
|
+
if (!Array.isArray(sessions)) {
|
|
381
|
+
sessions = [sessions];
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
// TODO:
|
|
385
|
+
// When connected to a sharded cluster the endSessions command
|
|
386
|
+
// can be sent to any mongos. When connected to a replica set the
|
|
387
|
+
// endSessions command MUST be sent to the primary if the primary
|
|
388
|
+
// is available, otherwise it MUST be sent to any available secondary.
|
|
389
|
+
// Is it enough to use: ReadPreference.primaryPreferred ?
|
|
390
|
+
this.command(
|
|
391
|
+
'admin.$cmd',
|
|
392
|
+
{ endSessions: sessions },
|
|
393
|
+
{ readPreference: ReadPreference.primaryPreferred },
|
|
394
|
+
() => {
|
|
395
|
+
// intentionally ignored, per spec
|
|
396
|
+
if (typeof callback === 'function') callback();
|
|
397
|
+
}
|
|
398
|
+
);
|
|
399
|
+
}
|
|
400
|
+
};
|
|
401
|
+
|
|
402
|
+
function topologyType(topology) {
|
|
403
|
+
if (topology.description) {
|
|
404
|
+
return topology.description.type;
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
if (topology.type === 'mongos') {
|
|
408
|
+
return TopologyType.Sharded;
|
|
409
|
+
} else if (topology.type === 'replset') {
|
|
410
|
+
return TopologyType.ReplicaSetWithPrimary;
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
return TopologyType.Single;
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
const RETRYABLE_WIRE_VERSION = 6;
|
|
417
|
+
|
|
418
|
+
/**
|
|
419
|
+
* Determines whether the provided topology supports retryable writes
|
|
420
|
+
*
|
|
421
|
+
* @param {Mongos|Replset} topology
|
|
422
|
+
*/
|
|
423
|
+
const isRetryableWritesSupported = function(topology) {
|
|
424
|
+
const maxWireVersion = topology.lastIsMaster().maxWireVersion;
|
|
425
|
+
if (maxWireVersion < RETRYABLE_WIRE_VERSION) {
|
|
426
|
+
return false;
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
if (!topology.logicalSessionTimeoutMinutes) {
|
|
430
|
+
return false;
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
if (topologyType(topology) === TopologyType.Single) {
|
|
434
|
+
return false;
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
return true;
|
|
438
|
+
};
|
|
439
|
+
|
|
440
|
+
module.exports.SessionMixins = SessionMixins;
|
|
441
|
+
module.exports.resolveClusterTime = resolveClusterTime;
|
|
442
|
+
module.exports.inquireServerState = inquireServerState;
|
|
443
|
+
module.exports.getTopologyType = getTopologyType;
|
|
444
|
+
module.exports.emitServerDescriptionChanged = emitServerDescriptionChanged;
|
|
445
|
+
module.exports.emitTopologyDescriptionChanged = emitTopologyDescriptionChanged;
|
|
446
|
+
module.exports.cloneOptions = cloneOptions;
|
|
447
|
+
module.exports.createClientInfo = createClientInfo;
|
|
448
|
+
module.exports.createCompressionInfo = createCompressionInfo;
|
|
449
|
+
module.exports.clone = clone;
|
|
450
|
+
module.exports.diff = diff;
|
|
451
|
+
module.exports.Interval = Interval;
|
|
452
|
+
module.exports.Timeout = Timeout;
|
|
453
|
+
module.exports.isRetryableWritesSupported = isRetryableWritesSupported;
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
const MongoError = require('./error').MongoError;
|
|
3
|
+
|
|
4
|
+
let TxnState;
|
|
5
|
+
let stateMachine;
|
|
6
|
+
|
|
7
|
+
(() => {
|
|
8
|
+
const NO_TRANSACTION = 'NO_TRANSACTION';
|
|
9
|
+
const STARTING_TRANSACTION = 'STARTING_TRANSACTION';
|
|
10
|
+
const TRANSACTION_IN_PROGRESS = 'TRANSACTION_IN_PROGRESS';
|
|
11
|
+
const TRANSACTION_COMMITTED = 'TRANSACTION_COMMITTED';
|
|
12
|
+
const TRANSACTION_COMMITTED_EMPTY = 'TRANSACTION_COMMITTED_EMPTY';
|
|
13
|
+
const TRANSACTION_ABORTED = 'TRANSACTION_ABORTED';
|
|
14
|
+
|
|
15
|
+
TxnState = {
|
|
16
|
+
NO_TRANSACTION,
|
|
17
|
+
STARTING_TRANSACTION,
|
|
18
|
+
TRANSACTION_IN_PROGRESS,
|
|
19
|
+
TRANSACTION_COMMITTED,
|
|
20
|
+
TRANSACTION_COMMITTED_EMPTY,
|
|
21
|
+
TRANSACTION_ABORTED
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
stateMachine = {
|
|
25
|
+
[NO_TRANSACTION]: [NO_TRANSACTION, STARTING_TRANSACTION],
|
|
26
|
+
[STARTING_TRANSACTION]: [
|
|
27
|
+
TRANSACTION_IN_PROGRESS,
|
|
28
|
+
TRANSACTION_COMMITTED,
|
|
29
|
+
TRANSACTION_COMMITTED_EMPTY,
|
|
30
|
+
TRANSACTION_ABORTED
|
|
31
|
+
],
|
|
32
|
+
[TRANSACTION_IN_PROGRESS]: [
|
|
33
|
+
TRANSACTION_IN_PROGRESS,
|
|
34
|
+
TRANSACTION_COMMITTED,
|
|
35
|
+
TRANSACTION_ABORTED
|
|
36
|
+
],
|
|
37
|
+
[TRANSACTION_COMMITTED]: [
|
|
38
|
+
TRANSACTION_COMMITTED,
|
|
39
|
+
TRANSACTION_COMMITTED_EMPTY,
|
|
40
|
+
STARTING_TRANSACTION,
|
|
41
|
+
NO_TRANSACTION
|
|
42
|
+
],
|
|
43
|
+
[TRANSACTION_ABORTED]: [STARTING_TRANSACTION, NO_TRANSACTION],
|
|
44
|
+
[TRANSACTION_COMMITTED_EMPTY]: [TRANSACTION_COMMITTED_EMPTY, NO_TRANSACTION]
|
|
45
|
+
};
|
|
46
|
+
})();
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* The MongoDB ReadConcern, which allows for control of the consistency and isolation properties
|
|
50
|
+
* of the data read from replica sets and replica set shards.
|
|
51
|
+
* @typedef {Object} ReadConcern
|
|
52
|
+
* @property {'local'|'available'|'majority'|'linearizable'|'snapshot'} level The readConcern Level
|
|
53
|
+
* @see https://docs.mongodb.com/manual/reference/read-concern/
|
|
54
|
+
*/
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* A MongoDB WriteConcern, which describes the level of acknowledgement
|
|
58
|
+
* requested from MongoDB for write operations.
|
|
59
|
+
* @typedef {Object} WriteConcern
|
|
60
|
+
* @property {number|'majority'|string} [w=1] requests acknowledgement that the write operation has
|
|
61
|
+
* propagated to a specified number of mongod hosts
|
|
62
|
+
* @property {boolean} [j=false] requests acknowledgement from MongoDB that the write operation has
|
|
63
|
+
* been written to the journal
|
|
64
|
+
* @property {number} [wtimeout] a time limit, in milliseconds, for the write concern
|
|
65
|
+
* @see https://docs.mongodb.com/manual/reference/write-concern/
|
|
66
|
+
*/
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Configuration options for a transaction.
|
|
70
|
+
* @typedef {Object} TransactionOptions
|
|
71
|
+
* @property {ReadConcern} [readConcern] A default read concern for commands in this transaction
|
|
72
|
+
* @property {WriteConcern} [writeConcern] A default writeConcern for commands in this transaction
|
|
73
|
+
* @property {ReadPreference} [readPreference] A default read preference for commands in this transaction
|
|
74
|
+
*/
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* A class maintaining state related to a server transaction. Internal Only
|
|
78
|
+
* @ignore
|
|
79
|
+
*/
|
|
80
|
+
class Transaction {
|
|
81
|
+
/**
|
|
82
|
+
* Create a transaction
|
|
83
|
+
*
|
|
84
|
+
* @ignore
|
|
85
|
+
* @param {TransactionOptions} [options] Optional settings
|
|
86
|
+
*/
|
|
87
|
+
constructor(options) {
|
|
88
|
+
options = options || {};
|
|
89
|
+
|
|
90
|
+
this.state = TxnState.NO_TRANSACTION;
|
|
91
|
+
this.options = {};
|
|
92
|
+
|
|
93
|
+
if (options.writeConcern || typeof options.w !== 'undefined') {
|
|
94
|
+
const w = options.writeConcern ? options.writeConcern.w : options.w;
|
|
95
|
+
if (w <= 0) {
|
|
96
|
+
throw new MongoError('Transactions do not support unacknowledged write concern');
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
this.options.writeConcern = options.writeConcern ? options.writeConcern : { w: options.w };
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
if (options.readConcern) this.options.readConcern = options.readConcern;
|
|
103
|
+
if (options.readPreference) this.options.readPreference = options.readPreference;
|
|
104
|
+
|
|
105
|
+
// TODO: This isn't technically necessary
|
|
106
|
+
this._pinnedServer = undefined;
|
|
107
|
+
this._recoveryToken = undefined;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
get server() {
|
|
111
|
+
return this._pinnedServer;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
get recoveryToken() {
|
|
115
|
+
return this._recoveryToken;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
get isPinned() {
|
|
119
|
+
return !!this.server;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* @ignore
|
|
124
|
+
* @return Whether this session is presently in a transaction
|
|
125
|
+
*/
|
|
126
|
+
get isActive() {
|
|
127
|
+
return (
|
|
128
|
+
[TxnState.STARTING_TRANSACTION, TxnState.TRANSACTION_IN_PROGRESS].indexOf(this.state) !== -1
|
|
129
|
+
);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Transition the transaction in the state machine
|
|
134
|
+
* @ignore
|
|
135
|
+
* @param {TxnState} state The new state to transition to
|
|
136
|
+
*/
|
|
137
|
+
transition(nextState) {
|
|
138
|
+
const nextStates = stateMachine[this.state];
|
|
139
|
+
if (nextStates && nextStates.indexOf(nextState) !== -1) {
|
|
140
|
+
this.state = nextState;
|
|
141
|
+
if (this.state === TxnState.NO_TRANSACTION || this.state === TxnState.STARTING_TRANSACTION) {
|
|
142
|
+
this.unpinServer();
|
|
143
|
+
}
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
throw new MongoError(
|
|
148
|
+
`Attempted illegal state transition from [${this.state}] to [${nextState}]`
|
|
149
|
+
);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
pinServer(server) {
|
|
153
|
+
if (this.isActive) {
|
|
154
|
+
this._pinnedServer = server;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
unpinServer() {
|
|
159
|
+
this._pinnedServer = undefined;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
function isTransactionCommand(command) {
|
|
164
|
+
return !!(command.commitTransaction || command.abortTransaction);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
module.exports = { TxnState, Transaction, isTransactionCommand };
|