Skip to content

Commit 5c867a1

Browse files
nbbeekenbaileympearson
authored andcommitted
feat(NODE-6313): add CSOT support to sessions and transactions (#4199)
1 parent a3f944a commit 5c867a1

24 files changed

+777
-233
lines changed

package-lock.json

Lines changed: 41 additions & 41 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
@@ -97,7 +97,7 @@
9797
"mocha": "^10.4.0",
9898
"mocha-sinon": "^2.1.2",
9999
"mongodb-client-encryption": "^6.1.0",
100-
"mongodb-legacy": "^6.1.0",
100+
"mongodb-legacy": "^6.1.1",
101101
"nyc": "^15.1.0",
102102
"prettier": "^3.3.3",
103103
"semver": "^7.6.3",

src/cmap/connection.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -750,6 +750,13 @@ export class Connection extends TypedEventEmitter<ConnectionEvents> {
750750
return;
751751
}
752752
}
753+
} catch (readError) {
754+
if (TimeoutError.is(readError)) {
755+
throw new MongoOperationTimeoutError(
756+
`Timed out during socket read (${readError.duration}ms)`
757+
);
758+
}
759+
throw readError;
753760
} finally {
754761
this.dataEvents = null;
755762
this.messageStream.pause();

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
@@ -470,10 +470,14 @@ export class Collection<TSchema extends Document = Document> {
470470
// Intentionally, we do not inherit options from parent for this operation.
471471
return await executeOperation(
472472
this.client,
473-
new RenameOperation(this as TODO_NODE_3286, newName, {
474-
...options,
475-
readPreference: ReadPreference.PRIMARY
476-
}) as TODO_NODE_3286
473+
new RenameOperation(
474+
this as TODO_NODE_3286,
475+
newName,
476+
resolveOptions(undefined, {
477+
...options,
478+
readPreference: ReadPreference.PRIMARY
479+
})
480+
) as TODO_NODE_3286
477481
);
478482
}
479483

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
@@ -128,6 +128,9 @@ function isAggregateError(e: unknown): e is Error & { errors: Error[] } {
128128
* mongodb-client-encryption has a dependency on this error, it uses the constructor with a string argument
129129
*/
130130
export class MongoError extends Error {
131+
get [Symbol.toStringTag]() {
132+
return this.name;
133+
}
131134
/** @internal */
132135
[kErrorLabels]: Set<string>;
133136
/**

src/operations/execute_operation.ts

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

9085
const readPreference = operation.readPreference ?? ReadPreference.primary;
9186
const inTransaction = !!session?.inTransaction();
@@ -107,6 +102,7 @@ export async function executeOperation<
107102
}
108103

109104
timeoutContext ??= TimeoutContext.create({
105+
session,
110106
serverSelectionTimeoutMS: client.s.options.serverSelectionTimeoutMS,
111107
waitQueueTimeoutMS: client.s.options.waitQueueTimeoutMS,
112108
timeoutMS: operation.options.timeoutMS

0 commit comments

Comments
 (0)