Skip to content

Commit 1595140

Browse files
authored
fix(NODE-3711): retry txn end on retryable write (#3047)
1 parent 9b980c4 commit 1595140

File tree

4 files changed

+39
-9
lines changed

4 files changed

+39
-9
lines changed

lib/core/error.js

+6-1
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

+2-1
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

+1-7
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

+30
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

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

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

0 commit comments

Comments
 (0)