From 009fdc7dfcc0197632cef5e3fdc250fdba68f7a5 Mon Sep 17 00:00:00 2001 From: Jason Carey Date: Tue, 25 Jul 2017 19:10:48 -0400 Subject: SERVER-30080 LogicalSessions in the shell Basic logical session implementation for the shell --- src/mongo/client/dbclient.cpp | 14 +++ src/mongo/client/dbclient_rs.h | 4 + src/mongo/client/dbclientinterface.h | 7 ++ src/mongo/db/dbdirectclient.h | 4 + src/mongo/db/logical_session_id.idl | 8 +- src/mongo/db/logical_session_id_helpers.cpp | 5 +- src/mongo/scripting/SConscript | 2 + src/mongo/scripting/engine.cpp | 2 + src/mongo/scripting/mozjs/end_sessions.idl | 37 +++++++ src/mongo/scripting/mozjs/implscope.cpp | 2 + src/mongo/scripting/mozjs/implscope.h | 7 ++ src/mongo/scripting/mozjs/mongo.cpp | 63 ++++++++++++ src/mongo/scripting/mozjs/mongo.h | 8 +- src/mongo/scripting/mozjs/session.cpp | 146 ++++++++++++++++++++++++++++ src/mongo/scripting/mozjs/session.h | 64 ++++++++++++ src/mongo/shell/SConscript | 7 +- src/mongo/shell/db.js | 24 ++++- src/mongo/shell/mongo.js | 21 +--- src/mongo/shell/session.js | 85 ++++++++++++++++ src/mongo/util/uuid.h | 1 + 20 files changed, 483 insertions(+), 28 deletions(-) create mode 100644 src/mongo/scripting/mozjs/end_sessions.idl create mode 100644 src/mongo/scripting/mozjs/session.cpp create mode 100644 src/mongo/scripting/mozjs/session.h create mode 100644 src/mongo/shell/session.js (limited to 'src/mongo') diff --git a/src/mongo/client/dbclient.cpp b/src/mongo/client/dbclient.cpp index 6e95116c13e..74d26aac7d5 100644 --- a/src/mongo/client/dbclient.cpp +++ b/src/mongo/client/dbclient.cpp @@ -38,6 +38,7 @@ #include "mongo/base/status.h" #include "mongo/base/status_with.h" +#include "mongo/bson/util/bson_extract.h" #include "mongo/bson/util/builder.h" #include "mongo/client/authenticate.h" #include "mongo/client/constants.h" @@ -774,6 +775,19 @@ Status DBClientConnection::connect(const HostAndPort& serverAddress, StringData return swProtocolSet.getStatus(); } + { + std::string msgField; + auto msgFieldExtractStatus = bsonExtractStringField(swIsMasterReply.data, "msg", &msgField); + + if (msgFieldExtractStatus == ErrorCodes::NoSuchKey) { + _isMongos = false; + } else if (!msgFieldExtractStatus.isOK()) { + return msgFieldExtractStatus; + } else { + _isMongos = (msgField == "isdbgrid"); + } + } + auto validateStatus = rpc::validateWireVersion(WireSpec::instance().outgoing, swProtocolSet.getValue().version); if (!validateStatus.isOK()) { diff --git a/src/mongo/client/dbclient_rs.h b/src/mongo/client/dbclient_rs.h index bddd64643b0..25844b264bc 100644 --- a/src/mongo/client/dbclient_rs.h +++ b/src/mongo/client/dbclient_rs.h @@ -217,6 +217,10 @@ public: */ virtual void reset(); + bool isMongos() const override { + return false; + } + /** * @bool setting if true, DBClientReplicaSet connections will make sure that secondary * connections are authenticated and log them before returning them to the pool. diff --git a/src/mongo/client/dbclientinterface.h b/src/mongo/client/dbclientinterface.h index 0734784ec19..ab9a4864cb6 100644 --- a/src/mongo/client/dbclientinterface.h +++ b/src/mongo/client/dbclientinterface.h @@ -746,6 +746,8 @@ public: virtual void reset() {} + virtual bool isMongos() const = 0; + protected: /** if the result of a command is ok*/ bool isOk(const BSONObj&); @@ -988,9 +990,14 @@ public: _checkConnection(); } + bool isMongos() const override { + return _isMongos; + } + protected: int _minWireVersion{0}; int _maxWireVersion{0}; + bool _isMongos = false; virtual void _auth(const BSONObj& params); diff --git a/src/mongo/db/dbdirectclient.h b/src/mongo/db/dbdirectclient.h index c35a2855e6f..c5d4bc78f6f 100644 --- a/src/mongo/db/dbdirectclient.h +++ b/src/mongo/db/dbdirectclient.h @@ -96,6 +96,10 @@ public: int getMinWireVersion() final; int getMaxWireVersion() final; + bool isMongos() const final { + return false; + } + private: OperationContext* _opCtx; }; diff --git a/src/mongo/db/logical_session_id.idl b/src/mongo/db/logical_session_id.idl index e60c57753c3..ccedd28e6f6 100644 --- a/src/mongo/db/logical_session_id.idl +++ b/src/mongo/db/logical_session_id.idl @@ -51,11 +51,17 @@ structs: id: uuid uid: sha256Block - LogicalSessionToClient: + LogicalSessionIdToClient: description: "A struct representing a LogicalSessionId to external clients" strict: true fields: id: uuid + + LogicalSessionToClient: + description: "A struct representing a LogicalSession reply to external clients" + strict: true + fields: + id: LogicalSessionIdToClient timeoutMinutes: int LogicalSessionRecord: diff --git a/src/mongo/db/logical_session_id_helpers.cpp b/src/mongo/db/logical_session_id_helpers.cpp index e152e20bafb..fbed0134c83 100644 --- a/src/mongo/db/logical_session_id_helpers.cpp +++ b/src/mongo/db/logical_session_id_helpers.cpp @@ -119,8 +119,11 @@ LogicalSessionRecord makeLogicalSessionRecord(const LogicalSessionId& lsid, Date } LogicalSessionToClient makeLogicalSessionToClient(const LogicalSessionId& lsid) { + LogicalSessionIdToClient lsitc; + lsitc.setId(lsid.getId()); + LogicalSessionToClient id; - id.setId(lsid.getId()); + id.setId(lsitc); id.setTimeoutMinutes(localLogicalSessionTimeoutMinutes); return id; diff --git a/src/mongo/scripting/SConscript b/src/mongo/scripting/SConscript index dadf3936156..1a91ce45af7 100644 --- a/src/mongo/scripting/SConscript +++ b/src/mongo/scripting/SConscript @@ -139,10 +139,12 @@ if usemozjs: 'mozjs/PosixNSPR.cpp', 'mozjs/proxyscope.cpp', 'mozjs/regexp.cpp', + 'mozjs/session.cpp', 'mozjs/timestamp.cpp', 'mozjs/uri.cpp', 'mozjs/valuereader.cpp', 'mozjs/valuewriter.cpp', + env.Idlc('mozjs/end_sessions.idl')[0], ], LIBDEPS=[ 'bson_template_evaluator', diff --git a/src/mongo/scripting/engine.cpp b/src/mongo/scripting/engine.cpp index 483ed7cecd9..02bd0f21bb8 100644 --- a/src/mongo/scripting/engine.cpp +++ b/src/mongo/scripting/engine.cpp @@ -300,6 +300,7 @@ extern const JSFile explain_query; extern const JSFile explainable; extern const JSFile mongo; extern const JSFile mr; +extern const JSFile session; extern const JSFile query; extern const JSFile utils; extern const JSFile utils_sh; @@ -315,6 +316,7 @@ void Scope::execCoreFiles() { execSetup(JSFiles::db); execSetup(JSFiles::mongo); execSetup(JSFiles::mr); + execSetup(JSFiles::session); execSetup(JSFiles::query); execSetup(JSFiles::bulk_api); execSetup(JSFiles::error_codes); diff --git a/src/mongo/scripting/mozjs/end_sessions.idl b/src/mongo/scripting/mozjs/end_sessions.idl new file mode 100644 index 00000000000..1cd8036ab59 --- /dev/null +++ b/src/mongo/scripting/mozjs/end_sessions.idl @@ -0,0 +1,37 @@ +# Copyright (C) 2017 MongoDB Inc. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License, version 3, +# as published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# + +# This IDL file describes the BSON format for a LogicalSessionId, and +# handles the serialization to and deserialization from its BSON representation +# for that class. + +global: + cpp_namespace: "mongo" + +imports: + - "mongo/idl/basic_types.idl" + +structs: + + EndSessions: + description: "A struct representing an endSessions command" + strict: true + fields: + endSessions: int + ids: array + "$clusterTime": + cpp_name: clusterTime + type: object + optional: true diff --git a/src/mongo/scripting/mozjs/implscope.cpp b/src/mongo/scripting/mozjs/implscope.cpp index f269058dddd..a6b42649d35 100644 --- a/src/mongo/scripting/mozjs/implscope.cpp +++ b/src/mongo/scripting/mozjs/implscope.cpp @@ -428,6 +428,7 @@ MozJSImplScope::MozJSImplScope(MozJSScriptEngine* engine) _objectProto(_context), _oidProto(_context), _regExpProto(_context), + _sessionProto(_context), _timestampProto(_context), _uriProto(_context) { kCurrentScope = this; @@ -869,6 +870,7 @@ void MozJSImplScope::installDBAccess() { _dbProto.install(_global); _dbQueryProto.install(_global); _dbCollectionProto.install(_global); + _sessionProto.install(_global); } void MozJSImplScope::installFork() { diff --git a/src/mongo/scripting/mozjs/implscope.h b/src/mongo/scripting/mozjs/implscope.h index c179d982398..0e879280924 100644 --- a/src/mongo/scripting/mozjs/implscope.h +++ b/src/mongo/scripting/mozjs/implscope.h @@ -59,6 +59,7 @@ #include "mongo/scripting/mozjs/object.h" #include "mongo/scripting/mozjs/oid.h" #include "mongo/scripting/mozjs/regexp.h" +#include "mongo/scripting/mozjs/session.h" #include "mongo/scripting/mozjs/timestamp.h" #include "mongo/scripting/mozjs/uri.h" #include "mongo/stdx/unordered_set.h" @@ -287,6 +288,11 @@ public: return _regExpProto; } + template + typename std::enable_if::value, WrapType&>::type getProto() { + return _sessionProto; + } + template typename std::enable_if::value, WrapType&>::type getProto() { return _timestampProto; @@ -432,6 +438,7 @@ private: WrapType _objectProto; WrapType _oidProto; WrapType _regExpProto; + WrapType _sessionProto; WrapType _timestampProto; WrapType _uriProto; }; diff --git a/src/mongo/scripting/mozjs/mongo.cpp b/src/mongo/scripting/mozjs/mongo.cpp index 4689325c5b5..885183eeca6 100644 --- a/src/mongo/scripting/mozjs/mongo.cpp +++ b/src/mongo/scripting/mozjs/mongo.cpp @@ -30,18 +30,22 @@ #include "mongo/scripting/mozjs/mongo.h" +#include "mongo/bson/simple_bsonelement_comparator.h" #include "mongo/client/dbclientinterface.h" #include "mongo/client/global_conn_pool.h" #include "mongo/client/mongo_uri.h" #include "mongo/client/native_sasl_client_session.h" #include "mongo/client/sasl_client_authenticate.h" #include "mongo/client/sasl_client_session.h" +#include "mongo/db/logical_session_id.h" +#include "mongo/db/logical_session_id_helpers.h" #include "mongo/db/namespace_string.h" #include "mongo/db/operation_context.h" #include "mongo/scripting/dbdirectclient_factory.h" #include "mongo/scripting/mozjs/cursor.h" #include "mongo/scripting/mozjs/implscope.h" #include "mongo/scripting/mozjs/objectwrapper.h" +#include "mongo/scripting/mozjs/session.h" #include "mongo/scripting/mozjs/valuereader.h" #include "mongo/scripting/mozjs/valuewriter.h" #include "mongo/scripting/mozjs/wrapconstrainedmethod.h" @@ -80,6 +84,9 @@ const JSFunctionSpec MongoBase::methods[] = { getMinWireVersion, MongoLocalInfo, MongoExternalInfo), MONGO_ATTACH_JS_CONSTRAINED_METHOD_NO_PROTO( getMaxWireVersion, MongoLocalInfo, MongoExternalInfo), + MONGO_ATTACH_JS_CONSTRAINED_METHOD_NO_PROTO(getClusterTime, MongoExternalInfo), + MONGO_ATTACH_JS_CONSTRAINED_METHOD_NO_PROTO(setClusterTime, MongoExternalInfo), + MONGO_ATTACH_JS_CONSTRAINED_METHOD_NO_PROTO(_startSession, MongoLocalInfo, MongoExternalInfo), JS_FS_END, }; @@ -93,6 +100,16 @@ const JSFunctionSpec MongoExternalInfo::freeFunctions[4] = { }; namespace { + +/** + * Mutex and storage for global cluster time via set/getClusterTime. + * + * We need this to be global so that we can gossip times seen on one connection across to other + * connections within the same cluster. + */ +stdx::mutex logicalTimeMutex; +BSONObj latestlogicalTime; + DBClientBase* getConnection(JS::CallArgs& args) { auto ret = static_cast*>(JS_GetPrivate(args.thisv().toObjectOrNull())) @@ -172,6 +189,20 @@ void setHiddenMongo(JSContext* cx, } } // namespace +BSONObj MongoBase::getClusterTime() { + stdx::lock_guard lk(logicalTimeMutex); + return latestlogicalTime; +} + +void MongoBase::setClusterTime(const BSONObj& newTime) { + stdx::lock_guard lk(logicalTimeMutex); + if (latestlogicalTime.isEmpty() || + SimpleBSONElementComparator::kInstance.evaluate(latestlogicalTime["clusterTime"] < + newTime["clusterTime"])) { + latestlogicalTime = newTime.getOwned(); + } +} + void MongoBase::finalize(JSFreeOp* fop, JSObject* obj) { auto conn = static_cast*>(JS_GetPrivate(obj)); @@ -754,6 +785,38 @@ void MongoBase::Functions::getMaxWireVersion::call(JSContext* cx, JS::CallArgs a args.rval().setInt32(conn->getMaxWireVersion()); } +void MongoBase::Functions::getClusterTime::call(JSContext* cx, JS::CallArgs args) { + auto ct = MongoBase::getClusterTime(); + + if (!ct.isEmpty()) { + ValueReader(cx, args.rval()).fromBSON(MongoBase::getClusterTime(), nullptr, true); + return; + } + + args.rval().setUndefined(); +} + +void MongoBase::Functions::setClusterTime::call(JSContext* cx, JS::CallArgs args) { + auto newTime = ObjectWrapper(cx, args.get(0)).toBSON(); + + MongoBase::setClusterTime(newTime); + + args.rval().setUndefined(); +} + +void MongoBase::Functions::_startSession::call(JSContext* cx, JS::CallArgs args) { + auto client = + static_cast*>(JS_GetPrivate(args.thisv().toObjectOrNull())); + + LogicalSessionIdToClient id; + id.setId(UUID::gen()); + + JS::RootedObject obj(cx); + SessionInfo::make(cx, &obj, *client, id.toBSON()); + + args.rval().setObjectOrNull(obj.get()); +} + void MongoExternalInfo::Functions::load::call(JSContext* cx, JS::CallArgs args) { auto scope = getScope(cx); diff --git a/src/mongo/scripting/mozjs/mongo.h b/src/mongo/scripting/mozjs/mongo.h index 297b4907c6d..6d8426520b3 100644 --- a/src/mongo/scripting/mozjs/mongo.h +++ b/src/mongo/scripting/mozjs/mongo.h @@ -62,12 +62,18 @@ struct MongoBase : public BaseInfo { MONGO_DECLARE_JS_FUNCTION(update); MONGO_DECLARE_JS_FUNCTION(getMinWireVersion); MONGO_DECLARE_JS_FUNCTION(getMaxWireVersion); + MONGO_DECLARE_JS_FUNCTION(getClusterTime); + MONGO_DECLARE_JS_FUNCTION(setClusterTime); + MONGO_DECLARE_JS_FUNCTION(_startSession); }; - static const JSFunctionSpec methods[19]; + static const JSFunctionSpec methods[22]; static const char* const className; static const unsigned classFlags = JSCLASS_HAS_PRIVATE; + + static BSONObj getClusterTime(); + static void setClusterTime(const BSONObj& newTime); }; /** diff --git a/src/mongo/scripting/mozjs/session.cpp b/src/mongo/scripting/mozjs/session.cpp new file mode 100644 index 00000000000..858524669c2 --- /dev/null +++ b/src/mongo/scripting/mozjs/session.cpp @@ -0,0 +1,146 @@ +/** + * Copyright (C) 2017 MongoDB Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + * As a special exception, the copyright holders give permission to link the + * code of portions of this program with the OpenSSL library under certain + * conditions as described in each individual source file and distribute + * linked combinations including the program with the OpenSSL library. You + * must comply with the GNU Affero General Public License in all respects + * for all of the code used other than as permitted herein. If you modify + * file(s) with this exception, you may extend this exception to your + * version of the file(s), but you are not obligated to do so. If you do not + * wish to do so, delete this exception statement from your version. If you + * delete this exception statement from all source files in the program, + * then also delete it in the license file. + */ + +#define MONGO_LOG_DEFAULT_COMPONENT ::mongo::logger::LogComponent::kQuery + +#include "mongo/platform/basic.h" + +#include "mongo/scripting/mozjs/session.h" + +#include "mongo/scripting/mozjs/bson.h" +#include "mongo/scripting/mozjs/end_sessions_gen.h" +#include "mongo/scripting/mozjs/implscope.h" +#include "mongo/scripting/mozjs/mongo.h" +#include "mongo/scripting/mozjs/valuereader.h" +#include "mongo/scripting/mozjs/wrapconstrainedmethod.h" +#include "mongo/util/log.h" + +namespace mongo { +namespace mozjs { + +const JSFunctionSpec SessionInfo::methods[3] = { + MONGO_ATTACH_JS_CONSTRAINED_METHOD_NO_PROTO(end, SessionInfo), + MONGO_ATTACH_JS_CONSTRAINED_METHOD_NO_PROTO(getId, SessionInfo), + JS_FS_END, +}; + +const char* const SessionInfo::className = "Session"; + +struct SessionHolder { + SessionHolder(std::shared_ptr client, BSONObj lsid) + : client(std::move(client)), lsid(std::move(lsid)) {} + + std::shared_ptr client; + BSONObj lsid; +}; + +namespace { + +SessionHolder* getHolder(JSObject* thisv) { + return static_cast(JS_GetPrivate(thisv)); +} + +SessionHolder* getHolder(JS::CallArgs& args) { + return getHolder(args.thisv().toObjectOrNull()); +} + +void endSession(SessionHolder* holder) { + if (!holder->client) { + return; + } + + EndSessions es; + + es.setEndSessions(1); + es.setIds({holder->lsid}); + if (holder->client->isMongos()) { + auto clusterTime = MongoBase::getClusterTime(); + + if (!clusterTime.isEmpty()) { + es.setClusterTime(clusterTime); + } + } + + BSONObj out; + holder->client->runCommand("admin", es.toBSON(), out); + + holder->client.reset(); +} + +} // namespace + +void SessionInfo::finalize(JSFreeOp* fop, JSObject* obj) { + auto holder = getHolder(obj); + + if (holder) { + const auto lsid = holder->lsid; + + try { + endSession(holder); + } catch (...) { + auto status = exceptionToStatus(); + + try { + LOG(0) << "Failed to end session " << lsid << " due to " << status; + } catch (...) { + // This is here in case logging fails. + } + } + + getScope(fop)->trackedDelete(holder); + } +} + +void SessionInfo::Functions::end::call(JSContext* cx, JS::CallArgs args) { + auto holder = getHolder(args); + invariant(holder); + + endSession(holder); + + args.rval().setUndefined(); +} + +void SessionInfo::Functions::getId::call(JSContext* cx, JS::CallArgs args) { + auto holder = getHolder(args); + invariant(holder); + + ValueReader(cx, args.rval()).fromBSON(holder->lsid, nullptr, 1); +} + +void SessionInfo::make(JSContext* cx, + JS::MutableHandleObject obj, + std::shared_ptr client, + BSONObj lsid) { + auto scope = getScope(cx); + + scope->getProto().newObject(obj); + JS_SetPrivate(obj, scope->trackedNew(std::move(client), std::move(lsid))); +} + +} // namespace mozjs +} // namespace mongo diff --git a/src/mongo/scripting/mozjs/session.h b/src/mongo/scripting/mozjs/session.h new file mode 100644 index 00000000000..b003b812415 --- /dev/null +++ b/src/mongo/scripting/mozjs/session.h @@ -0,0 +1,64 @@ +/** + * Copyright (C) 2017 MongoDB Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + * As a special exception, the copyright holders give permission to link the + * code of portions of this program with the OpenSSL library under certain + * conditions as described in each individual source file and distribute + * linked combinations including the program with the OpenSSL library. You + * must comply with the GNU Affero General Public License in all respects + * for all of the code used other than as permitted herein. If you modify + * file(s) with this exception, you may extend this exception to your + * version of the file(s), but you are not obligated to do so. If you do not + * wish to do so, delete this exception statement from your version. If you + * delete this exception statement from all source files in the program, + * then also delete it in the license file. + */ + +#pragma once + +#include "mongo/client/dbclientinterface.h" +#include "mongo/scripting/mozjs/wraptype.h" + +namespace mongo { +namespace mozjs { + +/** + * Wraps a Session in javascript + * + * Note that the install is private, so this class should only be constructible + * from C++. Current callers are all via the Mongo object. + */ +struct SessionInfo : public BaseInfo { + static void finalize(JSFreeOp* fop, JSObject* obj); + + struct Functions { + MONGO_DECLARE_JS_FUNCTION(end); + MONGO_DECLARE_JS_FUNCTION(getId); + }; + + static const JSFunctionSpec methods[3]; + + static const char* const className; + static const unsigned classFlags = JSCLASS_HAS_PRIVATE; + static const InstallType installType = InstallType::Private; + + static void make(JSContext* cx, + JS::MutableHandleObject obj, + std::shared_ptr client, + BSONObj lsid); +}; + +} // namespace mozjs +} // namespace mongo diff --git a/src/mongo/shell/SConscript b/src/mongo/shell/SConscript index 3e66a33cf4e..d342f1edc68 100644 --- a/src/mongo/shell/SConscript +++ b/src/mongo/shell/SConscript @@ -19,16 +19,17 @@ js_header = env.JSHeader( "collection.js", "crud_api.js", "db.js", - "explain_query.js", - "explainable.js", "error_codes.js", + "explainable.js", + "explain_query.js", "mongo.js", "mr.js", "query.js", + "session.js", "types.js", + "utils_auth.js", "utils.js", "utils_sh.js", - "utils_auth.js", ] ) diff --git a/src/mongo/shell/db.js b/src/mongo/shell/db.js index d7f21d35b78..31d0c25124d 100644 --- a/src/mongo/shell/db.js +++ b/src/mongo/shell/db.js @@ -17,7 +17,11 @@ var DB; }; DB.prototype.getSiblingDB = function(name) { - return this.getMongo().getDB(name); + if (this.getSession()) { + return this.getSession().getDatabase(name); + } else { + return this.getMongo().getDB(name); + } }; DB.prototype.getSisterDB = DB.prototype.getSiblingDB; @@ -135,9 +139,18 @@ var DB; return this.runCommand(cmdObjWithReadPref, null, options); }; + DB.prototype._attachSessionInfo = function(obj) { + var withSessionInfo = Object.extend({}, obj); + if (this._session) { + withSessionInfo.lsid = this._session._serverSession._id; + } + + return withSessionInfo; + }; + // runCommand uses this impl to actually execute the command DB.prototype._runCommandImpl = function(name, obj, options) { - return this.getMongo().runCommand(name, obj, options); + return this.getMongo().runCommand(name, this._attachSessionInfo(obj), options); }; DB.prototype.runCommand = function(obj, extra, queryOptions) { @@ -167,7 +180,8 @@ var DB; }; DB.prototype.runCommandWithMetadata = function(commandArgs, metadata) { - return this.getMongo().runCommandWithMetadata(this._name, metadata, commandArgs); + return this.getMongo().runCommandWithMetadata( + this._name, metadata, this._attachSessionInfo(commandArgs)); }; DB.prototype._dbCommand = DB.prototype.runCommand; @@ -1829,4 +1843,8 @@ var DB; return this.getMongo().setLogLevel(logLevel, component); }; + DB.prototype.getSession = function() { + return this._session; + }; + }()); diff --git a/src/mongo/shell/mongo.js b/src/mongo/shell/mongo.js index 1a8ab38af9c..aa3191549ab 100644 --- a/src/mongo/shell/mongo.js +++ b/src/mongo/shell/mongo.js @@ -563,23 +563,6 @@ Mongo.prototype.getOperationTime = function() { return this._operationTime; }; -/** - * Sets the clusterTime. - */ -Mongo.prototype.setClusterTime = function(logicalTimeObj) { - if (typeof logicalTimeObj === "object" && logicalTimeObj.hasOwnProperty("clusterTime") && - (this._clusterTime === undefined || this._clusterTime === null || - bsonWoCompare(logicalTimeObj.clusterTime, this._clusterTime.clusterTime) === 1)) { - this._clusterTime = logicalTimeObj; - } -}; - -/** - * Gets the clusterTime or null if unset. - */ -Mongo.prototype.getClusterTime = function() { - if (this._clusterTime === undefined) { - return null; - } - return this._clusterTime; +Mongo.prototype.startSession = function(opts) { + return new DriverSession(this, opts); }; diff --git a/src/mongo/shell/session.js b/src/mongo/shell/session.js new file mode 100644 index 00000000000..36c8169ba5f --- /dev/null +++ b/src/mongo/shell/session.js @@ -0,0 +1,85 @@ +/** + * Implements the sessions api for the shell. + */ +var _session_api_module = (function() { + "use strict"; + + var ServerSession = function(client) { + this._handle = client._startSession(this._id); + this._lastUsed = new Date(); + this._id = this._handle.getId(); + }; + + ServerSession.prototype.updateLastUsed = function() { + this._lastUsed = new Date(); + }; + + var DriverSession = function(client, opts) { + this._serverSession = new ServerSession(client); + this._client = client; + this._options = opts; + this._hasEnded = false; + }; + + DriverSession.prototype.getClient = function() { + return this._client; + }; + + DriverSession.prototype.getOptions = function() { + return this._options; + }; + + DriverSession.prototype.hasEnded = function() { + return this._hasEnded; + }; + + DriverSession.prototype.endSession = function() { + if (!this._hasEnded) { + this._hasEnded = true; + this._serverSession._handle.end(); + } + }; + + DriverSession.prototype.getDatabase = function(db) { + var db = new DB(this._client, db); + db._session = this; + return db; + }; + + var SessionOptions = function() {}; + + SessionOptions.prototype.getReadPreference = function() { + return this._readPreference; + }; + + SessionOptions.prototype.setReadPreference = function(readPreference) { + this._readPreference = readPreference; + }; + + SessionOptions.prototype.getReadConcern = function() { + return this._readConcern; + }; + + SessionOptions.prototype.setReadConcern = function(readConcern) { + this._readConcern = readConcern; + }; + + SessionOptions.prototype.getWriteConcern = function() { + return this._writeConcern; + }; + + SessionOptions.prototype.setWriteConcern = function(writeConcern) { + this._writeConcern = writeConcern; + }; + + var module = {}; + + module.DriverSession = DriverSession; + module.SessionOptions = SessionOptions; + + return module; +})(); + +// Globals +DriverSession = _session_api_module.DriverSession; +SessionOptions = _session_api_module.SessionOptions; diff --git a/src/mongo/util/uuid.h b/src/mongo/util/uuid.h index d8f75d0d7ce..8436f5936ba 100644 --- a/src/mongo/util/uuid.h +++ b/src/mongo/util/uuid.h @@ -57,6 +57,7 @@ class UUID { friend class One_UUID; friend class LogicalSessionId; friend class LogicalSessionToClient; + friend class LogicalSessionIdToClient; friend class LogicalSessionFromClient; friend class repl::OplogEntryBase; -- cgit v1.2.1