Skip to content

Commit

Permalink
fix(command): only add TransientTransactionError label when in a tran…
Browse files Browse the repository at this point in the history
…saction

Fixes NODE-2089
  • Loading branch information
daprahamian authored and mbroadst committed Nov 25, 2019
1 parent ce60476 commit 8bab074
Show file tree
Hide file tree
Showing 3 changed files with 86 additions and 3 deletions.
3 changes: 0 additions & 3 deletions lib/core/error.js
Expand Up @@ -60,9 +60,6 @@ class MongoNetworkError extends MongoError {
constructor(message) {
super(message);
this.name = 'MongoNetworkError';

// This is added as part of the transactions specification
this.errorLabels = ['TransientTransactionError'];
}
}

Expand Down
13 changes: 13 additions & 0 deletions lib/core/wireprotocol/command.js
Expand Up @@ -8,6 +8,7 @@ const isSharded = require('./shared').isSharded;
const databaseNamespace = require('./shared').databaseNamespace;
const isTransactionCommand = require('../transactions').isTransactionCommand;
const applySession = require('../sessions').applySession;
const MongoNetworkError = require('../error').MongoNetworkError;

function isClientEncryptionEnabled(server) {
return server.autoEncrypter;
Expand Down Expand Up @@ -90,6 +91,18 @@ function _command(server, ns, cmd, options, callback) {
const inTransaction = session && (session.inTransaction() || isTransactionCommand(finalCmd));
const commandResponseHandler = inTransaction
? function(err) {
// We need to add a TransientTransactionError errorLabel, as stated in the transaction spec.
if (
err &&
err instanceof MongoNetworkError &&
!err.hasErrorLabel('TransientTransactionError')
) {
if (err.errorLabels == null) {
err.errorLabels = [];
}
err.errorLabels.push('TransientTransactionError');
}

if (
!cmd.commitTransaction &&
err &&
Expand Down
73 changes: 73 additions & 0 deletions test/functional/transactions_tests.js
Expand Up @@ -7,6 +7,7 @@ const sessions = core.Sessions;
const TestRunnerContext = require('./spec-runner').TestRunnerContext;
const gatherTestSuites = require('./spec-runner').gatherTestSuites;
const generateTopologyTests = require('./spec-runner').generateTopologyTests;
const MongoNetworkError = require('../../lib/core').MongoNetworkError;

describe('Transactions', function() {
const testContext = new TestRunnerContext();
Expand Down Expand Up @@ -123,4 +124,76 @@ describe('Transactions', function() {
}
});
});

describe('TransientTransactionError', function() {
it('should have a TransientTransactionError label inside of a transaction', {
metadata: { requires: { topology: 'replicaset', mongodb: '>=4.0.0' } },
test: function(done) {
const configuration = this.configuration;
const client = configuration.newClient({ w: 1 }, { useUnifiedTopology: true });

client.connect((err, client) => {
const session = client.startSession();
const db = client.db(configuration.db);
db.createCollection('transaction_error_test', (err, coll) => {
expect(err).to.not.exist;
session.startTransaction();
coll.insertOne({ a: 1 }, { session }, err => {
expect(err).to.not.exist;
expect(session.inTransaction()).to.be.true;

db.executeDbAdminCommand(
{
configureFailPoint: 'failCommand',
mode: { times: 1 },
data: { failCommands: ['insert'], closeConnection: true }
},
() => {
expect(session.inTransaction()).to.be.true;
coll.insertOne({ b: 2 }, { session }, err => {
expect(err)
.to.exist.and.to.be.an.instanceof(MongoNetworkError)
.and.to.have.a.property('errorLabels')
.that.includes('TransientTransactionError');
session.endSession(() => client.close(done));
});
}
);
});
});
});
}
});

it('should not have a TransientTransactionError label outside of a transaction', {
metadata: { requires: { topology: 'replicaset', mongodb: '>=4.0.0' } },
test: function(done) {
const configuration = this.configuration;
const client = configuration.newClient({ w: 1 }, { useUnifiedTopology: true });

client.connect((err, client) => {
const db = client.db(configuration.db);
const coll = db.collection('transaction_error_test1');

db.executeDbAdminCommand(
{
configureFailPoint: 'failCommand',
mode: 'alwaysOn',
data: { failCommands: ['insert'], closeConnection: true }
},
() => {
coll.insertOne({ a: 1 }, err => {
expect(err)
.to.exist.and.to.be.an.instanceOf(MongoNetworkError)
.and.to.not.have.a.property('errorLabels');
db.executeDbAdminCommand({ configureFailPoint: 'failCommand', mode: 'off' }, () => {
client.close(done);
});
});
}
);
});
}
});
});
});

0 comments on commit 8bab074

Please sign in to comment.