-
Notifications
You must be signed in to change notification settings - Fork 1.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat(NODE-6231): Add CSOT behaviour for retryable reads and writes #4186
Changes from 12 commits
f160cd4
c87e5ab
132674f
1a4feab
ce50731
5505c68
27f6b50
c38de6f
bd3dbe1
23fb782
eab4240
5d72bda
cbce285
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -39,6 +39,7 @@ export class Timeout extends Promise<never> { | |
public ended: number | null = null; | ||
public duration: number; | ||
public timedOut = false; | ||
public cleared = false; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Add cleared flag that gets set on |
||
|
||
get remainingTime(): number { | ||
if (this.timedOut) return 0; | ||
|
@@ -53,7 +54,6 @@ export class Timeout extends Promise<never> { | |
/** Create a new timeout that expires in `duration` ms */ | ||
private constructor(executor: Executor = () => null, duration: number, unref = true) { | ||
let reject!: Reject; | ||
|
||
if (duration < 0) { | ||
throw new MongoInvalidArgumentError('Cannot create a Timeout with a negative duration'); | ||
} | ||
|
@@ -86,6 +86,7 @@ export class Timeout extends Promise<never> { | |
clear(): void { | ||
clearTimeout(this.id); | ||
this.id = undefined; | ||
this.cleared = true; | ||
} | ||
|
||
throwIfExpired(): void { | ||
|
@@ -214,16 +215,18 @@ export class CSOTTimeoutContext extends TimeoutContext { | |
|
||
get serverSelectionTimeout(): Timeout | null { | ||
// check for undefined | ||
if (typeof this._serverSelectionTimeout !== 'object') { | ||
if (typeof this._serverSelectionTimeout !== 'object' || this._serverSelectionTimeout?.cleared) { | ||
const { remainingTimeMS, serverSelectionTimeoutMS } = this; | ||
if (remainingTimeMS <= 0) | ||
throw new MongoOperationTimeoutError('Timed out in server selection'); | ||
durran marked this conversation as resolved.
Show resolved
Hide resolved
|
||
const usingServerSelectionTimeoutMS = | ||
this.serverSelectionTimeoutMS !== 0 && | ||
csotMin(this.timeoutMS, this.serverSelectionTimeoutMS) === this.serverSelectionTimeoutMS; | ||
|
||
serverSelectionTimeoutMS !== 0 && | ||
csotMin(remainingTimeMS, serverSelectionTimeoutMS) === serverSelectionTimeoutMS; | ||
if (usingServerSelectionTimeoutMS) { | ||
this._serverSelectionTimeout = Timeout.expires(this.serverSelectionTimeoutMS); | ||
this._serverSelectionTimeout = Timeout.expires(serverSelectionTimeoutMS); | ||
} else { | ||
if (this.timeoutMS > 0) { | ||
this._serverSelectionTimeout = Timeout.expires(this.timeoutMS); | ||
if (remainingTimeMS > 0 && Number.isFinite(remainingTimeMS)) { | ||
this._serverSelectionTimeout = Timeout.expires(remainingTimeMS); | ||
} else { | ||
this._serverSelectionTimeout = null; | ||
} | ||
|
@@ -234,7 +237,10 @@ export class CSOTTimeoutContext extends TimeoutContext { | |
} | ||
|
||
get connectionCheckoutTimeout(): Timeout | null { | ||
if (typeof this._connectionCheckoutTimeout !== 'object') { | ||
if ( | ||
typeof this._connectionCheckoutTimeout !== 'object' || | ||
this._connectionCheckoutTimeout?.cleared | ||
) { | ||
if (typeof this._serverSelectionTimeout === 'object') { | ||
// null or Timeout | ||
this._connectionCheckoutTimeout = this._serverSelectionTimeout; | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -6,7 +6,9 @@ import { runUnifiedSuite } from '../../tools/unified-spec-runner/runner'; | |
const enabled = [ | ||
'override-collection-timeoutMS', | ||
'override-database-timeoutMS', | ||
'override-operation-timeoutMS' | ||
'override-operation-timeoutMS', | ||
'retryability-legacy-timeouts', | ||
'retryability-timeoutMS' | ||
]; | ||
|
||
const cursorOperations = [ | ||
|
@@ -18,6 +20,11 @@ const cursorOperations = [ | |
'listCollectionNames' | ||
]; | ||
|
||
const bulkWriteOperations = [ | ||
'timeoutMS applies to whole operation, not individual attempts - bulkWrite on collection', | ||
'timeoutMS applies to whole operation, not individual attempts - insertMany on collection' | ||
Comment on lines
+24
to
+25
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Skipped for now. We seem to be throwing the TimeoutError at the correct place, but it's getting wrapped by the BulkWriteError |
||
]; | ||
|
||
describe('CSOT spec tests', function () { | ||
const specs = loadSpecTests(join('client-side-operations-timeout')); | ||
for (const spec of specs) { | ||
|
@@ -30,6 +37,10 @@ describe('CSOT spec tests', function () { | |
// Cursor operation | ||
if (test.operations.find(operation => cursorOperations.includes(operation.name))) | ||
test.skipReason = 'TODO(NODE-5684): Not working yet'; | ||
|
||
if (bulkWriteOperations.includes(test.description)) | ||
test.skipReason = | ||
'TODO(NODE-6274): update test runner to check errorResponse field of MongoBulkWriteError in isTimeoutError assertion'; | ||
} | ||
} | ||
runUnifiedSuite(specs); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
clear timeouts to ensure correct setting of the
serverSelectionTimeoutMS
value