From cb20cab73393fbf725627d5f7b1af5e797866870 Mon Sep 17 00:00:00 2001 From: Jason Carey Date: Tue, 1 Aug 2017 11:29:51 -0400 Subject: SERVER-28338 KillSessions Support --- src/mongo/SConscript | 1 + src/mongo/crypto/sha_block.h | 14 ++ src/mongo/db/SConscript | 28 +++ src/mongo/db/auth/action_types.txt | 1 + src/mongo/db/auth/authorization_session.cpp | 17 ++ src/mongo/db/auth/authorization_session.h | 21 +++ src/mongo/db/auth/role_graph_builtin_roles.cpp | 1 + src/mongo/db/commands.cpp | 16 +- src/mongo/db/commands.h | 31 ++-- src/mongo/db/commands/SConscript | 4 + .../kill_all_sessions_by_pattern_command.cpp | 119 ++++++++++++ .../db/commands/kill_all_sessions_command.cpp | 107 +++++++++++ src/mongo/db/commands/kill_sessions_command.cpp | 134 ++++++++++++++ src/mongo/db/cursor_manager.cpp | 27 ++- src/mongo/db/cursor_manager.h | 8 + src/mongo/db/db.cpp | 6 + src/mongo/db/kill_sessions.cpp | 131 ++++++++++++++ src/mongo/db/kill_sessions.h | 102 +++++++++++ src/mongo/db/kill_sessions.idl | 81 +++++++++ src/mongo/db/kill_sessions_common.cpp | 91 ++++++++++ src/mongo/db/kill_sessions_common.h | 141 +++++++++++++++ src/mongo/db/kill_sessions_local.cpp | 63 +++++++ src/mongo/db/kill_sessions_local.h | 44 +++++ src/mongo/db/logical_session_id.h | 3 + src/mongo/db/logical_session_id_test.cpp | 35 +++- src/mongo/db/pipeline/pipeline_d.cpp | 5 + src/mongo/db/s/sharding_task_executor.cpp | 29 ++- src/mongo/db/service_entry_point_mongod.cpp | 4 +- src/mongo/db/session_killer.cpp | 200 +++++++++++++++++++++ src/mongo/db/session_killer.h | 145 +++++++++++++++ src/mongo/executor/SConscript | 11 ++ src/mongo/executor/async_multicaster.cpp | 104 +++++++++++ src/mongo/executor/async_multicaster.h | 79 ++++++++ src/mongo/s/commands/SConscript | 4 + src/mongo/s/commands/cluster_multicast.cpp | 158 ++++++++++++++++ src/mongo/s/commands/cluster_multicast.idl | 37 ++++ src/mongo/s/commands/kill_sessions_remote.cpp | 137 ++++++++++++++ src/mongo/s/commands/kill_sessions_remote.h | 44 +++++ src/mongo/s/commands/strategy.cpp | 4 +- src/mongo/s/query/SConscript | 2 + src/mongo/s/query/cluster_cursor_manager.cpp | 12 ++ src/mongo/s/query/cluster_cursor_manager.h | 5 + src/mongo/s/server.cpp | 7 + 43 files changed, 2180 insertions(+), 33 deletions(-) create mode 100644 src/mongo/db/commands/kill_all_sessions_by_pattern_command.cpp create mode 100644 src/mongo/db/commands/kill_all_sessions_command.cpp create mode 100644 src/mongo/db/commands/kill_sessions_command.cpp create mode 100644 src/mongo/db/kill_sessions.cpp create mode 100644 src/mongo/db/kill_sessions.h create mode 100644 src/mongo/db/kill_sessions.idl create mode 100644 src/mongo/db/kill_sessions_common.cpp create mode 100644 src/mongo/db/kill_sessions_common.h create mode 100644 src/mongo/db/kill_sessions_local.cpp create mode 100644 src/mongo/db/kill_sessions_local.h create mode 100644 src/mongo/db/session_killer.cpp create mode 100644 src/mongo/db/session_killer.h create mode 100644 src/mongo/executor/async_multicaster.cpp create mode 100644 src/mongo/executor/async_multicaster.h create mode 100644 src/mongo/s/commands/cluster_multicast.cpp create mode 100644 src/mongo/s/commands/cluster_multicast.idl create mode 100644 src/mongo/s/commands/kill_sessions_remote.cpp create mode 100644 src/mongo/s/commands/kill_sessions_remote.h (limited to 'src/mongo') diff --git a/src/mongo/SConscript b/src/mongo/SConscript index 66fdcbc1909..96c41128916 100644 --- a/src/mongo/SConscript +++ b/src/mongo/SConscript @@ -290,6 +290,7 @@ env.Library( 'db/index_d', 'db/initialize_snmp', 'db/keys_collection_manager_direct', + 'db/kill_sessions_local', 'db/logical_session_cache_factory_mongod', 'db/mongod_options', 'db/mongodandmongos', diff --git a/src/mongo/crypto/sha_block.h b/src/mongo/crypto/sha_block.h index 443ef36ebf4..da61684976e 100644 --- a/src/mongo/crypto/sha_block.h +++ b/src/mongo/crypto/sha_block.h @@ -31,6 +31,7 @@ #include #include #include +#include #include #include "mongo/base/data_range.h" @@ -192,6 +193,19 @@ public: return !(*this == other); } + /** + * Custom hasher so SHABlocks can be used in unordered data structures. + * + * ex: std::unordered_set shaSet; + */ + struct Hash { + std::size_t operator()(const SHABlock& shaBlock) const { + uint32_t hash; + MurmurHash3_x86_32(shaBlock.data(), SHABlock::kHashLength, 0, &hash); + return hash; + } + }; + private: // The backing array of bytes for the sha block HashType _hash; diff --git a/src/mongo/db/SConscript b/src/mongo/db/SConscript index 0d42768d960..e0b2a253e61 100644 --- a/src/mongo/db/SConscript +++ b/src/mongo/db/SConscript @@ -1080,6 +1080,34 @@ env.Library( ], ) +env.Library( + target='kill_sessions', + source=[ + 'kill_sessions.cpp', + 'kill_sessions_common.cpp', + 'session_killer.cpp', + env.Idlc('kill_sessions.idl')[0], + ], + LIBDEPS=[ + '$BUILD_DIR/mongo/base', + ], + LIBDEPS_PRIVATE=[ + '$BUILD_DIR/mongo/db/auth/authcore', + '$BUILD_DIR/mongo/idl/idl_parser', + ], +) + +env.Library( + target='kill_sessions_local', + source=[ + 'kill_sessions_local.cpp', + ], + LIBDEPS=[ + 'clientcursor', + 'kill_sessions', + ], +) + env.Library( target='signed_logical_time', source=[ diff --git a/src/mongo/db/auth/action_types.txt b/src/mongo/db/auth/action_types.txt index dd9e6a9c8cc..d3b2745d319 100644 --- a/src/mongo/db/auth/action_types.txt +++ b/src/mongo/db/auth/action_types.txt @@ -64,6 +64,7 @@ "insert", "internal", # Special action type that represents internal actions "invalidateUserCache", +"killAnySession", "killCursors", "killop", "listCollections", diff --git a/src/mongo/db/auth/authorization_session.cpp b/src/mongo/db/auth/authorization_session.cpp index 4c72c4677e3..201acb11b00 100644 --- a/src/mongo/db/auth/authorization_session.cpp +++ b/src/mongo/db/auth/authorization_session.cpp @@ -117,6 +117,23 @@ using UserHolder = std::unique_ptr; } // namespace +AuthorizationSession::ScopedImpersonate::ScopedImpersonate(AuthorizationSession* authSession, + std::vector* users, + std::vector* roles) + : _authSession(*authSession), _users(*users), _roles(*roles) { + swap(); +} + +AuthorizationSession::ScopedImpersonate::~ScopedImpersonate() { + swap(); +} + +void AuthorizationSession::ScopedImpersonate::swap() { + using std::swap; + swap(_authSession._impersonatedUserNames, _users); + swap(_authSession._impersonatedRoleNames, _roles); +} + AuthorizationSession::AuthorizationSession(std::unique_ptr externalState) : _externalState(std::move(externalState)), _impersonationFlag(false) {} diff --git a/src/mongo/db/auth/authorization_session.h b/src/mongo/db/auth/authorization_session.h index 7da7d21c9a1..edb838c1959 100644 --- a/src/mongo/db/auth/authorization_session.h +++ b/src/mongo/db/auth/authorization_session.h @@ -69,6 +69,27 @@ class AuthorizationSession { MONGO_DISALLOW_COPYING(AuthorizationSession); public: + /** + * Provides a way to swap out impersonate data for the duration of the ScopedImpersonate's + * lifetime. + */ + class ScopedImpersonate { + public: + ScopedImpersonate(AuthorizationSession* authSession, + std::vector* users, + std::vector* roles); + ~ScopedImpersonate(); + + private: + void swap(); + + AuthorizationSession& _authSession; + std::vector& _users; + std::vector& _roles; + }; + + friend class ScopedImpersonate; + /** * Gets the AuthorizationSession associated with the given "client", or nullptr. * diff --git a/src/mongo/db/auth/role_graph_builtin_roles.cpp b/src/mongo/db/auth/role_graph_builtin_roles.cpp index a9ee381c277..36cc6b4c15a 100644 --- a/src/mongo/db/auth/role_graph_builtin_roles.cpp +++ b/src/mongo/db/auth/role_graph_builtin_roles.cpp @@ -216,6 +216,7 @@ MONGO_INITIALIZER(AuthorizationBuiltinRoles)(InitializerContext* context) { << ActionType::flushRouterConfig // clusterManager gets this also << ActionType::fsync << ActionType::invalidateUserCache // userAdminAnyDatabase gets this also + << ActionType::killAnySession << ActionType::killop << ActionType::replSetResizeOplog << ActionType::resync; // clusterManager gets this also diff --git a/src/mongo/db/commands.cpp b/src/mongo/db/commands.cpp index 5f5982d76f4..c8f99250182 100644 --- a/src/mongo/db/commands.cpp +++ b/src/mongo/db/commands.cpp @@ -189,7 +189,7 @@ BSONObj Command::runCommandDirectly(OperationContext* opCtx, const OpMsgRequest& BSONObjBuilder out; try { - bool ok = command->enhancedRun(opCtx, request, out); + bool ok = command->publicRun(opCtx, request, out); appendCommandStatus(out, ok); } catch (const StaleConfigException& ex) { // These exceptions are intended to be handled at a higher level and cannot losslessly @@ -321,6 +321,20 @@ Status Command::checkAuthorization(Command* c, return status; } +bool Command::publicRun(OperationContext* opCtx, + const OpMsgRequest& request, + BSONObjBuilder& result) { + try { + return enhancedRun(opCtx, request, result); + } catch (const DBException& e) { + if (e.code() == ErrorCodes::Unauthorized) { + audit::logCommandAuthzCheck( + opCtx->getClient(), request, this, ErrorCodes::Unauthorized); + } + throw; + } +} + bool Command::isHelpRequest(const BSONElement& helpElem) { return !helpElem.eoo() && helpElem.trueValue(); } diff --git a/src/mongo/db/commands.h b/src/mongo/db/commands.h index dfcda118277..79641088af1 100644 --- a/src/mongo/db/commands.h +++ b/src/mongo/db/commands.h @@ -91,18 +91,6 @@ public: */ virtual std::size_t reserveBytesForReply() const = 0; - /** - * Runs the command. - * - * The default implementation verifies that request has no document sections then forwards to - * BasicCommand::run(). - * - * For now commands should only implement if they need access to OP_MSG-specific functionality. - */ - virtual bool enhancedRun(OperationContext* opCtx, - const OpMsgRequest& request, - BSONObjBuilder& result) = 0; - /** * supportsWriteConcern returns true if this command should be parsed for a writeConcern * field and wait for that write concern to be satisfied after the command runs. @@ -349,6 +337,13 @@ public: _commandsFailed.increment(); } + /** + * Runs the command. + * + * Forwards to enhancedRun, but additionally runs audit checks if run throws unauthorized. + */ + bool publicRun(OperationContext* opCtx, const OpMsgRequest& request, BSONObjBuilder& result); + static const CommandMap& allCommands() { return *_commands; } @@ -514,6 +509,18 @@ private: static CommandMap* _commands; static CommandMap* _commandsByBestName; + /** + * Runs the command. + * + * The default implementation verifies that request has no document sections then forwards to + * BasicCommand::run(). + * + * For now commands should only implement if they need access to OP_MSG-specific functionality. + */ + virtual bool enhancedRun(OperationContext* opCtx, + const OpMsgRequest& request, + BSONObjBuilder& result) = 0; + // Counters for how many times this command has been executed and failed Counter64 _commandsExecuted; Counter64 _commandsFailed; diff --git a/src/mongo/db/commands/SConscript b/src/mongo/db/commands/SConscript index 3ba7f61d588..9ea7617f4d7 100644 --- a/src/mongo/db/commands/SConscript +++ b/src/mongo/db/commands/SConscript @@ -63,6 +63,9 @@ env.Library( "generic.cpp", "hashcmd.cpp", "isself.cpp", + "kill_all_sessions_by_pattern_command.cpp", + "kill_all_sessions_command.cpp", + "kill_sessions_command.cpp", "mr_common.cpp", "parameters.cpp", "refresh_logical_session_cache_now.cpp", @@ -85,6 +88,7 @@ env.Library( '$BUILD_DIR/mongo/db/exec/working_set', '$BUILD_DIR/mongo/db/index/key_generator', '$BUILD_DIR/mongo/db/index_names', + '$BUILD_DIR/mongo/db/kill_sessions', '$BUILD_DIR/mongo/db/lasterror', '$BUILD_DIR/mongo/db/log_process_details', '$BUILD_DIR/mongo/db/logical_session_cache', diff --git a/src/mongo/db/commands/kill_all_sessions_by_pattern_command.cpp b/src/mongo/db/commands/kill_all_sessions_by_pattern_command.cpp new file mode 100644 index 00000000000..48387e69e88 --- /dev/null +++ b/src/mongo/db/commands/kill_all_sessions_by_pattern_command.cpp @@ -0,0 +1,119 @@ +/** + * 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::kCommand + +#include "mongo/platform/basic.h" + +#include "mongo/base/init.h" +#include "mongo/db/auth/action_set.h" +#include "mongo/db/auth/action_type.h" +#include "mongo/db/auth/authorization_manager.h" +#include "mongo/db/auth/authorization_session.h" +#include "mongo/db/auth/privilege.h" +#include "mongo/db/client.h" +#include "mongo/db/commands.h" +#include "mongo/db/jsobj.h" +#include "mongo/db/kill_sessions.h" +#include "mongo/db/kill_sessions_common.h" +#include "mongo/db/kill_sessions_local.h" +#include "mongo/db/logical_session_cache.h" +#include "mongo/db/logical_session_id.h" +#include "mongo/db/logical_session_id_helpers.h" +#include "mongo/db/operation_context.h" +#include "mongo/db/stats/top.h" +#include "mongo/util/log.h" + +namespace mongo { + +class KillAllSessionsByPatternCommand final : public BasicCommand { + MONGO_DISALLOW_COPYING(KillAllSessionsByPatternCommand); + +public: + KillAllSessionsByPatternCommand() : BasicCommand("killAllSessionsByPattern") {} + + bool slaveOk() const override { + return true; + } + bool adminOnly() const override { + return false; + } + bool supportsWriteConcern(const BSONObj& cmd) const override { + return false; + } + void help(std::stringstream& help) const override { + help << "kill logical sessions by pattern"; + } + Status checkAuthForOperation(OperationContext* opCtx, + const std::string& dbname, + const BSONObj& cmdObj) override { + AuthorizationSession* authSession = AuthorizationSession::get(opCtx->getClient()); + if (!authSession->isAuthorizedForPrivilege( + Privilege{ResourcePattern::forClusterResource(), ActionType::killAnySession})) { + return Status(ErrorCodes::Unauthorized, "Unauthorized"); + } + return Status::OK(); + } + + virtual bool run(OperationContext* opCtx, + const std::string& db, + const BSONObj& cmdObj, + BSONObjBuilder& result) override { + IDLParserErrorContext ctx("KillAllSessionsByPatternCmd"); + auto ksc = KillAllSessionsByPatternCmd::parse(ctx, cmdObj); + + // The empty command kills all + if (ksc.getKillAllSessionsByPattern().empty()) { + ksc.setKillAllSessionsByPattern({makeKillAllSessionsByPattern(opCtx)}); + } else { + // If a pattern is passed, you may only pass impersonate data if you have the + // impersonate privilege. + auto authSession = AuthorizationSession::get(opCtx->getClient()); + + if (!authSession->isAuthorizedForPrivilege( + Privilege(ResourcePattern::forClusterResource(), ActionType::impersonate))) { + + for (const auto& pattern : ksc.getKillAllSessionsByPattern()) { + if (pattern.getUsers() || pattern.getRoles()) { + return appendCommandStatus( + result, + Status(ErrorCodes::Unauthorized, + "Not authorized to impersonate in killAllSessionsByPattern")); + } + } + } + } + + KillAllSessionsByPatternSet patterns{ksc.getKillAllSessionsByPattern().begin(), + ksc.getKillAllSessionsByPattern().end()}; + + return appendCommandStatus(result, killSessionsCmdHelper(opCtx, result, patterns)); + } +} killAllSessionsByPatternCommand; + +} // namespace mongo diff --git a/src/mongo/db/commands/kill_all_sessions_command.cpp b/src/mongo/db/commands/kill_all_sessions_command.cpp new file mode 100644 index 00000000000..ba200c4e443 --- /dev/null +++ b/src/mongo/db/commands/kill_all_sessions_command.cpp @@ -0,0 +1,107 @@ +/** + * 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::kCommand + +#include "mongo/platform/basic.h" + +#include "mongo/base/init.h" +#include "mongo/db/auth/action_set.h" +#include "mongo/db/auth/action_type.h" +#include "mongo/db/auth/authorization_manager.h" +#include "mongo/db/auth/authorization_session.h" +#include "mongo/db/auth/privilege.h" +#include "mongo/db/client.h" +#include "mongo/db/commands.h" +#include "mongo/db/jsobj.h" +#include "mongo/db/kill_sessions.h" +#include "mongo/db/kill_sessions_common.h" +#include "mongo/db/kill_sessions_local.h" +#include "mongo/db/logical_session_cache.h" +#include "mongo/db/logical_session_id.h" +#include "mongo/db/logical_session_id_helpers.h" +#include "mongo/db/operation_context.h" +#include "mongo/db/stats/top.h" +#include "mongo/util/log.h" + +namespace mongo { + +class KillAllSessionsCommand final : public BasicCommand { + MONGO_DISALLOW_COPYING(KillAllSessionsCommand); + +public: + KillAllSessionsCommand() : BasicCommand("killAllSessions") {} + + bool slaveOk() const override { + return true; + } + bool adminOnly() const override { + return false; + } + bool supportsWriteConcern(const BSONObj& cmd) const override { + return false; + } + void help(std::stringstream& help) const override { + help << "kill all logical sessions, for a user, and their operations"; + } + Status checkAuthForOperation(OperationContext* opCtx, + const std::string& dbname, + const BSONObj& cmdObj) override { + AuthorizationSession* authSession = AuthorizationSession::get(opCtx->getClient()); + if (!authSession->isAuthorizedForPrivilege( + Privilege{ResourcePattern::forClusterResource(), ActionType::killAnySession})) { + return Status(ErrorCodes::Unauthorized, "Unauthorized"); + } + return Status::OK(); + } + + virtual bool run(OperationContext* opCtx, + const std::string& db, + const BSONObj& cmdObj, + BSONObjBuilder& result) override { + IDLParserErrorContext ctx("KillAllSessionsCmd"); + auto ksc = KillAllSessionsCmd::parse(ctx, cmdObj); + + KillAllSessionsByPatternSet patterns; + + // The empty command kills all + if (ksc.getKillAllSessions().empty()) { + patterns.emplace(makeKillAllSessionsByPattern(opCtx)); + } else { + patterns.reserve(ksc.getKillAllSessions().size()); + + for (const auto& user : ksc.getKillAllSessions()) { + patterns.emplace(makeKillAllSessionsByPattern(opCtx, user)); + } + } + + return appendCommandStatus(result, killSessionsCmdHelper(opCtx, result, patterns)); + } +} killAllSessionsCommand; + +} // namespace mongo diff --git a/src/mongo/db/commands/kill_sessions_command.cpp b/src/mongo/db/commands/kill_sessions_command.cpp new file mode 100644 index 00000000000..6460863d599 --- /dev/null +++ b/src/mongo/db/commands/kill_sessions_command.cpp @@ -0,0 +1,134 @@ +/** + * 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::kCommand + +#include "mongo/platform/basic.h" + +#include "mongo/base/init.h" +#include "mongo/db/auth/action_set.h" +#include "mongo/db/auth/action_type.h" +#include "mongo/db/auth/authorization_manager.h" +#include "mongo/db/auth/authorization_session.h" +#include "mongo/db/auth/privilege.h" +#include "mongo/db/client.h" +#include "mongo/db/commands.h" +#include "mongo/db/jsobj.h" +#include "mongo/db/kill_sessions.h" +#include "mongo/db/kill_sessions_common.h" +#include "mongo/db/kill_sessions_local.h" +#include "mongo/db/logical_session_cache.h" +#include "mongo/db/logical_session_id.h" +#include "mongo/db/logical_session_id_helpers.h" +#include "mongo/db/operation_context.h" +#include "mongo/db/stats/top.h" +#include "mongo/util/log.h" + +namespace mongo { + +namespace { + +KillAllSessionsByPatternSet patternsForLoggedInUser(OperationContext* opCtx) { + auto client = opCtx->getClient(); + ServiceContext* serviceContext = client->getServiceContext(); + + KillAllSessionsByPatternSet patterns; + + if (AuthorizationManager::get(serviceContext)->isAuthEnabled()) { + auto authzSession = AuthorizationSession::get(client); + for (auto iter = authzSession->getAuthenticatedUserNames(); iter.more(); iter.next()) { + User* user = authzSession->lookupUser(*iter); + invariant(user); + + auto pattern = makeKillAllSessionsByPattern(opCtx); + pattern.setUid(user->getDigest()); + patterns.emplace(std::move(pattern)); + } + } else { + patterns.emplace(makeKillAllSessionsByPattern(opCtx)); + } + + return patterns; +} + +} // namespace + +class KillSessionsCommand final : public BasicCommand { + MONGO_DISALLOW_COPYING(KillSessionsCommand); + +public: + KillSessionsCommand() : BasicCommand("killSessions") {} + + bool slaveOk() const override { + return true; + } + bool adminOnly() const override { + return false; + } + bool supportsWriteConcern(const BSONObj& cmd) const override { + return false; + } + void help(std::stringstream& help) const override { + help << "kill a logical session and its operations"; + } + + // Any user can kill their own sessions + Status checkAuthForOperation(OperationContext* opCtx, + const std::string& dbname, + const BSONObj& cmdObj) override { + return Status::OK(); + } + + virtual bool run(OperationContext* opCtx, + const std::string& db, + const BSONObj& cmdObj, + BSONObjBuilder& result) override { + IDLParserErrorContext ctx("KillSessionsCmd"); + auto ksc = KillSessionsCmdFromClient::parse(ctx, cmdObj); + + KillAllSessionsByPatternSet patterns; + + if (ksc.getKillSessions().empty()) { + patterns = patternsForLoggedInUser(opCtx); + } else { + auto lsids = makeLogicalSessionIds( + ksc.getKillSessions(), + opCtx, + {Privilege{ResourcePattern::forClusterResource(), ActionType::killAnySession}}); + + patterns.reserve(lsids.size()); + for (const auto& lsid : lsids) { + patterns.emplace(makeKillAllSessionsByPattern(opCtx, lsid)); + } + } + + return appendCommandStatus(result, killSessionsCmdHelper(opCtx, result, patterns)); + } +} killSessionsCommand; + +} // namespace mongo diff --git a/src/mongo/db/cursor_manager.cpp b/src/mongo/db/cursor_manager.cpp index 2406f7422bc..d2013c14f0f 100644 --- a/src/mongo/db/cursor_manager.cpp +++ b/src/mongo/db/cursor_manager.cpp @@ -40,6 +40,7 @@ #include "mongo/db/catalog/database_holder.h" #include "mongo/db/client.h" #include "mongo/db/db_raii.h" +#include "mongo/db/kill_sessions_common.h" #include "mongo/db/namespace_string.h" #include "mongo/db/operation_context.h" #include "mongo/db/query/plan_executor.h" @@ -103,7 +104,8 @@ public: std::size_t timeoutCursors(OperationContext* opCtx, Date_t now); - void appendActiveSessions(OperationContext* opCtx, LogicalSessionIdSet* lsids); + template + void visitAllCursorManagers(OperationContext* opCtx, Visitor* visitor); int64_t nextSeed(); @@ -271,10 +273,9 @@ std::size_t GlobalCursorIdCache::timeoutCursors(OperationContext* opCtx, Date_t } } // namespace -void GlobalCursorIdCache::appendActiveSessions(OperationContext* opCtx, - LogicalSessionIdSet* lsids) { - // Get active session ids from the global cursor manager - globalCursorManager->appendActiveSessions(lsids); +template +void GlobalCursorIdCache::visitAllCursorManagers(OperationContext* opCtx, Visitor* visitor) { + (*visitor)(*globalCursorManager); // Compute the set of collection names that we have to get sessions for vector namespaces; @@ -298,7 +299,7 @@ void GlobalCursorIdCache::appendActiveSessions(OperationContext* opCtx, continue; } - collection->getCursorManager()->appendActiveSessions(lsids); + (*visitor)(*(collection->getCursorManager())); } } @@ -309,7 +310,19 @@ CursorManager* CursorManager::getGlobalCursorManager() { } void CursorManager::appendAllActiveSessions(OperationContext* opCtx, LogicalSessionIdSet* lsids) { - globalCursorIdCache->appendActiveSessions(opCtx, lsids); + auto visitor = [&](CursorManager& mgr) { mgr.appendActiveSessions(lsids); }; + globalCursorIdCache->visitAllCursorManagers(opCtx, &visitor); +} + +Status CursorManager::killCursorsWithMatchingSessions(OperationContext* opCtx, + const SessionKiller::Matcher& matcher) { + auto eraser = [&](CursorManager& mgr, CursorId id) { + uassertStatusOK(mgr.eraseCursor(opCtx, id, true)); + }; + + auto visitor = makeKillSessionsCursorManagerVisitor(opCtx, matcher, std::move(eraser)); + globalCursorIdCache->visitAllCursorManagers(opCtx, &visitor); + return visitor.getStatus(); } std::size_t CursorManager::timeoutCursorsGlobal(OperationContext* opCtx, Date_t now) { diff --git a/src/mongo/db/cursor_manager.h b/src/mongo/db/cursor_manager.h index 5a2aed6edc0..c2fdc12e93c 100644 --- a/src/mongo/db/cursor_manager.h +++ b/src/mongo/db/cursor_manager.h @@ -32,8 +32,10 @@ #include "mongo/db/clientcursor.h" #include "mongo/db/cursor_id.h" #include "mongo/db/invalidation_type.h" +#include "mongo/db/kill_sessions.h" #include "mongo/db/namespace_string.h" #include "mongo/db/record_id.h" +#include "mongo/db/session_killer.h" #include "mongo/platform/unordered_map.h" #include "mongo/platform/unordered_set.h" #include "mongo/stdx/unordered_set.h" @@ -85,6 +87,12 @@ public: */ static void appendAllActiveSessions(OperationContext* opCtx, LogicalSessionIdSet* lsids); + /** + * Kills cursors with matching logical sessions. + */ + static Status killCursorsWithMatchingSessions(OperationContext* opCtx, + const SessionKiller::Matcher& matcher); + CursorManager(NamespaceString nss); /** diff --git a/src/mongo/db/db.cpp b/src/mongo/db/db.cpp index ef48122ce6f..9defa4f328f 100644 --- a/src/mongo/db/db.cpp +++ b/src/mongo/db/db.cpp @@ -75,6 +75,8 @@ #include "mongo/db/introspect.h" #include "mongo/db/json.h" #include "mongo/db/keys_collection_manager.h" +#include "mongo/db/kill_sessions.h" +#include "mongo/db/kill_sessions_local.h" #include "mongo/db/log_process_details.h" #include "mongo/db/logical_clock.h" #include "mongo/db/logical_session_cache.h" @@ -109,6 +111,7 @@ #include "mongo/db/service_context_d.h" #include "mongo/db/service_entry_point_mongod.h" #include "mongo/db/session_catalog.h" +#include "mongo/db/session_killer.h" #include "mongo/db/startup_warnings_mongod.h" #include "mongo/db/stats/counters.h" #include "mongo/db/storage/encryption_hooks.h" @@ -762,6 +765,9 @@ ExitCode _initAndListen(int listenPort) { runner->startup().transitional_ignore(); globalServiceContext->setPeriodicRunner(std::move(runner)); + SessionKiller::set(globalServiceContext, + std::make_shared(globalServiceContext, killSessionsLocal)); + // Set up the logical session cache LogicalSessionCacheServer kind = LogicalSessionCacheServer::kStandalone; if (shardingInitialized) { diff --git a/src/mongo/db/kill_sessions.cpp b/src/mongo/db/kill_sessions.cpp new file mode 100644 index 00000000000..f16202c343e --- /dev/null +++ b/src/mongo/db/kill_sessions.cpp @@ -0,0 +1,131 @@ +/** + * 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. + */ + +#include "mongo/platform/basic.h" + +#include "mongo/db/kill_sessions.h" + +#include "mongo/db/auth/authorization_session.h" +#include "mongo/db/client.h" +#include "mongo/db/operation_context.h" +#include "mongo/db/service_context.h" + +namespace mongo { + +namespace { + +std::vector getKillAllSessionsImpersonateUsers(OperationContext* opCtx) { + AuthorizationSession* authSession = AuthorizationSession::get(opCtx->getClient()); + + std::vector out; + + for (auto iter = authSession->getAuthenticatedUserNames(); iter.more(); iter.next()) { + out.emplace_back(); + out.back().setUser(iter->getUser()); + out.back().setDb(iter->getDB()); + } + + return out; +} + +std::vector getKillAllSessionsImpersonateRoles(OperationContext* opCtx) { + AuthorizationSession* authSession = AuthorizationSession::get(opCtx->getClient()); + + std::vector out; + + for (auto iter = authSession->getAuthenticatedRoleNames(); iter.more(); iter.next()) { + out.emplace_back(); + out.back().setRole(iter->getRole()); + out.back().setDb(iter->getDB()); + } + + return out; +} + +} // namespace + +std::tuple, std::vector> getKillAllSessionsByPatternImpersonateData( + const KillAllSessionsByPattern& pattern) { + std::tuple, std::vector> out; + + auto& users = std::get<0>(out); + auto& roles = std::get<1>(out); + + if (pattern.getUsers()) { + users.reserve(pattern.getUsers()->size()); + + for (auto&& user : pattern.getUsers().get()) { + users.emplace_back(user.getUser(), user.getDb()); + } + } + + if (pattern.getRoles()) { + roles.reserve(pattern.getUsers()->size()); + + for (auto&& user : pattern.getUsers().get()) { + roles.emplace_back(user.getUser(), user.getDb()); + } + } + + return out; +} + +KillAllSessionsByPattern makeKillAllSessionsByPattern(OperationContext* opCtx) { + KillAllSessionsByPattern kasbp; + + kasbp.setUsers(getKillAllSessionsImpersonateUsers(opCtx)); + kasbp.setRoles(getKillAllSessionsImpersonateRoles(opCtx)); + + return kasbp; +} + +KillAllSessionsByPattern makeKillAllSessionsByPattern(OperationContext* opCtx, + const KillAllSessionsUser& kasu) { + KillAllSessionsByPattern kasbp = makeKillAllSessionsByPattern(opCtx); + + auto authMgr = AuthorizationManager::get(opCtx->getServiceContext()); + + User* user; + UserName un(kasu.getUser(), kasu.getDb()); + + uassertStatusOK(authMgr->acquireUser(opCtx, un, &user)); + kasbp.setUid(user->getDigest()); + authMgr->releaseUser(user); + + return kasbp; +} + +KillAllSessionsByPattern makeKillAllSessionsByPattern(OperationContext* opCtx, + const LogicalSessionId& lsid) { + KillAllSessionsByPattern kasbp = makeKillAllSessionsByPattern(opCtx); + kasbp.setLsid(lsid); + + return kasbp; +} + +} // namespace mongo diff --git a/src/mongo/db/kill_sessions.h b/src/mongo/db/kill_sessions.h new file mode 100644 index 00000000000..547c2d038a3 --- /dev/null +++ b/src/mongo/db/kill_sessions.h @@ -0,0 +1,102 @@ +/** + * 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 + +#include "mongo/db/auth/role_name.h" +#include "mongo/db/auth/user_name.h" +#include "mongo/db/kill_sessions_gen.h" +#include "mongo/db/logical_session_id.h" + +namespace mongo { + +class OperationContext; +class ServiceContext; + +struct KillAllSessionsByPatternHash { + std::size_t operator()(const KillAllSessionsByPattern& pattern) const { + if (pattern.getLsid()) { + return lsidHasher(*pattern.getLsid()); + } else if (pattern.getUid()) { + return uidHasher(*pattern.getUid()); + } + + // fall through for killAll + return 0; + } + + LogicalSessionIdHash lsidHasher; + SHA256Block::Hash uidHasher; +}; + +/** + * Patterns are specifically equal if they differ only by impersonate data. + */ +inline bool operator==(const KillAllSessionsByPattern& lhs, const KillAllSessionsByPattern& rhs) { + auto makeEqualityLens = [](const auto& pattern) { + return std::tie(pattern.getLsid(), pattern.getUid()); + }; + + return makeEqualityLens(lhs) == makeEqualityLens(rhs); +} + +inline bool operator!=(const KillAllSessionsByPattern& lhs, const KillAllSessionsByPattern& rhs) { + return !(lhs == rhs); +} + +using KillAllSessionsByPatternSet = + stdx::unordered_set; + +std::tuple, std::vector> getKillAllSessionsByPatternImpersonateData( + const KillAllSessionsByPattern& pattern); + +/** + * Note: All three of the below makeKillAllSessionsByPattern helpers take opCtx to inline the + * required impersonate data + */ + +/** + * Constructs a kill sessions pattern which kills all sessions + */ +KillAllSessionsByPattern makeKillAllSessionsByPattern(OperationContext* opCtx); + +/** + * Constructs a kill sessions pattern for a particular user + */ +KillAllSessionsByPattern makeKillAllSessionsByPattern(OperationContext* opCtx, + const KillAllSessionsUser& user); + +/** + * Constructs a kill sessions pattern for a particular logical session + */ +KillAllSessionsByPattern makeKillAllSessionsByPattern(OperationContext* opCtx, + const LogicalSessionId& lsid); + +} // namespace mongo diff --git a/src/mongo/db/kill_sessions.idl b/src/mongo/db/kill_sessions.idl new file mode 100644 index 00000000000..becc2c8ef9a --- /dev/null +++ b/src/mongo/db/kill_sessions.idl @@ -0,0 +1,81 @@ +# 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 . +# + +global: + cpp_namespace: "mongo" + +imports: + - "mongo/crypto/sha256_block.idl" + - "mongo/db/logical_session_id.idl" + - "mongo/idl/basic_types.idl" + +structs: + + KillSessionsCmdFromClient: + description: "A struct representing a killSessions command from a client" + strict: false + fields: + killSessions: array + + KillAllSessionsUser: + description: "A struct representing a killAllSessions User" + strict: true + fields: + user: string + db: string + + KillAllSessionsRole: + description: "A struct representing a killAllSessions Role" + strict: true + fields: + role: string + db: string + + KillAllSessionsCmd: + description: "A struct representing a killAllSessions command" + strict: false + fields: + killAllSessions: array + + KillAllSessionsByPattern: + description: "A struct representing a killAllSessionsByPatternCmd kill pattern" + strict: true + fields: + lsid: + type: LogicalSessionId + optional: true + uid: + type: sha256Block + optional: true + users: + description: "logged in users for impersonate" + type: array + optional: true + roles: + description: "logged in roles for impersonate" + type: array + optional: true + + KillAllSessionsByPatternCmd: + description: "A struct representing a killAllSessionsByPattern command" + strict: false + fields: + killAllSessionsByPattern: array + + KillSessionsCmdToServer: + description: "A struct representing a killSessions command to a server" + strict: true + fields: + killSessions: array diff --git a/src/mongo/db/kill_sessions_common.cpp b/src/mongo/db/kill_sessions_common.cpp new file mode 100644 index 00000000000..084e5eeec44 --- /dev/null +++ b/src/mongo/db/kill_sessions_common.cpp @@ -0,0 +1,91 @@ +/** + * 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::kCommand + +#include "mongo/platform/basic.h" + +#include "mongo/db/kill_sessions_common.h" + +#include "mongo/db/client.h" +#include "mongo/db/operation_context.h" +#include "mongo/db/service_context.h" +#include "mongo/db/session_killer.h" +#include "mongo/util/log.h" + +namespace mongo { + +SessionKiller::Result killSessionsLocalKillOps(OperationContext* opCtx, + const SessionKiller::Matcher& matcher) { + for (ServiceContext::LockedClientsCursor cursor(opCtx->getClient()->getServiceContext()); + Client* client = cursor.next();) { + invariant(client); + stdx::unique_lock lk(*client); + + OperationContext* opCtxToKill = client->getOperationContext(); + if (opCtxToKill) { + const auto& lsid = opCtxToKill->getLogicalSessionId(); + + if (lsid) { + if (const KillAllSessionsByPattern* pattern = matcher.match(*lsid)) { + ScopedKillAllSessionsByPatternImpersonator impersonator(opCtx, *pattern); + + log() << "killing op: " << opCtxToKill->getOpID() + << " as part of killing session: " << lsid->toBSON(); + + opCtx->getServiceContext()->killOperation(opCtxToKill); + } + } + } + } + + return {std::vector{}}; +} + +Status killSessionsCmdHelper(OperationContext* opCtx, + BSONObjBuilder& result, + const KillAllSessionsByPatternSet& patterns) { + auto killResult = SessionKiller::get(opCtx)->kill(opCtx, patterns); + + if (!killResult->isOK()) { + return killResult->getStatus(); + } + + if (!killResult->getValue().empty()) { + BSONArrayBuilder bab(result.subarrayStart("failedHosts")); + for (const auto& host : killResult->getValue()) { + bab.append(host.toString()); + } + + return Status(ErrorCodes::HostUnreachable, "Failed to kill on some hosts"); + } + + return Status::OK(); +} + +} // namespace mongo diff --git a/src/mongo/db/kill_sessions_common.h b/src/mongo/db/kill_sessions_common.h new file mode 100644 index 00000000000..4b6556db7a8 --- /dev/null +++ b/src/mongo/db/kill_sessions_common.h @@ -0,0 +1,141 @@ +/** + * 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/db/kill_sessions.h" + +#include + +#include "mongo/base/status.h" +#include "mongo/db/auth/authorization_session.h" +#include "mongo/db/operation_context.h" +#include "mongo/db/session_killer.h" +#include "mongo/stdx/unordered_set.h" +#include "mongo/util/stringutils.h" + +namespace mongo { + +/** + * Local killing involves looping over all local operations, checking to see if they have matching + * logical session ids, and killing if they do. + */ +SessionKiller::Result killSessionsLocalKillOps(OperationContext* opCtx, + const SessionKiller::Matcher& matcher); + +/** + * Helper for executing a pattern set from a kill sessions style command. + */ +Status killSessionsCmdHelper(OperationContext* opCtx, + BSONObjBuilder& result, + const KillAllSessionsByPatternSet& patterns); + +class ScopedKillAllSessionsByPatternImpersonator { +public: + ScopedKillAllSessionsByPatternImpersonator(OperationContext* opCtx, + const KillAllSessionsByPattern& pattern) { + AuthorizationSession* authSession = AuthorizationSession::get(opCtx->getClient()); + + if (pattern.getUsers() && pattern.getRoles()) { + std::tie(_names, _roles) = getKillAllSessionsByPatternImpersonateData(pattern); + _raii.emplace(authSession, &_names, &_roles); + } + } + +private: + std::vector _names; + std::vector _roles; + boost::optional _raii; +}; + +/** + * This elaborate bit of artiface helps us to adapt the shape of a cursor manager that we know from + * logical sessions with the different ways to cancel cursors in mongos versus mongod. I.e. the + * two types share no code, but do share enough shape to re-use some boilerplate. + */ +template +class KillSessionsCursorManagerVisitor { +public: + KillSessionsCursorManagerVisitor(OperationContext* opCtx, + const SessionKiller::Matcher& matcher, + Eraser&& eraser) + : _opCtx(opCtx), _matcher(matcher), _eraser(eraser) {} + + template + void operator()(Mgr& mgr) { + LogicalSessionIdSet activeSessions; + mgr.appendActiveSessions(&activeSessions); + + for (const auto& session : activeSessions) { + if (const KillAllSessionsByPattern* pattern = _matcher.match(session)) { + ScopedKillAllSessionsByPatternImpersonator impersonator(_opCtx, *pattern); + + auto cursors = mgr.getCursorsForSession(session); + for (const auto& id : cursors) { + try { + _eraser(mgr, id); + } catch (...) { + _failures.push_back(exceptionToStatus()); + } + } + } + } + } + + Status getStatus() const { + if (_failures.empty()) { + return Status::OK(); + } + + if (_failures.size() == 1) { + return _failures.back(); + } + + return Status(_failures.back().code(), + str::stream() << "Encountered " << _failures.size() + << " errors while killing cursors, " + "showing most recent error: " + << _failures.back().reason()); + } + +private: + OperationContext* _opCtx; + const SessionKiller::Matcher& _matcher; + std::vector _failures; + Eraser _eraser; +}; + +template +auto makeKillSessionsCursorManagerVisitor(OperationContext* opCtx, + const SessionKiller::Matcher& matcher, + Eraser&& eraser) { + return KillSessionsCursorManagerVisitor>{ + opCtx, matcher, std::forward(eraser)}; +} + +} // namespace mongo diff --git a/src/mongo/db/kill_sessions_local.cpp b/src/mongo/db/kill_sessions_local.cpp new file mode 100644 index 00000000000..981cab7d85c --- /dev/null +++ b/src/mongo/db/kill_sessions_local.cpp @@ -0,0 +1,63 @@ +/** + * 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::kCommand + +#include "mongo/platform/basic.h" + +#include "mongo/db/kill_sessions_local.h" + +#include "mongo/db/client.h" +#include "mongo/db/cursor_manager.h" +#include "mongo/db/kill_sessions_common.h" +#include "mongo/db/operation_context.h" +#include "mongo/db/service_context.h" +#include "mongo/util/log.h" + +namespace mongo { + +SessionKiller::Result killSessionsLocalKillCursors(OperationContext* opCtx, + const SessionKiller::Matcher& matcher) { + + auto status = CursorManager::killCursorsWithMatchingSessions(opCtx, matcher); + + if (status.isOK()) { + return std::vector{}; + } else { + return status; + } +} + +SessionKiller::Result killSessionsLocal(OperationContext* opCtx, + const SessionKiller::Matcher& matcher, + SessionKiller::UniformRandomBitGenerator* urbg) { + uassertStatusOK(killSessionsLocalKillCursors(opCtx, matcher)); + return uassertStatusOK(killSessionsLocalKillOps(opCtx, matcher)); +} + +} // namespace mongo diff --git a/src/mongo/db/kill_sessions_local.h b/src/mongo/db/kill_sessions_local.h new file mode 100644 index 00000000000..0785eb9d6a1 --- /dev/null +++ b/src/mongo/db/kill_sessions_local.h @@ -0,0 +1,44 @@ +/** + * 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/db/kill_sessions.h" + +#include "mongo/db/session_killer.h" + +namespace mongo { + +SessionKiller::Result killSessionsLocal(OperationContext* opCtx, + const SessionKiller::Matcher& matcher, + SessionKiller::UniformRandomBitGenerator* urbg); + +SessionKiller::Result killSessionsLocalKillCursors(OperationContext* opCtx, + const SessionKiller::Matcher& matcher); + +} // namespace mongo diff --git a/src/mongo/db/logical_session_id.h b/src/mongo/db/logical_session_id.h index 37937d79a56..24629bdd050 100644 --- a/src/mongo/db/logical_session_id.h +++ b/src/mongo/db/logical_session_id.h @@ -112,4 +112,7 @@ inline StringBuilder& operator<<(StringBuilder& s, const LogicalSessionFromClien using LogicalSessionIdSet = stdx::unordered_set; using LogicalSessionRecordSet = stdx::unordered_set; +template +using LogicalSessionIdMap = stdx::unordered_map; + } // namespace mongo diff --git a/src/mongo/db/logical_session_id_test.cpp b/src/mongo/db/logical_session_id_test.cpp index 48b25dc4148..c10ce634cfc 100644 --- a/src/mongo/db/logical_session_id_test.cpp +++ b/src/mongo/db/logical_session_id_test.cpp @@ -157,6 +157,24 @@ TEST_F(LogicalSessionIdTest, ConstructorFromClientWithPassedUidWithPermissions) ASSERT_EQ(lsid.getUid(), uid); } +TEST_F(LogicalSessionIdTest, ConstructorFromClientWithPassedUidWithNonImpersonatePermissions) { + auto id = UUID::gen(); + auto uid = SHA256Block{}; + addSimpleUser(UserName("simple", "test")); + + LogicalSessionFromClient req; + req.setId(id); + req.setUid(uid); + + LogicalSessionId lsid = makeLogicalSessionId( + req, + _opCtx.get(), + {Privilege{ResourcePattern::forClusterResource(), ActionType::startSession}}); + + ASSERT_EQ(lsid.getId(), id); + ASSERT_EQ(lsid.getUid(), uid); +} + TEST_F(LogicalSessionIdTest, ConstructorFromClientWithPassedUidWithoutAuthedUser) { auto id = UUID::gen(); auto uid = SHA256Block{}; @@ -168,7 +186,7 @@ TEST_F(LogicalSessionIdTest, ConstructorFromClientWithPassedUidWithoutAuthedUser ASSERT_THROWS(makeLogicalSessionId(req, _opCtx.get()), AssertionException); } -TEST_F(LogicalSessionIdTest, ConstructorFromClientWithPassedUidWithoutPermissions) { +TEST_F(LogicalSessionIdTest, ConstructorFromClientWithPassedNonMatchingUidWithoutPermissions) { auto id = UUID::gen(); auto uid = SHA256Block{}; addSimpleUser(UserName("simple", "test")); @@ -180,6 +198,21 @@ TEST_F(LogicalSessionIdTest, ConstructorFromClientWithPassedUidWithoutPermission ASSERT_THROWS(makeLogicalSessionId(req, _opCtx.get()), AssertionException); } +TEST_F(LogicalSessionIdTest, ConstructorFromClientWithPassedMatchingUidWithoutPermissions) { + auto id = UUID::gen(); + User* user = addSimpleUser(UserName("simple", "test")); + auto uid = user->getDigest(); + + LogicalSessionFromClient req; + req.setId(id); + req.setUid(uid); + + LogicalSessionId lsid = makeLogicalSessionId(req, _opCtx.get()); + + ASSERT_EQ(lsid.getId(), id); + ASSERT_EQ(lsid.getUid(), uid); +} + TEST_F(LogicalSessionIdTest, GenWithUser) { User* user = addSimpleUser(UserName("simple", "test")); auto lsid = makeLogicalSessionId(_opCtx.get()); diff --git a/src/mongo/db/pipeline/pipeline_d.cpp b/src/mongo/db/pipeline/pipeline_d.cpp index 0405d78e1b8..f796cd1a9fc 100644 --- a/src/mongo/db/pipeline/pipeline_d.cpp +++ b/src/mongo/db/pipeline/pipeline_d.cpp @@ -280,6 +280,11 @@ public: infoBuilder.append("killPending", true); } + if (clientOpCtx->getLogicalSessionId()) { + BSONObjBuilder bob(infoBuilder.subobjStart("lsid")); + clientOpCtx->getLogicalSessionId()->serialize(&bob); + } + CurOp::get(clientOpCtx) ->reportState(&infoBuilder, (truncateMode == CurrentOpTruncateMode::kTruncateOps)); diff --git a/src/mongo/db/s/sharding_task_executor.cpp b/src/mongo/db/s/sharding_task_executor.cpp index 967dbe713bb..08362e55d66 100644 --- a/src/mongo/db/s/sharding_task_executor.cpp +++ b/src/mongo/db/s/sharding_task_executor.cpp @@ -111,30 +111,43 @@ StatusWith ShardingTaskExecutor::scheduleRemoteCom return _executor->scheduleRemoteCommand(request, cb); } + boost::optional newRequest; + + if (request.opCtx->getLogicalSessionId() && !request.cmdObj.hasField("lsid")) { + newRequest.emplace(request); + BSONObjBuilder bob(std::move(newRequest->cmdObj)); + { + BSONObjBuilder subbob(bob.subobjStart("lsid")); + request.opCtx->getLogicalSessionId()->serialize(&subbob); + } + + newRequest->cmdObj = bob.obj(); + } + std::shared_ptr timeTracker = OperationTimeTracker::get(request.opCtx); auto clusterGLE = ClusterLastErrorInfo::get(request.opCtx->getClient()); - auto shardingCb = [timeTracker, clusterGLE, request, cb]( + auto shardingCb = [timeTracker, clusterGLE, cb]( const TaskExecutor::RemoteCommandCallbackArgs& args) { ON_BLOCK_EXIT([&cb, &args]() { cb(args); }); // Update replica set monitor info. - auto shard = grid.shardRegistry()->getShardForHostNoReload(request.target); + auto shard = grid.shardRegistry()->getShardForHostNoReload(args.request.target); if (!shard) { - LOG(1) << "Could not find shard containing host: " << request.target.toString(); + LOG(1) << "Could not find shard containing host: " << args.request.target.toString(); } if (!args.response.isOK()) { if (shard) { - shard->updateReplSetMonitor(request.target, args.response.status); + shard->updateReplSetMonitor(args.request.target, args.response.status); } LOG(1) << "Error processing the remote request, not updating operationTime or gLE"; return; } if (shard) { - shard->updateReplSetMonitor(request.target, + shard->updateReplSetMonitor(args.request.target, getStatusFromCommandResult(args.response.data)); } @@ -153,9 +166,9 @@ StatusWith ShardingTaskExecutor::scheduleRemoteCom if (swShardingMetadata.isOK()) { auto shardingMetadata = std::move(swShardingMetadata.getValue()); - auto shardConn = ConnectionString::parse(request.target.toString()); + auto shardConn = ConnectionString::parse(args.request.target.toString()); if (!shardConn.isOK()) { - severe() << "got bad host string in saveGLEStats: " << request.target; + severe() << "got bad host string in saveGLEStats: " << args.request.target; } clusterGLE->addHostOpTime(shardConn.getValue(), @@ -169,7 +182,7 @@ StatusWith ShardingTaskExecutor::scheduleRemoteCom } }; - return _executor->scheduleRemoteCommand(request, shardingCb); + return _executor->scheduleRemoteCommand(newRequest ? *newRequest : request, shardingCb); } void ShardingTaskExecutor::cancel(const CallbackHandle& cbHandle) { diff --git a/src/mongo/db/service_entry_point_mongod.cpp b/src/mongo/db/service_entry_point_mongod.cpp index 491a61dc9ea..64ab9ab7271 100644 --- a/src/mongo/db/service_entry_point_mongod.cpp +++ b/src/mongo/db/service_entry_point_mongod.cpp @@ -450,7 +450,7 @@ bool runCommandImpl(OperationContext* opCtx, return result; } - result = command->enhancedRun(opCtx, request, inPlaceReplyBob); + result = command->publicRun(opCtx, request, inPlaceReplyBob); } else { auto wcResult = extractWriteConcern(opCtx, cmd, db); if (!wcResult.isOK()) { @@ -471,7 +471,7 @@ bool runCommandImpl(OperationContext* opCtx, opCtx, command->getName(), lastOpBeforeRun, &inPlaceReplyBob); }); - result = command->enhancedRun(opCtx, request, inPlaceReplyBob); + result = command->publicRun(opCtx, request, inPlaceReplyBob); // Nothing in run() should change the writeConcern. dassert(SimpleBSONObjComparator::kInstance.evaluate(opCtx->getWriteConcern().toBSON() == diff --git a/src/mongo/db/session_killer.cpp b/src/mongo/db/session_killer.cpp new file mode 100644 index 00000000000..034cc93038b --- /dev/null +++ b/src/mongo/db/session_killer.cpp @@ -0,0 +1,200 @@ +/** + * 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. + */ + +#include "mongo/platform/basic.h" + +#include "mongo/db/session_killer.h" + +#include "mongo/db/auth/authorization_session.h" +#include "mongo/db/client.h" +#include "mongo/db/operation_context.h" +#include "mongo/db/service_context.h" +#include "mongo/util/destructor_guard.h" +#include "mongo/util/scopeguard.h" + +namespace mongo { + +namespace { +const auto getSessionKiller = ServiceContext::declareDecoration>(); +} // namespace + +SessionKiller::SessionKiller(ServiceContext* sc, KillFunc killer) + : _killFunc(std::move(killer)), _urbg(std::random_device{}()), _reapResults() { + _thread = stdx::thread([this, sc] { + // This is the background killing thread + + Client::setCurrent(sc->makeClient("SessionKiller")); + + stdx::unique_lock lk(_mutex); + + // While we're not in shutdown + while (!_inShutdown) { + // Wait until we're woken up, and should either shutdown, or have something new to reap. + _killerCV.wait(lk, [&] { return _inShutdown || _nextToReap.size(); }); + + // If we're in shutdown we're done + if (_inShutdown) { + return; + } + + // Otherwise make an opctx and head into kill + auto opCtx = cc().makeOperationContext(); + _periodicKill(opCtx.get(), lk); + } + }); +} + +SessionKiller::~SessionKiller() { + DESTRUCTOR_GUARD([&] { + { + stdx::lock_guard lk(_mutex); + _inShutdown = true; + } + _killerCV.notify_one(); + _callerCV.notify_all(); + _thread.join(); + }()); +} + +SessionKiller::ReapResult::ReapResult() : result(std::make_shared>()) {} + +SessionKiller::Matcher::Matcher(KillAllSessionsByPatternSet&& patterns) + : _patterns(std::move(patterns)) { + for (const auto& pattern : _patterns) { + if (pattern.getUid()) { + _uids.emplace(pattern.getUid().get(), &pattern); + } else if (pattern.getLsid()) { + _lsids.emplace(pattern.getLsid().get(), &pattern); + } else { + // If we're killing everything, it's the only pattern we care about. + decltype(_patterns) onlyKillAll{{pattern}}; + using std::swap; + swap(_patterns, onlyKillAll); + + _killAll = &(*_patterns.begin()); + break; + } + } +} + +const KillAllSessionsByPatternSet& SessionKiller::Matcher::getPatterns() const { + return _patterns; +} + +const KillAllSessionsByPattern* SessionKiller::Matcher::match(const LogicalSessionId& lsid) const { + if (_killAll) { + return _killAll; + } + + { + auto iter = _lsids.find(lsid); + if (iter != _lsids.end()) { + return iter->second; + } + } + + { + auto iter = _uids.find(lsid.getUid()); + if (iter != _uids.end()) { + return iter->second; + } + } + + return nullptr; +} + +SessionKiller* SessionKiller::get(ServiceContext* service) { + return getSessionKiller(service).get(); +} + +SessionKiller* SessionKiller::get(OperationContext* ctx) { + return get(ctx->getServiceContext()); +} + +std::shared_ptr SessionKiller::kill( + OperationContext* opCtx, const KillAllSessionsByPatternSet& toKill) { + stdx::unique_lock lk(_mutex); + + // Save a shared_ptr to the current reapResults (I.e. the next thing to get killed). + auto reapResults = _reapResults; + + // Dump all your lsids in. + for (const auto& item : toKill) { + _nextToReap.emplace(item); + } + + // Wake up the killer. + _killerCV.notify_one(); + + // Wait until our results are there, or the killer is shutting down. + opCtx->waitForConditionOrInterrupt( + _callerCV, lk, [&] { return reapResults.result->is_initialized() || _inShutdown; }); + + // If the killer is shutting down, throw. + uassert(ErrorCodes::ShutdownInProgress, "SessionKiller shutting down", !_inShutdown); + + // Otherwise, alias (via the aliasing ctor of shared_ptr) a shared_ptr to the actual results + // (inside the optional) to keep our contract. That ctor form returns a shared_ptr which + // returns one type, while keeping a refcount on a control block from a different type. + return {reapResults.result, reapResults.result->get_ptr()}; +} + +void SessionKiller::_periodicKill(OperationContext* opCtx, stdx::unique_lock& lk) { + // Pull our current workload onto the stack. Swap it for empties. + decltype(_nextToReap) nextToReap; + decltype(_reapResults) reapResults; + { + using std::swap; + swap(nextToReap, _nextToReap); + swap(reapResults, _reapResults); + } + + // Drop the lock and run the killer. + lk.unlock(); + + Matcher matcher(std::move(nextToReap)); + boost::optional results; + try { + results.emplace(_killFunc(opCtx, matcher, &_urbg)); + } catch (...) { + results.emplace(exceptionToStatus()); + } + lk.lock(); + + invariant(results); + + // Expose the results and notify callers + *(reapResults.result) = std::move(results); + _callerCV.notify_all(); +}; + +void SessionKiller::set(ServiceContext* sc, std::shared_ptr sk) { + getSessionKiller(sc) = sk; +} + +} // namespace mongo diff --git a/src/mongo/db/session_killer.h b/src/mongo/db/session_killer.h new file mode 100644 index 00000000000..2e13df038d8 --- /dev/null +++ b/src/mongo/db/session_killer.h @@ -0,0 +1,145 @@ +/** + * 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 +#include +#include +#include + +#include "mongo/base/status_with.h" +#include "mongo/db/kill_sessions.h" +#include "mongo/stdx/condition_variable.h" +#include "mongo/stdx/functional.h" +#include "mongo/stdx/mutex.h" +#include "mongo/stdx/thread.h" +#include "mongo/stdx/unordered_set.h" +#include "mongo/util/net/hostandport.h" + +namespace mongo { + +/** + * The SessionKiller enforces a single thread for session killing for any given ServiceContext. + * + * The killer owns a background thread which actually does the work, and callers batch their kills + * together each round, before the killer starts its dispatching, after which they batch up for the + * next round. + * + * The KillFunc's kill function is passed in to its constructor, and parameterizes its behavior + * depending on context (mongod/mongos). + */ +class SessionKiller { +public: + /** + * The Result of a call is either: + * + * Status::OK(), empty vector - we killed everything + * Status::OK(), filled vector - we killed something. HostAndPort is filled with nodes we + * failed to kill on. + * + * !Status::OK() - The kill function itself failed. I.e. we may have killed nothing. + * + * This contract has a helper in kill_sessions_common.h which adapts results for command + * implementations. (killSessionsCmdHelper) + */ + using Result = StatusWith>; + using UniformRandomBitGenerator = std::minstd_rand; + + class Matcher { + public: + Matcher(KillAllSessionsByPatternSet&& patterns); + + const KillAllSessionsByPatternSet& getPatterns() const; + + const KillAllSessionsByPattern* match(const LogicalSessionId& lsid) const; + + private: + KillAllSessionsByPatternSet _patterns; + LogicalSessionIdMap _lsids; + stdx::unordered_map _uids; + const KillAllSessionsByPattern* _killAll = nullptr; + }; + + /** + * A process specific kill function (we have a different impl in mongos versus mongod). + */ + using KillFunc = + stdx::function; + + /** + * The killer lives as a decoration on the service context. + */ + static SessionKiller* get(ServiceContext* service); + static SessionKiller* get(OperationContext* opCtx); + + /** + * This method binds the SessionKiller to the ServiceContext. + */ + static void set(ServiceContext* ctx, std::shared_ptr sk); + + explicit SessionKiller(ServiceContext* sc, KillFunc killer); + ~SessionKiller(); + + /** + * This is the api for killSessions commands to invoke the killer. It blocks until the kill is + * finished, or until it fails (times out on all nodes in mongos). + */ + std::shared_ptr kill(OperationContext* opCtx, + const KillAllSessionsByPatternSet& toKill); + +private: + /** + * This struct is a helper to default fill in the result, which is otherwise a little error + * prone. + */ + struct ReapResult { + ReapResult(); + + std::shared_ptr> result; + }; + + void _periodicKill(OperationContext* opCtx, stdx::unique_lock& lk); + + KillFunc _killFunc; + + stdx::thread _thread; + + stdx::mutex _mutex; + stdx::condition_variable _callerCV; + stdx::condition_variable _killerCV; + + UniformRandomBitGenerator _urbg; + + ReapResult _reapResults; + KillAllSessionsByPatternSet _nextToReap; + + bool _inShutdown = false; +}; + +} // namespace mongo diff --git a/src/mongo/executor/SConscript b/src/mongo/executor/SConscript index 3c8bcf9c60a..b1ed48063bb 100644 --- a/src/mongo/executor/SConscript +++ b/src/mongo/executor/SConscript @@ -26,6 +26,17 @@ env.Library(target='remote_command', '$BUILD_DIR/mongo/util/net/network', ]) +env.Library(target='async_multicaster', + source=[ + 'async_multicaster.cpp', + ], + LIBDEPS=[ + 'remote_command', + 'task_executor_interface', + '$BUILD_DIR/mongo/base', + '$BUILD_DIR/mongo/util/net/network', + ]) + env.Library(target='task_executor_interface', source=[ 'task_executor.cpp', diff --git a/src/mongo/executor/async_multicaster.cpp b/src/mongo/executor/async_multicaster.cpp new file mode 100644 index 00000000000..0be2b05f47b --- /dev/null +++ b/src/mongo/executor/async_multicaster.cpp @@ -0,0 +1,104 @@ +/** + * 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/executor/async_multicaster.h" + +#include + +#include "mongo/base/status.h" +#include "mongo/db/operation_context.h" +#include "mongo/stdx/condition_variable.h" +#include "mongo/stdx/mutex.h" +#include "mongo/util/assert_util.h" + +namespace mongo { +namespace executor { + +AsyncMulticaster::AsyncMulticaster(executor::TaskExecutor* executor, Options options) + : _options(options), _executor(executor) {} + +std::vector AsyncMulticaster::multicast( + const std::vector servers, + const std::string& theDbName, + const BSONObj& theCmdObj, + OperationContext* opCtx, + Milliseconds timeoutMillis) { + + // Everything goes into a state struct because we can get cancelled, and then our callback would + // be invoked later. + struct State { + State(size_t leftToDo) : leftToDo(leftToDo) {} + + stdx::mutex mutex; + stdx::condition_variable cv; + size_t leftToDo; + size_t running = 0; + + // To indicate which hosts fail. + std::vector out; + }; + + auto state = std::make_shared(servers.size()); + for (const auto& server : servers) { + stdx::unique_lock lk(state->mutex); + // spin up no more than maxConcurrency tasks at once + opCtx->waitForConditionOrInterrupt( + state->cv, lk, [&] { return state->running < _options.maxConcurrency; }); + ++state->running; + + uassertStatusOK(_executor->scheduleRemoteCommand( + RemoteCommandRequest{server, theDbName, theCmdObj, opCtx, timeoutMillis}, + [state](const TaskExecutor::RemoteCommandCallbackArgs& cbData) { + stdx::lock_guard lk(state->mutex); + + state->out.emplace_back( + std::forward_as_tuple(cbData.request.target, cbData.response)); + + // If we were the last job, flush the done flag and release via notify. + if (!--(state->leftToDo)) { + state->cv.notify_one(); + } + + if (--(state->running) < kMaxConcurrency) { + state->cv.notify_one(); + } + })); + } + + stdx::unique_lock lk(state->mutex); + opCtx->waitForConditionOrInterrupt(state->cv, lk, [&] { return state->leftToDo == 0; }); + + return std::move(state->out); +} + +} // namespace executor +} // namespace mongo diff --git a/src/mongo/executor/async_multicaster.h b/src/mongo/executor/async_multicaster.h new file mode 100644 index 00000000000..239fbf0978f --- /dev/null +++ b/src/mongo/executor/async_multicaster.h @@ -0,0 +1,79 @@ +/** + * 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 + +#include "mongo/executor/remote_command_response.h" +#include "mongo/executor/task_executor.h" +#include "mongo/stdx/mutex.h" +#include "mongo/util/net/hostandport.h" + +namespace mongo { +namespace executor { + +/** + * An async harness for scatter/gathering a command across an arbitrary number of specific hosts + */ +class AsyncMulticaster { +public: + using Reply = std::tuple; + static constexpr size_t kMaxConcurrency = 100; + + struct Options { + // maxConcurrency controls the maximum number of inflight operations. I.e. limiting it + // prevents the fan out from overwhelming the host, if the number of servers to multicast + // to is very high. + size_t maxConcurrency = kMaxConcurrency; + }; + + AsyncMulticaster(executor::TaskExecutor* executor, Options options); + + /** + * Sends the cmd out to all passed servers (via the executor), observing the multicaster's + * maxConcurrency. + * + * The timeout value on multicast is per operation. The overall timeout will be: + * timeoutMillis - if max concurrency is greater than servers.size() + * or + * timeoutMillis * (servers.size() / maxConcurrency) - if not + */ + std::vector multicast(const std::vector servers, + const std::string& theDbName, + const BSONObj& theCmdObj, + OperationContext* opCtx, + Milliseconds timeoutMillis); + +private: + Options _options; + executor::TaskExecutor* _executor; +}; + +} // namespace executor +} // namespace mongo diff --git a/src/mongo/s/commands/SConscript b/src/mongo/s/commands/SConscript index 75db97fa257..75fb56c876b 100644 --- a/src/mongo/s/commands/SConscript +++ b/src/mongo/s/commands/SConscript @@ -61,6 +61,7 @@ env.Library( 'cluster_merge_chunks_cmd.cpp', 'cluster_move_chunk_cmd.cpp', 'cluster_move_primary_cmd.cpp', + 'cluster_multicast.cpp', 'cluster_netstat_cmd.cpp', 'cluster_pipeline_cmd.cpp', 'cluster_plan_cache_cmd.cpp', @@ -79,7 +80,9 @@ env.Library( 'cluster_write.cpp', 'cluster_write_cmd.cpp', 'commands_public.cpp', + 'kill_sessions_remote.cpp', 'strategy.cpp', + env.Idlc('cluster_multicast.idl')[0], ], LIBDEPS=[ '$BUILD_DIR/mongo/db/auth/authmongos', @@ -88,6 +91,7 @@ env.Library( '$BUILD_DIR/mongo/db/commands/write_commands_common', '$BUILD_DIR/mongo/db/pipeline/aggregation', '$BUILD_DIR/mongo/db/views/views', + '$BUILD_DIR/mongo/executor/async_multicaster', '$BUILD_DIR/mongo/rpc/client_metadata', '$BUILD_DIR/mongo/s/async_requests_sender', '$BUILD_DIR/mongo/s/client/parallel', diff --git a/src/mongo/s/commands/cluster_multicast.cpp b/src/mongo/s/commands/cluster_multicast.cpp new file mode 100644 index 00000000000..56af56e255a --- /dev/null +++ b/src/mongo/s/commands/cluster_multicast.cpp @@ -0,0 +1,158 @@ +/** + * 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. + */ + +#include "mongo/platform/basic.h" + +#include + +#include "mongo/base/init.h" +#include "mongo/db/commands.h" +#include "mongo/executor/async_multicaster.h" +#include "mongo/executor/task_executor_pool.h" +#include "mongo/s/catalog/sharding_catalog_client.h" +#include "mongo/s/catalog/type_shard.h" +#include "mongo/s/client/shard_registry.h" +#include "mongo/s/commands/cluster_multicast_gen.h" +#include "mongo/s/grid.h" + +namespace mongo { +namespace { + +std::vector getAllClusterHosts(OperationContext* opCtx) { + auto registry = Grid::get(opCtx)->shardRegistry(); + + std::vector shardIds; + registry->getAllShardIds(&shardIds); + + std::vector servers; + for (const auto& shardId : shardIds) { + auto shard = uassertStatusOK(registry->getShard(opCtx, shardId)); + + auto cs = shard->getConnString(); + for (auto&& host : cs.getServers()) { + servers.emplace_back(host); + } + } + + return servers; +} + +class MulticastCmd : public BasicCommand { +public: + MulticastCmd() : BasicCommand("multicast") {} + + bool slaveOk() const override { + return true; + } + + bool adminOnly() const override { + return true; + } + + + bool supportsWriteConcern(const BSONObj& cmd) const override { + return false; + } + + void help(std::stringstream& help) const override { + help << "multicasts a command to hosts in a system"; + } + + // no privs because it's a test command + void addRequiredPrivileges(const std::string& dbname, + const BSONObj& cmdObj, + std::vector* out) override {} + + bool run(OperationContext* opCtx, + const std::string& dbname, + const BSONObj& cmdObj, + BSONObjBuilder& result) override { + IDLParserErrorContext ctx("ClusterMulticastArgs"); + auto args = ClusterMulticastArgs::parse(ctx, cmdObj); + + // Grab an arbitrary executor. + auto executor = Grid::get(opCtx)->getExecutorPool()->getArbitraryExecutor(); + + // Grab all hosts in the cluster. + auto servers = getAllClusterHosts(opCtx); + + executor::AsyncMulticaster::Options options; + if (args.getConcurrency()) { + options.maxConcurrency = *args.getConcurrency(); + } + + auto results = + executor::AsyncMulticaster(executor, options) + .multicast(servers, + args.getDb().toString(), + args.getMulticast(), + opCtx, + (args.getTimeout() ? Milliseconds(*args.getTimeout()) + : executor::RemoteCommandRequest::kNoTimeout)); + + bool success = true; + + { + BSONObjBuilder bob(result.subobjStart("hosts")); + + for (const auto& r : results) { + HostAndPort host; + executor::RemoteCommandResponse response; + std::tie(host, response) = r; + + if (!response.isOK() || !response.data["ok"].trueValue()) { + success = false; + } + + { + BSONObjBuilder subbob(bob.subobjStart(host.toString())); + + if (appendCommandStatus(subbob, response.status)) { + subbob.append("data", response.data); + subbob.append("metadata", response.metadata); + if (response.elapsedMillis) { + subbob.append("elapsedMillis", response.elapsedMillis->count()); + } + } + } + } + } + + return success; + } +}; + +MONGO_INITIALIZER(RegisterMulticast)(InitializerContext* context) { + if (Command::testCommandsEnabled) { + new MulticastCmd(); + } + return Status::OK(); +} + +} // namespace +} // namespace mongo diff --git a/src/mongo/s/commands/cluster_multicast.idl b/src/mongo/s/commands/cluster_multicast.idl new file mode 100644 index 00000000000..cbed46fd512 --- /dev/null +++ b/src/mongo/s/commands/cluster_multicast.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 . +# + +global: + cpp_namespace: "mongo" + +imports: + - "mongo/idl/basic_types.idl" + +structs: + + ClusterMulticastArgs: + description: "A struct representing cluster multicast args" + strict: false + fields: + multicast: object + $db: + type: string + cpp_name: db + concurrency: + type: int + optional: true + timeout: + type: int + optional: true diff --git a/src/mongo/s/commands/kill_sessions_remote.cpp b/src/mongo/s/commands/kill_sessions_remote.cpp new file mode 100644 index 00000000000..a89e407eb45 --- /dev/null +++ b/src/mongo/s/commands/kill_sessions_remote.cpp @@ -0,0 +1,137 @@ +/** + * 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::kCommand + +#include "mongo/platform/basic.h" + +#include "mongo/s/commands/kill_sessions_remote.h" + +#include "mongo/db/client.h" +#include "mongo/db/kill_sessions_common.h" +#include "mongo/db/operation_context.h" +#include "mongo/db/server_parameters.h" +#include "mongo/db/service_context.h" +#include "mongo/executor/async_multicaster.h" +#include "mongo/executor/task_executor_pool.h" +#include "mongo/s/client/shard.h" +#include "mongo/s/client/shard_registry.h" +#include "mongo/s/grid.h" +#include "mongo/s/query/cluster_cursor_manager.h" +#include "mongo/util/log.h" + +namespace mongo { + +namespace { + +constexpr size_t kMaxConcurrency = 100; +constexpr Milliseconds kTimeout = Milliseconds(60000); + +MONGO_EXPORT_STARTUP_SERVER_PARAMETER(KillSessionsMaxConcurrency, int, kMaxConcurrency); +MONGO_EXPORT_STARTUP_SERVER_PARAMETER(KillSessionsPerHostTimeoutMS, int, kTimeout.count()); + +/** + * Get all hosts in the cluster. + */ +std::vector getAllClusterHosts(OperationContext* opCtx) { + auto registry = Grid::get(opCtx)->shardRegistry(); + + std::vector shardIds; + registry->getAllShardIds(&shardIds); + + std::vector servers; + for (const auto& shardId : shardIds) { + auto shard = uassertStatusOK(registry->getShard(opCtx, shardId)); + + auto cs = shard->getConnString(); + for (auto&& host : cs.getServers()) { + servers.emplace_back(host); + } + } + + return servers; +} + +/** + * A function for running an arbitrary command on all shards. Only returns which hosts failed. + */ +SessionKiller::Result parallelExec(OperationContext* opCtx, + const BSONObj& cmd, + SessionKiller::UniformRandomBitGenerator* urbg) { + // Grab an arbitrary executor. + auto executor = Grid::get(opCtx)->getExecutorPool()->getArbitraryExecutor(); + + // Grab all hosts in the cluster. + auto servers = getAllClusterHosts(opCtx); + std::shuffle(servers.begin(), servers.end(), *urbg); + + // To indicate which hosts fail. + std::vector failed; + + executor::AsyncMulticaster::Options options; + options.maxConcurrency = KillSessionsMaxConcurrency; + auto results = + executor::AsyncMulticaster(executor, options) + .multicast(servers, "admin", cmd, opCtx, Milliseconds(KillSessionsPerHostTimeoutMS)); + + for (const auto& result : results) { + if (!std::get<1>(result).isOK()) { + failed.push_back(std::get<0>(result)); + } + } + + return failed; +} + +Status killSessionsRemoteKillCursor(OperationContext* opCtx, + const SessionKiller::Matcher& matcher) { + return Grid::get(opCtx)->getCursorManager()->killCursorsWithMatchingSessions(opCtx, matcher); +} + +} // namespace + +/** + * This kill function (meant for mongos), kills matching local ops first, then fans out to all other + * nodes in the cluster to kill them as well. + */ +SessionKiller::Result killSessionsRemote(OperationContext* opCtx, + const SessionKiller::Matcher& matcher, + SessionKiller::UniformRandomBitGenerator* urbg) { + // First kill local sessions. + uassertStatusOK(killSessionsRemoteKillCursor(opCtx, matcher)); + uassertStatusOK(killSessionsLocalKillOps(opCtx, matcher)); + + // Generate the kill command. + KillAllSessionsByPatternCmd cmd; + cmd.setKillAllSessionsByPattern(std::vector{ + matcher.getPatterns().begin(), matcher.getPatterns().end()}); + + return parallelExec(opCtx, cmd.toBSON(), urbg); +} + +} // namespace mongo diff --git a/src/mongo/s/commands/kill_sessions_remote.h b/src/mongo/s/commands/kill_sessions_remote.h new file mode 100644 index 00000000000..0be3cc7189e --- /dev/null +++ b/src/mongo/s/commands/kill_sessions_remote.h @@ -0,0 +1,44 @@ +/** + * 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/db/kill_sessions.h" + +#include "mongo/db/session_killer.h" + +namespace mongo { + +/** + * The killSessions killer for running on mongos + */ +SessionKiller::Result killSessionsRemote(OperationContext* opCtx, + const SessionKiller::Matcher& patterns, + SessionKiller::UniformRandomBitGenerator* urbg); + +} // namespace mongo diff --git a/src/mongo/s/commands/strategy.cpp b/src/mongo/s/commands/strategy.cpp index a17e73199d4..9d6aa9e72f5 100644 --- a/src/mongo/s/commands/strategy.cpp +++ b/src/mongo/s/commands/strategy.cpp @@ -217,14 +217,14 @@ void execCommandClient(OperationContext* opCtx, try { bool ok = false; if (!supportsWriteConcern) { - ok = c->enhancedRun(opCtx, request, result); + ok = c->publicRun(opCtx, request, result); } else { // Change the write concern while running the command. const auto oldWC = opCtx->getWriteConcern(); ON_BLOCK_EXIT([&] { opCtx->setWriteConcern(oldWC); }); opCtx->setWriteConcern(wcResult.getValue()); - ok = c->enhancedRun(opCtx, request, result); + ok = c->publicRun(opCtx, request, result); } if (!ok) { c->incrementCommandsFailed(); diff --git a/src/mongo/s/query/SConscript b/src/mongo/s/query/SConscript index 2977050be19..259e2d28244 100644 --- a/src/mongo/s/query/SConscript +++ b/src/mongo/s/query/SConscript @@ -133,6 +133,8 @@ env.Library( ], LIBDEPS=[ '$BUILD_DIR/mongo/base', + '$BUILD_DIR/mongo/db/auth/authcore', + '$BUILD_DIR/mongo/db/kill_sessions', '$BUILD_DIR/mongo/db/logical_session_id', ], ) diff --git a/src/mongo/s/query/cluster_cursor_manager.cpp b/src/mongo/s/query/cluster_cursor_manager.cpp index 99e7b2afa82..25e63a50d54 100644 --- a/src/mongo/s/query/cluster_cursor_manager.cpp +++ b/src/mongo/s/query/cluster_cursor_manager.cpp @@ -34,6 +34,7 @@ #include +#include "mongo/db/kill_sessions_common.h" #include "mongo/util/clock_source.h" #include "mongo/util/log.h" #include "mongo/util/mongoutils/str.h" @@ -471,6 +472,17 @@ void ClusterCursorManager::appendActiveSessions(LogicalSessionIdSet* lsids) cons } } +Status ClusterCursorManager::killCursorsWithMatchingSessions( + OperationContext* opCtx, const SessionKiller::Matcher& matcher) { + auto eraser = [&](ClusterCursorManager& mgr, CursorId id) { + uassertStatusOK(mgr.killCursor(getNamespaceForCursorId(id).get(), id)); + }; + + auto visitor = makeKillSessionsCursorManagerVisitor(opCtx, matcher, std::move(eraser)); + visitor(*this); + return visitor.getStatus(); +} + stdx::unordered_set ClusterCursorManager::getCursorsForSession( LogicalSessionId lsid) const { stdx::lock_guard lk(_mutex); diff --git a/src/mongo/s/query/cluster_cursor_manager.h b/src/mongo/s/query/cluster_cursor_manager.h index f6da640bb1e..d6eb8f47abf 100644 --- a/src/mongo/s/query/cluster_cursor_manager.h +++ b/src/mongo/s/query/cluster_cursor_manager.h @@ -32,7 +32,9 @@ #include #include "mongo/db/cursor_id.h" +#include "mongo/db/kill_sessions.h" #include "mongo/db/namespace_string.h" +#include "mongo/db/session_killer.h" #include "mongo/platform/random.h" #include "mongo/s/query/cluster_client_cursor.h" #include "mongo/stdx/mutex.h" @@ -353,6 +355,9 @@ public: */ void appendActiveSessions(LogicalSessionIdSet* lsids) const; + Status killCursorsWithMatchingSessions(OperationContext* opCtx, + const SessionKiller::Matcher& matcher); + /** * Returns a list of all open cursors for the given session. */ diff --git a/src/mongo/s/server.cpp b/src/mongo/s/server.cpp index 521702e56b8..27de3a7994f 100644 --- a/src/mongo/s/server.cpp +++ b/src/mongo/s/server.cpp @@ -52,6 +52,7 @@ #include "mongo/db/client.h" #include "mongo/db/ftdc/ftdc_mongos.h" #include "mongo/db/initialize_server_global_state.h" +#include "mongo/db/kill_sessions.h" #include "mongo/db/lasterror.h" #include "mongo/db/log_process_details.h" #include "mongo/db/logical_clock.h" @@ -62,6 +63,7 @@ #include "mongo/db/server_options.h" #include "mongo/db/service_context.h" #include "mongo/db/service_context_noop.h" +#include "mongo/db/session_killer.h" #include "mongo/db/startup_warnings_common.h" #include "mongo/db/wire_version.h" #include "mongo/executor/task_executor_pool.h" @@ -75,6 +77,7 @@ #include "mongo/s/client/shard_registry.h" #include "mongo/s/client/shard_remote.h" #include "mongo/s/client/sharding_connection_hook.h" +#include "mongo/s/commands/kill_sessions_remote.h" #include "mongo/s/config_server_catalog_cache_loader.h" #include "mongo/s/grid.h" #include "mongo/s/is_mongos.h" @@ -395,6 +398,10 @@ static ExitCode runMongosServer() { runner->startup().transitional_ignore(); getGlobalServiceContext()->setPeriodicRunner(std::move(runner)); + SessionKiller::set( + getGlobalServiceContext(), + std::make_shared(getGlobalServiceContext(), killSessionsRemote)); + // Set up the logical session cache LogicalSessionCache::set(getGlobalServiceContext(), makeLogicalSessionCacheS()); -- cgit v1.2.1