Skip to content

Commit 1a4feab

Browse files
committed
Add logic to reset server selection timeout for retry
1 parent 132674f commit 1a4feab

File tree

1 file changed

+26
-16
lines changed

1 file changed

+26
-16
lines changed

src/timeout.ts

Lines changed: 26 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { clearTimeout, setTimeout } from 'timers';
22

3-
import { MongoInvalidArgumentError, MongoOperationTimeoutError, MongoRuntimeError } from './error';
4-
import { csotMin, noop } from './utils';
3+
import { MongoOperationTimeoutError, MongoRuntimeError } from './error';
4+
import { noop } from './utils';
55

66
/** @internal */
77
export class TimeoutError extends Error {
@@ -39,6 +39,7 @@ export class Timeout extends Promise<never> {
3939
public ended: number | null = null;
4040
public duration: number;
4141
public timedOut = false;
42+
public cleared = false;
4243

4344
get remainingTime(): number {
4445
if (this.timedOut) return 0;
@@ -54,10 +55,6 @@ export class Timeout extends Promise<never> {
5455
private constructor(executor: Executor = () => null, duration: number, unref = true) {
5556
let reject!: Reject;
5657

57-
if (duration < 0) {
58-
throw new MongoInvalidArgumentError('Cannot create a Timeout with a negative duration');
59-
}
60-
6158
super((_, promiseReject) => {
6259
reject = promiseReject;
6360

@@ -77,6 +74,12 @@ export class Timeout extends Promise<never> {
7774
// Ensure we do not keep the Node.js event loop running
7875
this.id.unref();
7976
}
77+
} else if (duration < 0) {
78+
process.nextTick(() => {
79+
this.ended = Math.trunc(performance.now());
80+
this.timedOut = true;
81+
reject(new TimeoutError('Timed out immediately due to negative duration'));
82+
});
8083
}
8184
}
8285

@@ -86,12 +89,18 @@ export class Timeout extends Promise<never> {
8689
clear(): void {
8790
clearTimeout(this.id);
8891
this.id = undefined;
92+
this.cleared = true;
8993
}
9094

9195
throwIfExpired(): void {
9296
if (this.timedOut) throw new TimeoutError('Timed out');
9397
}
9498

99+
/**
100+
* Creates a `Timeout` instance that expires after `durationMS`. Note that when `durationMS` is
101+
* 0, this creates a `Timeout` that never expires. Note also that a negative `durationMS` will
102+
* expire on the next tick of the event loop
103+
* */
95104
public static expires(durationMS: number, unref?: boolean): Timeout {
96105
return new Timeout(undefined, durationMS, unref);
97106
}
@@ -214,27 +223,28 @@ export class CSOTTimeoutContext extends TimeoutContext {
214223

215224
get serverSelectionTimeout(): Timeout | null {
216225
// check for undefined
217-
if (typeof this._serverSelectionTimeout !== 'object') {
226+
if (typeof this._serverSelectionTimeout !== 'object' || this._serverSelectionTimeout?.cleared) {
227+
const { remainingTimeMS, serverSelectionTimeoutMS } = this;
218228
const usingServerSelectionTimeoutMS =
219-
this.serverSelectionTimeoutMS !== 0 &&
220-
csotMin(this.timeoutMS, this.serverSelectionTimeoutMS) === this.serverSelectionTimeoutMS;
229+
serverSelectionTimeoutMS !== 0 &&
230+
!Number.isFinite(remainingTimeMS) &&
231+
remainingTimeMS > serverSelectionTimeoutMS;
221232

222233
if (usingServerSelectionTimeoutMS) {
223-
this._serverSelectionTimeout = Timeout.expires(this.serverSelectionTimeoutMS);
234+
this._serverSelectionTimeout = Timeout.expires(serverSelectionTimeoutMS);
224235
} else {
225-
if (this.timeoutMS > 0) {
226-
this._serverSelectionTimeout = Timeout.expires(this.timeoutMS);
227-
} else {
228-
this._serverSelectionTimeout = null;
229-
}
236+
this._serverSelectionTimeout = Timeout.expires(remainingTimeMS);
230237
}
231238
}
232239

233240
return this._serverSelectionTimeout;
234241
}
235242

236243
get connectionCheckoutTimeout(): Timeout | null {
237-
if (typeof this._connectionCheckoutTimeout !== 'object') {
244+
if (
245+
typeof this._connectionCheckoutTimeout !== 'object' ||
246+
this._connectionCheckoutTimeout?.cleared
247+
) {
238248
if (typeof this._serverSelectionTimeout === 'object') {
239249
// null or Timeout
240250
this._connectionCheckoutTimeout = this._serverSelectionTimeout;

0 commit comments

Comments
 (0)