Skip to content

Commit 1c48970

Browse files
authored
feat(NODE-6313): add CSOT support to sessions and transactions (#4199)
1 parent 260d679 commit 1c48970

24 files changed

+739
-195
lines changed

package-lock.json

Lines changed: 5 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@
9999
"mocha": "^10.4.0",
100100
"mocha-sinon": "^2.1.2",
101101
"mongodb-client-encryption": "^6.1.0",
102-
"mongodb-legacy": "^6.0.1",
102+
"mongodb-legacy": "^6.1.1",
103103
"nyc": "^15.1.0",
104104
"prettier": "^2.8.8",
105105
"semver": "^7.6.0",

src/cmap/connection.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -737,6 +737,13 @@ export class Connection extends TypedEventEmitter<ConnectionEvents> {
737737
return;
738738
}
739739
}
740+
} catch (readError) {
741+
if (TimeoutError.is(readError)) {
742+
throw new MongoOperationTimeoutError(
743+
`Timed out during socket read (${readError.duration}ms)`
744+
);
745+
}
746+
throw readError;
740747
} finally {
741748
this.dataEvents = null;
742749
this.throwIfAborted();

src/cmap/wire_protocol/on_data.ts

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import { type EventEmitter } from 'events';
22

3-
import { MongoOperationTimeoutError } from '../../error';
4-
import { type TimeoutContext, TimeoutError } from '../../timeout';
3+
import { type TimeoutContext } from '../../timeout';
54
import { List, promiseWithResolvers } from '../../utils';
65

76
/**
@@ -91,8 +90,11 @@ export function onData(
9190
// Adding event handlers
9291
emitter.on('data', eventHandler);
9392
emitter.on('error', errorHandler);
93+
94+
const timeoutForSocketRead = timeoutContext?.timeoutForSocketRead;
95+
timeoutForSocketRead?.throwIfExpired();
9496
// eslint-disable-next-line github/no-then
95-
timeoutContext?.timeoutForSocketRead?.then(undefined, errorHandler);
97+
timeoutForSocketRead?.then(undefined, errorHandler);
9698

9799
return iterator;
98100

@@ -104,12 +106,9 @@ export function onData(
104106

105107
function errorHandler(err: Error) {
106108
const promise = unconsumedPromises.shift();
107-
const timeoutError = TimeoutError.is(err)
108-
? new MongoOperationTimeoutError('Timed out during socket read')
109-
: undefined;
110109

111-
if (promise != null) promise.reject(timeoutError ?? err);
112-
else error = timeoutError ?? err;
110+
if (promise != null) promise.reject(err);
111+
else error = err;
113112
void closeHandler();
114113
}
115114

src/collection.ts

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -469,10 +469,14 @@ export class Collection<TSchema extends Document = Document> {
469469
// Intentionally, we do not inherit options from parent for this operation.
470470
return await executeOperation(
471471
this.client,
472-
new RenameOperation(this as TODO_NODE_3286, newName, {
473-
...options,
474-
readPreference: ReadPreference.PRIMARY
475-
}) as TODO_NODE_3286
472+
new RenameOperation(
473+
this as TODO_NODE_3286,
474+
newName,
475+
resolveOptions(undefined, {
476+
...options,
477+
readPreference: ReadPreference.PRIMARY
478+
})
479+
) as TODO_NODE_3286
476480
);
477481
}
478482

src/db.ts

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -275,12 +275,16 @@ export class Db {
275275
// Intentionally, we do not inherit options from parent for this operation.
276276
return await executeOperation(
277277
this.client,
278-
new RunCommandOperation(this, command, {
279-
...resolveBSONOptions(options),
280-
timeoutMS: options?.timeoutMS ?? this.timeoutMS,
281-
session: options?.session,
282-
readPreference: options?.readPreference
283-
})
278+
new RunCommandOperation(
279+
this,
280+
command,
281+
resolveOptions(undefined, {
282+
...resolveBSONOptions(options),
283+
timeoutMS: options?.timeoutMS ?? this.timeoutMS,
284+
session: options?.session,
285+
readPreference: options?.readPreference
286+
})
287+
)
284288
);
285289
}
286290

@@ -385,7 +389,11 @@ export class Db {
385389
new RenameOperation(
386390
this.collection<TSchema>(fromCollection) as TODO_NODE_3286,
387391
toCollection,
388-
{ ...options, new_collection: true, readPreference: ReadPreference.primary }
392+
resolveOptions(undefined, {
393+
...options,
394+
new_collection: true,
395+
readPreference: ReadPreference.primary
396+
})
389397
) as TODO_NODE_3286
390398
);
391399
}

src/error.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,9 @@ function isAggregateError(e: unknown): e is Error & { errors: Error[] } {
124124
* mongodb-client-encryption has a dependency on this error, it uses the constructor with a string argument
125125
*/
126126
export class MongoError extends Error {
127+
get [Symbol.toStringTag]() {
128+
return this.name;
129+
}
127130
/** @internal */
128131
[kErrorLabels]: Set<string>;
129132
/**

src/operations/execute_operation.ts

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ type ResultTypeFromOperation<TOperation> = TOperation extends AbstractOperation<
5959
export async function executeOperation<
6060
T extends AbstractOperation<TResult>,
6161
TResult = ResultTypeFromOperation<T>
62-
>(client: MongoClient, operation: T, timeoutContext?: TimeoutContext): Promise<TResult> {
62+
>(client: MongoClient, operation: T, timeoutContext?: TimeoutContext | null): Promise<TResult> {
6363
if (!(operation instanceof AbstractOperation)) {
6464
// TODO(NODE-3483): Extend MongoRuntimeError
6565
throw new MongoRuntimeError('This method requires a valid operation instance');
@@ -82,11 +82,6 @@ export async function executeOperation<
8282
} else if (session.client !== client) {
8383
throw new MongoInvalidArgumentError('ClientSession must be from the same MongoClient');
8484
}
85-
if (session.explicit && session?.timeoutMS != null && operation.options.timeoutMS != null) {
86-
throw new MongoInvalidArgumentError(
87-
'Do not specify timeoutMS on operation if already specified on an explicit session'
88-
);
89-
}
9085

9186
const readPreference = operation.readPreference ?? ReadPreference.primary;
9287
const inTransaction = !!session?.inTransaction();
@@ -108,6 +103,7 @@ export async function executeOperation<
108103
}
109104

110105
timeoutContext ??= TimeoutContext.create({
106+
session,
111107
serverSelectionTimeoutMS: client.s.options.serverSelectionTimeoutMS,
112108
waitQueueTimeoutMS: client.s.options.waitQueueTimeoutMS,
113109
timeoutMS: operation.options.timeoutMS

0 commit comments

Comments
 (0)