Skip to content

Commit d4502aa

Browse files
author
Thomas Reggi
authored
fix: error label checking & insertOne where retryWrites is false
NODE-2742
1 parent 7bbc783 commit d4502aa

File tree

6 files changed

+26
-6
lines changed

6 files changed

+26
-6
lines changed

src/bulk/common.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -550,7 +550,7 @@ function executeCommands(
550550
function resultHandler(err?: AnyError, result?: Document) {
551551
// Error is a driver related error not a bulk op error, return early
552552
if (err && 'message' in err && !(err instanceof MongoWriteConcernError)) {
553-
return callback(err);
553+
return callback(new BulkWriteError(err, new BulkWriteResult(bulkOperation.s.bulkResult)));
554554
}
555555

556556
if (err instanceof MongoWriteConcernError) {

src/error.ts

+1-2
Original file line numberDiff line numberDiff line change
@@ -275,9 +275,8 @@ const RETRYABLE_WRITE_ERROR_CODES = new Set([
275275

276276
export function isRetryableWriteError(error: MongoError): boolean {
277277
if (error instanceof MongoWriteConcernError) {
278-
return RETRYABLE_WRITE_ERROR_CODES.has(error.result?.code);
278+
return RETRYABLE_WRITE_ERROR_CODES.has(error.result?.code ?? error.code ?? 0);
279279
}
280-
281280
return RETRYABLE_WRITE_ERROR_CODES.has(error.code ?? 0);
282281
}
283282

src/sdam/server.ts

+12-1
Original file line numberDiff line numberDiff line change
@@ -558,6 +558,12 @@ function inActiveTransaction(session: ClientSession | undefined, cmd: Document)
558558
return session && session.inTransaction() && !isTransactionCommand(cmd);
559559
}
560560

561+
/** this checks the retryWrites option passed down from the client options, it
562+
* does not check if the server supports retryable writes */
563+
function isRetryableWritesEnabled(topology: Topology) {
564+
return topology.s.options.retryWrites !== false;
565+
}
566+
561567
function makeOperationHandler(
562568
server: Server,
563569
connection: Connection,
@@ -573,7 +579,11 @@ function makeOperationHandler(
573579
session.serverSession.isDirty = true;
574580
}
575581

576-
if (supportsRetryableWrites(server) && !inActiveTransaction(session, cmd)) {
582+
if (
583+
(isRetryableWritesEnabled(server.s.topology) || isTransactionCommand(cmd)) &&
584+
supportsRetryableWrites(server) &&
585+
!inActiveTransaction(session, cmd)
586+
) {
577587
err.addErrorLabel('RetryableWriteError');
578588
}
579589

@@ -584,6 +594,7 @@ function makeOperationHandler(
584594
} else {
585595
// if pre-4.4 server, then add error label if its a retryable write error
586596
if (
597+
(isRetryableWritesEnabled(server.s.topology) || isTransactionCommand(cmd)) &&
587598
maxWireVersion(server) < 9 &&
588599
isRetryableWriteError(err) &&
589600
!inActiveTransaction(session, cmd)

test/functional/retryable_writes.test.js

+7-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ const parseRunOn = require('../functional/spec-runner').parseRunOn;
66

77
describe('Retryable Writes', function () {
88
let ctx = {};
9-
109
loadSpecTests('retryable-writes').forEach(suite => {
1110
const environmentRequirementList = parseRunOn(suite.runOn);
1211
environmentRequirementList.forEach(requires => {
@@ -86,6 +85,10 @@ function executeScenarioTest(test, ctx) {
8685
const args = generateArguments(test);
8786

8887
let result = ctx.collection[test.operation.name].apply(ctx.collection, args);
88+
const outcome = test.outcome && test.outcome.result;
89+
const errorLabelsContain = outcome && outcome.errorLabelsContain;
90+
const errorLabelsOmit = outcome && outcome.errorLabelsOmit;
91+
const hasResult = outcome && !errorLabelsContain && !errorLabelsOmit;
8992
if (test.outcome.error) {
9093
result = result
9194
.then(() => expect(false).to.be.true)
@@ -94,6 +97,9 @@ function executeScenarioTest(test, ctx) {
9497
expect(err.message, 'expected operations to fail, but they succeeded').to.not.match(
9598
/expected false to be true/
9699
);
100+
if (hasResult) expect(err.result).to.matchMongoSpec(test.outcome.result);
101+
if (errorLabelsContain) expect(err.errorLabels).to.have.members(errorLabelsContain);
102+
if (errorLabelsOmit) expect(err.errorLabels).to.not.have.members(errorLabelsOmit);
97103
});
98104
} else if (test.outcome.result) {
99105
const expected = transformToFixUpsertedId(test.outcome.result);

test/spec/retryable-writes/insertOne-serverErrors.json

+4-1
Original file line numberDiff line numberDiff line change
@@ -966,7 +966,10 @@
966966
],
967967
"writeConcernError": {
968968
"code": 91,
969-
"errmsg": "Replication is being shut down"
969+
"errmsg": "Replication is being shut down",
970+
"errorLabels": [
971+
"RetryableWriteError"
972+
]
970973
}
971974
}
972975
},

test/spec/retryable-writes/insertOne-serverErrors.yml

+1
Original file line numberDiff line numberDiff line change
@@ -442,6 +442,7 @@ tests:
442442
writeConcernError:
443443
code: 91
444444
errmsg: Replication is being shut down
445+
errorLabels: ["RetryableWriteError"]
445446
operation:
446447
name: "insertOne"
447448
arguments:

0 commit comments

Comments
 (0)