From 8e48340301c668742a5a5bb2a8f1bb68553faaf2 Mon Sep 17 00:00:00 2001 From: Kevin Pulo Date: Thu, 9 Aug 2018 04:00:41 +0000 Subject: SERVER-33606 fail to create logical session in shell connected to old servers Also add --disableImplicitSessions shell cmdline arg. (cherry picked from commit 23794c14b03bb272daad7a2b25eca0b80c03a31c) --- src/mongo/shell/mongo.js | 26 +++++++++++++++++++++++--- src/mongo/shell/session.js | 32 +++++++++++++++++++++++++++----- src/mongo/shell/shell_options.cpp | 8 ++++++++ src/mongo/shell/shell_options.h | 1 + src/mongo/shell/shell_utils.cpp | 2 +- 5 files changed, 60 insertions(+), 9 deletions(-) diff --git a/src/mongo/shell/mongo.js b/src/mongo/shell/mongo.js index faf2b624b66..e574f0b99d7 100644 --- a/src/mongo/shell/mongo.js +++ b/src/mongo/shell/mongo.js @@ -263,6 +263,10 @@ connect = function(url, user, pass) { } } + if (_shouldUseImplicitSessions()) { + chatty("Implicit session: " + db.getSession()); + } + // Implicit sessions should not be used when opening a connection. In particular, the buildInfo // command is erroneously marked as requiring auth in MongoDB 3.6 and therefore fails if a // logical session id is included in the request. @@ -445,14 +449,30 @@ Mongo.prototype._getDefaultSession = function getDefaultSession() { // a logical session id. These implicit sessions are intentionally not causally consistent. If // implicit sessions have been globally disabled, a dummy session is used instead of a real one. if (!this.hasOwnProperty("_defaultSession")) { - this._defaultSession = _shouldUseImplicitSessions() - ? this.startSession({causalConsistency: false}) - : new _DummyDriverSession(this); + if (_shouldUseImplicitSessions()) { + try { + this._defaultSession = this.startSession({causalConsistency: false}); + } catch (e) { + if (e instanceof DriverSession.UnsupportedError) { + chatty("WARNING: No implicit session: " + e.message); + this._setDummyDefaultSession(); + } else { + print("ERROR: Implicit session failed: " + e.message); + throw(e); + } + } + } else { + this._setDummyDefaultSession(); + } this._defaultSession._isExplicit = false; } return this._defaultSession; }; +Mongo.prototype._setDummyDefaultSession = function setDummyDefaultSession() { + this._defaultSession = new _DummyDriverSession(this); +}; + Mongo.prototype.isCausalConsistency = function isCausalConsistency() { if (!this.hasOwnProperty("_causalConsistency")) { this._causalConsistency = false; diff --git a/src/mongo/shell/session.js b/src/mongo/shell/session.js index ecd80b288cb..905e3682b7a 100644 --- a/src/mongo/shell/session.js +++ b/src/mongo/shell/session.js @@ -80,11 +80,11 @@ var { }; } - function SessionAwareClient(client) { - const kWireVersionSupportingCausalConsistency = 6; - const kWireVersionSupportingLogicalSession = 6; - const kWireVersionSupportingRetryableWrites = 6; + const kWireVersionSupportingCausalConsistency = 6; + const kWireVersionSupportingLogicalSession = 6; + const kWireVersionSupportingRetryableWrites = 6; + function SessionAwareClient(client) { this.getReadPreference = function getReadPreference(driverSession) { const sessionOptions = driverSession.getOptions(); if (sessionOptions.getReadPreference() !== undefined) { @@ -444,8 +444,17 @@ var { let _nextTxnNum = 0; this.client = new SessionAwareClient(client); + if (!serverSupports(kWireVersionSupportingLogicalSession)) { + throw new DriverSession.UnsupportedError( + "Logical Sessions are only supported on server versions 3.6 and greater."); + } this.handle = client._startSession(); + function serverSupports(wireVersion) { + return client.getMinWireVersion() <= wireVersion && + wireVersion <= client.getMaxWireVersion(); + } + this.getLastUsed = function getLastUsed() { return _lastUsed; }; @@ -585,7 +594,7 @@ var { } function makeDriverSessionConstructor(implMethods, defaultOptions = {}) { - return function(client, options = defaultOptions) { + var driverSessionConstructor = function(client, options = defaultOptions) { let _options = options; let _hasEnded = false; @@ -685,6 +694,19 @@ var { return "session " + tojson(sessionId); }; }; + + // Having a specific Error for when logical sessions aren't supported by the server, allows + // the correct fallback behavior in this case (while propagating other errors). + driverSessionConstructor.UnsupportedError = function(message) { + this.name = "DriverSession.UnsupportedError"; + this.message = message; + this.stack = this.toString() + "\n" + (new Error()).stack; + }; + driverSessionConstructor.UnsupportedError.prototype = Object.create(Error.prototype); + driverSessionConstructor.UnsupportedError.prototype.constructor = + driverSessionConstructor.UnsupportedError; + + return driverSessionConstructor; } const DriverSession = makeDriverSessionConstructor({ diff --git a/src/mongo/shell/shell_options.cpp b/src/mongo/shell/shell_options.cpp index 0f1e667a36d..6ac0a498956 100644 --- a/src/mongo/shell/shell_options.cpp +++ b/src/mongo/shell/shell_options.cpp @@ -214,6 +214,11 @@ Status addMongoShellOptions(moe::OptionSection* options) { moe::Switch, "automatically retry write operations upon transient network errors"); + options->addOptionChaining("disableImplicitSessions", + "disableImplicitSessions", + moe::Switch, + "do not automatically create and use implicit sessions"); + options ->addOptionChaining( "rpcProtocols", "rpcProtocols", moe::String, " none, opQueryOnly, opCommandOnly, all") @@ -372,6 +377,9 @@ Status storeMongoShellOptions(const moe::Environment& params, if (params.count("retryWrites")) { shellGlobalParams.shouldRetryWrites = true; } + if (params.count("disableImplicitSessions")) { + shellGlobalParams.shouldUseImplicitSessions = false; + } if (params.count("rpcProtocols")) { std::string protos = params["rpcProtocols"].as(); auto parsedRPCProtos = rpc::parseProtocolSet(protos); diff --git a/src/mongo/shell/shell_options.h b/src/mongo/shell/shell_options.h index 6b1852b114a..bee473d2ea9 100644 --- a/src/mongo/shell/shell_options.h +++ b/src/mongo/shell/shell_options.h @@ -72,6 +72,7 @@ struct ShellGlobalParams { std::string writeMode = "commands"; std::string readMode = "compatibility"; bool shouldRetryWrites = false; + bool shouldUseImplicitSessions = true; boost::optional rpcProtocols = boost::none; diff --git a/src/mongo/shell/shell_utils.cpp b/src/mongo/shell/shell_utils.cpp index 196e4060523..e672fedf739 100644 --- a/src/mongo/shell/shell_utils.cpp +++ b/src/mongo/shell/shell_utils.cpp @@ -218,7 +218,7 @@ BSONObj shouldRetryWrites(const BSONObj&, void* data) { } BSONObj shouldUseImplicitSessions(const BSONObj&, void* data) { - return BSON("" << true); + return BSON("" << shellGlobalParams.shouldUseImplicitSessions); } BSONObj interpreterVersion(const BSONObj& a, void* data) { -- cgit v1.2.1