Skip to content

Commit 3b725ef

Browse files
authored
fix(utils): only set retryWrites to true for valid operations
Fixes NODE-1641
1 parent f93a8c3 commit 3b725ef

File tree

3 files changed

+43
-40
lines changed

3 files changed

+43
-40
lines changed

lib/bulk/common.js

+7-9
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@ const Long = require('mongodb-core').BSON.Long;
44
const MongoError = require('mongodb-core').MongoError;
55
const toError = require('../utils').toError;
66
const handleCallback = require('../utils').handleCallback;
7+
const applyRetryableWrites = require('../utils').applyRetryableWrites;
78
const applyWriteConcern = require('../utils').applyWriteConcern;
8-
const shallowClone = require('../utils').shallowClone;
99
const ObjectID = require('mongodb-core').BSON.ObjectID;
1010
const BSON = require('mongodb-core').BSON;
1111

@@ -702,13 +702,11 @@ class BulkOperationBase {
702702
const maxWriteBatchSize =
703703
isMaster && isMaster.maxWriteBatchSize ? isMaster.maxWriteBatchSize : 1000;
704704

705-
// Get the write concern
706-
let writeConcern = applyWriteConcern(
707-
shallowClone(options),
708-
{ collection: collection },
709-
options
710-
);
711-
writeConcern = writeConcern.writeConcern;
705+
// Final options for retryable writes and write concern
706+
let finalOptions = Object.assign({}, options);
707+
finalOptions = applyRetryableWrites(finalOptions, collection.s.db);
708+
finalOptions = applyWriteConcern(finalOptions, { collection: collection }, options);
709+
const writeConcern = finalOptions.writeConcern;
712710

713711
// Get the promiseLibrary
714712
const promiseLibrary = options.promiseLibrary || Promise;
@@ -754,7 +752,7 @@ class BulkOperationBase {
754752
// Topology
755753
topology: topology,
756754
// Options
757-
options: options,
755+
options: finalOptions,
758756
// Current operation
759757
currentOp: currentOp,
760758
// Executed

lib/operations/collection_ops.js

+21-26
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
'use strict';
22

33
const applyWriteConcern = require('../utils').applyWriteConcern;
4+
const applyRetryableWrites = require('../utils').applyRetryableWrites;
45
const checkCollectionName = require('../utils').checkCollectionName;
56
const Code = require('mongodb-core').BSON.Code;
67
const createIndexDb = require('./db_ops').createIndex;
@@ -97,12 +98,10 @@ function bulkWrite(coll, operations, options, callback) {
9798
return callback(err, null);
9899
}
99100

100-
// Final options for write concern
101-
const finalOptions = applyWriteConcern(
102-
Object.assign({}, options),
103-
{ db: coll.s.db, collection: coll },
104-
options
105-
);
101+
// Final options for retryable writes and write concern
102+
let finalOptions = Object.assign({}, options);
103+
finalOptions = applyRetryableWrites(finalOptions, coll.s.db);
104+
finalOptions = applyWriteConcern(finalOptions, { db: coll.s.db, collection: coll }, options);
106105

107106
const writeCon = finalOptions.writeConcern ? finalOptions.writeConcern : {};
108107
const capabilities = coll.s.topology.capabilities();
@@ -504,8 +503,10 @@ function findAndModify(coll, query, sort, doc, options, callback) {
504503
// No check on the documents
505504
options.checkKeys = false;
506505

507-
// Get the write concern settings
508-
const finalOptions = applyWriteConcern(options, { db: coll.s.db, collection: coll }, options);
506+
// Final options for retryable writes and write concern
507+
let finalOptions = Object.assign({}, options);
508+
finalOptions = applyRetryableWrites(finalOptions, coll.s.db);
509+
finalOptions = applyWriteConcern(finalOptions, { db: coll.s.db, collection: coll }, options);
509510

510511
// Decorate the findAndModify command with the write Concern
511512
if (finalOptions.writeConcern) {
@@ -805,12 +806,10 @@ function insertDocuments(coll, docs, options, callback) {
805806
// Ensure we are operating on an array op docs
806807
docs = Array.isArray(docs) ? docs : [docs];
807808

808-
// Get the write concern options
809-
const finalOptions = applyWriteConcern(
810-
Object.assign({}, options),
811-
{ db: coll.s.db, collection: coll },
812-
options
813-
);
809+
// Final options for retryable writes and write concern
810+
let finalOptions = Object.assign({}, options);
811+
finalOptions = applyRetryableWrites(finalOptions, coll.s.db);
812+
finalOptions = applyWriteConcern(finalOptions, { db: coll.s.db, collection: coll }, options);
814813

815814
// If keep going set unordered
816815
if (finalOptions.keepGoing === true) finalOptions.ordered = false;
@@ -1138,12 +1137,10 @@ function removeDocuments(coll, selector, options, callback) {
11381137
// Create an empty options object if the provided one is null
11391138
options = options || {};
11401139

1141-
// Get the write concern options
1142-
const finalOptions = applyWriteConcern(
1143-
Object.assign({}, options),
1144-
{ db: coll.s.db, collection: coll },
1145-
options
1146-
);
1140+
// Final options for retryable writes and write concern
1141+
let finalOptions = Object.assign({}, options);
1142+
finalOptions = applyRetryableWrites(finalOptions, coll.s.db);
1143+
finalOptions = applyWriteConcern(finalOptions, { db: coll.s.db, collection: coll }, options);
11471144

11481145
// If selector is null set empty
11491146
if (selector == null) selector = {};
@@ -1336,12 +1333,10 @@ function updateDocuments(coll, selector, document, options, callback) {
13361333
if (document == null || typeof document !== 'object')
13371334
return callback(toError('document must be a valid JavaScript object'));
13381335

1339-
// Get the write concern options
1340-
const finalOptions = applyWriteConcern(
1341-
Object.assign({}, options),
1342-
{ db: coll.s.db, collection: coll },
1343-
options
1344-
);
1336+
// Final options for retryable writes and write concern
1337+
let finalOptions = Object.assign({}, options);
1338+
finalOptions = applyRetryableWrites(finalOptions, coll.s.db);
1339+
finalOptions = applyWriteConcern(finalOptions, { db: coll.s.db, collection: coll }, options);
13451340

13461341
// Do we return the actual result document
13471342
// Either use override on the function, or go back to default on either the collection

lib/utils.js

+15-5
Original file line numberDiff line numberDiff line change
@@ -441,6 +441,20 @@ const executeOperation = (topology, operation, args, options) => {
441441
});
442442
};
443443

444+
/**
445+
* Applies retryWrites: true to a command if retryWrites is set on the command's database.
446+
*
447+
* @param {object} target The target command to which we will apply retryWrites.
448+
* @param {object} db The database from which we can inherit a retryWrites value.
449+
*/
450+
function applyRetryableWrites(target, db) {
451+
if (db && db.s.options.retryWrites) {
452+
target.retryWrites = true;
453+
}
454+
455+
return target;
456+
}
457+
444458
/**
445459
* Applies a write concern to a command based on well defined inheritance rules, optionally
446460
* detecting support for the write concern in the first place.
@@ -455,11 +469,6 @@ function applyWriteConcern(target, sources, options) {
455469
const db = sources.db;
456470
const coll = sources.collection;
457471

458-
// NOTE: there is probably a much better place for this
459-
if (db && db.s.options.retryWrites) {
460-
target.retryWrites = true;
461-
}
462-
463472
if (options.session && options.session.inTransaction()) {
464473
// writeConcern is not allowed within a multi-statement transaction
465474
if (target.writeConcern) {
@@ -701,6 +710,7 @@ module.exports = {
701710
mergeOptionsAndWriteConcern,
702711
translateReadPreference,
703712
executeOperation,
713+
applyRetryableWrites,
704714
applyWriteConcern,
705715
resolveReadPreference,
706716
isPromiseLike,

0 commit comments

Comments
 (0)