mongodb 7.0.0-dev.20251218.sha.f0af829f → 7.0.0-dev.20251220.sha.e70fdc98
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/lib/change_stream.js +3 -1
- package/lib/change_stream.js.map +1 -1
- package/lib/cmap/auth/gssapi.js +2 -1
- package/lib/cmap/auth/gssapi.js.map +1 -1
- package/lib/cmap/handshake/client_metadata.js +1 -1
- package/lib/cmap/handshake/client_metadata.js.map +1 -1
- package/lib/mongo_client.js +1 -1
- package/lib/mongo_client.js.map +1 -1
- package/lib/operations/execute_operation.js +5 -4
- package/lib/operations/execute_operation.js.map +1 -1
- package/lib/sdam/server_selection.js +135 -72
- package/lib/sdam/server_selection.js.map +1 -1
- package/lib/sdam/topology.js +5 -4
- package/lib/sdam/topology.js.map +1 -1
- package/lib/sessions.js +74 -38
- package/lib/sessions.js.map +1 -1
- package/mongodb.d.ts +2 -1
- package/package.json +1 -1
- package/src/change_stream.ts +3 -1
- package/src/cmap/auth/gssapi.ts +2 -1
- package/src/cmap/handshake/client_metadata.ts +1 -1
- package/src/index.ts +1 -1
- package/src/mongo_client.ts +2 -2
- package/src/operations/execute_operation.ts +6 -5
- package/src/sdam/server_selection.ts +187 -95
- package/src/sdam/topology.ts +14 -11
- package/src/sessions.ts +100 -44
|
@@ -17,8 +17,8 @@ import {
|
|
|
17
17
|
} from '../error';
|
|
18
18
|
import type { MongoClient } from '../mongo_client';
|
|
19
19
|
import { ReadPreference } from '../read_preference';
|
|
20
|
-
import type { ServerDescription } from '../sdam/server_description';
|
|
21
20
|
import {
|
|
21
|
+
DeprioritizedServers,
|
|
22
22
|
sameServerSelector,
|
|
23
23
|
secondaryWritableServerSelector,
|
|
24
24
|
type ServerSelector
|
|
@@ -207,7 +207,8 @@ async function tryOperation<T extends AbstractOperation, TResult = ResultTypeFro
|
|
|
207
207
|
session,
|
|
208
208
|
operationName: operation.commandName,
|
|
209
209
|
timeoutContext,
|
|
210
|
-
signal: operation.options.signal
|
|
210
|
+
signal: operation.options.signal,
|
|
211
|
+
deprioritizedServers: new DeprioritizedServers()
|
|
211
212
|
});
|
|
212
213
|
|
|
213
214
|
const hasReadAspect = operation.hasAspect(Aspect.READ_OPERATION);
|
|
@@ -234,7 +235,7 @@ async function tryOperation<T extends AbstractOperation, TResult = ResultTypeFro
|
|
|
234
235
|
|
|
235
236
|
const maxTries = willRetry ? (timeoutContext.csotEnabled() ? Infinity : 2) : 1;
|
|
236
237
|
let previousOperationError: MongoError | undefined;
|
|
237
|
-
|
|
238
|
+
const deprioritizedServers = new DeprioritizedServers();
|
|
238
239
|
|
|
239
240
|
for (let tries = 0; tries < maxTries; tries++) {
|
|
240
241
|
if (previousOperationError) {
|
|
@@ -270,7 +271,7 @@ async function tryOperation<T extends AbstractOperation, TResult = ResultTypeFro
|
|
|
270
271
|
server = await topology.selectServer(selector, {
|
|
271
272
|
session,
|
|
272
273
|
operationName: operation.commandName,
|
|
273
|
-
|
|
274
|
+
deprioritizedServers,
|
|
274
275
|
signal: operation.options.signal
|
|
275
276
|
});
|
|
276
277
|
|
|
@@ -303,7 +304,7 @@ async function tryOperation<T extends AbstractOperation, TResult = ResultTypeFro
|
|
|
303
304
|
) {
|
|
304
305
|
throw previousOperationError;
|
|
305
306
|
}
|
|
306
|
-
|
|
307
|
+
deprioritizedServers.add(server.description);
|
|
307
308
|
previousOperationError = operationError;
|
|
308
309
|
|
|
309
310
|
// Reset timeouts
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { MongoInvalidArgumentError } from '../error';
|
|
1
|
+
import { MongoInvalidArgumentError, MongoRuntimeError } from '../error';
|
|
2
2
|
import { ReadPreference } from '../read_preference';
|
|
3
3
|
import { ServerType, TopologyType } from './common';
|
|
4
4
|
import type { ServerDescription, TagSet } from './server_description';
|
|
@@ -15,21 +15,52 @@ export const MIN_SECONDARY_WRITE_WIRE_VERSION = 13;
|
|
|
15
15
|
export type ServerSelector = (
|
|
16
16
|
topologyDescription: TopologyDescription,
|
|
17
17
|
servers: ServerDescription[],
|
|
18
|
-
deprioritized
|
|
18
|
+
deprioritized: DeprioritizedServers
|
|
19
19
|
) => ServerDescription[];
|
|
20
20
|
|
|
21
|
+
/** @internal */
|
|
22
|
+
export class DeprioritizedServers {
|
|
23
|
+
private deprioritized: Set<string> = new Set();
|
|
24
|
+
|
|
25
|
+
constructor(descriptions?: Iterable<ServerDescription>) {
|
|
26
|
+
for (const description of descriptions ?? []) {
|
|
27
|
+
this.add(description);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
add({ address }: ServerDescription) {
|
|
32
|
+
this.deprioritized.add(address);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
has({ address }: ServerDescription): boolean {
|
|
36
|
+
return this.deprioritized.has(address);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function filterDeprioritized(
|
|
41
|
+
candidates: ServerDescription[],
|
|
42
|
+
deprioritized: DeprioritizedServers
|
|
43
|
+
): ServerDescription[] {
|
|
44
|
+
const filtered = candidates.filter(candidate => !deprioritized.has(candidate));
|
|
45
|
+
|
|
46
|
+
return filtered.length ? filtered : candidates;
|
|
47
|
+
}
|
|
48
|
+
|
|
21
49
|
/**
|
|
22
50
|
* Returns a server selector that selects for writable servers
|
|
23
51
|
*/
|
|
24
52
|
export function writableServerSelector(): ServerSelector {
|
|
25
53
|
return function writableServer(
|
|
26
54
|
topologyDescription: TopologyDescription,
|
|
27
|
-
servers: ServerDescription[]
|
|
55
|
+
servers: ServerDescription[],
|
|
56
|
+
deprioritized: DeprioritizedServers
|
|
28
57
|
): ServerDescription[] {
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
58
|
+
const eligibleServers = filterDeprioritized(
|
|
59
|
+
servers.filter(({ isWritable }) => isWritable),
|
|
60
|
+
deprioritized
|
|
32
61
|
);
|
|
62
|
+
|
|
63
|
+
return latencyWindowReducer(topologyDescription, eligibleServers);
|
|
33
64
|
};
|
|
34
65
|
}
|
|
35
66
|
|
|
@@ -39,8 +70,9 @@ export function writableServerSelector(): ServerSelector {
|
|
|
39
70
|
*/
|
|
40
71
|
export function sameServerSelector(description?: ServerDescription): ServerSelector {
|
|
41
72
|
return function sameServerSelector(
|
|
42
|
-
|
|
43
|
-
servers: ServerDescription[]
|
|
73
|
+
_topologyDescription: TopologyDescription,
|
|
74
|
+
servers: ServerDescription[],
|
|
75
|
+
_deprioritized: DeprioritizedServers
|
|
44
76
|
): ServerDescription[] {
|
|
45
77
|
if (!description) return [];
|
|
46
78
|
// Filter the servers to match the provided description only if
|
|
@@ -113,7 +145,7 @@ function maxStalenessReducer(
|
|
|
113
145
|
primaryFilter
|
|
114
146
|
)[0];
|
|
115
147
|
|
|
116
|
-
return servers.
|
|
148
|
+
return servers.filter((server: ServerDescription) => {
|
|
117
149
|
const stalenessMS =
|
|
118
150
|
server.lastUpdateTime -
|
|
119
151
|
server.lastWriteDate -
|
|
@@ -122,12 +154,8 @@ function maxStalenessReducer(
|
|
|
122
154
|
|
|
123
155
|
const staleness = stalenessMS / 1000;
|
|
124
156
|
const maxStalenessSeconds = readPreference.maxStalenessSeconds ?? 0;
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
return result;
|
|
130
|
-
}, []);
|
|
157
|
+
return staleness <= maxStalenessSeconds;
|
|
158
|
+
});
|
|
131
159
|
}
|
|
132
160
|
|
|
133
161
|
if (topologyDescription.type === TopologyType.ReplicaSetNoPrimary) {
|
|
@@ -139,40 +167,38 @@ function maxStalenessReducer(
|
|
|
139
167
|
s.lastWriteDate > max.lastWriteDate ? s : max
|
|
140
168
|
);
|
|
141
169
|
|
|
142
|
-
return servers.
|
|
170
|
+
return servers.filter((server: ServerDescription) => {
|
|
143
171
|
const stalenessMS =
|
|
144
172
|
sMax.lastWriteDate - server.lastWriteDate + topologyDescription.heartbeatFrequencyMS;
|
|
145
173
|
|
|
146
174
|
const staleness = stalenessMS / 1000;
|
|
147
175
|
const maxStalenessSeconds = readPreference.maxStalenessSeconds ?? 0;
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
return result;
|
|
153
|
-
}, []);
|
|
176
|
+
return staleness <= maxStalenessSeconds;
|
|
177
|
+
});
|
|
154
178
|
}
|
|
155
179
|
|
|
156
180
|
return servers;
|
|
157
181
|
}
|
|
158
182
|
|
|
159
183
|
/**
|
|
160
|
-
* Determines whether a server's tags match a given set of tags
|
|
184
|
+
* Determines whether a server's tags match a given set of tags.
|
|
185
|
+
*
|
|
186
|
+
* A tagset matches the server's tags if every k-v pair in the tagset
|
|
187
|
+
* is also in the server's tagset.
|
|
188
|
+
*
|
|
189
|
+
* Note that this does not requires that every k-v pair in the server's tagset is also
|
|
190
|
+
* in the client's tagset. The server's tagset is required only to be a superset of the
|
|
191
|
+
* client's tags.
|
|
192
|
+
*
|
|
193
|
+
* @see https://github.com/mongodb/specifications/blob/master/source/server-selection/server-selection.md#tag_sets
|
|
161
194
|
*
|
|
162
195
|
* @param tagSet - The requested tag set to match
|
|
163
196
|
* @param serverTags - The server's tags
|
|
164
197
|
*/
|
|
165
198
|
function tagSetMatch(tagSet: TagSet, serverTags: TagSet) {
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
const key = keys[i];
|
|
170
|
-
if (serverTagKeys.indexOf(key) === -1 || serverTags[key] !== tagSet[key]) {
|
|
171
|
-
return false;
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
return true;
|
|
199
|
+
return Object.entries(tagSet).every(
|
|
200
|
+
([key, value]) => serverTags[key] != null && serverTags[key] === value
|
|
201
|
+
);
|
|
176
202
|
}
|
|
177
203
|
|
|
178
204
|
/**
|
|
@@ -183,24 +209,17 @@ function tagSetMatch(tagSet: TagSet, serverTags: TagSet) {
|
|
|
183
209
|
* @returns The list of servers matching the requested tags
|
|
184
210
|
*/
|
|
185
211
|
function tagSetReducer(
|
|
186
|
-
|
|
212
|
+
{ tags }: ReadPreference,
|
|
187
213
|
servers: ServerDescription[]
|
|
188
214
|
): ServerDescription[] {
|
|
189
|
-
if (
|
|
190
|
-
|
|
191
|
-
(Array.isArray(readPreference.tags) && readPreference.tags.length === 0)
|
|
192
|
-
) {
|
|
215
|
+
if (tags == null || tags.length === 0) {
|
|
216
|
+
// empty tag sets match all servers
|
|
193
217
|
return servers;
|
|
194
218
|
}
|
|
195
219
|
|
|
196
|
-
for (
|
|
197
|
-
const
|
|
198
|
-
|
|
199
|
-
(matched: ServerDescription[], server: ServerDescription) => {
|
|
200
|
-
if (tagSetMatch(tagSet, server.tags)) matched.push(server);
|
|
201
|
-
return matched;
|
|
202
|
-
},
|
|
203
|
-
[]
|
|
220
|
+
for (const tagSet of tags) {
|
|
221
|
+
const serversMatchingTagset = servers.filter((s: ServerDescription) =>
|
|
222
|
+
tagSetMatch(tagSet, s.tags)
|
|
204
223
|
);
|
|
205
224
|
|
|
206
225
|
if (serversMatchingTagset.length) {
|
|
@@ -231,10 +250,7 @@ function latencyWindowReducer(
|
|
|
231
250
|
);
|
|
232
251
|
|
|
233
252
|
const high = low + topologyDescription.localThresholdMS;
|
|
234
|
-
return servers.
|
|
235
|
-
if (server.roundTripTime <= high && server.roundTripTime >= low) result.push(server);
|
|
236
|
-
return result;
|
|
237
|
-
}, []);
|
|
253
|
+
return servers.filter(server => server.roundTripTime <= high && server.roundTripTime >= low);
|
|
238
254
|
}
|
|
239
255
|
|
|
240
256
|
// filters
|
|
@@ -258,6 +274,107 @@ function loadBalancerFilter(server: ServerDescription): boolean {
|
|
|
258
274
|
return server.type === ServerType.LoadBalancer;
|
|
259
275
|
}
|
|
260
276
|
|
|
277
|
+
function isDeprioritizedFactory(
|
|
278
|
+
deprioritized: DeprioritizedServers
|
|
279
|
+
): (server: ServerDescription) => boolean {
|
|
280
|
+
return server =>
|
|
281
|
+
// if any deprioritized servers equal the server, here we are.
|
|
282
|
+
!deprioritized.has(server);
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
function secondarySelector(
|
|
286
|
+
readPreference: ReadPreference,
|
|
287
|
+
topologyDescription: TopologyDescription,
|
|
288
|
+
servers: ServerDescription[],
|
|
289
|
+
deprioritized: DeprioritizedServers
|
|
290
|
+
) {
|
|
291
|
+
const mode = readPreference.mode;
|
|
292
|
+
switch (mode) {
|
|
293
|
+
case 'primary':
|
|
294
|
+
// Note: no need to filter for deprioritized servers. A replica set has only one primary; that means that
|
|
295
|
+
// we are in one of two scenarios:
|
|
296
|
+
// 1. deprioritized servers is empty - return the primary.
|
|
297
|
+
// 2. deprioritized servers contains the primary - return the primary.
|
|
298
|
+
return servers.filter(primaryFilter);
|
|
299
|
+
case 'primaryPreferred': {
|
|
300
|
+
const primary = servers.filter(primaryFilter);
|
|
301
|
+
|
|
302
|
+
// If there is a primary and it is not deprioritized, use the primary. Otherwise,
|
|
303
|
+
// check for secondaries.
|
|
304
|
+
const eligiblePrimary = primary.filter(isDeprioritizedFactory(deprioritized));
|
|
305
|
+
if (eligiblePrimary.length) {
|
|
306
|
+
return eligiblePrimary;
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
// If we make it here, we either have:
|
|
310
|
+
// 1. a deprioritized primary
|
|
311
|
+
// 2. no eligible primary
|
|
312
|
+
// secondaries take precedence of deprioritized primaries.
|
|
313
|
+
const secondaries = tagSetReducer(
|
|
314
|
+
readPreference,
|
|
315
|
+
maxStalenessReducer(readPreference, topologyDescription, servers.filter(secondaryFilter))
|
|
316
|
+
);
|
|
317
|
+
|
|
318
|
+
const eligibleSecondaries = secondaries.filter(isDeprioritizedFactory(deprioritized));
|
|
319
|
+
if (eligibleSecondaries.length) {
|
|
320
|
+
return latencyWindowReducer(topologyDescription, eligibleSecondaries);
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
// if we make it here, we have no primaries or secondaries that not deprioritized.
|
|
324
|
+
// prefer the primary (which may not exist, if the topology has no primary).
|
|
325
|
+
// otherwise, return the secondaries (which also may not exist, but there is nothing else to check here).
|
|
326
|
+
return primary.length ? primary : latencyWindowReducer(topologyDescription, secondaries);
|
|
327
|
+
}
|
|
328
|
+
case 'nearest': {
|
|
329
|
+
const eligible = filterDeprioritized(
|
|
330
|
+
tagSetReducer(
|
|
331
|
+
readPreference,
|
|
332
|
+
maxStalenessReducer(readPreference, topologyDescription, servers.filter(nearestFilter))
|
|
333
|
+
),
|
|
334
|
+
deprioritized
|
|
335
|
+
);
|
|
336
|
+
return latencyWindowReducer(topologyDescription, eligible);
|
|
337
|
+
}
|
|
338
|
+
case 'secondary':
|
|
339
|
+
case 'secondaryPreferred': {
|
|
340
|
+
const secondaries = tagSetReducer(
|
|
341
|
+
readPreference,
|
|
342
|
+
maxStalenessReducer(readPreference, topologyDescription, servers.filter(secondaryFilter))
|
|
343
|
+
);
|
|
344
|
+
const eligibleSecondaries = secondaries.filter(isDeprioritizedFactory(deprioritized));
|
|
345
|
+
|
|
346
|
+
if (eligibleSecondaries.length) {
|
|
347
|
+
return latencyWindowReducer(topologyDescription, eligibleSecondaries);
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
// we have no eligible secondaries, try for a primary if we can.
|
|
351
|
+
if (mode === ReadPreference.SECONDARY_PREFERRED) {
|
|
352
|
+
const primary = servers.filter(primaryFilter);
|
|
353
|
+
|
|
354
|
+
// unlike readPreference=primary, here we do filter for deprioritized servers.
|
|
355
|
+
// if the primary is deprioritized, deprioritized secondaries take precedence.
|
|
356
|
+
const eligiblePrimary = primary.filter(isDeprioritizedFactory(deprioritized));
|
|
357
|
+
if (eligiblePrimary.length) return eligiblePrimary;
|
|
358
|
+
|
|
359
|
+
// we have no eligible primary nor secondaries that have not been deprioritized
|
|
360
|
+
return secondaries.length
|
|
361
|
+
? latencyWindowReducer(topologyDescription, secondaries)
|
|
362
|
+
: primary;
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
// return all secondaries in the latency window.
|
|
366
|
+
return latencyWindowReducer(topologyDescription, secondaries);
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
default: {
|
|
370
|
+
const _exhaustiveCheck: never = mode;
|
|
371
|
+
throw new MongoRuntimeError(
|
|
372
|
+
`unexpected readPreference=${mode} (should never happen). Please report a bug in the Node driver Jira project.`
|
|
373
|
+
);
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
|
|
261
378
|
/**
|
|
262
379
|
* Returns a function which selects servers based on a provided read preference
|
|
263
380
|
*
|
|
@@ -271,53 +388,28 @@ export function readPreferenceServerSelector(readPreference: ReadPreference): Se
|
|
|
271
388
|
return function readPreferenceServers(
|
|
272
389
|
topologyDescription: TopologyDescription,
|
|
273
390
|
servers: ServerDescription[],
|
|
274
|
-
deprioritized:
|
|
391
|
+
deprioritized: DeprioritizedServers
|
|
275
392
|
): ServerDescription[] {
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
const mode = readPreference.mode;
|
|
297
|
-
if (mode === ReadPreference.PRIMARY) {
|
|
298
|
-
return servers.filter(primaryFilter);
|
|
299
|
-
}
|
|
300
|
-
|
|
301
|
-
if (mode === ReadPreference.PRIMARY_PREFERRED) {
|
|
302
|
-
const result = servers.filter(primaryFilter);
|
|
303
|
-
if (result.length) {
|
|
304
|
-
return result;
|
|
393
|
+
switch (topologyDescription.type) {
|
|
394
|
+
case 'Single':
|
|
395
|
+
return latencyWindowReducer(topologyDescription, servers.filter(knownFilter));
|
|
396
|
+
case 'ReplicaSetNoPrimary':
|
|
397
|
+
case 'ReplicaSetWithPrimary':
|
|
398
|
+
return secondarySelector(readPreference, topologyDescription, servers, deprioritized);
|
|
399
|
+
case 'Sharded': {
|
|
400
|
+
const selectable = filterDeprioritized(servers, deprioritized);
|
|
401
|
+
return latencyWindowReducer(topologyDescription, selectable.filter(knownFilter));
|
|
402
|
+
}
|
|
403
|
+
case 'Unknown':
|
|
404
|
+
return [];
|
|
405
|
+
case 'LoadBalanced':
|
|
406
|
+
return servers.filter(loadBalancerFilter);
|
|
407
|
+
default: {
|
|
408
|
+
const _exhaustiveCheck: never = topologyDescription.type;
|
|
409
|
+
throw new MongoRuntimeError(
|
|
410
|
+
`unexpected topology type: ${topologyDescription.type} (this should never happen). Please file a bug in the Node driver Jira project.`
|
|
411
|
+
);
|
|
305
412
|
}
|
|
306
413
|
}
|
|
307
|
-
|
|
308
|
-
const filter = mode === ReadPreference.NEAREST ? nearestFilter : secondaryFilter;
|
|
309
|
-
const selectedServers = latencyWindowReducer(
|
|
310
|
-
topologyDescription,
|
|
311
|
-
tagSetReducer(
|
|
312
|
-
readPreference,
|
|
313
|
-
maxStalenessReducer(readPreference, topologyDescription, servers.filter(filter))
|
|
314
|
-
)
|
|
315
|
-
);
|
|
316
|
-
|
|
317
|
-
if (mode === ReadPreference.SECONDARY_PREFERRED && selectedServers.length === 0) {
|
|
318
|
-
return servers.filter(primaryFilter);
|
|
319
|
-
}
|
|
320
|
-
|
|
321
|
-
return selectedServers;
|
|
322
414
|
};
|
|
323
415
|
}
|
package/src/sdam/topology.ts
CHANGED
|
@@ -70,7 +70,11 @@ import {
|
|
|
70
70
|
import type { ServerMonitoringMode } from './monitor';
|
|
71
71
|
import { Server, type ServerEvents, type ServerOptions } from './server';
|
|
72
72
|
import { compareTopologyVersion, ServerDescription } from './server_description';
|
|
73
|
-
import {
|
|
73
|
+
import {
|
|
74
|
+
DeprioritizedServers,
|
|
75
|
+
readPreferenceServerSelector,
|
|
76
|
+
type ServerSelector
|
|
77
|
+
} from './server_selection';
|
|
74
78
|
import {
|
|
75
79
|
ServerSelectionFailedEvent,
|
|
76
80
|
ServerSelectionStartedEvent,
|
|
@@ -105,7 +109,7 @@ export interface ServerSelectionRequest {
|
|
|
105
109
|
cancelled: boolean;
|
|
106
110
|
operationName: string;
|
|
107
111
|
waitingLogged: boolean;
|
|
108
|
-
|
|
112
|
+
deprioritizedServers: DeprioritizedServers;
|
|
109
113
|
}
|
|
110
114
|
|
|
111
115
|
/** @internal */
|
|
@@ -169,7 +173,9 @@ export interface SelectServerOptions {
|
|
|
169
173
|
serverSelectionTimeoutMS?: number;
|
|
170
174
|
session?: ClientSession;
|
|
171
175
|
operationName: string;
|
|
172
|
-
|
|
176
|
+
|
|
177
|
+
/** @internal */
|
|
178
|
+
deprioritizedServers: DeprioritizedServers;
|
|
173
179
|
/**
|
|
174
180
|
* @internal
|
|
175
181
|
* TODO(NODE-6496): Make this required by making ChangeStream use LegacyTimeoutContext
|
|
@@ -455,7 +461,8 @@ export class Topology extends TypedEventEmitter<TopologyEvents> {
|
|
|
455
461
|
const selectServerOptions = {
|
|
456
462
|
operationName: 'handshake',
|
|
457
463
|
...options,
|
|
458
|
-
timeoutContext
|
|
464
|
+
timeoutContext,
|
|
465
|
+
deprioritizedServers: new DeprioritizedServers()
|
|
459
466
|
};
|
|
460
467
|
|
|
461
468
|
try {
|
|
@@ -605,7 +612,7 @@ export class Topology extends TypedEventEmitter<TopologyEvents> {
|
|
|
605
612
|
startTime: processTimeMS(),
|
|
606
613
|
operationName: options.operationName,
|
|
607
614
|
waitingLogged: false,
|
|
608
|
-
|
|
615
|
+
deprioritizedServers: options.deprioritizedServers
|
|
609
616
|
};
|
|
610
617
|
|
|
611
618
|
const abortListener = addAbortListener(options.signal, function () {
|
|
@@ -957,13 +964,9 @@ function processWaitQueue(topology: Topology) {
|
|
|
957
964
|
let selectedDescriptions;
|
|
958
965
|
try {
|
|
959
966
|
const serverSelector = waitQueueMember.serverSelector;
|
|
960
|
-
const
|
|
967
|
+
const deprioritizedServers = waitQueueMember.deprioritizedServers;
|
|
961
968
|
selectedDescriptions = serverSelector
|
|
962
|
-
? serverSelector(
|
|
963
|
-
topology.description,
|
|
964
|
-
serverDescriptions,
|
|
965
|
-
previousServer ? [previousServer] : []
|
|
966
|
-
)
|
|
969
|
+
? serverSelector(topology.description, serverDescriptions, deprioritizedServers)
|
|
967
970
|
: serverDescriptions;
|
|
968
971
|
} catch (selectorError) {
|
|
969
972
|
if (
|