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,1121 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var inherits = require('util').inherits,
|
|
4
|
+
f = require('util').format,
|
|
5
|
+
diff = require('./shared').diff,
|
|
6
|
+
EventEmitter = require('events').EventEmitter,
|
|
7
|
+
Logger = require('../connection/logger'),
|
|
8
|
+
ReadPreference = require('./read_preference'),
|
|
9
|
+
MongoError = require('../error').MongoError,
|
|
10
|
+
Buffer = require('safe-buffer').Buffer;
|
|
11
|
+
|
|
12
|
+
var TopologyType = {
|
|
13
|
+
Single: 'Single',
|
|
14
|
+
ReplicaSetNoPrimary: 'ReplicaSetNoPrimary',
|
|
15
|
+
ReplicaSetWithPrimary: 'ReplicaSetWithPrimary',
|
|
16
|
+
Sharded: 'Sharded',
|
|
17
|
+
Unknown: 'Unknown'
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
var ServerType = {
|
|
21
|
+
Standalone: 'Standalone',
|
|
22
|
+
Mongos: 'Mongos',
|
|
23
|
+
PossiblePrimary: 'PossiblePrimary',
|
|
24
|
+
RSPrimary: 'RSPrimary',
|
|
25
|
+
RSSecondary: 'RSSecondary',
|
|
26
|
+
RSArbiter: 'RSArbiter',
|
|
27
|
+
RSOther: 'RSOther',
|
|
28
|
+
RSGhost: 'RSGhost',
|
|
29
|
+
Unknown: 'Unknown'
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
var ReplSetState = function(options) {
|
|
33
|
+
options = options || {};
|
|
34
|
+
// Add event listener
|
|
35
|
+
EventEmitter.call(this);
|
|
36
|
+
// Topology state
|
|
37
|
+
this.topologyType = TopologyType.ReplicaSetNoPrimary;
|
|
38
|
+
this.setName = options.setName;
|
|
39
|
+
|
|
40
|
+
// Server set
|
|
41
|
+
this.set = {};
|
|
42
|
+
|
|
43
|
+
// Unpacked options
|
|
44
|
+
this.id = options.id;
|
|
45
|
+
this.setName = options.setName;
|
|
46
|
+
|
|
47
|
+
// Replicaset logger
|
|
48
|
+
this.logger = options.logger || Logger('ReplSet', options);
|
|
49
|
+
|
|
50
|
+
// Server selection index
|
|
51
|
+
this.index = 0;
|
|
52
|
+
// Acceptable latency
|
|
53
|
+
this.acceptableLatency = options.acceptableLatency || 15;
|
|
54
|
+
|
|
55
|
+
// heartbeatFrequencyMS
|
|
56
|
+
this.heartbeatFrequencyMS = options.heartbeatFrequencyMS || 10000;
|
|
57
|
+
|
|
58
|
+
// Server side
|
|
59
|
+
this.primary = null;
|
|
60
|
+
this.secondaries = [];
|
|
61
|
+
this.arbiters = [];
|
|
62
|
+
this.passives = [];
|
|
63
|
+
this.ghosts = [];
|
|
64
|
+
// Current unknown hosts
|
|
65
|
+
this.unknownServers = [];
|
|
66
|
+
// In set status
|
|
67
|
+
this.set = {};
|
|
68
|
+
// Status
|
|
69
|
+
this.maxElectionId = null;
|
|
70
|
+
this.maxSetVersion = 0;
|
|
71
|
+
// Description of the Replicaset
|
|
72
|
+
this.replicasetDescription = {
|
|
73
|
+
topologyType: 'Unknown',
|
|
74
|
+
servers: []
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
this.logicalSessionTimeoutMinutes = undefined;
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
inherits(ReplSetState, EventEmitter);
|
|
81
|
+
|
|
82
|
+
ReplSetState.prototype.hasPrimaryAndSecondary = function() {
|
|
83
|
+
return this.primary != null && this.secondaries.length > 0;
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
ReplSetState.prototype.hasPrimaryOrSecondary = function() {
|
|
87
|
+
return this.hasPrimary() || this.hasSecondary();
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
ReplSetState.prototype.hasPrimary = function() {
|
|
91
|
+
return this.primary != null;
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
ReplSetState.prototype.hasSecondary = function() {
|
|
95
|
+
return this.secondaries.length > 0;
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
ReplSetState.prototype.get = function(host) {
|
|
99
|
+
var servers = this.allServers();
|
|
100
|
+
|
|
101
|
+
for (var i = 0; i < servers.length; i++) {
|
|
102
|
+
if (servers[i].name.toLowerCase() === host.toLowerCase()) {
|
|
103
|
+
return servers[i];
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
return null;
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
ReplSetState.prototype.allServers = function(options) {
|
|
111
|
+
options = options || {};
|
|
112
|
+
var servers = this.primary ? [this.primary] : [];
|
|
113
|
+
servers = servers.concat(this.secondaries);
|
|
114
|
+
if (!options.ignoreArbiters) servers = servers.concat(this.arbiters);
|
|
115
|
+
servers = servers.concat(this.passives);
|
|
116
|
+
return servers;
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
ReplSetState.prototype.destroy = function(options, callback) {
|
|
120
|
+
const serversToDestroy = this.secondaries
|
|
121
|
+
.concat(this.arbiters)
|
|
122
|
+
.concat(this.passives)
|
|
123
|
+
.concat(this.ghosts);
|
|
124
|
+
if (this.primary) serversToDestroy.push(this.primary);
|
|
125
|
+
|
|
126
|
+
let serverCount = serversToDestroy.length;
|
|
127
|
+
const serverDestroyed = () => {
|
|
128
|
+
serverCount--;
|
|
129
|
+
if (serverCount > 0) {
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// Clear out the complete state
|
|
134
|
+
this.secondaries = [];
|
|
135
|
+
this.arbiters = [];
|
|
136
|
+
this.passives = [];
|
|
137
|
+
this.ghosts = [];
|
|
138
|
+
this.unknownServers = [];
|
|
139
|
+
this.set = {};
|
|
140
|
+
this.primary = null;
|
|
141
|
+
|
|
142
|
+
// Emit the topology changed
|
|
143
|
+
emitTopologyDescriptionChanged(this);
|
|
144
|
+
|
|
145
|
+
if (typeof callback === 'function') {
|
|
146
|
+
callback(null, null);
|
|
147
|
+
}
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
if (serverCount === 0) {
|
|
151
|
+
serverDestroyed();
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
serversToDestroy.forEach(server => server.destroy(options, serverDestroyed));
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
ReplSetState.prototype.remove = function(server, options) {
|
|
159
|
+
options = options || {};
|
|
160
|
+
|
|
161
|
+
// Get the server name and lowerCase it
|
|
162
|
+
var serverName = server.name.toLowerCase();
|
|
163
|
+
|
|
164
|
+
// Only remove if the current server is not connected
|
|
165
|
+
var servers = this.primary ? [this.primary] : [];
|
|
166
|
+
servers = servers.concat(this.secondaries);
|
|
167
|
+
servers = servers.concat(this.arbiters);
|
|
168
|
+
servers = servers.concat(this.passives);
|
|
169
|
+
|
|
170
|
+
// Check if it's active and this is just a failed connection attempt
|
|
171
|
+
for (var i = 0; i < servers.length; i++) {
|
|
172
|
+
if (
|
|
173
|
+
!options.force &&
|
|
174
|
+
servers[i].equals(server) &&
|
|
175
|
+
servers[i].isConnected &&
|
|
176
|
+
servers[i].isConnected()
|
|
177
|
+
) {
|
|
178
|
+
return;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// If we have it in the set remove it
|
|
183
|
+
if (this.set[serverName]) {
|
|
184
|
+
this.set[serverName].type = ServerType.Unknown;
|
|
185
|
+
this.set[serverName].electionId = null;
|
|
186
|
+
this.set[serverName].setName = null;
|
|
187
|
+
this.set[serverName].setVersion = null;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// Remove type
|
|
191
|
+
var removeType = null;
|
|
192
|
+
|
|
193
|
+
// Remove from any lists
|
|
194
|
+
if (this.primary && this.primary.equals(server)) {
|
|
195
|
+
this.primary = null;
|
|
196
|
+
this.topologyType = TopologyType.ReplicaSetNoPrimary;
|
|
197
|
+
removeType = 'primary';
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// Remove from any other server lists
|
|
201
|
+
removeType = removeFrom(server, this.secondaries) ? 'secondary' : removeType;
|
|
202
|
+
removeType = removeFrom(server, this.arbiters) ? 'arbiter' : removeType;
|
|
203
|
+
removeType = removeFrom(server, this.passives) ? 'secondary' : removeType;
|
|
204
|
+
removeFrom(server, this.ghosts);
|
|
205
|
+
removeFrom(server, this.unknownServers);
|
|
206
|
+
|
|
207
|
+
// Push to unknownServers
|
|
208
|
+
this.unknownServers.push(serverName);
|
|
209
|
+
|
|
210
|
+
// Do we have a removeType
|
|
211
|
+
if (removeType) {
|
|
212
|
+
this.emit('left', removeType, server);
|
|
213
|
+
}
|
|
214
|
+
};
|
|
215
|
+
|
|
216
|
+
const isArbiter = ismaster => ismaster.arbiterOnly && ismaster.setName;
|
|
217
|
+
|
|
218
|
+
ReplSetState.prototype.update = function(server) {
|
|
219
|
+
var self = this;
|
|
220
|
+
// Get the current ismaster
|
|
221
|
+
var ismaster = server.lastIsMaster();
|
|
222
|
+
|
|
223
|
+
// Get the server name and lowerCase it
|
|
224
|
+
var serverName = server.name.toLowerCase();
|
|
225
|
+
|
|
226
|
+
//
|
|
227
|
+
// Add any hosts
|
|
228
|
+
//
|
|
229
|
+
if (ismaster) {
|
|
230
|
+
// Join all the possible new hosts
|
|
231
|
+
var hosts = Array.isArray(ismaster.hosts) ? ismaster.hosts : [];
|
|
232
|
+
hosts = hosts.concat(Array.isArray(ismaster.arbiters) ? ismaster.arbiters : []);
|
|
233
|
+
hosts = hosts.concat(Array.isArray(ismaster.passives) ? ismaster.passives : []);
|
|
234
|
+
hosts = hosts.map(function(s) {
|
|
235
|
+
return s.toLowerCase();
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
// Add all hosts as unknownServers
|
|
239
|
+
for (var i = 0; i < hosts.length; i++) {
|
|
240
|
+
// Add to the list of unknown server
|
|
241
|
+
if (
|
|
242
|
+
this.unknownServers.indexOf(hosts[i]) === -1 &&
|
|
243
|
+
(!this.set[hosts[i]] || this.set[hosts[i]].type === ServerType.Unknown)
|
|
244
|
+
) {
|
|
245
|
+
this.unknownServers.push(hosts[i].toLowerCase());
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
if (!this.set[hosts[i]]) {
|
|
249
|
+
this.set[hosts[i]] = {
|
|
250
|
+
type: ServerType.Unknown,
|
|
251
|
+
electionId: null,
|
|
252
|
+
setName: null,
|
|
253
|
+
setVersion: null
|
|
254
|
+
};
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
//
|
|
260
|
+
// Unknown server
|
|
261
|
+
//
|
|
262
|
+
if (!ismaster && !inList(ismaster, server, this.unknownServers)) {
|
|
263
|
+
self.set[serverName] = {
|
|
264
|
+
type: ServerType.Unknown,
|
|
265
|
+
setVersion: null,
|
|
266
|
+
electionId: null,
|
|
267
|
+
setName: null
|
|
268
|
+
};
|
|
269
|
+
// Update set information about the server instance
|
|
270
|
+
self.set[serverName].type = ServerType.Unknown;
|
|
271
|
+
self.set[serverName].electionId = ismaster ? ismaster.electionId : ismaster;
|
|
272
|
+
self.set[serverName].setName = ismaster ? ismaster.setName : ismaster;
|
|
273
|
+
self.set[serverName].setVersion = ismaster ? ismaster.setVersion : ismaster;
|
|
274
|
+
|
|
275
|
+
if (self.unknownServers.indexOf(server.name) === -1) {
|
|
276
|
+
self.unknownServers.push(serverName);
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
// Set the topology
|
|
280
|
+
return false;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
// Update logicalSessionTimeoutMinutes
|
|
284
|
+
if (ismaster.logicalSessionTimeoutMinutes !== undefined && !isArbiter(ismaster)) {
|
|
285
|
+
if (
|
|
286
|
+
self.logicalSessionTimeoutMinutes === undefined ||
|
|
287
|
+
ismaster.logicalSessionTimeoutMinutes === null
|
|
288
|
+
) {
|
|
289
|
+
self.logicalSessionTimeoutMinutes = ismaster.logicalSessionTimeoutMinutes;
|
|
290
|
+
} else {
|
|
291
|
+
self.logicalSessionTimeoutMinutes = Math.min(
|
|
292
|
+
self.logicalSessionTimeoutMinutes,
|
|
293
|
+
ismaster.logicalSessionTimeoutMinutes
|
|
294
|
+
);
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
//
|
|
299
|
+
// Is this a mongos
|
|
300
|
+
//
|
|
301
|
+
if (ismaster && ismaster.msg === 'isdbgrid') {
|
|
302
|
+
if (this.primary && this.primary.name === serverName) {
|
|
303
|
+
this.primary = null;
|
|
304
|
+
this.topologyType = TopologyType.ReplicaSetNoPrimary;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
return false;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
// A RSGhost instance
|
|
311
|
+
if (ismaster.isreplicaset) {
|
|
312
|
+
self.set[serverName] = {
|
|
313
|
+
type: ServerType.RSGhost,
|
|
314
|
+
setVersion: null,
|
|
315
|
+
electionId: null,
|
|
316
|
+
setName: ismaster.setName
|
|
317
|
+
};
|
|
318
|
+
|
|
319
|
+
if (this.primary && this.primary.name === serverName) {
|
|
320
|
+
this.primary = null;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
// Set the topology
|
|
324
|
+
this.topologyType = this.primary
|
|
325
|
+
? TopologyType.ReplicaSetWithPrimary
|
|
326
|
+
: TopologyType.ReplicaSetNoPrimary;
|
|
327
|
+
if (ismaster.setName) this.setName = ismaster.setName;
|
|
328
|
+
|
|
329
|
+
// Set the topology
|
|
330
|
+
return false;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
// A RSOther instance
|
|
334
|
+
if (
|
|
335
|
+
(ismaster.setName && ismaster.hidden) ||
|
|
336
|
+
(ismaster.setName &&
|
|
337
|
+
!ismaster.ismaster &&
|
|
338
|
+
!ismaster.secondary &&
|
|
339
|
+
!ismaster.arbiterOnly &&
|
|
340
|
+
!ismaster.passive)
|
|
341
|
+
) {
|
|
342
|
+
self.set[serverName] = {
|
|
343
|
+
type: ServerType.RSOther,
|
|
344
|
+
setVersion: null,
|
|
345
|
+
electionId: null,
|
|
346
|
+
setName: ismaster.setName
|
|
347
|
+
};
|
|
348
|
+
|
|
349
|
+
// Set the topology
|
|
350
|
+
this.topologyType = this.primary
|
|
351
|
+
? TopologyType.ReplicaSetWithPrimary
|
|
352
|
+
: TopologyType.ReplicaSetNoPrimary;
|
|
353
|
+
if (ismaster.setName) this.setName = ismaster.setName;
|
|
354
|
+
return false;
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
//
|
|
358
|
+
// Standalone server, destroy and return
|
|
359
|
+
//
|
|
360
|
+
if (ismaster && ismaster.ismaster && !ismaster.setName) {
|
|
361
|
+
this.topologyType = this.primary ? TopologyType.ReplicaSetWithPrimary : TopologyType.Unknown;
|
|
362
|
+
this.remove(server, { force: true });
|
|
363
|
+
return false;
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
//
|
|
367
|
+
// Server in maintanance mode
|
|
368
|
+
//
|
|
369
|
+
if (ismaster && !ismaster.ismaster && !ismaster.secondary && !ismaster.arbiterOnly) {
|
|
370
|
+
this.remove(server, { force: true });
|
|
371
|
+
return false;
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
//
|
|
375
|
+
// If the .me field does not match the passed in server
|
|
376
|
+
//
|
|
377
|
+
if (ismaster.me && ismaster.me.toLowerCase() !== serverName) {
|
|
378
|
+
if (this.logger.isWarn()) {
|
|
379
|
+
this.logger.warn(
|
|
380
|
+
f(
|
|
381
|
+
'the seedlist server was removed due to its address %s not matching its ismaster.me address %s',
|
|
382
|
+
server.name,
|
|
383
|
+
ismaster.me
|
|
384
|
+
)
|
|
385
|
+
);
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
// Delete from the set
|
|
389
|
+
delete this.set[serverName];
|
|
390
|
+
// Delete unknown servers
|
|
391
|
+
removeFrom(server, self.unknownServers);
|
|
392
|
+
|
|
393
|
+
// Destroy the instance
|
|
394
|
+
server.destroy();
|
|
395
|
+
|
|
396
|
+
// Set the type of topology we have
|
|
397
|
+
if (this.primary && !this.primary.equals(server)) {
|
|
398
|
+
this.topologyType = TopologyType.ReplicaSetWithPrimary;
|
|
399
|
+
} else {
|
|
400
|
+
this.topologyType = TopologyType.ReplicaSetNoPrimary;
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
//
|
|
404
|
+
// We have a potential primary
|
|
405
|
+
//
|
|
406
|
+
if (!this.primary && ismaster.primary) {
|
|
407
|
+
this.set[ismaster.primary.toLowerCase()] = {
|
|
408
|
+
type: ServerType.PossiblePrimary,
|
|
409
|
+
setName: null,
|
|
410
|
+
electionId: null,
|
|
411
|
+
setVersion: null
|
|
412
|
+
};
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
return false;
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
//
|
|
419
|
+
// Primary handling
|
|
420
|
+
//
|
|
421
|
+
if (!this.primary && ismaster.ismaster && ismaster.setName) {
|
|
422
|
+
var ismasterElectionId = server.lastIsMaster().electionId;
|
|
423
|
+
if (this.setName && this.setName !== ismaster.setName) {
|
|
424
|
+
this.topologyType = TopologyType.ReplicaSetNoPrimary;
|
|
425
|
+
return new MongoError(
|
|
426
|
+
f(
|
|
427
|
+
'setName from ismaster does not match provided connection setName [%s] != [%s]',
|
|
428
|
+
ismaster.setName,
|
|
429
|
+
this.setName
|
|
430
|
+
)
|
|
431
|
+
);
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
if (!this.maxElectionId && ismasterElectionId) {
|
|
435
|
+
this.maxElectionId = ismasterElectionId;
|
|
436
|
+
} else if (this.maxElectionId && ismasterElectionId) {
|
|
437
|
+
var result = compareObjectIds(this.maxElectionId, ismasterElectionId);
|
|
438
|
+
// Get the electionIds
|
|
439
|
+
var ismasterSetVersion = server.lastIsMaster().setVersion;
|
|
440
|
+
|
|
441
|
+
if (result === 1) {
|
|
442
|
+
this.topologyType = TopologyType.ReplicaSetNoPrimary;
|
|
443
|
+
return false;
|
|
444
|
+
} else if (result === 0 && ismasterSetVersion) {
|
|
445
|
+
if (ismasterSetVersion < this.maxSetVersion) {
|
|
446
|
+
this.topologyType = TopologyType.ReplicaSetNoPrimary;
|
|
447
|
+
return false;
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
this.maxSetVersion = ismasterSetVersion;
|
|
452
|
+
this.maxElectionId = ismasterElectionId;
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
// Hande normalization of server names
|
|
456
|
+
var normalizedHosts = ismaster.hosts.map(function(x) {
|
|
457
|
+
return x.toLowerCase();
|
|
458
|
+
});
|
|
459
|
+
var locationIndex = normalizedHosts.indexOf(serverName);
|
|
460
|
+
|
|
461
|
+
// Validate that the server exists in the host list
|
|
462
|
+
if (locationIndex !== -1) {
|
|
463
|
+
self.primary = server;
|
|
464
|
+
self.set[serverName] = {
|
|
465
|
+
type: ServerType.RSPrimary,
|
|
466
|
+
setVersion: ismaster.setVersion,
|
|
467
|
+
electionId: ismaster.electionId,
|
|
468
|
+
setName: ismaster.setName
|
|
469
|
+
};
|
|
470
|
+
|
|
471
|
+
// Set the topology
|
|
472
|
+
this.topologyType = TopologyType.ReplicaSetWithPrimary;
|
|
473
|
+
if (ismaster.setName) this.setName = ismaster.setName;
|
|
474
|
+
removeFrom(server, self.unknownServers);
|
|
475
|
+
removeFrom(server, self.secondaries);
|
|
476
|
+
removeFrom(server, self.passives);
|
|
477
|
+
self.emit('joined', 'primary', server);
|
|
478
|
+
} else {
|
|
479
|
+
this.topologyType = TopologyType.ReplicaSetNoPrimary;
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
emitTopologyDescriptionChanged(self);
|
|
483
|
+
return true;
|
|
484
|
+
} else if (ismaster.ismaster && ismaster.setName) {
|
|
485
|
+
// Get the electionIds
|
|
486
|
+
var currentElectionId = self.set[self.primary.name.toLowerCase()].electionId;
|
|
487
|
+
var currentSetVersion = self.set[self.primary.name.toLowerCase()].setVersion;
|
|
488
|
+
var currentSetName = self.set[self.primary.name.toLowerCase()].setName;
|
|
489
|
+
ismasterElectionId = server.lastIsMaster().electionId;
|
|
490
|
+
ismasterSetVersion = server.lastIsMaster().setVersion;
|
|
491
|
+
var ismasterSetName = server.lastIsMaster().setName;
|
|
492
|
+
|
|
493
|
+
// Is it the same server instance
|
|
494
|
+
if (this.primary.equals(server) && currentSetName === ismasterSetName) {
|
|
495
|
+
return false;
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
// If we do not have the same rs name
|
|
499
|
+
if (currentSetName && currentSetName !== ismasterSetName) {
|
|
500
|
+
if (!this.primary.equals(server)) {
|
|
501
|
+
this.topologyType = TopologyType.ReplicaSetWithPrimary;
|
|
502
|
+
} else {
|
|
503
|
+
this.topologyType = TopologyType.ReplicaSetNoPrimary;
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
return false;
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
// Check if we need to replace the server
|
|
510
|
+
if (currentElectionId && ismasterElectionId) {
|
|
511
|
+
result = compareObjectIds(currentElectionId, ismasterElectionId);
|
|
512
|
+
|
|
513
|
+
if (result === 1) {
|
|
514
|
+
return false;
|
|
515
|
+
} else if (result === 0 && currentSetVersion > ismasterSetVersion) {
|
|
516
|
+
return false;
|
|
517
|
+
}
|
|
518
|
+
} else if (!currentElectionId && ismasterElectionId && ismasterSetVersion) {
|
|
519
|
+
if (ismasterSetVersion < this.maxSetVersion) {
|
|
520
|
+
return false;
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
if (!this.maxElectionId && ismasterElectionId) {
|
|
525
|
+
this.maxElectionId = ismasterElectionId;
|
|
526
|
+
} else if (this.maxElectionId && ismasterElectionId) {
|
|
527
|
+
result = compareObjectIds(this.maxElectionId, ismasterElectionId);
|
|
528
|
+
|
|
529
|
+
if (result === 1) {
|
|
530
|
+
return false;
|
|
531
|
+
} else if (result === 0 && currentSetVersion && ismasterSetVersion) {
|
|
532
|
+
if (ismasterSetVersion < this.maxSetVersion) {
|
|
533
|
+
return false;
|
|
534
|
+
}
|
|
535
|
+
} else {
|
|
536
|
+
if (ismasterSetVersion < this.maxSetVersion) {
|
|
537
|
+
return false;
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
this.maxElectionId = ismasterElectionId;
|
|
542
|
+
this.maxSetVersion = ismasterSetVersion;
|
|
543
|
+
} else {
|
|
544
|
+
this.maxSetVersion = ismasterSetVersion;
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
// Modify the entry to unknown
|
|
548
|
+
self.set[self.primary.name.toLowerCase()] = {
|
|
549
|
+
type: ServerType.Unknown,
|
|
550
|
+
setVersion: null,
|
|
551
|
+
electionId: null,
|
|
552
|
+
setName: null
|
|
553
|
+
};
|
|
554
|
+
|
|
555
|
+
// Signal primary left
|
|
556
|
+
self.emit('left', 'primary', this.primary);
|
|
557
|
+
// Destroy the instance
|
|
558
|
+
self.primary.destroy();
|
|
559
|
+
// Set the new instance
|
|
560
|
+
self.primary = server;
|
|
561
|
+
// Set the set information
|
|
562
|
+
self.set[serverName] = {
|
|
563
|
+
type: ServerType.RSPrimary,
|
|
564
|
+
setVersion: ismaster.setVersion,
|
|
565
|
+
electionId: ismaster.electionId,
|
|
566
|
+
setName: ismaster.setName
|
|
567
|
+
};
|
|
568
|
+
|
|
569
|
+
// Set the topology
|
|
570
|
+
this.topologyType = TopologyType.ReplicaSetWithPrimary;
|
|
571
|
+
if (ismaster.setName) this.setName = ismaster.setName;
|
|
572
|
+
removeFrom(server, self.unknownServers);
|
|
573
|
+
removeFrom(server, self.secondaries);
|
|
574
|
+
removeFrom(server, self.passives);
|
|
575
|
+
self.emit('joined', 'primary', server);
|
|
576
|
+
emitTopologyDescriptionChanged(self);
|
|
577
|
+
return true;
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
// A possible instance
|
|
581
|
+
if (!this.primary && ismaster.primary) {
|
|
582
|
+
self.set[ismaster.primary.toLowerCase()] = {
|
|
583
|
+
type: ServerType.PossiblePrimary,
|
|
584
|
+
setVersion: null,
|
|
585
|
+
electionId: null,
|
|
586
|
+
setName: null
|
|
587
|
+
};
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
//
|
|
591
|
+
// Secondary handling
|
|
592
|
+
//
|
|
593
|
+
if (
|
|
594
|
+
ismaster.secondary &&
|
|
595
|
+
ismaster.setName &&
|
|
596
|
+
!inList(ismaster, server, this.secondaries) &&
|
|
597
|
+
this.setName &&
|
|
598
|
+
this.setName === ismaster.setName
|
|
599
|
+
) {
|
|
600
|
+
addToList(self, ServerType.RSSecondary, ismaster, server, this.secondaries);
|
|
601
|
+
// Set the topology
|
|
602
|
+
this.topologyType = this.primary
|
|
603
|
+
? TopologyType.ReplicaSetWithPrimary
|
|
604
|
+
: TopologyType.ReplicaSetNoPrimary;
|
|
605
|
+
if (ismaster.setName) this.setName = ismaster.setName;
|
|
606
|
+
removeFrom(server, self.unknownServers);
|
|
607
|
+
|
|
608
|
+
// Remove primary
|
|
609
|
+
if (this.primary && this.primary.name.toLowerCase() === serverName) {
|
|
610
|
+
server.destroy();
|
|
611
|
+
this.primary = null;
|
|
612
|
+
self.emit('left', 'primary', server);
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
// Emit secondary joined replicaset
|
|
616
|
+
self.emit('joined', 'secondary', server);
|
|
617
|
+
emitTopologyDescriptionChanged(self);
|
|
618
|
+
return true;
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
//
|
|
622
|
+
// Arbiter handling
|
|
623
|
+
//
|
|
624
|
+
if (
|
|
625
|
+
isArbiter(ismaster) &&
|
|
626
|
+
!inList(ismaster, server, this.arbiters) &&
|
|
627
|
+
this.setName &&
|
|
628
|
+
this.setName === ismaster.setName
|
|
629
|
+
) {
|
|
630
|
+
addToList(self, ServerType.RSArbiter, ismaster, server, this.arbiters);
|
|
631
|
+
// Set the topology
|
|
632
|
+
this.topologyType = this.primary
|
|
633
|
+
? TopologyType.ReplicaSetWithPrimary
|
|
634
|
+
: TopologyType.ReplicaSetNoPrimary;
|
|
635
|
+
if (ismaster.setName) this.setName = ismaster.setName;
|
|
636
|
+
removeFrom(server, self.unknownServers);
|
|
637
|
+
self.emit('joined', 'arbiter', server);
|
|
638
|
+
emitTopologyDescriptionChanged(self);
|
|
639
|
+
return true;
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
//
|
|
643
|
+
// Passive handling
|
|
644
|
+
//
|
|
645
|
+
if (
|
|
646
|
+
ismaster.passive &&
|
|
647
|
+
ismaster.setName &&
|
|
648
|
+
!inList(ismaster, server, this.passives) &&
|
|
649
|
+
this.setName &&
|
|
650
|
+
this.setName === ismaster.setName
|
|
651
|
+
) {
|
|
652
|
+
addToList(self, ServerType.RSSecondary, ismaster, server, this.passives);
|
|
653
|
+
// Set the topology
|
|
654
|
+
this.topologyType = this.primary
|
|
655
|
+
? TopologyType.ReplicaSetWithPrimary
|
|
656
|
+
: TopologyType.ReplicaSetNoPrimary;
|
|
657
|
+
if (ismaster.setName) this.setName = ismaster.setName;
|
|
658
|
+
removeFrom(server, self.unknownServers);
|
|
659
|
+
|
|
660
|
+
// Remove primary
|
|
661
|
+
if (this.primary && this.primary.name.toLowerCase() === serverName) {
|
|
662
|
+
server.destroy();
|
|
663
|
+
this.primary = null;
|
|
664
|
+
self.emit('left', 'primary', server);
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
self.emit('joined', 'secondary', server);
|
|
668
|
+
emitTopologyDescriptionChanged(self);
|
|
669
|
+
return true;
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
//
|
|
673
|
+
// Remove the primary
|
|
674
|
+
//
|
|
675
|
+
if (this.set[serverName] && this.set[serverName].type === ServerType.RSPrimary) {
|
|
676
|
+
self.emit('left', 'primary', this.primary);
|
|
677
|
+
this.primary.destroy();
|
|
678
|
+
this.primary = null;
|
|
679
|
+
this.topologyType = TopologyType.ReplicaSetNoPrimary;
|
|
680
|
+
return false;
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
this.topologyType = this.primary
|
|
684
|
+
? TopologyType.ReplicaSetWithPrimary
|
|
685
|
+
: TopologyType.ReplicaSetNoPrimary;
|
|
686
|
+
return false;
|
|
687
|
+
};
|
|
688
|
+
|
|
689
|
+
/**
|
|
690
|
+
* Recalculate single server max staleness
|
|
691
|
+
* @method
|
|
692
|
+
*/
|
|
693
|
+
ReplSetState.prototype.updateServerMaxStaleness = function(server, haInterval) {
|
|
694
|
+
// Locate the max secondary lastwrite
|
|
695
|
+
var max = 0;
|
|
696
|
+
// Go over all secondaries
|
|
697
|
+
for (var i = 0; i < this.secondaries.length; i++) {
|
|
698
|
+
max = Math.max(max, this.secondaries[i].lastWriteDate);
|
|
699
|
+
}
|
|
700
|
+
|
|
701
|
+
// Perform this servers staleness calculation
|
|
702
|
+
if (server.ismaster.maxWireVersion >= 5 && server.ismaster.secondary && this.hasPrimary()) {
|
|
703
|
+
server.staleness =
|
|
704
|
+
server.lastUpdateTime -
|
|
705
|
+
server.lastWriteDate -
|
|
706
|
+
(this.primary.lastUpdateTime - this.primary.lastWriteDate) +
|
|
707
|
+
haInterval;
|
|
708
|
+
} else if (server.ismaster.maxWireVersion >= 5 && server.ismaster.secondary) {
|
|
709
|
+
server.staleness = max - server.lastWriteDate + haInterval;
|
|
710
|
+
}
|
|
711
|
+
};
|
|
712
|
+
|
|
713
|
+
/**
|
|
714
|
+
* Recalculate all the staleness values for secodaries
|
|
715
|
+
* @method
|
|
716
|
+
*/
|
|
717
|
+
ReplSetState.prototype.updateSecondariesMaxStaleness = function(haInterval) {
|
|
718
|
+
for (var i = 0; i < this.secondaries.length; i++) {
|
|
719
|
+
this.updateServerMaxStaleness(this.secondaries[i], haInterval);
|
|
720
|
+
}
|
|
721
|
+
};
|
|
722
|
+
|
|
723
|
+
/**
|
|
724
|
+
* Pick a server by the passed in ReadPreference
|
|
725
|
+
* @method
|
|
726
|
+
* @param {ReadPreference} readPreference The ReadPreference instance to use
|
|
727
|
+
*/
|
|
728
|
+
ReplSetState.prototype.pickServer = function(readPreference) {
|
|
729
|
+
// If no read Preference set to primary by default
|
|
730
|
+
readPreference = readPreference || ReadPreference.primary;
|
|
731
|
+
|
|
732
|
+
// maxStalenessSeconds is not allowed with a primary read
|
|
733
|
+
if (readPreference.preference === 'primary' && readPreference.maxStalenessSeconds != null) {
|
|
734
|
+
return new MongoError('primary readPreference incompatible with maxStalenessSeconds');
|
|
735
|
+
}
|
|
736
|
+
|
|
737
|
+
// Check if we have any non compatible servers for maxStalenessSeconds
|
|
738
|
+
var allservers = this.primary ? [this.primary] : [];
|
|
739
|
+
allservers = allservers.concat(this.secondaries);
|
|
740
|
+
|
|
741
|
+
// Does any of the servers not support the right wire protocol version
|
|
742
|
+
// for maxStalenessSeconds when maxStalenessSeconds specified on readPreference. Then error out
|
|
743
|
+
if (readPreference.maxStalenessSeconds != null) {
|
|
744
|
+
for (var i = 0; i < allservers.length; i++) {
|
|
745
|
+
if (allservers[i].ismaster.maxWireVersion < 5) {
|
|
746
|
+
return new MongoError(
|
|
747
|
+
'maxStalenessSeconds not supported by at least one of the replicaset members'
|
|
748
|
+
);
|
|
749
|
+
}
|
|
750
|
+
}
|
|
751
|
+
}
|
|
752
|
+
|
|
753
|
+
// Do we have the nearest readPreference
|
|
754
|
+
if (readPreference.preference === 'nearest' && readPreference.maxStalenessSeconds == null) {
|
|
755
|
+
return pickNearest(this, readPreference);
|
|
756
|
+
} else if (
|
|
757
|
+
readPreference.preference === 'nearest' &&
|
|
758
|
+
readPreference.maxStalenessSeconds != null
|
|
759
|
+
) {
|
|
760
|
+
return pickNearestMaxStalenessSeconds(this, readPreference);
|
|
761
|
+
}
|
|
762
|
+
|
|
763
|
+
// Get all the secondaries
|
|
764
|
+
var secondaries = this.secondaries;
|
|
765
|
+
|
|
766
|
+
// Check if we can satisfy and of the basic read Preferences
|
|
767
|
+
if (readPreference.equals(ReadPreference.secondary) && secondaries.length === 0) {
|
|
768
|
+
return new MongoError('no secondary server available');
|
|
769
|
+
}
|
|
770
|
+
|
|
771
|
+
if (
|
|
772
|
+
readPreference.equals(ReadPreference.secondaryPreferred) &&
|
|
773
|
+
secondaries.length === 0 &&
|
|
774
|
+
this.primary == null
|
|
775
|
+
) {
|
|
776
|
+
return new MongoError('no secondary or primary server available');
|
|
777
|
+
}
|
|
778
|
+
|
|
779
|
+
if (readPreference.equals(ReadPreference.primary) && this.primary == null) {
|
|
780
|
+
return new MongoError('no primary server available');
|
|
781
|
+
}
|
|
782
|
+
|
|
783
|
+
// Secondary preferred or just secondaries
|
|
784
|
+
if (
|
|
785
|
+
readPreference.equals(ReadPreference.secondaryPreferred) ||
|
|
786
|
+
readPreference.equals(ReadPreference.secondary)
|
|
787
|
+
) {
|
|
788
|
+
if (secondaries.length > 0 && readPreference.maxStalenessSeconds == null) {
|
|
789
|
+
// Pick nearest of any other servers available
|
|
790
|
+
var server = pickNearest(this, readPreference);
|
|
791
|
+
// No server in the window return primary
|
|
792
|
+
if (server) {
|
|
793
|
+
return server;
|
|
794
|
+
}
|
|
795
|
+
} else if (secondaries.length > 0 && readPreference.maxStalenessSeconds != null) {
|
|
796
|
+
// Pick nearest of any other servers available
|
|
797
|
+
server = pickNearestMaxStalenessSeconds(this, readPreference);
|
|
798
|
+
// No server in the window return primary
|
|
799
|
+
if (server) {
|
|
800
|
+
return server;
|
|
801
|
+
}
|
|
802
|
+
}
|
|
803
|
+
|
|
804
|
+
if (readPreference.equals(ReadPreference.secondaryPreferred)) {
|
|
805
|
+
return this.primary;
|
|
806
|
+
}
|
|
807
|
+
|
|
808
|
+
return null;
|
|
809
|
+
}
|
|
810
|
+
|
|
811
|
+
// Primary preferred
|
|
812
|
+
if (readPreference.equals(ReadPreference.primaryPreferred)) {
|
|
813
|
+
server = null;
|
|
814
|
+
|
|
815
|
+
// We prefer the primary if it's available
|
|
816
|
+
if (this.primary) {
|
|
817
|
+
return this.primary;
|
|
818
|
+
}
|
|
819
|
+
|
|
820
|
+
// Pick a secondary
|
|
821
|
+
if (secondaries.length > 0 && readPreference.maxStalenessSeconds == null) {
|
|
822
|
+
server = pickNearest(this, readPreference);
|
|
823
|
+
} else if (secondaries.length > 0 && readPreference.maxStalenessSeconds != null) {
|
|
824
|
+
server = pickNearestMaxStalenessSeconds(this, readPreference);
|
|
825
|
+
}
|
|
826
|
+
|
|
827
|
+
// Did we find a server
|
|
828
|
+
if (server) return server;
|
|
829
|
+
}
|
|
830
|
+
|
|
831
|
+
// Return the primary
|
|
832
|
+
return this.primary;
|
|
833
|
+
};
|
|
834
|
+
|
|
835
|
+
//
|
|
836
|
+
// Filter serves by tags
|
|
837
|
+
var filterByTags = function(readPreference, servers) {
|
|
838
|
+
if (readPreference.tags == null) return servers;
|
|
839
|
+
var filteredServers = [];
|
|
840
|
+
var tagsArray = Array.isArray(readPreference.tags) ? readPreference.tags : [readPreference.tags];
|
|
841
|
+
|
|
842
|
+
// Iterate over the tags
|
|
843
|
+
for (var j = 0; j < tagsArray.length; j++) {
|
|
844
|
+
var tags = tagsArray[j];
|
|
845
|
+
|
|
846
|
+
// Iterate over all the servers
|
|
847
|
+
for (var i = 0; i < servers.length; i++) {
|
|
848
|
+
var serverTag = servers[i].lastIsMaster().tags || {};
|
|
849
|
+
|
|
850
|
+
// Did we find the a matching server
|
|
851
|
+
var found = true;
|
|
852
|
+
// Check if the server is valid
|
|
853
|
+
for (var name in tags) {
|
|
854
|
+
if (serverTag[name] !== tags[name]) {
|
|
855
|
+
found = false;
|
|
856
|
+
}
|
|
857
|
+
}
|
|
858
|
+
|
|
859
|
+
// Add to candidate list
|
|
860
|
+
if (found) {
|
|
861
|
+
filteredServers.push(servers[i]);
|
|
862
|
+
}
|
|
863
|
+
}
|
|
864
|
+
}
|
|
865
|
+
|
|
866
|
+
// Returned filtered servers
|
|
867
|
+
return filteredServers;
|
|
868
|
+
};
|
|
869
|
+
|
|
870
|
+
function pickNearestMaxStalenessSeconds(self, readPreference) {
|
|
871
|
+
// Only get primary and secondaries as seeds
|
|
872
|
+
var servers = [];
|
|
873
|
+
|
|
874
|
+
// Get the maxStalenessMS
|
|
875
|
+
var maxStalenessMS = readPreference.maxStalenessSeconds * 1000;
|
|
876
|
+
|
|
877
|
+
// Check if the maxStalenessMS > 90 seconds
|
|
878
|
+
if (maxStalenessMS < 90 * 1000) {
|
|
879
|
+
return new MongoError('maxStalenessSeconds must be set to at least 90 seconds');
|
|
880
|
+
}
|
|
881
|
+
|
|
882
|
+
// Add primary to list if not a secondary read preference
|
|
883
|
+
if (
|
|
884
|
+
self.primary &&
|
|
885
|
+
readPreference.preference !== 'secondary' &&
|
|
886
|
+
readPreference.preference !== 'secondaryPreferred'
|
|
887
|
+
) {
|
|
888
|
+
servers.push(self.primary);
|
|
889
|
+
}
|
|
890
|
+
|
|
891
|
+
// Add all the secondaries
|
|
892
|
+
for (var i = 0; i < self.secondaries.length; i++) {
|
|
893
|
+
servers.push(self.secondaries[i]);
|
|
894
|
+
}
|
|
895
|
+
|
|
896
|
+
// If we have a secondaryPreferred readPreference and no server add the primary
|
|
897
|
+
if (self.primary && servers.length === 0 && readPreference.preference !== 'secondaryPreferred') {
|
|
898
|
+
servers.push(self.primary);
|
|
899
|
+
}
|
|
900
|
+
|
|
901
|
+
// Filter by tags
|
|
902
|
+
servers = filterByTags(readPreference, servers);
|
|
903
|
+
|
|
904
|
+
// Filter by latency
|
|
905
|
+
servers = servers.filter(function(s) {
|
|
906
|
+
return s.staleness <= maxStalenessMS;
|
|
907
|
+
});
|
|
908
|
+
|
|
909
|
+
// Sort by time
|
|
910
|
+
servers.sort(function(a, b) {
|
|
911
|
+
return a.lastIsMasterMS - b.lastIsMasterMS;
|
|
912
|
+
});
|
|
913
|
+
|
|
914
|
+
// No servers, default to primary
|
|
915
|
+
if (servers.length === 0) {
|
|
916
|
+
return null;
|
|
917
|
+
}
|
|
918
|
+
|
|
919
|
+
// Ensure index does not overflow the number of available servers
|
|
920
|
+
self.index = self.index % servers.length;
|
|
921
|
+
|
|
922
|
+
// Get the server
|
|
923
|
+
var server = servers[self.index];
|
|
924
|
+
// Add to the index
|
|
925
|
+
self.index = self.index + 1;
|
|
926
|
+
// Return the first server of the sorted and filtered list
|
|
927
|
+
return server;
|
|
928
|
+
}
|
|
929
|
+
|
|
930
|
+
function pickNearest(self, readPreference) {
|
|
931
|
+
// Only get primary and secondaries as seeds
|
|
932
|
+
var servers = [];
|
|
933
|
+
|
|
934
|
+
// Add primary to list if not a secondary read preference
|
|
935
|
+
if (
|
|
936
|
+
self.primary &&
|
|
937
|
+
readPreference.preference !== 'secondary' &&
|
|
938
|
+
readPreference.preference !== 'secondaryPreferred'
|
|
939
|
+
) {
|
|
940
|
+
servers.push(self.primary);
|
|
941
|
+
}
|
|
942
|
+
|
|
943
|
+
// Add all the secondaries
|
|
944
|
+
for (var i = 0; i < self.secondaries.length; i++) {
|
|
945
|
+
servers.push(self.secondaries[i]);
|
|
946
|
+
}
|
|
947
|
+
|
|
948
|
+
// If we have a secondaryPreferred readPreference and no server add the primary
|
|
949
|
+
if (servers.length === 0 && self.primary && readPreference.preference !== 'secondaryPreferred') {
|
|
950
|
+
servers.push(self.primary);
|
|
951
|
+
}
|
|
952
|
+
|
|
953
|
+
// Filter by tags
|
|
954
|
+
servers = filterByTags(readPreference, servers);
|
|
955
|
+
|
|
956
|
+
// Sort by time
|
|
957
|
+
servers.sort(function(a, b) {
|
|
958
|
+
return a.lastIsMasterMS - b.lastIsMasterMS;
|
|
959
|
+
});
|
|
960
|
+
|
|
961
|
+
// Locate lowest time (picked servers are lowest time + acceptable Latency margin)
|
|
962
|
+
var lowest = servers.length > 0 ? servers[0].lastIsMasterMS : 0;
|
|
963
|
+
|
|
964
|
+
// Filter by latency
|
|
965
|
+
servers = servers.filter(function(s) {
|
|
966
|
+
return s.lastIsMasterMS <= lowest + self.acceptableLatency;
|
|
967
|
+
});
|
|
968
|
+
|
|
969
|
+
// No servers, default to primary
|
|
970
|
+
if (servers.length === 0) {
|
|
971
|
+
return null;
|
|
972
|
+
}
|
|
973
|
+
|
|
974
|
+
// Ensure index does not overflow the number of available servers
|
|
975
|
+
self.index = self.index % servers.length;
|
|
976
|
+
// Get the server
|
|
977
|
+
var server = servers[self.index];
|
|
978
|
+
// Add to the index
|
|
979
|
+
self.index = self.index + 1;
|
|
980
|
+
// Return the first server of the sorted and filtered list
|
|
981
|
+
return server;
|
|
982
|
+
}
|
|
983
|
+
|
|
984
|
+
function inList(ismaster, server, list) {
|
|
985
|
+
for (var i = 0; i < list.length; i++) {
|
|
986
|
+
if (list[i] && list[i].name && list[i].name.toLowerCase() === server.name.toLowerCase())
|
|
987
|
+
return true;
|
|
988
|
+
}
|
|
989
|
+
|
|
990
|
+
return false;
|
|
991
|
+
}
|
|
992
|
+
|
|
993
|
+
function addToList(self, type, ismaster, server, list) {
|
|
994
|
+
var serverName = server.name.toLowerCase();
|
|
995
|
+
// Update set information about the server instance
|
|
996
|
+
self.set[serverName].type = type;
|
|
997
|
+
self.set[serverName].electionId = ismaster ? ismaster.electionId : ismaster;
|
|
998
|
+
self.set[serverName].setName = ismaster ? ismaster.setName : ismaster;
|
|
999
|
+
self.set[serverName].setVersion = ismaster ? ismaster.setVersion : ismaster;
|
|
1000
|
+
// Add to the list
|
|
1001
|
+
list.push(server);
|
|
1002
|
+
}
|
|
1003
|
+
|
|
1004
|
+
function compareObjectIds(id1, id2) {
|
|
1005
|
+
var a = Buffer.from(id1.toHexString(), 'hex');
|
|
1006
|
+
var b = Buffer.from(id2.toHexString(), 'hex');
|
|
1007
|
+
|
|
1008
|
+
if (a === b) {
|
|
1009
|
+
return 0;
|
|
1010
|
+
}
|
|
1011
|
+
|
|
1012
|
+
if (typeof Buffer.compare === 'function') {
|
|
1013
|
+
return Buffer.compare(a, b);
|
|
1014
|
+
}
|
|
1015
|
+
|
|
1016
|
+
var x = a.length;
|
|
1017
|
+
var y = b.length;
|
|
1018
|
+
var len = Math.min(x, y);
|
|
1019
|
+
|
|
1020
|
+
for (var i = 0; i < len; i++) {
|
|
1021
|
+
if (a[i] !== b[i]) {
|
|
1022
|
+
break;
|
|
1023
|
+
}
|
|
1024
|
+
}
|
|
1025
|
+
|
|
1026
|
+
if (i !== len) {
|
|
1027
|
+
x = a[i];
|
|
1028
|
+
y = b[i];
|
|
1029
|
+
}
|
|
1030
|
+
|
|
1031
|
+
return x < y ? -1 : y < x ? 1 : 0;
|
|
1032
|
+
}
|
|
1033
|
+
|
|
1034
|
+
function removeFrom(server, list) {
|
|
1035
|
+
for (var i = 0; i < list.length; i++) {
|
|
1036
|
+
if (list[i].equals && list[i].equals(server)) {
|
|
1037
|
+
list.splice(i, 1);
|
|
1038
|
+
return true;
|
|
1039
|
+
} else if (typeof list[i] === 'string' && list[i].toLowerCase() === server.name.toLowerCase()) {
|
|
1040
|
+
list.splice(i, 1);
|
|
1041
|
+
return true;
|
|
1042
|
+
}
|
|
1043
|
+
}
|
|
1044
|
+
|
|
1045
|
+
return false;
|
|
1046
|
+
}
|
|
1047
|
+
|
|
1048
|
+
function emitTopologyDescriptionChanged(self) {
|
|
1049
|
+
if (self.listeners('topologyDescriptionChanged').length > 0) {
|
|
1050
|
+
var topology = 'Unknown';
|
|
1051
|
+
var setName = self.setName;
|
|
1052
|
+
|
|
1053
|
+
if (self.hasPrimaryAndSecondary()) {
|
|
1054
|
+
topology = 'ReplicaSetWithPrimary';
|
|
1055
|
+
} else if (!self.hasPrimary() && self.hasSecondary()) {
|
|
1056
|
+
topology = 'ReplicaSetNoPrimary';
|
|
1057
|
+
}
|
|
1058
|
+
|
|
1059
|
+
// Generate description
|
|
1060
|
+
var description = {
|
|
1061
|
+
topologyType: topology,
|
|
1062
|
+
setName: setName,
|
|
1063
|
+
servers: []
|
|
1064
|
+
};
|
|
1065
|
+
|
|
1066
|
+
// Add the primary to the list
|
|
1067
|
+
if (self.hasPrimary()) {
|
|
1068
|
+
var desc = self.primary.getDescription();
|
|
1069
|
+
desc.type = 'RSPrimary';
|
|
1070
|
+
description.servers.push(desc);
|
|
1071
|
+
}
|
|
1072
|
+
|
|
1073
|
+
// Add all the secondaries
|
|
1074
|
+
description.servers = description.servers.concat(
|
|
1075
|
+
self.secondaries.map(function(x) {
|
|
1076
|
+
var description = x.getDescription();
|
|
1077
|
+
description.type = 'RSSecondary';
|
|
1078
|
+
return description;
|
|
1079
|
+
})
|
|
1080
|
+
);
|
|
1081
|
+
|
|
1082
|
+
// Add all the arbiters
|
|
1083
|
+
description.servers = description.servers.concat(
|
|
1084
|
+
self.arbiters.map(function(x) {
|
|
1085
|
+
var description = x.getDescription();
|
|
1086
|
+
description.type = 'RSArbiter';
|
|
1087
|
+
return description;
|
|
1088
|
+
})
|
|
1089
|
+
);
|
|
1090
|
+
|
|
1091
|
+
// Add all the passives
|
|
1092
|
+
description.servers = description.servers.concat(
|
|
1093
|
+
self.passives.map(function(x) {
|
|
1094
|
+
var description = x.getDescription();
|
|
1095
|
+
description.type = 'RSSecondary';
|
|
1096
|
+
return description;
|
|
1097
|
+
})
|
|
1098
|
+
);
|
|
1099
|
+
|
|
1100
|
+
// Get the diff
|
|
1101
|
+
var diffResult = diff(self.replicasetDescription, description);
|
|
1102
|
+
|
|
1103
|
+
// Create the result
|
|
1104
|
+
var result = {
|
|
1105
|
+
topologyId: self.id,
|
|
1106
|
+
previousDescription: self.replicasetDescription,
|
|
1107
|
+
newDescription: description,
|
|
1108
|
+
diff: diffResult
|
|
1109
|
+
};
|
|
1110
|
+
|
|
1111
|
+
// Emit the topologyDescription change
|
|
1112
|
+
// if(diffResult.servers.length > 0) {
|
|
1113
|
+
self.emit('topologyDescriptionChanged', result);
|
|
1114
|
+
// }
|
|
1115
|
+
|
|
1116
|
+
// Set the new description
|
|
1117
|
+
self.replicasetDescription = description;
|
|
1118
|
+
}
|
|
1119
|
+
}
|
|
1120
|
+
|
|
1121
|
+
module.exports = ReplSetState;
|