Skip to content

Commit da29690

Browse files
committed
fix(NODE-3711): retry txn end on retryable write
1 parent 9b980c4 commit da29690

File tree

4 files changed

+38
-9
lines changed

4 files changed

+38
-9
lines changed

lib/core/error.js

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -246,6 +246,10 @@ const RETRYABLE_WRITE_ERROR_CODES = new Set([
246246
MONGODB_ERROR_CODES.ExceededTimeLimit
247247
]);
248248

249+
function isRetryableEndTransactionError(error) {
250+
return error.hasErrorLabel('RetryableWriteError');
251+
}
252+
249253
function isRetryableWriteError(error) {
250254
if (error instanceof MongoWriteConcernError) {
251255
return (
@@ -347,5 +351,6 @@ module.exports = {
347351
isSDAMUnrecoverableError,
348352
isNodeShuttingDownError,
349353
isRetryableWriteError,
350-
isNetworkErrorBeforeHandshake
354+
isNetworkErrorBeforeHandshake,
355+
isRetryableEndTransactionError
351356
};

lib/core/sessions.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ const Binary = BSON.Binary;
77
const uuidV4 = require('./utils').uuidV4;
88
const MongoError = require('./error').MongoError;
99
const isRetryableError = require('././error').isRetryableError;
10+
const isRetryableEndTransactionError = require('././error').isRetryableEndTransactionError;
1011
const MongoNetworkError = require('./error').MongoNetworkError;
1112
const MongoWriteConcernError = require('./error').MongoWriteConcernError;
1213
const Transaction = require('./transactions').Transaction;
@@ -511,7 +512,7 @@ function endTransaction(session, commandName, callback) {
511512

512513
// send the command
513514
session.topology.command('admin.$cmd', command, { session }, (err, reply) => {
514-
if (err && isRetryableError(err)) {
515+
if (err && isRetryableEndTransactionError(err)) {
515516
// SPEC-1185: apply majority write concern when retrying commitTransaction
516517
if (command.commitTransaction) {
517518
// per txns spec, must unpin session in this case

test/functional/transactions.test.js

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -131,13 +131,7 @@ describe('Transactions', function() {
131131

132132
// Will be implemented as part of NODE-2034
133133
'Client side error in command starting transaction',
134-
'Client side error when transaction is in progress',
135-
136-
// Will be implemented as part of NODE-2538
137-
'abortTransaction only retries once with RetryableWriteError from server',
138-
'abortTransaction does not retry without RetryableWriteError label',
139-
'commitTransaction does not retry error without RetryableWriteError label',
140-
'commitTransaction retries once with RetryableWriteError from server'
134+
'Client side error when transaction is in progress'
141135
];
142136

143137
return SKIP_TESTS.indexOf(spec.description) === -1;

test/unit/error.test.js

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
const expect = require('chai').expect;
44
const MongoNetworkError = require('../../lib/core/error').MongoNetworkError;
5+
const isRetryableEndTransactionError = require('../../lib/core/error').isRetryableEndTransactionError;
56

67
describe('MongoErrors', function() {
78
describe('MongoNetworkError', function() {
@@ -19,4 +20,32 @@ describe('MongoErrors', function() {
1920
expect(Object.getOwnPropertySymbols(errorWithoutOption).length).to.equal(0);
2021
});
2122
});
23+
24+
describe('#isRetryableEndTransactionError', function () {
25+
context('when the error has a RetryableWriteError label', function () {
26+
const error = new MongoNetworkError('');
27+
error.addErrorLabel('RetryableWriteError');
28+
29+
it('returns true', function () {
30+
expect(isRetryableEndTransactionError(error)).to.be.true;
31+
});
32+
});
33+
34+
context('when the error does not have a RetryableWriteError label', function () {
35+
const error = new MongoNetworkError('');
36+
error.addErrorLabel('InvalidLabel');
37+
38+
it('returns false', function () {
39+
expect(isRetryableEndTransactionError(error)).to.be.false;
40+
});
41+
});
42+
43+
context('when the error does not have any label', function () {
44+
const error = new MongoNetworkError('');
45+
46+
it('returns false', function () {
47+
expect(isRetryableEndTransactionError(error)).to.be.false;
48+
});
49+
});
50+
});
2251
});

0 commit comments

Comments
 (0)