mongodb 4.7.0 → 4.9.0
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/bson.js +4 -2
- package/lib/bson.js.map +1 -1
- package/lib/bulk/common.js +1 -0
- package/lib/bulk/common.js.map +1 -1
- package/lib/change_stream.js +136 -271
- package/lib/change_stream.js.map +1 -1
- package/lib/cmap/command_monitoring_events.js +2 -32
- package/lib/cmap/command_monitoring_events.js.map +1 -1
- package/lib/cmap/commands.js +1 -153
- package/lib/cmap/commands.js.map +1 -1
- package/lib/cmap/connect.js +3 -6
- package/lib/cmap/connect.js.map +1 -1
- package/lib/cmap/connection.js +21 -84
- package/lib/cmap/connection.js.map +1 -1
- package/lib/cmap/connection_pool.js +196 -170
- package/lib/cmap/connection_pool.js.map +1 -1
- package/lib/cmap/message_stream.js.map +1 -1
- package/lib/cmap/wire_protocol/compression.js +2 -6
- package/lib/cmap/wire_protocol/compression.js.map +1 -1
- package/lib/cmap/wire_protocol/constants.js +1 -3
- package/lib/cmap/wire_protocol/constants.js.map +1 -1
- package/lib/collection.js +24 -9
- package/lib/collection.js.map +1 -1
- package/lib/connection_string.js.map +1 -1
- package/lib/cursor/abstract_cursor.js +62 -75
- package/lib/cursor/abstract_cursor.js.map +1 -1
- package/lib/cursor/change_stream_cursor.js +115 -0
- package/lib/cursor/change_stream_cursor.js.map +1 -0
- package/lib/cursor/list_collections_cursor.js +37 -0
- package/lib/cursor/list_collections_cursor.js.map +1 -0
- package/lib/cursor/list_indexes_cursor.js +36 -0
- package/lib/cursor/list_indexes_cursor.js.map +1 -0
- package/lib/db.js +2 -2
- package/lib/db.js.map +1 -1
- package/lib/deps.js.map +1 -1
- package/lib/encrypter.js +3 -13
- package/lib/encrypter.js.map +1 -1
- package/lib/index.js +28 -21
- package/lib/index.js.map +1 -1
- package/lib/mongo_client.js +62 -21
- package/lib/mongo_client.js.map +1 -1
- package/lib/mongo_types.js.map +1 -1
- package/lib/operations/common_functions.js.map +1 -1
- package/lib/operations/create_collection.js.map +1 -1
- package/lib/operations/distinct.js +5 -5
- package/lib/operations/distinct.js.map +1 -1
- package/lib/operations/estimated_document_count.js +5 -0
- package/lib/operations/estimated_document_count.js.map +1 -1
- package/lib/operations/execute_operation.js +17 -8
- package/lib/operations/execute_operation.js.map +1 -1
- package/lib/operations/find.js +3 -0
- package/lib/operations/find.js.map +1 -1
- package/lib/operations/get_more.js +32 -7
- package/lib/operations/get_more.js.map +1 -1
- package/lib/operations/indexes.js +39 -65
- package/lib/operations/indexes.js.map +1 -1
- package/lib/operations/kill_cursors.js +32 -0
- package/lib/operations/kill_cursors.js.map +1 -0
- package/lib/operations/list_collections.js +1 -33
- package/lib/operations/list_collections.js.map +1 -1
- package/lib/operations/operation.js +4 -1
- package/lib/operations/operation.js.map +1 -1
- package/lib/read_preference.js.map +1 -1
- package/lib/sdam/common.js +2 -1
- package/lib/sdam/common.js.map +1 -1
- package/lib/sdam/monitor.js +2 -1
- package/lib/sdam/monitor.js.map +1 -1
- package/lib/sdam/server.js +1 -52
- package/lib/sdam/server.js.map +1 -1
- package/lib/sdam/server_description.js +51 -58
- package/lib/sdam/server_description.js.map +1 -1
- package/lib/sdam/srv_polling.js +2 -2
- package/lib/sdam/srv_polling.js.map +1 -1
- package/lib/sdam/topology.js +28 -67
- package/lib/sdam/topology.js.map +1 -1
- package/lib/sdam/topology_description.js +24 -42
- package/lib/sdam/topology_description.js.map +1 -1
- package/lib/sessions.js +29 -31
- package/lib/sessions.js.map +1 -1
- package/lib/utils.js +65 -70
- package/lib/utils.js.map +1 -1
- package/mongodb.d.ts +136 -73
- package/package.json +23 -22
- package/src/bson.ts +4 -0
- package/src/bulk/common.ts +1 -0
- package/src/change_stream.ts +147 -373
- package/src/cmap/command_monitoring_events.ts +3 -37
- package/src/cmap/commands.ts +2 -190
- package/src/cmap/connect.ts +20 -25
- package/src/cmap/connection.ts +27 -139
- package/src/cmap/connection_pool.ts +208 -169
- package/src/cmap/message_stream.ts +2 -3
- package/src/cmap/wire_protocol/compression.ts +8 -6
- package/src/cmap/wire_protocol/constants.ts +0 -2
- package/src/collection.ts +27 -13
- package/src/connection_string.ts +1 -1
- package/src/cursor/abstract_cursor.ts +98 -87
- package/src/cursor/change_stream_cursor.ts +194 -0
- package/src/cursor/list_collections_cursor.ts +52 -0
- package/src/cursor/list_indexes_cursor.ts +41 -0
- package/src/db.ts +2 -5
- package/src/deps.ts +13 -22
- package/src/encrypter.ts +4 -14
- package/src/index.ts +13 -9
- package/src/mongo_client.ts +102 -33
- package/src/mongo_types.ts +81 -57
- package/src/operations/common_functions.ts +1 -1
- package/src/operations/create_collection.ts +1 -2
- package/src/operations/distinct.ts +7 -9
- package/src/operations/estimated_document_count.ts +6 -0
- package/src/operations/execute_operation.ts +17 -8
- package/src/operations/find.ts +9 -0
- package/src/operations/get_more.ts +56 -13
- package/src/operations/indexes.ts +52 -89
- package/src/operations/kill_cursors.ts +53 -0
- package/src/operations/list_collections.ts +0 -43
- package/src/operations/operation.ts +5 -1
- package/src/read_preference.ts +5 -9
- package/src/sdam/common.ts +2 -0
- package/src/sdam/monitor.ts +2 -1
- package/src/sdam/server.ts +4 -89
- package/src/sdam/server_description.ts +70 -80
- package/src/sdam/srv_polling.ts +1 -1
- package/src/sdam/topology.ts +37 -103
- package/src/sdam/topology_description.ts +44 -68
- package/src/sessions.ts +32 -39
- package/src/utils.ts +78 -75
package/src/sdam/topology.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import Denque = require('denque');
|
|
2
|
-
import { setTimeout } from 'timers';
|
|
2
|
+
import { clearTimeout, setTimeout } from 'timers';
|
|
3
|
+
import { promisify } from 'util';
|
|
3
4
|
|
|
4
5
|
import type { BSONSerializeOptions, Document } from '../bson';
|
|
5
6
|
import { deserialize, serialize } from '../bson';
|
|
@@ -29,20 +30,14 @@ import {
|
|
|
29
30
|
MongoServerSelectionError,
|
|
30
31
|
MongoTopologyClosedError
|
|
31
32
|
} from '../error';
|
|
32
|
-
import type { MongoClient,
|
|
33
|
+
import type { MongoClient, ServerApi } from '../mongo_client';
|
|
33
34
|
import { TypedEventEmitter } from '../mongo_types';
|
|
34
35
|
import { ReadPreference, ReadPreferenceLike } from '../read_preference';
|
|
35
|
-
import {
|
|
36
|
-
ClientSession,
|
|
37
|
-
ClientSessionOptions,
|
|
38
|
-
ServerSessionId,
|
|
39
|
-
ServerSessionPool
|
|
40
|
-
} from '../sessions';
|
|
36
|
+
import type { ClientSession } from '../sessions';
|
|
41
37
|
import type { Transaction } from '../transactions';
|
|
42
38
|
import {
|
|
43
39
|
Callback,
|
|
44
40
|
ClientMetadata,
|
|
45
|
-
eachAsync,
|
|
46
41
|
emitWarning,
|
|
47
42
|
EventEmitterWithState,
|
|
48
43
|
HostAddress,
|
|
@@ -120,10 +115,6 @@ export interface TopologyPrivate {
|
|
|
120
115
|
minHeartbeatFrequencyMS: number;
|
|
121
116
|
/** A map of server instances to normalized addresses */
|
|
122
117
|
servers: Map<string, Server>;
|
|
123
|
-
/** Server Session Pool */
|
|
124
|
-
sessionPool: ServerSessionPool;
|
|
125
|
-
/** Active client sessions */
|
|
126
|
-
sessions: Set<ClientSession>;
|
|
127
118
|
credentials?: MongoCredentials;
|
|
128
119
|
clusterTime?: ClusterTime;
|
|
129
120
|
/** timers created for the initial connect to a server */
|
|
@@ -316,10 +307,6 @@ export class Topology extends TypedEventEmitter<TopologyEvents> {
|
|
|
316
307
|
minHeartbeatFrequencyMS: options.minHeartbeatFrequencyMS,
|
|
317
308
|
// a map of server instances to normalized addresses
|
|
318
309
|
servers: new Map(),
|
|
319
|
-
// Server Session Pool
|
|
320
|
-
sessionPool: new ServerSessionPool(this),
|
|
321
|
-
// Active client sessions
|
|
322
|
-
sessions: new Set(),
|
|
323
310
|
credentials: options?.credentials,
|
|
324
311
|
clusterTime: undefined,
|
|
325
312
|
|
|
@@ -443,13 +430,13 @@ export class Topology extends TypedEventEmitter<TopologyEvents> {
|
|
|
443
430
|
}
|
|
444
431
|
}
|
|
445
432
|
|
|
433
|
+
const exitWithError = (error: Error) =>
|
|
434
|
+
callback ? callback(error) : this.emit(Topology.ERROR, error);
|
|
435
|
+
|
|
446
436
|
const readPreference = options.readPreference ?? ReadPreference.primary;
|
|
447
437
|
this.selectServer(readPreferenceServerSelector(readPreference), options, (err, server) => {
|
|
448
438
|
if (err) {
|
|
449
|
-
this.close();
|
|
450
|
-
|
|
451
|
-
typeof callback === 'function' ? callback(err) : this.emit(Topology.ERROR, err);
|
|
452
|
-
return;
|
|
439
|
+
return this.close({ force: false }, () => exitWithError(err));
|
|
453
440
|
}
|
|
454
441
|
|
|
455
442
|
// TODO: NODE-2471
|
|
@@ -457,15 +444,14 @@ export class Topology extends TypedEventEmitter<TopologyEvents> {
|
|
|
457
444
|
if (!skipPingOnConnect && server && this.s.credentials) {
|
|
458
445
|
server.command(ns('admin.$cmd'), { ping: 1 }, {}, err => {
|
|
459
446
|
if (err) {
|
|
460
|
-
|
|
461
|
-
return;
|
|
447
|
+
return exitWithError(err);
|
|
462
448
|
}
|
|
463
449
|
|
|
464
450
|
stateTransition(this, STATE_CONNECTED);
|
|
465
451
|
this.emit(Topology.OPEN, this);
|
|
466
452
|
this.emit(Topology.CONNECT, this);
|
|
467
453
|
|
|
468
|
-
|
|
454
|
+
callback?.(undefined, this);
|
|
469
455
|
});
|
|
470
456
|
|
|
471
457
|
return;
|
|
@@ -475,7 +461,7 @@ export class Topology extends TypedEventEmitter<TopologyEvents> {
|
|
|
475
461
|
this.emit(Topology.OPEN, this);
|
|
476
462
|
this.emit(Topology.CONNECT, this);
|
|
477
463
|
|
|
478
|
-
|
|
464
|
+
callback?.(undefined, this);
|
|
479
465
|
});
|
|
480
466
|
}
|
|
481
467
|
|
|
@@ -489,52 +475,38 @@ export class Topology extends TypedEventEmitter<TopologyEvents> {
|
|
|
489
475
|
if (typeof options === 'boolean') {
|
|
490
476
|
options = { force: options };
|
|
491
477
|
}
|
|
492
|
-
|
|
493
478
|
options = options ?? {};
|
|
494
|
-
if (this.s.state === STATE_CLOSED || this.s.state === STATE_CLOSING) {
|
|
495
|
-
if (typeof callback === 'function') {
|
|
496
|
-
callback();
|
|
497
|
-
}
|
|
498
479
|
|
|
499
|
-
|
|
480
|
+
if (this.s.state === STATE_CLOSED || this.s.state === STATE_CLOSING) {
|
|
481
|
+
return callback?.();
|
|
500
482
|
}
|
|
501
483
|
|
|
502
|
-
|
|
484
|
+
const destroyedServers = Array.from(this.s.servers.values(), server => {
|
|
485
|
+
return promisify(destroyServer)(server, this, options);
|
|
486
|
+
});
|
|
503
487
|
|
|
504
|
-
|
|
505
|
-
|
|
488
|
+
Promise.all(destroyedServers)
|
|
489
|
+
.then(() => {
|
|
490
|
+
this.s.servers.clear();
|
|
506
491
|
|
|
507
|
-
|
|
508
|
-
this.s.srvPoller.stop();
|
|
509
|
-
this.s.srvPoller.removeListener(SrvPoller.SRV_RECORD_DISCOVERY, this.s.detectSrvRecords);
|
|
510
|
-
}
|
|
492
|
+
stateTransition(this, STATE_CLOSING);
|
|
511
493
|
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
stateTransition(this, STATE_CLOSED);
|
|
529
|
-
|
|
530
|
-
if (typeof callback === 'function') {
|
|
531
|
-
callback(err);
|
|
532
|
-
}
|
|
533
|
-
}
|
|
534
|
-
);
|
|
535
|
-
});
|
|
536
|
-
}
|
|
537
|
-
);
|
|
494
|
+
drainWaitQueue(this[kWaitQueue], new MongoTopologyClosedError());
|
|
495
|
+
drainTimerQueue(this.s.connectionTimers);
|
|
496
|
+
|
|
497
|
+
if (this.s.srvPoller) {
|
|
498
|
+
this.s.srvPoller.stop();
|
|
499
|
+
this.s.srvPoller.removeListener(SrvPoller.SRV_RECORD_DISCOVERY, this.s.detectSrvRecords);
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
this.removeListener(Topology.TOPOLOGY_DESCRIPTION_CHANGED, this.s.detectShardedTopology);
|
|
503
|
+
|
|
504
|
+
stateTransition(this, STATE_CLOSED);
|
|
505
|
+
|
|
506
|
+
// emit an event for close
|
|
507
|
+
this.emit(Topology.TOPOLOGY_CLOSED, new TopologyClosedEvent(this.s.id));
|
|
508
|
+
})
|
|
509
|
+
.finally(() => callback?.());
|
|
538
510
|
}
|
|
539
511
|
|
|
540
512
|
/**
|
|
@@ -628,44 +600,6 @@ export class Topology extends TypedEventEmitter<TopologyEvents> {
|
|
|
628
600
|
return this.loadBalanced || this.description.logicalSessionTimeoutMinutes != null;
|
|
629
601
|
}
|
|
630
602
|
|
|
631
|
-
/** Start a logical session */
|
|
632
|
-
startSession(options: ClientSessionOptions, clientOptions?: MongoOptions): ClientSession {
|
|
633
|
-
const session = new ClientSession(this.client, this.s.sessionPool, options, clientOptions);
|
|
634
|
-
session.once('ended', () => {
|
|
635
|
-
this.s.sessions.delete(session);
|
|
636
|
-
});
|
|
637
|
-
|
|
638
|
-
this.s.sessions.add(session);
|
|
639
|
-
return session;
|
|
640
|
-
}
|
|
641
|
-
|
|
642
|
-
/** Send endSessions command(s) with the given session ids */
|
|
643
|
-
endSessions(sessions: ServerSessionId[], callback?: Callback<Document>): void {
|
|
644
|
-
if (!Array.isArray(sessions)) {
|
|
645
|
-
sessions = [sessions];
|
|
646
|
-
}
|
|
647
|
-
|
|
648
|
-
this.selectServer(
|
|
649
|
-
readPreferenceServerSelector(ReadPreference.primaryPreferred),
|
|
650
|
-
{},
|
|
651
|
-
(err, server) => {
|
|
652
|
-
if (err || !server) {
|
|
653
|
-
if (typeof callback === 'function') callback(err);
|
|
654
|
-
return;
|
|
655
|
-
}
|
|
656
|
-
|
|
657
|
-
server.command(
|
|
658
|
-
ns('admin.$cmd'),
|
|
659
|
-
{ endSessions: sessions },
|
|
660
|
-
{ noResponse: true },
|
|
661
|
-
(err, result) => {
|
|
662
|
-
if (typeof callback === 'function') callback(err, result);
|
|
663
|
-
}
|
|
664
|
-
);
|
|
665
|
-
}
|
|
666
|
-
);
|
|
667
|
-
}
|
|
668
|
-
|
|
669
603
|
/**
|
|
670
604
|
* Update the internal TopologyDescription with a ServerDescription
|
|
671
605
|
*
|
|
@@ -790,7 +724,7 @@ export class Topology extends TypedEventEmitter<TopologyEvents> {
|
|
|
790
724
|
return this.description.commonWireVersion;
|
|
791
725
|
}
|
|
792
726
|
|
|
793
|
-
get logicalSessionTimeoutMinutes(): number |
|
|
727
|
+
get logicalSessionTimeoutMinutes(): number | null {
|
|
794
728
|
return this.description.logicalSessionTimeoutMinutes;
|
|
795
729
|
}
|
|
796
730
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { ObjectId } from '../bson';
|
|
2
2
|
import * as WIRE_CONSTANTS from '../cmap/wire_protocol/constants';
|
|
3
|
-
import {
|
|
4
|
-
import { shuffle } from '../utils';
|
|
3
|
+
import { MongoRuntimeError, MongoServerError } from '../error';
|
|
4
|
+
import { compareObjectId, shuffle } from '../utils';
|
|
5
5
|
import { ServerType, TopologyType } from './common';
|
|
6
6
|
import { ServerDescription } from './server_description';
|
|
7
7
|
import type { SrvPollingEvent } from './srv_polling';
|
|
@@ -32,29 +32,29 @@ export interface TopologyDescriptionOptions {
|
|
|
32
32
|
*/
|
|
33
33
|
export class TopologyDescription {
|
|
34
34
|
type: TopologyType;
|
|
35
|
-
setName
|
|
36
|
-
maxSetVersion
|
|
37
|
-
maxElectionId
|
|
35
|
+
setName: string | null;
|
|
36
|
+
maxSetVersion: number | null;
|
|
37
|
+
maxElectionId: ObjectId | null;
|
|
38
38
|
servers: Map<string, ServerDescription>;
|
|
39
39
|
stale: boolean;
|
|
40
40
|
compatible: boolean;
|
|
41
41
|
compatibilityError?: string;
|
|
42
|
-
logicalSessionTimeoutMinutes
|
|
42
|
+
logicalSessionTimeoutMinutes: number | null;
|
|
43
43
|
heartbeatFrequencyMS: number;
|
|
44
44
|
localThresholdMS: number;
|
|
45
|
-
commonWireVersion
|
|
45
|
+
commonWireVersion: number;
|
|
46
46
|
|
|
47
47
|
/**
|
|
48
48
|
* Create a TopologyDescription
|
|
49
49
|
*/
|
|
50
50
|
constructor(
|
|
51
51
|
topologyType: TopologyType,
|
|
52
|
-
serverDescriptions
|
|
53
|
-
setName
|
|
54
|
-
maxSetVersion
|
|
55
|
-
maxElectionId
|
|
56
|
-
commonWireVersion
|
|
57
|
-
options
|
|
52
|
+
serverDescriptions: Map<string, ServerDescription> | null = null,
|
|
53
|
+
setName: string | null = null,
|
|
54
|
+
maxSetVersion: number | null = null,
|
|
55
|
+
maxElectionId: ObjectId | null = null,
|
|
56
|
+
commonWireVersion: number | null = null,
|
|
57
|
+
options: TopologyDescriptionOptions | null = null
|
|
58
58
|
) {
|
|
59
59
|
options = options ?? {};
|
|
60
60
|
|
|
@@ -64,22 +64,10 @@ export class TopologyDescription {
|
|
|
64
64
|
this.compatible = true;
|
|
65
65
|
this.heartbeatFrequencyMS = options.heartbeatFrequencyMS ?? 0;
|
|
66
66
|
this.localThresholdMS = options.localThresholdMS ?? 15;
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
if (maxSetVersion) {
|
|
73
|
-
this.maxSetVersion = maxSetVersion;
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
if (maxElectionId) {
|
|
77
|
-
this.maxElectionId = maxElectionId;
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
if (commonWireVersion) {
|
|
81
|
-
this.commonWireVersion = commonWireVersion;
|
|
82
|
-
}
|
|
67
|
+
this.setName = setName ?? null;
|
|
68
|
+
this.maxElectionId = maxElectionId ?? null;
|
|
69
|
+
this.maxSetVersion = maxSetVersion ?? null;
|
|
70
|
+
this.commonWireVersion = commonWireVersion ?? 0;
|
|
83
71
|
|
|
84
72
|
// determine server compatibility
|
|
85
73
|
for (const serverDescription of this.servers.values()) {
|
|
@@ -108,12 +96,12 @@ export class TopologyDescription {
|
|
|
108
96
|
// value among ServerDescriptions of all data-bearing server types. If any have a null
|
|
109
97
|
// logicalSessionTimeoutMinutes, then TopologyDescription.logicalSessionTimeoutMinutes MUST be
|
|
110
98
|
// set to null.
|
|
111
|
-
this.logicalSessionTimeoutMinutes =
|
|
99
|
+
this.logicalSessionTimeoutMinutes = null;
|
|
112
100
|
for (const [, server] of this.servers) {
|
|
113
101
|
if (server.isReadable) {
|
|
114
102
|
if (server.logicalSessionTimeoutMinutes == null) {
|
|
115
103
|
// If any of the servers have a null logicalSessionsTimeout, then the whole topology does
|
|
116
|
-
this.logicalSessionTimeoutMinutes =
|
|
104
|
+
this.logicalSessionTimeoutMinutes = null;
|
|
117
105
|
break;
|
|
118
106
|
}
|
|
119
107
|
|
|
@@ -200,11 +188,6 @@ export class TopologyDescription {
|
|
|
200
188
|
// potentially mutated values
|
|
201
189
|
let { type: topologyType, setName, maxSetVersion, maxElectionId, commonWireVersion } = this;
|
|
202
190
|
|
|
203
|
-
if (serverDescription.setName && setName && serverDescription.setName !== setName) {
|
|
204
|
-
// TODO(NODE-4159): servers with an incorrect setName should be removed not marked Unknown
|
|
205
|
-
serverDescription = new ServerDescription(address, undefined);
|
|
206
|
-
}
|
|
207
|
-
|
|
208
191
|
const serverType = serverDescription.type;
|
|
209
192
|
const serverDescriptions = new Map(this.servers);
|
|
210
193
|
|
|
@@ -217,6 +200,19 @@ export class TopologyDescription {
|
|
|
217
200
|
}
|
|
218
201
|
}
|
|
219
202
|
|
|
203
|
+
if (
|
|
204
|
+
typeof serverDescription.setName === 'string' &&
|
|
205
|
+
typeof setName === 'string' &&
|
|
206
|
+
serverDescription.setName !== setName
|
|
207
|
+
) {
|
|
208
|
+
if (topologyType === TopologyType.Single) {
|
|
209
|
+
// "Single" Topology with setName mismatch is direct connection usage, mark unknown do not remove
|
|
210
|
+
serverDescription = new ServerDescription(address);
|
|
211
|
+
} else {
|
|
212
|
+
serverDescriptions.delete(address);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
220
216
|
// update the actual server description
|
|
221
217
|
serverDescriptions.set(address, serverDescription);
|
|
222
218
|
|
|
@@ -311,7 +307,7 @@ export class TopologyDescription {
|
|
|
311
307
|
);
|
|
312
308
|
}
|
|
313
309
|
|
|
314
|
-
get error():
|
|
310
|
+
get error(): MongoServerError | null {
|
|
315
311
|
const descriptionsWithError = Array.from(this.servers.values()).filter(
|
|
316
312
|
(sd: ServerDescription) => sd.error
|
|
317
313
|
);
|
|
@@ -319,7 +315,8 @@ export class TopologyDescription {
|
|
|
319
315
|
if (descriptionsWithError.length > 0) {
|
|
320
316
|
return descriptionsWithError[0].error;
|
|
321
317
|
}
|
|
322
|
-
|
|
318
|
+
|
|
319
|
+
return null;
|
|
323
320
|
}
|
|
324
321
|
|
|
325
322
|
/**
|
|
@@ -363,34 +360,13 @@ function topologyTypeForServerType(serverType: ServerType): TopologyType {
|
|
|
363
360
|
}
|
|
364
361
|
}
|
|
365
362
|
|
|
366
|
-
// TODO: improve these docs when ObjectId is properly typed
|
|
367
|
-
function compareObjectId(oid1: Document, oid2: Document): number {
|
|
368
|
-
if (oid1 == null) {
|
|
369
|
-
return -1;
|
|
370
|
-
}
|
|
371
|
-
|
|
372
|
-
if (oid2 == null) {
|
|
373
|
-
return 1;
|
|
374
|
-
}
|
|
375
|
-
|
|
376
|
-
if (oid1.id instanceof Buffer && oid2.id instanceof Buffer) {
|
|
377
|
-
const oid1Buffer = oid1.id;
|
|
378
|
-
const oid2Buffer = oid2.id;
|
|
379
|
-
return oid1Buffer.compare(oid2Buffer);
|
|
380
|
-
}
|
|
381
|
-
|
|
382
|
-
const oid1String = oid1.toString();
|
|
383
|
-
const oid2String = oid2.toString();
|
|
384
|
-
return oid1String.localeCompare(oid2String);
|
|
385
|
-
}
|
|
386
|
-
|
|
387
363
|
function updateRsFromPrimary(
|
|
388
364
|
serverDescriptions: Map<string, ServerDescription>,
|
|
389
365
|
serverDescription: ServerDescription,
|
|
390
|
-
setName
|
|
391
|
-
maxSetVersion
|
|
392
|
-
maxElectionId
|
|
393
|
-
): [TopologyType, string
|
|
366
|
+
setName: string | null = null,
|
|
367
|
+
maxSetVersion: number | null = null,
|
|
368
|
+
maxElectionId: ObjectId | null = null
|
|
369
|
+
): [TopologyType, string | null, number | null, ObjectId | null] {
|
|
394
370
|
setName = setName || serverDescription.setName;
|
|
395
371
|
if (setName !== serverDescription.setName) {
|
|
396
372
|
serverDescriptions.delete(serverDescription.address);
|
|
@@ -457,7 +433,7 @@ function updateRsFromPrimary(
|
|
|
457
433
|
function updateRsWithPrimaryFromMember(
|
|
458
434
|
serverDescriptions: Map<string, ServerDescription>,
|
|
459
435
|
serverDescription: ServerDescription,
|
|
460
|
-
setName
|
|
436
|
+
setName: string | null = null
|
|
461
437
|
): TopologyType {
|
|
462
438
|
if (setName == null) {
|
|
463
439
|
// TODO(NODE-3483): should be an appropriate runtime error
|
|
@@ -477,10 +453,10 @@ function updateRsWithPrimaryFromMember(
|
|
|
477
453
|
function updateRsNoPrimaryFromMember(
|
|
478
454
|
serverDescriptions: Map<string, ServerDescription>,
|
|
479
455
|
serverDescription: ServerDescription,
|
|
480
|
-
setName
|
|
481
|
-
): [TopologyType, string
|
|
456
|
+
setName: string | null = null
|
|
457
|
+
): [TopologyType, string | null] {
|
|
482
458
|
const topologyType = TopologyType.ReplicaSetNoPrimary;
|
|
483
|
-
setName = setName
|
|
459
|
+
setName = setName ?? serverDescription.setName;
|
|
484
460
|
if (setName !== serverDescription.setName) {
|
|
485
461
|
serverDescriptions.delete(serverDescription.address);
|
|
486
462
|
return [topologyType, setName];
|
package/src/sessions.ts
CHANGED
|
@@ -27,7 +27,6 @@ import { PromiseProvider } from './promise_provider';
|
|
|
27
27
|
import { ReadConcernLevel } from './read_concern';
|
|
28
28
|
import { ReadPreference } from './read_preference';
|
|
29
29
|
import { _advanceClusterTime, ClusterTime, TopologyType } from './sdam/common';
|
|
30
|
-
import type { Topology } from './sdam/topology';
|
|
31
30
|
import { isTransactionCommand, Transaction, TransactionOptions, TxnState } from './transactions';
|
|
32
31
|
import {
|
|
33
32
|
calculateDurationInMs,
|
|
@@ -269,9 +268,10 @@ export class ClientSession extends TypedEventEmitter<ClientSessionEvents> {
|
|
|
269
268
|
if (serverSession != null) {
|
|
270
269
|
// release the server session back to the pool
|
|
271
270
|
this.sessionPool.release(serverSession);
|
|
272
|
-
// Make sure a new serverSession never makes it
|
|
271
|
+
// Make sure a new serverSession never makes it onto this ClientSession
|
|
273
272
|
Object.defineProperty(this, kServerSession, {
|
|
274
|
-
value: ServerSession.clone(serverSession)
|
|
273
|
+
value: ServerSession.clone(serverSession),
|
|
274
|
+
writable: false
|
|
275
275
|
});
|
|
276
276
|
}
|
|
277
277
|
|
|
@@ -383,7 +383,7 @@ export class ClientSession extends TypedEventEmitter<ClientSessionEvents> {
|
|
|
383
383
|
*/
|
|
384
384
|
startTransaction(options?: TransactionOptions): void {
|
|
385
385
|
if (this[kSnapshotEnabled]) {
|
|
386
|
-
throw new MongoCompatibilityError('Transactions are not
|
|
386
|
+
throw new MongoCompatibilityError('Transactions are not supported in snapshot sessions');
|
|
387
387
|
}
|
|
388
388
|
|
|
389
389
|
if (this.inTransaction()) {
|
|
@@ -616,7 +616,7 @@ function attemptTransaction<TSchema>(
|
|
|
616
616
|
}
|
|
617
617
|
|
|
618
618
|
if (!isPromiseLike(promise)) {
|
|
619
|
-
session.abortTransaction();
|
|
619
|
+
session.abortTransaction().catch(() => null);
|
|
620
620
|
throw new MongoInvalidArgumentError(
|
|
621
621
|
'Function provided to `withTransaction` must return a Promise'
|
|
622
622
|
);
|
|
@@ -878,39 +878,18 @@ export class ServerSession {
|
|
|
878
878
|
* @internal
|
|
879
879
|
*/
|
|
880
880
|
export class ServerSessionPool {
|
|
881
|
-
|
|
881
|
+
client: MongoClient;
|
|
882
882
|
sessions: ServerSession[];
|
|
883
883
|
|
|
884
|
-
constructor(
|
|
885
|
-
if (
|
|
886
|
-
throw new MongoRuntimeError('ServerSessionPool requires a
|
|
884
|
+
constructor(client: MongoClient) {
|
|
885
|
+
if (client == null) {
|
|
886
|
+
throw new MongoRuntimeError('ServerSessionPool requires a MongoClient');
|
|
887
887
|
}
|
|
888
888
|
|
|
889
|
-
this.
|
|
889
|
+
this.client = client;
|
|
890
890
|
this.sessions = [];
|
|
891
891
|
}
|
|
892
892
|
|
|
893
|
-
/** Ends all sessions in the session pool */
|
|
894
|
-
endAllPooledSessions(callback?: Callback<void>): void {
|
|
895
|
-
if (this.sessions.length) {
|
|
896
|
-
this.topology.endSessions(
|
|
897
|
-
this.sessions.map((session: ServerSession) => session.id),
|
|
898
|
-
() => {
|
|
899
|
-
this.sessions = [];
|
|
900
|
-
if (typeof callback === 'function') {
|
|
901
|
-
callback();
|
|
902
|
-
}
|
|
903
|
-
}
|
|
904
|
-
);
|
|
905
|
-
|
|
906
|
-
return;
|
|
907
|
-
}
|
|
908
|
-
|
|
909
|
-
if (typeof callback === 'function') {
|
|
910
|
-
callback();
|
|
911
|
-
}
|
|
912
|
-
}
|
|
913
|
-
|
|
914
893
|
/**
|
|
915
894
|
* Acquire a Server Session from the pool.
|
|
916
895
|
* Iterates through each session in the pool, removing any stale sessions
|
|
@@ -918,16 +897,29 @@ export class ServerSessionPool {
|
|
|
918
897
|
* pool and returned. If no non-stale session is found, a new ServerSession is created.
|
|
919
898
|
*/
|
|
920
899
|
acquire(): ServerSession {
|
|
921
|
-
const sessionTimeoutMinutes = this.topology
|
|
900
|
+
const sessionTimeoutMinutes = this.client.topology?.logicalSessionTimeoutMinutes ?? 10;
|
|
922
901
|
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
902
|
+
let session: ServerSession | null = null;
|
|
903
|
+
|
|
904
|
+
// Try to obtain from session pool
|
|
905
|
+
while (this.sessions.length > 0) {
|
|
906
|
+
const potentialSession = this.sessions.shift();
|
|
907
|
+
if (
|
|
908
|
+
potentialSession != null &&
|
|
909
|
+
(!!this.client.topology?.loadBalanced ||
|
|
910
|
+
!potentialSession.hasTimedOut(sessionTimeoutMinutes))
|
|
911
|
+
) {
|
|
912
|
+
session = potentialSession;
|
|
913
|
+
break;
|
|
927
914
|
}
|
|
928
915
|
}
|
|
929
916
|
|
|
930
|
-
|
|
917
|
+
// If nothing valid came from the pool make a new one
|
|
918
|
+
if (session == null) {
|
|
919
|
+
session = new ServerSession();
|
|
920
|
+
}
|
|
921
|
+
|
|
922
|
+
return session;
|
|
931
923
|
}
|
|
932
924
|
|
|
933
925
|
/**
|
|
@@ -938,9 +930,9 @@ export class ServerSessionPool {
|
|
|
938
930
|
* @param session - The session to release to the pool
|
|
939
931
|
*/
|
|
940
932
|
release(session: ServerSession): void {
|
|
941
|
-
const sessionTimeoutMinutes = this.topology
|
|
933
|
+
const sessionTimeoutMinutes = this.client.topology?.logicalSessionTimeoutMinutes ?? 10;
|
|
942
934
|
|
|
943
|
-
if (this.topology
|
|
935
|
+
if (this.client.topology?.loadBalanced && !sessionTimeoutMinutes) {
|
|
944
936
|
this.sessions.unshift(session);
|
|
945
937
|
}
|
|
946
938
|
|
|
@@ -1010,6 +1002,7 @@ export function applySession(
|
|
|
1010
1002
|
if (isRetryableWrite || inTxnOrTxnCommand) {
|
|
1011
1003
|
serverSession.txnNumber += session[kTxnNumberIncrement];
|
|
1012
1004
|
session[kTxnNumberIncrement] = 0;
|
|
1005
|
+
// TODO(NODE-2674): Preserve int64 sent from MongoDB
|
|
1013
1006
|
command.txnNumber = Long.fromNumber(serverSession.txnNumber);
|
|
1014
1007
|
}
|
|
1015
1008
|
|