From 2d51eb795a652be4d7253af11cad71da20ea6534 Mon Sep 17 00:00:00 2001 From: Durran Jordan Date: Thu, 18 Nov 2021 11:14:43 +0100 Subject: [PATCH] test(NODE-3711): retry txn end on retryable write --- src/error.ts | 4 ++++ src/sessions.ts | 7 ++++++- test/functional/transactions.test.js | 8 +------- test/unit/error.test.js | 29 ++++++++++++++++++++++++++++ 4 files changed, 40 insertions(+), 8 deletions(-) diff --git a/src/error.ts b/src/error.ts index fb64d5c87a2..6d4e31626c6 100644 --- a/src/error.ts +++ b/src/error.ts @@ -697,6 +697,10 @@ const RETRYABLE_WRITE_ERROR_CODES = new Set([ MONGODB_ERROR_CODES.ExceededTimeLimit ]); +export function isRetryableEndTransactionError(error: MongoError): boolean { + return error.hasErrorLabel('RetryableWriteError'); +} + export function isRetryableWriteError(error: MongoError): boolean { if (error instanceof MongoWriteConcernError) { return RETRYABLE_WRITE_ERROR_CODES.has(error.result?.code ?? error.code ?? 0); diff --git a/src/sessions.ts b/src/sessions.ts index ec865f1ea45..e6eb0c99738 100644 --- a/src/sessions.ts +++ b/src/sessions.ts @@ -8,6 +8,7 @@ import { MongoError, MongoInvalidArgumentError, isRetryableError, + isRetryableEndTransactionError, MongoCompatibilityError, MongoNetworkError, MongoWriteConcernError, @@ -767,7 +768,11 @@ function endTransaction(session: ClientSession, commandName: string, callback: C session.unpin(); } - if (err && isRetryableError(err as MongoError)) { + /* eslint no-console: 0 */ + console.log('\n\n\nerr', err); + if (err && isRetryableEndTransactionError(err as MongoError)) { + /* eslint no-console: 0 */ + console.log('retrying'); // SPEC-1185: apply majority write concern when retrying commitTransaction if (command.commitTransaction) { // per txns spec, must unpin session in this case diff --git a/test/functional/transactions.test.js b/test/functional/transactions.test.js index 9f18ef8c093..836fb6b9820 100644 --- a/test/functional/transactions.test.js +++ b/test/functional/transactions.test.js @@ -111,13 +111,7 @@ const SKIP_TESTS = [ // Will be implemented as part of NODE-2034 'Client side error in command starting transaction', - 'Client side error when transaction is in progress', - - // Will be implemented as part of NODE-2538 - 'abortTransaction only retries once with RetryableWriteError from server', - 'abortTransaction does not retry without RetryableWriteError label', - 'commitTransaction does not retry error without RetryableWriteError label', - 'commitTransaction retries once with RetryableWriteError from server' + 'Client side error when transaction is in progress' ]; describe('Transactions Spec Legacy Tests', function () { diff --git a/test/unit/error.test.js b/test/unit/error.test.js index dd74d9023a6..1015352c57f 100644 --- a/test/unit/error.test.js +++ b/test/unit/error.test.js @@ -8,6 +8,7 @@ const { ReplSetFixture } = require('../tools/common'); const { ns } = require('../../src/utils'); const { Topology } = require('../../src/sdam/topology'); const { MongoNetworkError, MongoWriteConcernError } = require('../../src/index'); +const { isRetryableEndTransactionError } = require('../../src/error'); const { PoolClosedError: MongoPoolClosedError, WaitQueueTimeoutError: MongoWaitQueueTimeoutError @@ -36,6 +37,34 @@ describe('MongoErrors', () => { }); } + describe('#isRetryableEndTransactionError', function () { + context('when the error has a RetryableWriteError label', function () { + const error = new MongoNetworkError(''); + error.addErrorLabel('RetryableWriteError'); + + it('returns true', function () { + expect(isRetryableEndTransactionError(error)).to.be.true; + }); + }); + + context('when the error does not have a RetryableWriteError label', function () { + const error = new MongoNetworkError(''); + error.addErrorLabel('InvalidLabel'); + + it('returns false', function () { + expect(isRetryableEndTransactionError(error)).to.be.false; + }); + }); + + context('when the error does not have any label', function () { + const error = new MongoNetworkError(''); + + it('returns false', function () { + expect(isRetryableEndTransactionError(error)).to.be.false; + }); + }); + }); + describe('when MongoNetworkError is constructed', () => { it('should only define beforeHandshake symbol if boolean option passed in', function () { const errorWithOptionTrue = new MongoNetworkError('', { beforeHandshake: true });