summaryrefslogtreecommitdiff
path: root/src/mongo/scripting
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/mongo/scripting
parentad30a49a33b8773cbc07388bb257d605cbd6aa12 (diff)
downloadmongo-009fdc7dfcc0197632cef5e3fdc250fdba68f7a5.tar.gz
SERVER-30080 LogicalSessions in the shell
Basic logical session implementation for the shell
Diffstat (limited to 'src/mongo/scripting')
-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
9 files changed, 330 insertions, 1 deletions
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