summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorJason Carey <jcarey@argv.me>2017-07-25 19:10:48 -0400
committerJason Carey <jcarey@argv.me>2017-08-01 17:57:19 -0400
commit009fdc7dfcc0197632cef5e3fdc250fdba68f7a5 (patch)
treec8b1ebb23f9e67a10feb79b14130bf71e7c940c0 /src
parentad30a49a33b8773cbc07388bb257d605cbd6aa12 (diff)
downloadmongo-009fdc7dfcc0197632cef5e3fdc250fdba68f7a5.tar.gz
SERVER-30080 LogicalSessions in the shell
Basic logical session implementation for the shell
Diffstat (limited to 'src')
-rw-r--r--src/mongo/client/dbclient.cpp14
-rw-r--r--src/mongo/client/dbclient_rs.h4
-rw-r--r--src/mongo/client/dbclientinterface.h7
-rw-r--r--src/mongo/db/dbdirectclient.h4
-rw-r--r--src/mongo/db/logical_session_id.idl8
-rw-r--r--src/mongo/db/logical_session_id_helpers.cpp5
-rw-r--r--src/mongo/scripting/SConscript2
-rw-r--r--src/mongo/scripting/engine.cpp2
-rw-r--r--src/mongo/scripting/mozjs/end_sessions.idl37
-rw-r--r--src/mongo/scripting/mozjs/implscope.cpp2
-rw-r--r--src/mongo/scripting/mozjs/implscope.h7
-rw-r--r--src/mongo/scripting/mozjs/mongo.cpp63
-rw-r--r--src/mongo/scripting/mozjs/mongo.h8
-rw-r--r--src/mongo/scripting/mozjs/session.cpp146
-rw-r--r--src/mongo/scripting/mozjs/session.h64
-rw-r--r--src/mongo/shell/SConscript7
-rw-r--r--src/mongo/shell/db.js24
-rw-r--r--src/mongo/shell/mongo.js21
-rw-r--r--src/mongo/shell/session.js85
-rw-r--r--src/mongo/util/uuid.h1
20 files changed, 483 insertions, 28 deletions
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 <http://www.gnu.org/licenses/>.
+#
+
+# 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<object>
+ "$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"
@@ -288,6 +289,11 @@ public:
}
template <typename T>
+ typename std::enable_if<std::is_same<T, SessionInfo>::value, WrapType<T>&>::type getProto() {
+ return _sessionProto;
+ }
+
+ template <typename T>
typename std::enable_if<std::is_same<T, TimestampInfo>::value, WrapType<T>&>::type getProto() {
return _timestampProto;
}
@@ -432,6 +438,7 @@ private:
WrapType<ObjectInfo> _objectProto;
WrapType<OIDInfo> _oidProto;
WrapType<RegExpInfo> _regExpProto;
+ WrapType<SessionInfo> _sessionProto;
WrapType<TimestampInfo> _timestampProto;
WrapType<URIInfo> _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<std::shared_ptr<DBClientBase>*>(JS_GetPrivate(args.thisv().toObjectOrNull()))
@@ -172,6 +189,20 @@ void setHiddenMongo(JSContext* cx,
}
} // namespace
+BSONObj MongoBase::getClusterTime() {
+ stdx::lock_guard<stdx::mutex> lk(logicalTimeMutex);
+ return latestlogicalTime;
+}
+
+void MongoBase::setClusterTime(const BSONObj& newTime) {
+ stdx::lock_guard<stdx::mutex> 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<std::shared_ptr<DBClientBase>*>(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<std::shared_ptr<DBClientBase>*>(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 <http://www.gnu.org/licenses/>.
+ *
+ * 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<DBClientBase> client, BSONObj lsid)
+ : client(std::move(client)), lsid(std::move(lsid)) {}
+
+ std::shared_ptr<DBClientBase> client;
+ BSONObj lsid;
+};
+
+namespace {
+
+SessionHolder* getHolder(JSObject* thisv) {
+ return static_cast<SessionHolder*>(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<DBClientBase> client,
+ BSONObj lsid) {
+ auto scope = getScope(cx);
+
+ scope->getProto<SessionInfo>().newObject(obj);
+ JS_SetPrivate(obj, scope->trackedNew<SessionHolder>(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 <http://www.gnu.org/licenses/>.
+ *
+ * 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<DBClientBase> 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;