diff options
author | Jason Carey <jcarey@argv.me> | 2017-07-25 19:10:48 -0400 |
---|---|---|
committer | Jason Carey <jcarey@argv.me> | 2017-08-01 17:57:19 -0400 |
commit | 009fdc7dfcc0197632cef5e3fdc250fdba68f7a5 (patch) | |
tree | c8b1ebb23f9e67a10feb79b14130bf71e7c940c0 /src/mongo/scripting | |
parent | ad30a49a33b8773cbc07388bb257d605cbd6aa12 (diff) | |
download | mongo-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/SConscript | 2 | ||||
-rw-r--r-- | src/mongo/scripting/engine.cpp | 2 | ||||
-rw-r--r-- | src/mongo/scripting/mozjs/end_sessions.idl | 37 | ||||
-rw-r--r-- | src/mongo/scripting/mozjs/implscope.cpp | 2 | ||||
-rw-r--r-- | src/mongo/scripting/mozjs/implscope.h | 7 | ||||
-rw-r--r-- | src/mongo/scripting/mozjs/mongo.cpp | 63 | ||||
-rw-r--r-- | src/mongo/scripting/mozjs/mongo.h | 8 | ||||
-rw-r--r-- | src/mongo/scripting/mozjs/session.cpp | 146 | ||||
-rw-r--r-- | src/mongo/scripting/mozjs/session.h | 64 |
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 |