mongodb 4.1.1 → 4.2.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/README.md +24 -17
- package/lib/admin.js +5 -5
- package/lib/admin.js.map +1 -1
- package/lib/bulk/common.js +60 -56
- package/lib/bulk/common.js.map +1 -1
- package/lib/change_stream.js +15 -13
- package/lib/change_stream.js.map +1 -1
- package/lib/cmap/auth/gssapi.js +1 -1
- package/lib/cmap/auth/gssapi.js.map +1 -1
- package/lib/cmap/auth/mongo_credentials.js +2 -1
- package/lib/cmap/auth/mongo_credentials.js.map +1 -1
- package/lib/cmap/auth/mongocr.js +2 -2
- package/lib/cmap/auth/mongocr.js.map +1 -1
- package/lib/cmap/auth/mongodb_aws.js +23 -28
- package/lib/cmap/auth/mongodb_aws.js.map +1 -1
- package/lib/cmap/auth/plain.js +1 -1
- package/lib/cmap/auth/plain.js.map +1 -1
- package/lib/cmap/auth/scram.js +5 -5
- package/lib/cmap/auth/scram.js.map +1 -1
- package/lib/cmap/auth/x509.js +1 -1
- package/lib/cmap/auth/x509.js.map +1 -1
- package/lib/cmap/command_monitoring_events.js +15 -15
- package/lib/cmap/command_monitoring_events.js.map +1 -1
- package/lib/cmap/commands.js +1 -1
- package/lib/cmap/commands.js.map +1 -1
- package/lib/cmap/connect.js +2 -2
- package/lib/cmap/connect.js.map +1 -1
- package/lib/cmap/connection.js +15 -15
- package/lib/cmap/connection.js.map +1 -1
- package/lib/cmap/connection_pool.js +3 -3
- package/lib/cmap/connection_pool.js.map +1 -1
- package/lib/cmap/message_stream.js +2 -2
- package/lib/cmap/message_stream.js.map +1 -1
- package/lib/cmap/stream_description.js +1 -1
- package/lib/cmap/stream_description.js.map +1 -1
- package/lib/cmap/wire_protocol/constants.js +4 -4
- package/lib/collection.js +42 -41
- package/lib/collection.js.map +1 -1
- package/lib/connection_string.js +94 -58
- package/lib/connection_string.js.map +1 -1
- package/lib/cursor/abstract_cursor.js +11 -11
- package/lib/cursor/abstract_cursor.js.map +1 -1
- package/lib/cursor/aggregation_cursor.js +15 -15
- package/lib/cursor/aggregation_cursor.js.map +1 -1
- package/lib/cursor/find_cursor.js +23 -23
- package/lib/cursor/find_cursor.js.map +1 -1
- package/lib/db.js +20 -20
- package/lib/db.js.map +1 -1
- package/lib/deps.js +1 -1
- package/lib/deps.js.map +1 -1
- package/lib/gridfs/index.js +3 -3
- package/lib/gridfs/index.js.map +1 -1
- package/lib/gridfs/upload.js +1 -1
- package/lib/gridfs/upload.js.map +1 -1
- package/lib/index.js +8 -2
- package/lib/index.js.map +1 -1
- package/lib/logger.js +5 -5
- package/lib/logger.js.map +1 -1
- package/lib/mongo_client.js +7 -7
- package/lib/mongo_client.js.map +1 -1
- package/lib/mongo_types.js.map +1 -1
- package/lib/operations/add_user.js +3 -3
- package/lib/operations/add_user.js.map +1 -1
- package/lib/operations/aggregate.js +3 -4
- package/lib/operations/aggregate.js.map +1 -1
- package/lib/operations/bulk_write.js +1 -1
- package/lib/operations/bulk_write.js.map +1 -1
- package/lib/operations/command.js +7 -3
- package/lib/operations/command.js.map +1 -1
- package/lib/operations/common_functions.js +1 -1
- package/lib/operations/common_functions.js.map +1 -1
- package/lib/operations/connect.js +1 -1
- package/lib/operations/connect.js.map +1 -1
- package/lib/operations/count.js +1 -1
- package/lib/operations/count.js.map +1 -1
- package/lib/operations/count_documents.js.map +1 -1
- package/lib/operations/create_collection.js +1 -1
- package/lib/operations/create_collection.js.map +1 -1
- package/lib/operations/delete.js +6 -6
- package/lib/operations/delete.js.map +1 -1
- package/lib/operations/distinct.js +4 -4
- package/lib/operations/distinct.js.map +1 -1
- package/lib/operations/drop.js +2 -2
- package/lib/operations/drop.js.map +1 -1
- package/lib/operations/estimated_document_count.js +2 -2
- package/lib/operations/estimated_document_count.js.map +1 -1
- package/lib/operations/eval.js.map +1 -1
- package/lib/operations/execute_operation.js +17 -7
- package/lib/operations/execute_operation.js.map +1 -1
- package/lib/operations/find.js +8 -8
- package/lib/operations/find.js.map +1 -1
- package/lib/operations/find_and_modify.js +7 -7
- package/lib/operations/find_and_modify.js.map +1 -1
- package/lib/operations/indexes.js +14 -14
- package/lib/operations/indexes.js.map +1 -1
- package/lib/operations/insert.js +5 -5
- package/lib/operations/insert.js.map +1 -1
- package/lib/operations/list_collections.js +12 -7
- package/lib/operations/list_collections.js.map +1 -1
- package/lib/operations/list_databases.js +1 -1
- package/lib/operations/list_databases.js.map +1 -1
- package/lib/operations/map_reduce.js +6 -6
- package/lib/operations/map_reduce.js.map +1 -1
- package/lib/operations/operation.js +2 -1
- package/lib/operations/operation.js.map +1 -1
- package/lib/operations/remove_user.js +1 -1
- package/lib/operations/remove_user.js.map +1 -1
- package/lib/operations/rename.js +2 -2
- package/lib/operations/rename.js.map +1 -1
- package/lib/operations/set_profiling_level.js +1 -1
- package/lib/operations/set_profiling_level.js.map +1 -1
- package/lib/operations/stats.js +2 -2
- package/lib/operations/stats.js.map +1 -1
- package/lib/operations/update.js +12 -12
- package/lib/operations/update.js.map +1 -1
- package/lib/sdam/monitor.js +14 -14
- package/lib/sdam/monitor.js.map +1 -1
- package/lib/sdam/server.js +19 -12
- package/lib/sdam/server.js.map +1 -1
- package/lib/sdam/server_description.js +3 -3
- package/lib/sdam/server_description.js.map +1 -1
- package/lib/sdam/server_selection.js +20 -1
- package/lib/sdam/server_selection.js.map +1 -1
- package/lib/sdam/srv_polling.js +9 -9
- package/lib/sdam/srv_polling.js.map +1 -1
- package/lib/sdam/topology.js +29 -21
- package/lib/sdam/topology.js.map +1 -1
- package/lib/sdam/topology_description.js +34 -12
- package/lib/sdam/topology_description.js.map +1 -1
- package/lib/sessions.js +19 -19
- package/lib/sessions.js.map +1 -1
- package/lib/transactions.js.map +1 -1
- package/lib/utils.js +53 -46
- package/lib/utils.js.map +1 -1
- package/mongodb.d.ts +97 -50
- package/mongodb.ts34.d.ts +96 -50
- package/package.json +44 -47
- package/src/bulk/common.ts +56 -35
- package/src/change_stream.ts +8 -6
- package/src/cmap/auth/mongo_credentials.ts +14 -3
- package/src/cmap/auth/mongodb_aws.ts +32 -31
- package/src/cmap/command_monitoring_events.ts +1 -1
- package/src/cmap/connection.ts +1 -0
- package/src/cmap/wire_protocol/constants.ts +4 -4
- package/src/collection.ts +32 -24
- package/src/connection_string.ts +103 -52
- package/src/cursor/abstract_cursor.ts +6 -4
- package/src/cursor/aggregation_cursor.ts +3 -3
- package/src/cursor/find_cursor.ts +1 -1
- package/src/db.ts +5 -6
- package/src/deps.ts +56 -19
- package/src/gridfs/download.ts +1 -1
- package/src/gridfs/upload.ts +11 -7
- package/src/index.ts +12 -1
- package/src/mongo_client.ts +18 -11
- package/src/mongo_types.ts +10 -13
- package/src/operations/aggregate.ts +1 -2
- package/src/operations/command.ts +5 -0
- package/src/operations/count.ts +1 -1
- package/src/operations/count_documents.ts +1 -1
- package/src/operations/create_collection.ts +1 -1
- package/src/operations/eval.ts +1 -1
- package/src/operations/execute_operation.ts +13 -2
- package/src/operations/indexes.ts +2 -2
- package/src/operations/list_collections.ts +12 -4
- package/src/operations/list_databases.ts +6 -1
- package/src/operations/operation.ts +3 -0
- package/src/sdam/server.ts +8 -0
- package/src/sdam/server_selection.ts +25 -0
- package/src/sdam/srv_polling.ts +12 -11
- package/src/sdam/topology.ts +27 -10
- package/src/sdam/topology_description.ts +35 -11
- package/src/transactions.ts +2 -2
- package/src/utils.ts +67 -56
package/src/mongo_types.ts
CHANGED
|
@@ -38,9 +38,11 @@ export type WithId<TSchema> = EnhancedOmit<TSchema, '_id'> & { _id: InferIdType<
|
|
|
38
38
|
* `TSchema['_id'] extends ObjectId` which translated to "Is the _id property ObjectId?"
|
|
39
39
|
* we instead ask "Does ObjectId look like (have the same shape) as the _id?"
|
|
40
40
|
*/
|
|
41
|
-
export type OptionalId<TSchema
|
|
42
|
-
?
|
|
43
|
-
|
|
41
|
+
export type OptionalId<TSchema> = TSchema extends { _id?: any }
|
|
42
|
+
? ObjectId extends TSchema['_id'] // a Schema with ObjectId _id type or "any" or "indexed type" provided
|
|
43
|
+
? EnhancedOmit<TSchema, '_id'> & { _id?: InferIdType<TSchema> } // a Schema provided but _id type is not ObjectId
|
|
44
|
+
: WithId<TSchema>
|
|
45
|
+
: EnhancedOmit<TSchema, '_id'> & { _id?: InferIdType<TSchema> }; // TODO(NODE-3285): Improve type readability
|
|
44
46
|
|
|
45
47
|
/** TypeScript Omit (Exclude to be specific) does not work for objects with an "any" indexed type, and breaks discriminated unions @public */
|
|
46
48
|
export type EnhancedOmit<TRecordOrUnion, KeyUnion> = string extends keyof TRecordOrUnion
|
|
@@ -55,8 +57,7 @@ export type WithoutId<TSchema> = Omit<TSchema, '_id'>;
|
|
|
55
57
|
/** A MongoDB filter can be some portion of the schema or a set of operators @public */
|
|
56
58
|
export type Filter<TSchema> = {
|
|
57
59
|
[P in keyof TSchema]?: Condition<TSchema[P]>;
|
|
58
|
-
} &
|
|
59
|
-
RootFilterOperators<TSchema>;
|
|
60
|
+
} & RootFilterOperators<TSchema>;
|
|
60
61
|
|
|
61
62
|
/** @public */
|
|
62
63
|
export type Condition<T> = AlternativeType<T> | FilterOperators<AlternativeType<T>>;
|
|
@@ -255,8 +256,7 @@ export type SetFields<TSchema> = ({
|
|
|
255
256
|
readonly [key in KeysOfAType<TSchema, ReadonlyArray<any> | undefined>]?:
|
|
256
257
|
| OptionalId<Flatten<TSchema[key]>>
|
|
257
258
|
| AddToSetOperators<Array<OptionalId<Flatten<TSchema[key]>>>>;
|
|
258
|
-
} &
|
|
259
|
-
NotAcceptedFields<TSchema, ReadonlyArray<any> | undefined>) & {
|
|
259
|
+
} & NotAcceptedFields<TSchema, ReadonlyArray<any> | undefined>) & {
|
|
260
260
|
readonly [key: string]: AddToSetOperators<any> | any;
|
|
261
261
|
};
|
|
262
262
|
|
|
@@ -265,8 +265,7 @@ export type PushOperator<TSchema> = ({
|
|
|
265
265
|
readonly [key in KeysOfAType<TSchema, ReadonlyArray<any>>]?:
|
|
266
266
|
| Flatten<TSchema[key]>
|
|
267
267
|
| ArrayOperator<Array<Flatten<TSchema[key]>>>;
|
|
268
|
-
} &
|
|
269
|
-
NotAcceptedFields<TSchema, ReadonlyArray<any>>) & {
|
|
268
|
+
} & NotAcceptedFields<TSchema, ReadonlyArray<any>>) & {
|
|
270
269
|
readonly [key: string]: ArrayOperator<any> | any;
|
|
271
270
|
};
|
|
272
271
|
|
|
@@ -275,16 +274,14 @@ export type PullOperator<TSchema> = ({
|
|
|
275
274
|
readonly [key in KeysOfAType<TSchema, ReadonlyArray<any>>]?:
|
|
276
275
|
| Partial<Flatten<TSchema[key]>>
|
|
277
276
|
| FilterOperations<Flatten<TSchema[key]>>;
|
|
278
|
-
} &
|
|
279
|
-
NotAcceptedFields<TSchema, ReadonlyArray<any>>) & {
|
|
277
|
+
} & NotAcceptedFields<TSchema, ReadonlyArray<any>>) & {
|
|
280
278
|
readonly [key: string]: FilterOperators<any> | any;
|
|
281
279
|
};
|
|
282
280
|
|
|
283
281
|
/** @public */
|
|
284
282
|
export type PullAllOperator<TSchema> = ({
|
|
285
283
|
readonly [key in KeysOfAType<TSchema, ReadonlyArray<any>>]?: TSchema[key];
|
|
286
|
-
} &
|
|
287
|
-
NotAcceptedFields<TSchema, ReadonlyArray<any>>) & {
|
|
284
|
+
} & NotAcceptedFields<TSchema, ReadonlyArray<any>>) & {
|
|
288
285
|
readonly [key: string]: ReadonlyArray<any>;
|
|
289
286
|
};
|
|
290
287
|
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { CommandOperation, CommandOperationOptions, CollationOptions } from './command';
|
|
2
|
-
import { ReadPreference } from '../read_preference';
|
|
3
2
|
import { MongoInvalidArgumentError } from '../error';
|
|
4
3
|
import { maxWireVersion, MongoDBNamespace } from '../utils';
|
|
5
4
|
import { Aspect, defineAspects, Hint } from './operation';
|
|
@@ -65,7 +64,7 @@ export class AggregateOperation<T = Document> extends CommandOperation<T> {
|
|
|
65
64
|
}
|
|
66
65
|
|
|
67
66
|
if (this.hasWriteStage) {
|
|
68
|
-
this.
|
|
67
|
+
this.trySecondaryWrite = true;
|
|
69
68
|
}
|
|
70
69
|
|
|
71
70
|
if (this.explain && this.writeConcern) {
|
|
@@ -10,6 +10,7 @@ import type { Server } from '../sdam/server';
|
|
|
10
10
|
import type { BSONSerializeOptions, Document } from '../bson';
|
|
11
11
|
import type { ReadConcernLike } from './../read_concern';
|
|
12
12
|
import { Explain, ExplainOptions } from '../explain';
|
|
13
|
+
import { MIN_SECONDARY_WRITE_WIRE_VERSION } from '../sdam/server_selection';
|
|
13
14
|
|
|
14
15
|
const SUPPORTS_WRITE_CONCERN_AND_COLLATION = 5;
|
|
15
16
|
|
|
@@ -126,6 +127,10 @@ export abstract class CommandOperation<T> extends AbstractOperation<T> {
|
|
|
126
127
|
Object.assign(cmd, { readConcern: this.readConcern });
|
|
127
128
|
}
|
|
128
129
|
|
|
130
|
+
if (this.trySecondaryWrite && serverWireVersion < MIN_SECONDARY_WRITE_WIRE_VERSION) {
|
|
131
|
+
options.omitReadPreference = true;
|
|
132
|
+
}
|
|
133
|
+
|
|
129
134
|
if (options.collation && serverWireVersion < SUPPORTS_WRITE_CONCERN_AND_COLLATION) {
|
|
130
135
|
callback(
|
|
131
136
|
new MongoCompatibilityError(
|
package/src/operations/count.ts
CHANGED
|
@@ -25,7 +25,7 @@ export class CountOperation extends CommandOperation<number> {
|
|
|
25
25
|
query: Document;
|
|
26
26
|
|
|
27
27
|
constructor(namespace: MongoDBNamespace, filter: Document, options: CountOptions) {
|
|
28
|
-
super(
|
|
28
|
+
super({ s: { namespace: namespace } } as unknown as Collection, options);
|
|
29
29
|
|
|
30
30
|
this.options = options;
|
|
31
31
|
this.collectionName = namespace.collection;
|
|
@@ -40,7 +40,7 @@ export class CountDocumentsOperation extends AggregateOperation<number> {
|
|
|
40
40
|
}
|
|
41
41
|
|
|
42
42
|
// NOTE: We're avoiding creating a cursor here to reduce the callstack.
|
|
43
|
-
const response =
|
|
43
|
+
const response = result as unknown as Document;
|
|
44
44
|
if (response.cursor == null || response.cursor.firstBatch == null) {
|
|
45
45
|
callback(undefined, 0);
|
|
46
46
|
return;
|
|
@@ -37,7 +37,7 @@ const ILLEGAL_COMMAND_FIELDS = new Set([
|
|
|
37
37
|
export interface TimeSeriesCollectionOptions extends Document {
|
|
38
38
|
timeField: string;
|
|
39
39
|
metaField?: string;
|
|
40
|
-
granularity?: string;
|
|
40
|
+
granularity?: 'seconds' | 'minutes' | 'hours' | string;
|
|
41
41
|
}
|
|
42
42
|
|
|
43
43
|
/** @public */
|
package/src/operations/eval.ts
CHANGED
|
@@ -43,7 +43,7 @@ export class EvalOperation extends CommandOperation<Document> {
|
|
|
43
43
|
let finalParameters: Document[] = [];
|
|
44
44
|
|
|
45
45
|
// If not a code object translate to one
|
|
46
|
-
if (!(finalCode && (
|
|
46
|
+
if (!(finalCode && (finalCode as unknown as { _bsontype: string })._bsontype === 'Code')) {
|
|
47
47
|
finalCode = new Code(finalCode as never);
|
|
48
48
|
}
|
|
49
49
|
|
|
@@ -17,6 +17,7 @@ import type { Topology } from '../sdam/topology';
|
|
|
17
17
|
import type { ClientSession } from '../sessions';
|
|
18
18
|
import type { Document } from '../bson';
|
|
19
19
|
import { supportsRetryableWrites } from '../utils';
|
|
20
|
+
import { secondaryWritableServerSelector, ServerSelector } from '../sdam/server_selection';
|
|
20
21
|
|
|
21
22
|
const MMAPv1_RETRY_WRITES_ERROR_CODE = MONGODB_ERROR_CODES.IllegalOperation;
|
|
22
23
|
const MMAPv1_RETRY_WRITES_ERROR_MESSAGE =
|
|
@@ -150,6 +151,16 @@ function executeWithServerSelection(
|
|
|
150
151
|
session.unpin();
|
|
151
152
|
}
|
|
152
153
|
|
|
154
|
+
let selector: ReadPreference | ServerSelector;
|
|
155
|
+
|
|
156
|
+
// If operation should try to write to secondary use the custom server selector
|
|
157
|
+
// otherwise provide the read preference.
|
|
158
|
+
if (operation.trySecondaryWrite) {
|
|
159
|
+
selector = secondaryWritableServerSelector(topology.commonWireVersion, readPreference);
|
|
160
|
+
} else {
|
|
161
|
+
selector = readPreference;
|
|
162
|
+
}
|
|
163
|
+
|
|
153
164
|
const serverSelectionOptions = { session };
|
|
154
165
|
function callbackWithRetry(err?: any, result?: any) {
|
|
155
166
|
if (err == null) {
|
|
@@ -182,7 +193,7 @@ function executeWithServerSelection(
|
|
|
182
193
|
}
|
|
183
194
|
|
|
184
195
|
// select a new server, and attempt to retry the operation
|
|
185
|
-
topology.selectServer(
|
|
196
|
+
topology.selectServer(selector, serverSelectionOptions, (e?: any, server?: any) => {
|
|
186
197
|
if (
|
|
187
198
|
e ||
|
|
188
199
|
(operation.hasAspect(Aspect.READ_OPERATION) && !supportsRetryableReads(server)) ||
|
|
@@ -227,7 +238,7 @@ function executeWithServerSelection(
|
|
|
227
238
|
}
|
|
228
239
|
|
|
229
240
|
// select a server, and execute the operation against it
|
|
230
|
-
topology.selectServer(
|
|
241
|
+
topology.selectServer(selector, serverSelectionOptions, (err?: any, server?: any) => {
|
|
231
242
|
if (err) {
|
|
232
243
|
callback(err);
|
|
233
244
|
return;
|
|
@@ -159,7 +159,7 @@ function makeIndexSpec(indexSpec: IndexSpecification, options: any): IndexDescri
|
|
|
159
159
|
}
|
|
160
160
|
|
|
161
161
|
/** @internal */
|
|
162
|
-
export class IndexesOperation extends AbstractOperation<Document> {
|
|
162
|
+
export class IndexesOperation extends AbstractOperation<Document[]> {
|
|
163
163
|
options: IndexInformationOptions;
|
|
164
164
|
collection: Collection;
|
|
165
165
|
|
|
@@ -169,7 +169,7 @@ export class IndexesOperation extends AbstractOperation<Document> {
|
|
|
169
169
|
this.collection = collection;
|
|
170
170
|
}
|
|
171
171
|
|
|
172
|
-
execute(server: Server, session: ClientSession, callback: Callback<Document>): void {
|
|
172
|
+
execute(server: Server, session: ClientSession, callback: Callback<Document[]>): void {
|
|
173
173
|
const coll = this.collection;
|
|
174
174
|
const options = this.options;
|
|
175
175
|
|
|
@@ -15,6 +15,8 @@ const LIST_COLLECTIONS_WIRE_VERSION = 3;
|
|
|
15
15
|
export interface ListCollectionsOptions extends CommandOperationOptions {
|
|
16
16
|
/** Since 4.0: If true, will only return the collection name in the response, and will omit additional info */
|
|
17
17
|
nameOnly?: boolean;
|
|
18
|
+
/** Since 4.0: If true and nameOnly is true, allows a user without the required privilege (i.e. listCollections action on the database) to run the command when access control is enforced. */
|
|
19
|
+
authorizedCollections?: boolean;
|
|
18
20
|
/** The batchSize for the returned command cursor or if pre 2.8 the systems batch collection */
|
|
19
21
|
batchSize?: number;
|
|
20
22
|
}
|
|
@@ -25,6 +27,7 @@ export class ListCollectionsOperation extends CommandOperation<string[]> {
|
|
|
25
27
|
db: Db;
|
|
26
28
|
filter: Document;
|
|
27
29
|
nameOnly: boolean;
|
|
30
|
+
authorizedCollections: boolean;
|
|
28
31
|
batchSize?: number;
|
|
29
32
|
|
|
30
33
|
constructor(db: Db, filter: Document, options?: ListCollectionsOptions) {
|
|
@@ -34,6 +37,7 @@ export class ListCollectionsOperation extends CommandOperation<string[]> {
|
|
|
34
37
|
this.db = db;
|
|
35
38
|
this.filter = filter;
|
|
36
39
|
this.nameOnly = !!this.options.nameOnly;
|
|
40
|
+
this.authorizedCollections = !!this.options.authorizedCollections;
|
|
37
41
|
|
|
38
42
|
if (typeof this.options.batchSize === 'number') {
|
|
39
43
|
this.batchSize = this.options.batchSize;
|
|
@@ -90,14 +94,18 @@ export class ListCollectionsOperation extends CommandOperation<string[]> {
|
|
|
90
94
|
return;
|
|
91
95
|
}
|
|
92
96
|
|
|
93
|
-
|
|
97
|
+
return super.executeCommand(server, session, this.generateCommand(), callback);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/* This is here for the purpose of unit testing the final command that gets sent. */
|
|
101
|
+
generateCommand(): Document {
|
|
102
|
+
return {
|
|
94
103
|
listCollections: 1,
|
|
95
104
|
filter: this.filter,
|
|
96
105
|
cursor: this.batchSize ? { batchSize: this.batchSize } : {},
|
|
97
|
-
nameOnly: this.nameOnly
|
|
106
|
+
nameOnly: this.nameOnly,
|
|
107
|
+
authorizedCollections: this.authorizedCollections
|
|
98
108
|
};
|
|
99
|
-
|
|
100
|
-
return super.executeCommand(server, session, command, callback);
|
|
101
109
|
}
|
|
102
110
|
}
|
|
103
111
|
|
|
@@ -7,7 +7,12 @@ import type { Db } from '../db';
|
|
|
7
7
|
import type { ClientSession } from '../sessions';
|
|
8
8
|
|
|
9
9
|
/** @public */
|
|
10
|
-
export
|
|
10
|
+
export interface ListDatabasesResult {
|
|
11
|
+
databases: ({ name: string; sizeOnDisk?: number; empty?: boolean } & Document)[];
|
|
12
|
+
totalSize?: number;
|
|
13
|
+
totalSizeMb?: number;
|
|
14
|
+
ok: 1 | 0;
|
|
15
|
+
}
|
|
11
16
|
|
|
12
17
|
/** @public */
|
|
13
18
|
export interface ListDatabasesOptions extends CommandOperationOptions {
|
|
@@ -31,6 +31,7 @@ export interface OperationOptions extends BSONSerializeOptions {
|
|
|
31
31
|
|
|
32
32
|
/** @internal Hints to `executeOperation` that this operation should not unpin on an ended transaction */
|
|
33
33
|
bypassPinningCheck?: boolean;
|
|
34
|
+
omitReadPreference?: boolean;
|
|
34
35
|
}
|
|
35
36
|
|
|
36
37
|
/** @internal */
|
|
@@ -49,6 +50,7 @@ export abstract class AbstractOperation<TResult = any> {
|
|
|
49
50
|
readPreference: ReadPreference;
|
|
50
51
|
server!: Server;
|
|
51
52
|
bypassPinningCheck: boolean;
|
|
53
|
+
trySecondaryWrite: boolean;
|
|
52
54
|
|
|
53
55
|
// BSON serialization options
|
|
54
56
|
bsonOptions?: BSONSerializeOptions;
|
|
@@ -72,6 +74,7 @@ export abstract class AbstractOperation<TResult = any> {
|
|
|
72
74
|
|
|
73
75
|
this.options = options;
|
|
74
76
|
this.bypassPinningCheck = !!options.bypassPinningCheck;
|
|
77
|
+
this.trySecondaryWrite = false;
|
|
75
78
|
}
|
|
76
79
|
|
|
77
80
|
abstract execute(server: Server, session: ClientSession, callback: Callback<TResult>): void;
|
package/src/sdam/server.ts
CHANGED
|
@@ -299,6 +299,14 @@ export class Server extends TypedEventEmitter<ServerEvents> {
|
|
|
299
299
|
// Clone the options
|
|
300
300
|
const finalOptions = Object.assign({}, options, { wireProtocolCommand: false });
|
|
301
301
|
|
|
302
|
+
// There are cases where we need to flag the read preference not to get sent in
|
|
303
|
+
// the command, such as pre-5.0 servers attempting to perform an aggregate write
|
|
304
|
+
// with a non-primary read preference. In this case the effective read preference
|
|
305
|
+
// (primary) is not the same as the provided and must be removed completely.
|
|
306
|
+
if (finalOptions.omitReadPreference) {
|
|
307
|
+
delete finalOptions.readPreference;
|
|
308
|
+
}
|
|
309
|
+
|
|
302
310
|
// error if collation not supported
|
|
303
311
|
if (collationNotSupported(this, cmd)) {
|
|
304
312
|
callback(new MongoCompatibilityError(`Server ${this.name} does not support collation`));
|
|
@@ -8,6 +8,9 @@ import type { ServerDescription, TagSet } from './server_description';
|
|
|
8
8
|
const IDLE_WRITE_PERIOD = 10000;
|
|
9
9
|
const SMALLEST_MAX_STALENESS_SECONDS = 90;
|
|
10
10
|
|
|
11
|
+
// Minimum version to try writes on secondaries.
|
|
12
|
+
export const MIN_SECONDARY_WRITE_WIRE_VERSION = 13;
|
|
13
|
+
|
|
11
14
|
/** @public */
|
|
12
15
|
export type ServerSelector = (
|
|
13
16
|
topologyDescription: TopologyDescription,
|
|
@@ -28,6 +31,28 @@ export function writableServerSelector(): ServerSelector {
|
|
|
28
31
|
);
|
|
29
32
|
}
|
|
30
33
|
|
|
34
|
+
/**
|
|
35
|
+
* Returns a server selector that uses a read preference to select a
|
|
36
|
+
* server potentially for a write on a secondary.
|
|
37
|
+
*/
|
|
38
|
+
export function secondaryWritableServerSelector(
|
|
39
|
+
wireVersion?: number,
|
|
40
|
+
readPreference?: ReadPreference
|
|
41
|
+
): ServerSelector {
|
|
42
|
+
// If server version < 5.0, read preference always primary.
|
|
43
|
+
// If server version >= 5.0...
|
|
44
|
+
// - If read preference is supplied, use that.
|
|
45
|
+
// - If no read preference is supplied, use primary.
|
|
46
|
+
if (
|
|
47
|
+
!readPreference ||
|
|
48
|
+
!wireVersion ||
|
|
49
|
+
(wireVersion && wireVersion < MIN_SECONDARY_WRITE_WIRE_VERSION)
|
|
50
|
+
) {
|
|
51
|
+
return readPreferenceServerSelector(ReadPreference.primary);
|
|
52
|
+
}
|
|
53
|
+
return readPreferenceServerSelector(readPreference);
|
|
54
|
+
}
|
|
55
|
+
|
|
31
56
|
/**
|
|
32
57
|
* Reduces the passed in array of servers by the rules of the "Max Staleness" specification
|
|
33
58
|
* found here: https://github.com/mongodb/specifications/blob/master/source/max-staleness/max-staleness.rst
|
package/src/sdam/srv_polling.ts
CHANGED
|
@@ -29,18 +29,15 @@ export class SrvPollingEvent {
|
|
|
29
29
|
this.srvRecords = srvRecords;
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
-
|
|
33
|
-
return new
|
|
34
|
-
this.srvRecords.map(record => {
|
|
35
|
-
const host = new HostAddress(`${record.name}:${record.port}`);
|
|
36
|
-
return [host.toString(), host];
|
|
37
|
-
})
|
|
38
|
-
);
|
|
32
|
+
hostnames(): Set<string> {
|
|
33
|
+
return new Set(this.srvRecords.map(r => HostAddress.fromSrvRecord(r).toString()));
|
|
39
34
|
}
|
|
40
35
|
}
|
|
41
36
|
|
|
42
37
|
/** @internal */
|
|
43
38
|
export interface SrvPollerOptions extends LoggerOptions {
|
|
39
|
+
srvServiceName: string;
|
|
40
|
+
srvMaxHosts: number;
|
|
44
41
|
srvHost: string;
|
|
45
42
|
heartbeatFrequencyMS: number;
|
|
46
43
|
}
|
|
@@ -58,6 +55,8 @@ export class SrvPoller extends TypedEventEmitter<SrvPollerEvents> {
|
|
|
58
55
|
logger: Logger;
|
|
59
56
|
haMode: boolean;
|
|
60
57
|
generation: number;
|
|
58
|
+
srvMaxHosts: number;
|
|
59
|
+
srvServiceName: string;
|
|
61
60
|
_timeout?: NodeJS.Timeout;
|
|
62
61
|
|
|
63
62
|
/** @event */
|
|
@@ -71,8 +70,10 @@ export class SrvPoller extends TypedEventEmitter<SrvPollerEvents> {
|
|
|
71
70
|
}
|
|
72
71
|
|
|
73
72
|
this.srvHost = options.srvHost;
|
|
73
|
+
this.srvMaxHosts = options.srvMaxHosts ?? 0;
|
|
74
|
+
this.srvServiceName = options.srvServiceName ?? 'mongodb';
|
|
74
75
|
this.rescanSrvIntervalMS = 60000;
|
|
75
|
-
this.heartbeatFrequencyMS = options.heartbeatFrequencyMS
|
|
76
|
+
this.heartbeatFrequencyMS = options.heartbeatFrequencyMS ?? 10000;
|
|
76
77
|
this.logger = new Logger('srvPoller', options);
|
|
77
78
|
|
|
78
79
|
this.haMode = false;
|
|
@@ -82,7 +83,7 @@ export class SrvPoller extends TypedEventEmitter<SrvPollerEvents> {
|
|
|
82
83
|
}
|
|
83
84
|
|
|
84
85
|
get srvAddress(): string {
|
|
85
|
-
return `
|
|
86
|
+
return `_${this.srvServiceName}._tcp.${this.srvHost}`;
|
|
86
87
|
}
|
|
87
88
|
|
|
88
89
|
get intervalMS(): number {
|
|
@@ -143,13 +144,13 @@ export class SrvPoller extends TypedEventEmitter<SrvPollerEvents> {
|
|
|
143
144
|
}
|
|
144
145
|
|
|
145
146
|
const finalAddresses: dns.SrvRecord[] = [];
|
|
146
|
-
|
|
147
|
+
for (const record of srvRecords) {
|
|
147
148
|
if (matchesParentDomain(record.name, this.srvHost)) {
|
|
148
149
|
finalAddresses.push(record);
|
|
149
150
|
} else {
|
|
150
151
|
this.parentDomainMismatch(record);
|
|
151
152
|
}
|
|
152
|
-
}
|
|
153
|
+
}
|
|
153
154
|
|
|
154
155
|
if (!finalAddresses.length) {
|
|
155
156
|
this.failure('No valid addresses found at host');
|
package/src/sdam/topology.ts
CHANGED
|
@@ -27,7 +27,8 @@ import {
|
|
|
27
27
|
HostAddress,
|
|
28
28
|
ns,
|
|
29
29
|
emitWarning,
|
|
30
|
-
EventEmitterWithState
|
|
30
|
+
EventEmitterWithState,
|
|
31
|
+
shuffle
|
|
31
32
|
} from '../utils';
|
|
32
33
|
import {
|
|
33
34
|
TopologyType,
|
|
@@ -140,6 +141,8 @@ export interface TopologyPrivate {
|
|
|
140
141
|
|
|
141
142
|
/** @public */
|
|
142
143
|
export interface TopologyOptions extends BSONSerializeOptions, ServerOptions {
|
|
144
|
+
srvMaxHosts: number;
|
|
145
|
+
srvServiceName: string;
|
|
143
146
|
hosts: HostAddress[];
|
|
144
147
|
retryWrites: boolean;
|
|
145
148
|
retryReads: boolean;
|
|
@@ -182,8 +185,8 @@ export type TopologyEvents = {
|
|
|
182
185
|
topologyOpening(event: TopologyOpeningEvent): void;
|
|
183
186
|
topologyDescriptionChanged(event: TopologyDescriptionChangedEvent): void;
|
|
184
187
|
error(error: Error): void;
|
|
185
|
-
/**
|
|
186
|
-
open(
|
|
188
|
+
/** @internal */
|
|
189
|
+
open(topology: Topology): void;
|
|
187
190
|
close(): void;
|
|
188
191
|
timeout(): void;
|
|
189
192
|
} & Omit<ServerEvents, 'connect'> &
|
|
@@ -290,8 +293,15 @@ export class Topology extends TypedEventEmitter<TopologyEvents> {
|
|
|
290
293
|
const topologyType = topologyTypeFromOptions(options);
|
|
291
294
|
const topologyId = globalTopologyCounter++;
|
|
292
295
|
|
|
296
|
+
const selectedHosts =
|
|
297
|
+
options.srvMaxHosts == null ||
|
|
298
|
+
options.srvMaxHosts === 0 ||
|
|
299
|
+
options.srvMaxHosts >= seedlist.length
|
|
300
|
+
? seedlist
|
|
301
|
+
: shuffle(seedlist, options.srvMaxHosts);
|
|
302
|
+
|
|
293
303
|
const serverDescriptions = new Map();
|
|
294
|
-
for (const hostAddress of
|
|
304
|
+
for (const hostAddress of selectedHosts) {
|
|
295
305
|
serverDescriptions.set(hostAddress.toString(), new ServerDescription(hostAddress));
|
|
296
306
|
}
|
|
297
307
|
|
|
@@ -339,7 +349,9 @@ export class Topology extends TypedEventEmitter<TopologyEvents> {
|
|
|
339
349
|
options.srvPoller ??
|
|
340
350
|
new SrvPoller({
|
|
341
351
|
heartbeatFrequencyMS: this.s.heartbeatFrequencyMS,
|
|
342
|
-
srvHost: options.srvHost
|
|
352
|
+
srvHost: options.srvHost,
|
|
353
|
+
srvMaxHosts: options.srvMaxHosts,
|
|
354
|
+
srvServiceName: options.srvServiceName
|
|
343
355
|
});
|
|
344
356
|
|
|
345
357
|
this.on(Topology.TOPOLOGY_DESCRIPTION_CHANGED, this.s.detectShardedTopology);
|
|
@@ -363,7 +375,10 @@ export class Topology extends TypedEventEmitter<TopologyEvents> {
|
|
|
363
375
|
|
|
364
376
|
private detectSrvRecords(ev: SrvPollingEvent) {
|
|
365
377
|
const previousTopologyDescription = this.s.description;
|
|
366
|
-
this.s.description = this.s.description.updateFromSrvPollingEvent(
|
|
378
|
+
this.s.description = this.s.description.updateFromSrvPollingEvent(
|
|
379
|
+
ev,
|
|
380
|
+
this.s.options.srvMaxHosts
|
|
381
|
+
);
|
|
367
382
|
if (this.s.description === previousTopologyDescription) {
|
|
368
383
|
// Nothing changed, so return
|
|
369
384
|
return;
|
|
@@ -456,8 +471,7 @@ export class Topology extends TypedEventEmitter<TopologyEvents> {
|
|
|
456
471
|
}
|
|
457
472
|
|
|
458
473
|
stateTransition(this, STATE_CONNECTED);
|
|
459
|
-
|
|
460
|
-
this.emit(Topology.OPEN, err, this);
|
|
474
|
+
this.emit(Topology.OPEN, this);
|
|
461
475
|
this.emit(Topology.CONNECT, this);
|
|
462
476
|
|
|
463
477
|
if (typeof callback === 'function') callback(undefined, this);
|
|
@@ -467,8 +481,7 @@ export class Topology extends TypedEventEmitter<TopologyEvents> {
|
|
|
467
481
|
}
|
|
468
482
|
|
|
469
483
|
stateTransition(this, STATE_CONNECTED);
|
|
470
|
-
|
|
471
|
-
this.emit(Topology.OPEN, err, this);
|
|
484
|
+
this.emit(Topology.OPEN, this);
|
|
472
485
|
this.emit(Topology.CONNECT, this);
|
|
473
486
|
|
|
474
487
|
if (typeof callback === 'function') callback(undefined, this);
|
|
@@ -797,6 +810,10 @@ export class Topology extends TypedEventEmitter<TopologyEvents> {
|
|
|
797
810
|
return result;
|
|
798
811
|
}
|
|
799
812
|
|
|
813
|
+
get commonWireVersion(): number | undefined {
|
|
814
|
+
return this.description.commonWireVersion;
|
|
815
|
+
}
|
|
816
|
+
|
|
800
817
|
get logicalSessionTimeoutMinutes(): number | undefined {
|
|
801
818
|
return this.description.logicalSessionTimeoutMinutes;
|
|
802
819
|
}
|
|
@@ -4,6 +4,7 @@ import { TopologyType, ServerType } from './common';
|
|
|
4
4
|
import type { ObjectId, Document } from '../bson';
|
|
5
5
|
import type { SrvPollingEvent } from './srv_polling';
|
|
6
6
|
import { MongoError, MongoRuntimeError } from '../error';
|
|
7
|
+
import { shuffle } from '../utils';
|
|
7
8
|
|
|
8
9
|
// constants related to compatibility checks
|
|
9
10
|
const MIN_SUPPORTED_SERVER_VERSION = WIRE_CONSTANTS.MIN_SUPPORTED_SERVER_VERSION;
|
|
@@ -139,23 +140,46 @@ export class TopologyDescription {
|
|
|
139
140
|
* Returns a new TopologyDescription based on the SrvPollingEvent
|
|
140
141
|
* @internal
|
|
141
142
|
*/
|
|
142
|
-
updateFromSrvPollingEvent(ev: SrvPollingEvent): TopologyDescription {
|
|
143
|
-
|
|
144
|
-
const
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
143
|
+
updateFromSrvPollingEvent(ev: SrvPollingEvent, srvMaxHosts = 0): TopologyDescription {
|
|
144
|
+
/** The SRV addresses defines the set of addresses we should be using */
|
|
145
|
+
const incomingHostnames = ev.hostnames();
|
|
146
|
+
const currentHostnames = new Set(this.servers.keys());
|
|
147
|
+
|
|
148
|
+
const hostnamesToAdd = new Set<string>(incomingHostnames);
|
|
149
|
+
const hostnamesToRemove = new Set<string>();
|
|
150
|
+
for (const hostname of currentHostnames) {
|
|
151
|
+
// filter hostnamesToAdd (made from incomingHostnames) down to what is *not* present in currentHostnames
|
|
152
|
+
hostnamesToAdd.delete(hostname);
|
|
153
|
+
if (!incomingHostnames.has(hostname)) {
|
|
154
|
+
// If the SRV Records no longer include this hostname
|
|
155
|
+
// we have to stop using it
|
|
156
|
+
hostnamesToRemove.add(hostname);
|
|
150
157
|
}
|
|
151
158
|
}
|
|
152
159
|
|
|
153
|
-
if (
|
|
160
|
+
if (hostnamesToAdd.size === 0 && hostnamesToRemove.size === 0) {
|
|
161
|
+
// No new hosts to add and none to remove
|
|
154
162
|
return this;
|
|
155
163
|
}
|
|
156
164
|
|
|
157
|
-
|
|
158
|
-
|
|
165
|
+
const serverDescriptions = new Map(this.servers);
|
|
166
|
+
for (const removedHost of hostnamesToRemove) {
|
|
167
|
+
serverDescriptions.delete(removedHost);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
if (hostnamesToAdd.size > 0) {
|
|
171
|
+
if (srvMaxHosts === 0) {
|
|
172
|
+
// Add all!
|
|
173
|
+
for (const hostToAdd of hostnamesToAdd) {
|
|
174
|
+
serverDescriptions.set(hostToAdd, new ServerDescription(hostToAdd));
|
|
175
|
+
}
|
|
176
|
+
} else if (serverDescriptions.size < srvMaxHosts) {
|
|
177
|
+
// Add only the amount needed to get us back to srvMaxHosts
|
|
178
|
+
const selectedHosts = shuffle(hostnamesToAdd, srvMaxHosts - serverDescriptions.size);
|
|
179
|
+
for (const selectedHostToAdd of selectedHosts) {
|
|
180
|
+
serverDescriptions.set(selectedHostToAdd, new ServerDescription(selectedHostToAdd));
|
|
181
|
+
}
|
|
182
|
+
}
|
|
159
183
|
}
|
|
160
184
|
|
|
161
185
|
return new TopologyDescription(
|
package/src/transactions.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { ReadPreference } from './read_preference';
|
|
2
2
|
import { MongoRuntimeError, MongoTransactionError } from './error';
|
|
3
|
-
import { ReadConcern } from './read_concern';
|
|
3
|
+
import { ReadConcern, ReadConcernLike } from './read_concern';
|
|
4
4
|
import { WriteConcern } from './write_concern';
|
|
5
5
|
import type { Server } from './sdam/server';
|
|
6
6
|
import type { CommandOperationOptions } from './operations/command';
|
|
@@ -63,7 +63,7 @@ const COMMITTED_STATES: Set<TxnState> = new Set([
|
|
|
63
63
|
export interface TransactionOptions extends CommandOperationOptions {
|
|
64
64
|
// TODO(NODE-3344): These options use the proper class forms of these settings, it should accept the basic enum values too
|
|
65
65
|
/** A default read concern for commands in this transaction */
|
|
66
|
-
readConcern?:
|
|
66
|
+
readConcern?: ReadConcernLike;
|
|
67
67
|
/** A default writeConcern for commands in this transaction */
|
|
68
68
|
writeConcern?: WriteConcern;
|
|
69
69
|
/** A default read preference for commands in this transaction */
|