Skip to content
This repository has been archived by the owner on Feb 4, 2022. It is now read-only.

Commit

Permalink
feat(error): add more specific error type for write concern errors
Browse files Browse the repository at this point in the history
Presently we wrap all reported `writeConcernError`s with a simple
`MongoError`, which makes it difficult to specialize on this case
in situations such as retry logic.

NODE-1516
  • Loading branch information
mbroadst committed Jun 18, 2018
1 parent a81678b commit 347c5d7
Show file tree
Hide file tree
Showing 4 changed files with 88 additions and 18 deletions.
5 changes: 5 additions & 0 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,14 @@ try {
} catch (err) {} // eslint-disable-line

module.exports = {
// Errors
MongoError: require('./lib/error').MongoError,
MongoNetworkError: require('./lib/error').MongoNetworkError,
MongoParseError: require('./lib/error').MongoParseError,
MongoTimeoutError: require('./lib/error').MongoTimeoutError,
MongoWriteConcernError: require('./lib/error').MongoWriteConcernError,
mongoErrorContextSymbol: require('./lib/error').mongoErrorContextSymbol,
// Core
Connection: require('./lib/connection/connection'),
Server: require('./lib/topologies/server'),
ReplSet: require('./lib/topologies/replset'),
Expand Down
35 changes: 17 additions & 18 deletions lib/connection/pool.js
Original file line number Diff line number Diff line change
@@ -1,23 +1,22 @@
'use strict';

var inherits = require('util').inherits,
EventEmitter = require('events').EventEmitter,
Connection = require('./connection'),
MongoError = require('../error').MongoError,
MongoNetworkError = require('../error').MongoNetworkError,
Logger = require('./logger'),
f = require('util').format,
Query = require('./commands').Query,
CommandResult = require('./command_result'),
MESSAGE_HEADER_SIZE = require('../wireprotocol/shared').MESSAGE_HEADER_SIZE,
opcodes = require('../wireprotocol/shared').opcodes,
compress = require('../wireprotocol/compression').compress,
compressorIDs = require('../wireprotocol/compression').compressorIDs,
uncompressibleCommands = require('../wireprotocol/compression').uncompressibleCommands,
resolveClusterTime = require('../topologies/shared').resolveClusterTime;

const inherits = require('util').inherits;
const EventEmitter = require('events').EventEmitter;
const Connection = require('./connection');
const MongoError = require('../error').MongoError;
const MongoNetworkError = require('../error').MongoNetworkError;
const MongoWriteConcernError = require('../error').MongoWriteConcernError;
const Logger = require('./logger');
const f = require('util').format;
const Query = require('./commands').Query;
const CommandResult = require('./command_result');
const MESSAGE_HEADER_SIZE = require('../wireprotocol/shared').MESSAGE_HEADER_SIZE;
const opcodes = require('../wireprotocol/shared').opcodes;
const compress = require('../wireprotocol/compression').compress;
const compressorIDs = require('../wireprotocol/compression').compressorIDs;
const uncompressibleCommands = require('../wireprotocol/compression').uncompressibleCommands;
const resolveClusterTime = require('../topologies/shared').resolveClusterTime;
const apm = require('./apm');

const defaultAuthProviders = require('../auth/defaultAuthProviders').defaultAuthProviders;

var DISCONNECTED = 'disconnected';
Expand Down Expand Up @@ -585,7 +584,7 @@ function messageHandler(self) {
return handleOperationCallback(
self,
workItem.cb,
new MongoError(responseDoc.writeConcernError)
new MongoWriteConcernError(responseDoc.writeConcernError)
);
}
}
Expand Down
16 changes: 16 additions & 0 deletions lib/error.js
Original file line number Diff line number Diff line change
Expand Up @@ -123,11 +123,27 @@ function isRetryableError(error) {
return false;
}

/**
* An error thrown when the server reports a writeConcernError
*
* @class
* @param {Error|string|object} message The error message
* @property {string} message The error message
* @return {MongoWriteConcernError} A MongoWriteConcernError instance
* @extends {MongoError}
*/
const MongoWriteConcernError = function(message) {
MongoError.call(this, message);
this.name = 'MongoWriteConcernError';
};
util.inherits(MongoWriteConcernError, MongoError);

module.exports = {
MongoError,
MongoNetworkError,
MongoParseError,
MongoTimeoutError,
MongoWriteConcernError,
mongoErrorContextSymbol,
isRetryableError
};
50 changes: 50 additions & 0 deletions test/tests/unit/pool_tests.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
'use strict';

const expect = require('chai').expect;
const mock = require('mongodb-mock-server');
const Server = require('../../../lib/topologies/server');
const MongoWriteConcernError = require('../../../lib/error').MongoWriteConcernError;

const test = {};
describe('Pool (unit)', function() {
afterEach(() => mock.cleanup());
beforeEach(() => {
return mock.createServer().then(mockServer => {
test.server = mockServer;
});
});

it('should throw a MongoWriteConcernError when a writeConcernError is present', function(done) {
test.server.setMessageHandler(request => {
const doc = request.document;
if (doc.ismaster) {
return request.reply(Object.assign({}, mock.DEFAULT_ISMASTER));
} else if (doc.insert) {
return request.reply({
ok: 1,
writeConcernError: {
code: 64,
codeName: 'WriteConcernFailed',
errmsg: 'waiting for replication timed out',
errInfo: {
wtimeout: true
}
}
});
}
});

const client = new Server(test.server.address());
client.on('error', done);
client.once('connect', () => {
client.insert('fake.ns', { a: 1 }, (err, result) => {
expect(err).to.exist;
expect(result).to.not.exist;
expect(err).to.be.instanceOf(MongoWriteConcernError);
done();
});
});

client.connect();
});
});

0 comments on commit 347c5d7

Please sign in to comment.