diff options
author | David Storch <david.storch@10gen.com> | 2019-02-04 10:45:12 -0500 |
---|---|---|
committer | David Storch <david.storch@10gen.com> | 2019-02-05 08:34:35 -0500 |
commit | cb1aabbba7093b6c42b6986a9ad0aea31528929a (patch) | |
tree | acd49931110ac57d13e70f870528929b1b2dd631 /src/mongo/db | |
parent | 9a90dfc2fbe769d3caf02a3550f00bc9fa9df483 (diff) | |
download | mongo-cb1aabbba7093b6c42b6986a9ad0aea31528929a.tar.gz |
SERVER-37454 Delete GlobalCursorIdCache.
Diffstat (limited to 'src/mongo/db')
-rw-r--r-- | src/mongo/db/SConscript | 1 | ||||
-rw-r--r-- | src/mongo/db/clientcursor.cpp | 2 | ||||
-rw-r--r-- | src/mongo/db/cursor_manager.cpp | 136 | ||||
-rw-r--r-- | src/mongo/db/cursor_manager.h | 15 | ||||
-rw-r--r-- | src/mongo/db/run_op_kill_cursors.cpp | 106 | ||||
-rw-r--r-- | src/mongo/db/run_op_kill_cursors.h | 48 | ||||
-rw-r--r-- | src/mongo/db/service_entry_point_common.cpp | 18 |
7 files changed, 170 insertions, 156 deletions
diff --git a/src/mongo/db/SConscript b/src/mongo/db/SConscript index ed2e69d5a79..b135cb559a2 100644 --- a/src/mongo/db/SConscript +++ b/src/mongo/db/SConscript @@ -1175,6 +1175,7 @@ env.Library( 'query/plan_yield_policy.cpp', 'query/query_yield.cpp', 'query/stage_builder.cpp', + 'run_op_kill_cursors.cpp', ], LIBDEPS=[ '$BUILD_DIR/mongo/base', diff --git a/src/mongo/db/clientcursor.cpp b/src/mongo/db/clientcursor.cpp index 30c52ef2adc..41c2b814d5c 100644 --- a/src/mongo/db/clientcursor.cpp +++ b/src/mongo/db/clientcursor.cpp @@ -279,7 +279,7 @@ public: const ServiceContext::UniqueOperationContext opCtx = cc().makeOperationContext(); auto now = opCtx->getServiceContext()->getPreciseClockSource()->now(); cursorStatsTimedOut.increment( - CursorManager::timeoutCursorsGlobal(opCtx.get(), now)); + CursorManager::getGlobalCursorManager()->timeoutCursors(opCtx.get(), now)); } MONGO_IDLE_THREAD_BLOCK; sleepsecs(getClientCursorMonitorFrequencySecs()); diff --git a/src/mongo/db/cursor_manager.cpp b/src/mongo/db/cursor_manager.cpp index 7b7fef06152..3479b122b47 100644 --- a/src/mongo/db/cursor_manager.cpp +++ b/src/mongo/db/cursor_manager.cpp @@ -60,122 +60,16 @@ namespace mongo { -using std::vector; - constexpr int CursorManager::kNumPartitions; -namespace { - -class GlobalCursorIdCache { -public: - GlobalCursorIdCache(); - ~GlobalCursorIdCache(); - - /** - * works globally - */ - bool killCursor(OperationContext* opCtx, CursorId id, bool checkAuth); - - void appendStats(BSONObjBuilder& builder); - - std::size_t timeoutCursors(OperationContext* opCtx, Date_t now); - - int64_t nextSeed(); - -private: - // '_mutex' must not be held when acquiring a CursorManager mutex to avoid deadlock. - SimpleMutex _mutex; - - using CursorIdToNssMap = stdx::unordered_map<CursorId, NamespaceString>; - using IdToNssMap = stdx::unordered_map<unsigned, NamespaceString>; - - IdToNssMap _idToNss; - unsigned _nextId; - - std::unique_ptr<SecureRandom> _secureRandom; -}; - -// Note that "globalCursorIdCache" must be declared before "globalCursorManager", as the latter -// calls into the former during destruction. -std::unique_ptr<GlobalCursorIdCache> globalCursorIdCache; std::unique_ptr<CursorManager> globalCursorManager; -MONGO_INITIALIZER(GlobalCursorIdCache)(InitializerContext* context) { - globalCursorIdCache.reset(new GlobalCursorIdCache()); - return Status::OK(); -} - -MONGO_INITIALIZER_WITH_PREREQUISITES(GlobalCursorManager, ("GlobalCursorIdCache")) +MONGO_INITIALIZER(GlobalCursorManager) (InitializerContext* context) { - globalCursorManager.reset(new CursorManager()); + globalCursorManager = std::make_unique<CursorManager>(); return Status::OK(); } -GlobalCursorIdCache::GlobalCursorIdCache() : _nextId(0), _secureRandom() {} - -GlobalCursorIdCache::~GlobalCursorIdCache() {} - -int64_t GlobalCursorIdCache::nextSeed() { - stdx::lock_guard<SimpleMutex> lk(_mutex); - if (!_secureRandom) - _secureRandom = SecureRandom::create(); - return _secureRandom->nextInt64(); -} - -bool GlobalCursorIdCache::killCursor(OperationContext* opCtx, CursorId id, bool checkAuth) { - // Figure out what the namespace of this cursor is. - NamespaceString nss; - { - auto pin = globalCursorManager->pinCursor(opCtx, id, CursorManager::kNoCheckSession); - if (!pin.isOK()) { - // Either the cursor doesn't exist, or it was killed during the last time it was being - // used, and was cleaned up after this call. Either way, we cannot kill it. - return false; - } - nss = pin.getValue().getCursor()->nss(); - } - invariant(nss.isValid()); - - boost::optional<AutoStatsTracker> statsTracker; - if (!nss.isCollectionlessCursorNamespace()) { - const boost::optional<int> dbProfilingLevel = boost::none; - statsTracker.emplace(opCtx, - nss, - Top::LockType::NotLocked, - AutoStatsTracker::LogMode::kUpdateTopAndCurop, - dbProfilingLevel); - } - - // Check if we are authorized to kill this cursor. - if (checkAuth) { - auto ccPin = globalCursorManager->pinCursor(opCtx, id, CursorManager::kNoCheckSession); - if (!ccPin.isOK()) { - audit::logKillCursorsAuthzCheck(opCtx->getClient(), nss, id, ccPin.getStatus().code()); - return false; - } - - AuthorizationSession* as = AuthorizationSession::get(opCtx->getClient()); - auto cursorOwner = ccPin.getValue().getCursor()->getAuthenticatedUsers(); - auto authStatus = as->checkAuthForKillCursors(nss, cursorOwner); - if (!authStatus.isOK()) { - audit::logKillCursorsAuthzCheck(opCtx->getClient(), nss, id, authStatus.code()); - return false; - } - } - - Status killStatus = globalCursorManager->killCursor(opCtx, id, checkAuth); - massert(28697, - killStatus.reason(), - killStatus.code() == ErrorCodes::OK || killStatus.code() == ErrorCodes::CursorNotFound); - return killStatus.isOK(); -} - -std::size_t GlobalCursorIdCache::timeoutCursors(OperationContext* opCtx, Date_t now) { - return globalCursorManager->timeoutCursors(opCtx, now); -} - -} // namespace - CursorManager* CursorManager::getGlobalCursorManager() { return globalCursorManager.get(); } @@ -193,32 +87,8 @@ std::pair<Status, int> CursorManager::killCursorsWithMatchingSessions( bySessionCursorKiller.getCursorsKilled()); } -std::size_t CursorManager::timeoutCursorsGlobal(OperationContext* opCtx, Date_t now) { - return globalCursorIdCache->timeoutCursors(opCtx, now); -} - -int CursorManager::killCursorGlobalIfAuthorized(OperationContext* opCtx, int n, const char* _ids) { - ConstDataCursor ids(_ids); - int numDeleted = 0; - for (int i = 0; i < n; i++) { - if (killCursorGlobalIfAuthorized(opCtx, ids.readAndAdvance<LittleEndian<int64_t>>())) - numDeleted++; - if (globalInShutdownDeprecated()) - break; - } - return numDeleted; -} -bool CursorManager::killCursorGlobalIfAuthorized(OperationContext* opCtx, CursorId id) { - return globalCursorIdCache->killCursor(opCtx, id, true); -} -bool CursorManager::killCursorGlobal(OperationContext* opCtx, CursorId id) { - return globalCursorIdCache->killCursor(opCtx, id, false); -} - -// -------------------------- - CursorManager::CursorManager() - : _random(stdx::make_unique<PseudoRandom>(globalCursorIdCache->nextSeed())), + : _random(stdx::make_unique<PseudoRandom>(SecureRandom::create()->nextInt64())), _cursorMap(stdx::make_unique<Partitioned<stdx::unordered_map<CursorId, ClientCursor*>>>()) {} CursorManager::~CursorManager() { diff --git a/src/mongo/db/cursor_manager.h b/src/mongo/db/cursor_manager.h index 2671d251ead..f05beb73dc4 100644 --- a/src/mongo/db/cursor_manager.h +++ b/src/mongo/db/cursor_manager.h @@ -73,21 +73,6 @@ public: */ static CursorManager* getGlobalCursorManager(); - static int killCursorGlobalIfAuthorized(OperationContext* opCtx, int n, const char* ids); - - static bool killCursorGlobalIfAuthorized(OperationContext* opCtx, CursorId id); - - static bool killCursorGlobal(OperationContext* opCtx, CursorId id); - - /** - * Deletes inactive cursors from the global cursor manager. Returns the number of cursors that - * were timed out. - * - * TODO SERVER-37454: This method can become non-static now that there are no per-collection - * cursor managers. - */ - static std::size_t timeoutCursorsGlobal(OperationContext* opCtx, Date_t now); - CursorManager(); /** diff --git a/src/mongo/db/run_op_kill_cursors.cpp b/src/mongo/db/run_op_kill_cursors.cpp new file mode 100644 index 00000000000..bbade52a554 --- /dev/null +++ b/src/mongo/db/run_op_kill_cursors.cpp @@ -0,0 +1,106 @@ +/** + * Copyright (C) 2018-present MongoDB, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the Server Side Public License, version 1, + * as published by MongoDB, Inc. + * + * 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 + * Server Side Public License for more details. + * + * You should have received a copy of the Server Side Public License + * along with this program. If not, see + * <http://www.mongodb.com/licensing/server-side-public-license>. + * + * 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 Server Side 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/run_op_kill_cursors.h" + +#include "mongo/base/data_cursor.h" +#include "mongo/db/audit.h" +#include "mongo/db/auth/authorization_session.h" +#include "mongo/db/cursor_id.h" +#include "mongo/db/cursor_manager.h" +#include "mongo/db/db_raii.h" +#include "mongo/util/exit.h" + +namespace mongo { + +namespace { + +bool killCursorIfAuthorized(OperationContext* opCtx, CursorId id) { + auto cursorManager = CursorManager::getGlobalCursorManager(); + + auto pin = cursorManager->pinCursor(opCtx, id, CursorManager::kNoCheckSession); + if (!pin.isOK()) { + // Either the cursor doesn't exist, or it was killed during the last time it was being + // used, and was cleaned up after this call. Either way, we cannot kill it. Write the + // attempt to the audit log before returning. + audit::logKillCursorsAuthzCheck(opCtx->getClient(), {}, id, pin.getStatus().code()); + return false; + } + auto nss = pin.getValue().getCursor()->nss(); + invariant(nss.isValid()); + + boost::optional<AutoStatsTracker> statsTracker; + if (!nss.isCollectionlessCursorNamespace()) { + const boost::optional<int> dbProfilingLevel = boost::none; + statsTracker.emplace(opCtx, + nss, + Top::LockType::NotLocked, + AutoStatsTracker::LogMode::kUpdateTopAndCurop, + dbProfilingLevel); + } + + AuthorizationSession* as = AuthorizationSession::get(opCtx->getClient()); + auto cursorOwner = pin.getValue().getCursor()->getAuthenticatedUsers(); + auto authStatus = as->checkAuthForKillCursors(nss, cursorOwner); + if (!authStatus.isOK()) { + audit::logKillCursorsAuthzCheck(opCtx->getClient(), nss, id, authStatus.code()); + return false; + } + + // Release the pin so that the cursor can be killed. + pin.getValue().release(); + + Status killStatus = cursorManager->killCursor(opCtx, id, true /* shouldAudit */); + massert(28697, + killStatus.reason(), + killStatus.code() == ErrorCodes::OK || killStatus.code() == ErrorCodes::CursorNotFound); + return killStatus.isOK(); +} + +} // namespace + +int runOpKillCursors(OperationContext* opCtx, size_t numCursorIds, const char* idsArray) { + ConstDataCursor idsDataCursor(idsArray); + int numKilled = 0; + for (size_t i = 0; i < numCursorIds; i++) { + CursorId nextCursorId = idsDataCursor.readAndAdvance<LittleEndian<int64_t>>(); + if (killCursorIfAuthorized(opCtx, nextCursorId)) { + ++numKilled; + } + + if (globalInShutdownDeprecated()) { + break; + } + } + return numKilled; +} + +} // namespace mongo diff --git a/src/mongo/db/run_op_kill_cursors.h b/src/mongo/db/run_op_kill_cursors.h new file mode 100644 index 00000000000..c4ffc1d742d --- /dev/null +++ b/src/mongo/db/run_op_kill_cursors.h @@ -0,0 +1,48 @@ +/** + * Copyright (C) 2018-present MongoDB, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the Server Side Public License, version 1, + * as published by MongoDB, Inc. + * + * 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 + * Server Side Public License for more details. + * + * You should have received a copy of the Server Side Public License + * along with this program. If not, see + * <http://www.mongodb.com/licensing/server-side-public-license>. + * + * 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 Server Side 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/operation_context.h" + +namespace mongo { + +/** + * Kills the cursors inside the array of cursor ids pointed to by 'idsArray', after checking that + * the logged in user is authorized to do so. 'numCursorIds' is the number of cursors in 'idsArray'. + * Also responsible for audit logging. + * + * Returns the number of cursors killed. + * + * The caller is responsible for validating that 'numCursorIds' > 0 and that the array is actually + * of size 'numCursorIds'. + */ +int runOpKillCursors(OperationContext* opCtx, size_t numCursorIds, const char* idsArray); + +} // namespace mongo diff --git a/src/mongo/db/service_entry_point_common.cpp b/src/mongo/db/service_entry_point_common.cpp index c4679785fe1..ee40ef54b4b 100644 --- a/src/mongo/db/service_entry_point_common.cpp +++ b/src/mongo/db/service_entry_point_common.cpp @@ -66,6 +66,7 @@ #include "mongo/db/repl/repl_client_info.h" #include "mongo/db/repl/replication_coordinator.h" #include "mongo/db/repl/speculative_majority_read_info.h" +#include "mongo/db/run_op_kill_cursors.h" #include "mongo/db/s/operation_sharding_state.h" #include "mongo/db/s/sharded_connection_info.h" #include "mongo/db/s/sharding_state.h" @@ -996,7 +997,7 @@ void receivedKillCursors(OperationContext* opCtx, const Message& m) { const char* cursorArray = dbmessage.getArray(n); - int found = CursorManager::killCursorGlobalIfAuthorized(opCtx, n, cursorArray); + int found = runOpKillCursors(opCtx, static_cast<size_t>(n), cursorArray); if (shouldLog(logger::LogSeverity::Debug(1)) || found != n) { LOG(found == n ? 1 : 0) << "killcursors: found " << found << " of " << n; @@ -1099,12 +1100,15 @@ DbResponse receivedGetMore(OperationContext* opCtx, // Make sure that killCursorGlobal does not throw an exception if it is interrupted. UninterruptibleLockGuard noInterrupt(opCtx->lockState()); - // If a cursor with id 'cursorid' was authorized, it may have been advanced - // before an exception terminated processGetMore. Erase the ClientCursor - // because it may now be out of sync with the client's iteration state. - // SERVER-7952 - // TODO Temporary code, see SERVER-4563 for a cleanup overview. - CursorManager::killCursorGlobal(opCtx, cursorid); + // If an error was thrown prior to auth checks, then the cursor should remain alive in + // order to prevent an unauthorized user from resulting in the death of a cursor. In + // other error cases, the cursor is dead and should be cleaned up. + // + // If killing the cursor fails, ignore the error and don't try again. The cursor should + // be reaped by the client cursor timeout thread. + CursorManager::getGlobalCursorManager() + ->killCursor(opCtx, cursorid, false /* shouldAudit */) + .ignore(); } BSONObjBuilder err; |