mongodb-core
Advanced tools
Comparing version 3.0.8 to 3.0.9
@@ -0,1 +1,11 @@ | ||
<a name="3.0.9"></a> | ||
## [3.0.9](https://github.com/mongodb-js/mongodb-core/compare/v3.0.8...v3.0.9) (2018-06-01) | ||
### Features | ||
* **retryableWrites:** adding more support for retries ([abea96d](https://github.com/mongodb-js/mongodb-core/commit/abea96d)) | ||
<a name="3.0.8"></a> | ||
@@ -2,0 +12,0 @@ ## [3.0.8](https://github.com/mongodb-js/mongodb-core/compare/v3.0.7...v3.0.8) (2018-05-08) |
@@ -590,11 +590,15 @@ 'use strict'; | ||
// Establish if we have an error | ||
if ( | ||
workItem.command && | ||
message.documents[0] && | ||
(message.documents[0].ok === 0 || | ||
message.documents[0]['$err'] || | ||
message.documents[0]['errmsg'] || | ||
message.documents[0]['code']) | ||
) { | ||
return handleOperationCallback(self, workItem.cb, new MongoError(message.documents[0])); | ||
if (workItem.command && message.documents[0]) { | ||
const responseDoc = message.documents[0]; | ||
if (responseDoc.ok === 0 || responseDoc.$err || responseDoc.errmsg || responseDoc.code) { | ||
return handleOperationCallback(self, workItem.cb, new MongoError(responseDoc)); | ||
} | ||
if (responseDoc.writeConcernError) { | ||
return handleOperationCallback( | ||
self, | ||
workItem.cb, | ||
new MongoError(responseDoc.writeConcernError) | ||
); | ||
} | ||
} | ||
@@ -601,0 +605,0 @@ |
@@ -78,6 +78,36 @@ 'use strict'; | ||
// see: https://github.com/mongodb/specifications/blob/master/source/retryable-writes/retryable-writes.rst#terms | ||
const RETRYABLE_ERROR_CODES = new Set([ | ||
6, // HostUnreachable | ||
7, // HostNotFound | ||
64, // WriteConcernFailed | ||
89, // NetworkTimeout | ||
91, // ShutdownInProgress | ||
189, // PrimarySteppedDown | ||
9001, // SocketException | ||
11600, // InterruptedAtShutdown | ||
11602, // InterruptedDueToReplStateChange | ||
10107, // NotMaster | ||
13435, // NotMasterNoSlaveOk | ||
13436 // NotMasterOrSecondary | ||
]); | ||
function isRetryableError(error) { | ||
if ( | ||
RETRYABLE_ERROR_CODES.has(error.code) || | ||
error instanceof MongoNetworkError || | ||
error.message.match(/not master/) || | ||
error.message.match(/node is recovering/) | ||
) { | ||
return true; | ||
} | ||
return false; | ||
} | ||
module.exports = { | ||
MongoError: MongoError, | ||
MongoNetworkError: MongoNetworkError, | ||
MongoParseError: MongoParseError | ||
MongoError, | ||
MongoNetworkError, | ||
MongoParseError, | ||
isRetryableError | ||
}; |
@@ -101,2 +101,9 @@ 'use strict'; | ||
} | ||
/** | ||
* Increment the transaction number on the internal ServerSession | ||
*/ | ||
incrementTransactionNumber() { | ||
this.serverSession.txnNumber++; | ||
} | ||
} | ||
@@ -103,0 +110,0 @@ |
@@ -10,3 +10,2 @@ 'use strict'; | ||
MongoError = require('../error').MongoError, | ||
errors = require('../error'), | ||
Server = require('./server'), | ||
@@ -19,5 +18,5 @@ clone = require('./shared').clone, | ||
isRetryableWritesSupported = require('./shared').isRetryableWritesSupported, | ||
getNextTransactionNumber = require('./shared').getNextTransactionNumber, | ||
relayEvents = require('./shared').relayEvents; | ||
const isRetryableError = require('../error').isRetryableError; | ||
const BSON = retrieveBSON(); | ||
@@ -913,8 +912,8 @@ | ||
// increment and assign txnNumber | ||
options.txnNumber = getNextTransactionNumber(options.session); | ||
options.willRetryWrite = true; | ||
options.session.incrementTransactionNumber(); | ||
server[op](ns, ops, options, (err, result) => { | ||
if (!err) return callback(null, result); | ||
if (!(err instanceof errors.MongoNetworkError) && !err.message.match(/not master/)) { | ||
if (!isRetryableError(err)) { | ||
return callback(err); | ||
@@ -1038,2 +1037,7 @@ } | ||
const RETRYABLE_WRITE_OPERATIONS = ['findandmodify', 'insert', 'update', 'delete']; | ||
function isWriteCommand(command) { | ||
return RETRYABLE_WRITE_OPERATIONS.some(op => command[op.toLowerCase()]); | ||
} | ||
/** | ||
@@ -1077,4 +1081,31 @@ * Execute a command | ||
const willRetryWrite = | ||
!options.retrying && | ||
options.retryWrites && | ||
options.session && | ||
isRetryableWritesSupported(self) && | ||
isWriteCommand(cmd); | ||
const cb = (err, result) => { | ||
if (!err) return callback(null, result); | ||
if (!isRetryableError(err)) { | ||
return callback(err); | ||
} | ||
if (willRetryWrite) { | ||
const newOptions = Object.assign({}, clonedOptions, { retrying: true }); | ||
return this.command(ns, cmd, newOptions, callback); | ||
} | ||
return callback(err); | ||
}; | ||
// increment and assign txnNumber | ||
if (willRetryWrite) { | ||
options.session.incrementTransactionNumber(); | ||
options.willRetryWrite = willRetryWrite; | ||
} | ||
// Execute the command | ||
server.command(ns, cmd, clonedOptions, callback); | ||
server.command(ns, cmd, clonedOptions, cb); | ||
}; | ||
@@ -1081,0 +1112,0 @@ |
@@ -11,3 +11,2 @@ 'use strict'; | ||
MongoError = require('../error').MongoError, | ||
errors = require('../error'), | ||
Server = require('./server'), | ||
@@ -21,5 +20,6 @@ ReplSetState = require('./replset_state'), | ||
isRetryableWritesSupported = require('./shared').isRetryableWritesSupported, | ||
getNextTransactionNumber = require('./shared').getNextTransactionNumber, | ||
relayEvents = require('./shared').relayEvents; | ||
const isRetryableError = require('../error').isRetryableError; | ||
var MongoCR = require('../auth/mongocr'), | ||
@@ -1212,3 +1212,3 @@ X509 = require('../auth/x509'), | ||
if (!err) return callback(null, result); | ||
if (!(err instanceof errors.MongoNetworkError) && !err.message.match(/not master/)) { | ||
if (!isRetryableError(err)) { | ||
return callback(err); | ||
@@ -1236,6 +1236,7 @@ } | ||
if (willRetryWrite) { | ||
options.txnNumber = getNextTransactionNumber(options.session); | ||
options.session.incrementTransactionNumber(); | ||
options.willRetryWrite = willRetryWrite; | ||
} | ||
return self.s.replicaSetState.primary[op](ns, ops, options, handler); | ||
self.s.replicaSetState.primary[op](ns, ops, options, handler); | ||
} | ||
@@ -1297,2 +1298,7 @@ | ||
const RETRYABLE_WRITE_OPERATIONS = ['findandmodify', 'insert', 'update', 'delete']; | ||
function isWriteCommand(command) { | ||
return RETRYABLE_WRITE_OPERATIONS.some(op => command[op.toLowerCase()]); | ||
} | ||
/** | ||
@@ -1359,4 +1365,36 @@ * Execute a command | ||
const willRetryWrite = | ||
!options.retrying && | ||
options.retryWrites && | ||
options.session && | ||
isRetryableWritesSupported(self) && | ||
isWriteCommand(cmd); | ||
const cb = (err, result) => { | ||
if (!err) return callback(null, result); | ||
if (!isRetryableError(err)) { | ||
return callback(err); | ||
} | ||
if (willRetryWrite) { | ||
const newOptions = Object.assign({}, options, { retrying: true }); | ||
return this.command(ns, cmd, newOptions, callback); | ||
} | ||
// Per SDAM, remove primary from replicaset | ||
if (this.s.replicaSetState.primary) { | ||
this.s.replicaSetState.remove(this.s.replicaSetState.primary, { force: true }); | ||
} | ||
return callback(err); | ||
}; | ||
// increment and assign txnNumber | ||
if (willRetryWrite) { | ||
options.session.incrementTransactionNumber(); | ||
options.willRetryWrite = willRetryWrite; | ||
} | ||
// Execute the command | ||
server.command(ns, cmd, options, callback); | ||
server.command(ns, cmd, options, cb); | ||
}; | ||
@@ -1363,0 +1401,0 @@ |
'use strict'; | ||
const os = require('os'), | ||
f = require('util').format, | ||
ReadPreference = require('./read_preference'), | ||
retrieveBSON = require('../connection/utils').retrieveBSON; | ||
const os = require('os'); | ||
const f = require('util').format; | ||
const ReadPreference = require('./read_preference'); | ||
const BSON = retrieveBSON(); | ||
/** | ||
@@ -424,12 +421,2 @@ * Emit event if it exists | ||
/** | ||
* Increment the transaction number on the ServerSession contained by the provided ClientSession | ||
* | ||
* @param {ClientSession} session | ||
*/ | ||
const getNextTransactionNumber = function(session) { | ||
session.serverSession.txnNumber++; | ||
return BSON.Long.fromNumber(session.serverSession.txnNumber); | ||
}; | ||
/** | ||
* Relays events for a given listener and emitter | ||
@@ -458,3 +445,2 @@ * | ||
module.exports.isRetryableWritesSupported = isRetryableWritesSupported; | ||
module.exports.getNextTransactionNumber = getNextTransactionNumber; | ||
module.exports.relayEvents = relayEvents; |
@@ -59,5 +59,8 @@ 'use strict'; | ||
// optionally add a `txnNumber` if retryable writes are being attempted | ||
if (typeof options.txnNumber !== 'undefined') { | ||
writeCommand.txnNumber = options.txnNumber; | ||
// apply txnNumber if requested | ||
if (typeof options.session !== 'undefined') { | ||
const serverSession = options.session.serverSession; | ||
if (serverSession.txnNumber && options.willRetryWrite) { | ||
writeCommand.txnNumber = BSON.Long.fromNumber(serverSession.txnNumber); | ||
} | ||
} | ||
@@ -567,2 +570,10 @@ | ||
// apply txnNumber if requested | ||
if (typeof options.session !== 'undefined') { | ||
const serverSession = options.session.serverSession; | ||
if (serverSession.txnNumber && options.willRetryWrite) { | ||
finalCmd.txnNumber = BSON.Long.fromNumber(serverSession.txnNumber); | ||
} | ||
} | ||
// We have a Mongos topology, check if we need to add a readPreference | ||
@@ -569,0 +580,0 @@ if (topology.type === 'mongos' && readPreference && readPreference.preference !== 'primary') { |
{ | ||
"name": "mongodb-core", | ||
"version": "3.0.8", | ||
"version": "3.0.9", | ||
"description": "Core MongoDB driver functionality, no bells and whistles and meant for integration not end applications", | ||
@@ -5,0 +5,0 @@ "main": "index.js", |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
625896
45
12249