From 392f5a692b96e6a328b22dee11d8ca2057589bf1 Mon Sep 17 00:00:00 2001 From: Katherine Walker Date: Mon, 12 Aug 2019 13:24:50 -0400 Subject: [PATCH] fix(topology): add new error for retryWrites on MMAPv1 Fixes NODE-2098 --- lib/core/sdam/topology.js | 2 ++ lib/core/topologies/mongos.js | 2 ++ lib/core/topologies/replset.js | 2 ++ lib/core/topologies/shared.js | 22 ++++++++++++++++++++++ 4 files changed, 28 insertions(+) diff --git a/lib/core/sdam/topology.js b/lib/core/sdam/topology.js index 0372dea895..3b046f2dda 100644 --- a/lib/core/sdam/topology.js +++ b/lib/core/sdam/topology.js @@ -25,6 +25,7 @@ const createClientInfo = require('../topologies/shared').createClientInfo; const MongoError = require('../error').MongoError; const resolveClusterTime = require('../topologies/shared').resolveClusterTime; const SrvPoller = require('./srv_polling').SrvPoller; +const getMMAPError = require('../topologies/shared').getMMAPError; // Global state let globalTopologyCounter = 0; @@ -972,6 +973,7 @@ function executeWriteOperation(args, options, callback) { const handler = (err, result) => { if (!err) return callback(null, result); if (!isRetryableError(err)) { + err = getMMAPError(err); return callback(err); } diff --git a/lib/core/topologies/mongos.js b/lib/core/topologies/mongos.js index 9dcf5f77c6..5164e07897 100644 --- a/lib/core/topologies/mongos.js +++ b/lib/core/topologies/mongos.js @@ -17,6 +17,7 @@ const isRetryableWritesSupported = require('./shared').isRetryableWritesSupporte const relayEvents = require('../utils').relayEvents; const isRetryableError = require('../error').isRetryableError; const BSON = retrieveBSON(); +const getMMAPError = require('./shared').getMMAPError; /** * @fileOverview The **Mongos** class is a class that represents a Mongos Proxy topology and is @@ -880,6 +881,7 @@ function executeWriteOperation(args, options, callback) { const handler = (err, result) => { if (!err) return callback(null, result); if (!isRetryableError(err) || !willRetryWrite) { + err = getMMAPError(err); return callback(err); } diff --git a/lib/core/topologies/replset.js b/lib/core/topologies/replset.js index 3bf6b6ec43..141d937c20 100644 --- a/lib/core/topologies/replset.js +++ b/lib/core/topologies/replset.js @@ -20,6 +20,7 @@ const relayEvents = require('../utils').relayEvents; const isRetryableError = require('../error').isRetryableError; const BSON = retrieveBSON(); const calculateDurationInMs = require('../utils').calculateDurationInMs; +const getMMAPError = require('./shared').getMMAPError; // // States @@ -1190,6 +1191,7 @@ function executeWriteOperation(args, options, callback) { const handler = (err, result) => { if (!err) return callback(null, result); if (!isRetryableError(err)) { + err = getMMAPError(err); return callback(err); } diff --git a/lib/core/topologies/shared.js b/lib/core/topologies/shared.js index 5f39130d23..dbcebcd64e 100644 --- a/lib/core/topologies/shared.js +++ b/lib/core/topologies/shared.js @@ -5,6 +5,9 @@ const f = require('util').format; const ReadPreference = require('./read_preference'); const Buffer = require('safe-buffer').Buffer; const TopologyType = require('../sdam/topology_description').TopologyType; +const MongoError = require('../error').MongoError; + +const MMAPv1_RETRY_WRITES_ERROR_CODE = 20; /** * Emit event if it exists @@ -437,6 +440,24 @@ const isRetryableWritesSupported = function(topology) { return true; }; +const MMAPv1_RETRY_WRITES_ERROR_MESSAGE = + 'This MongoDB deployment does not support retryable writes. Please add retryWrites=false to your connection string.'; + +function getMMAPError(err) { + if (err.code !== MMAPv1_RETRY_WRITES_ERROR_CODE || !err.errmsg.includes('Transaction numbers')) { + return err; + } + + // According to the retryable writes spec, we must replace the error message in this case. + // We need to replace err.message so the thrown message is correct and we need to replace err.errmsg to meet the spec requirement. + const newErr = new MongoError({ + message: MMAPv1_RETRY_WRITES_ERROR_MESSAGE, + errmsg: MMAPv1_RETRY_WRITES_ERROR_MESSAGE, + originalError: err + }); + return newErr; +} + module.exports.SessionMixins = SessionMixins; module.exports.resolveClusterTime = resolveClusterTime; module.exports.inquireServerState = inquireServerState; @@ -451,3 +472,4 @@ module.exports.diff = diff; module.exports.Interval = Interval; module.exports.Timeout = Timeout; module.exports.isRetryableWritesSupported = isRetryableWritesSupported; +module.exports.getMMAPError = getMMAPError;