diff options
author | Neil Shweky <neilshweky@gmail.com> | 2021-10-27 13:52:45 +0000 |
---|---|---|
committer | Evergreen Agent <no-reply@evergreen.mongodb.com> | 2021-10-27 14:22:28 +0000 |
commit | 106b96548c5214a8e246a1cf6ac005a3985c16d4 (patch) | |
tree | 9d60577e227bd4c34a6d7d156ecc5a61c63ef52e /src | |
parent | dd41a99e2c177d5bd3a38229d85458117480c5d9 (diff) | |
download | mongo-106b96548c5214a8e246a1cf6ac005a3985c16d4.tar.gz |
SERVER-58352: Remove ClusterCursorManager::getNamespaceForCursorId(), and related simplifications
Diffstat (limited to 'src')
-rw-r--r-- | src/mongo/db/SConscript | 1 | ||||
-rw-r--r-- | src/mongo/db/allocate_cursor_id.cpp | 65 | ||||
-rw-r--r-- | src/mongo/db/allocate_cursor_id.h | 48 | ||||
-rw-r--r-- | src/mongo/db/cursor_manager.cpp | 43 | ||||
-rw-r--r-- | src/mongo/s/commands/cluster_killcursors_cmd.cpp | 5 | ||||
-rw-r--r-- | src/mongo/s/commands/cluster_killoperations_cmd.cpp | 41 | ||||
-rw-r--r-- | src/mongo/s/commands/cluster_map_reduce_agg.cpp | 2 | ||||
-rw-r--r-- | src/mongo/s/query/cluster_cursor_manager.cpp | 373 | ||||
-rw-r--r-- | src/mongo/s/query/cluster_cursor_manager.h | 91 | ||||
-rw-r--r-- | src/mongo/s/query/cluster_cursor_manager_test.cpp | 362 | ||||
-rw-r--r-- | src/mongo/s/query/cluster_find.cpp | 2 |
11 files changed, 352 insertions, 681 deletions
diff --git a/src/mongo/db/SConscript b/src/mongo/db/SConscript index 2864d3b020f..188a9209ef6 100644 --- a/src/mongo/db/SConscript +++ b/src/mongo/db/SConscript @@ -1964,6 +1964,7 @@ env.Library( env.Library( target='generic_cursor', source=[ + 'allocate_cursor_id.cpp', 'generic_cursor.idl', ], LIBDEPS=[ diff --git a/src/mongo/db/allocate_cursor_id.cpp b/src/mongo/db/allocate_cursor_id.cpp new file mode 100644 index 00000000000..e9b8371aadf --- /dev/null +++ b/src/mongo/db/allocate_cursor_id.cpp @@ -0,0 +1,65 @@ +/** + * Copyright (C) 2021-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/db/allocate_cursor_id.h" + +#include "mongo/util/assert_util.h" + +namespace mongo::generic_cursor { + +CursorId allocateCursorId(const std::function<bool(CursorId)>& pred, PseudoRandom& random) { + for (int i = 0; i < 10000; i++) { + CursorId id = random.nextInt64(); + + // A cursor id of zero is reserved to indicate that the cursor has been closed. If the + // random number generator gives us zero, then try again. + if (id == 0) { + continue; + } + + // Avoid negative cursor ids by taking the absolute value. If the cursor id is the minimum + // representable negative number, then just generate another random id. + if (id == std::numeric_limits<CursorId>::min()) { + continue; + } + id = std::abs(id); + + if (pred(id)) { + // The cursor id is not already in use, so return it. + return id; + } + + // The cursor id is already in use. Generate another random id. + } + + // We failed to generate a unique cursor id. + fassertFailed(17360); +} + +} // namespace mongo::generic_cursor diff --git a/src/mongo/db/allocate_cursor_id.h b/src/mongo/db/allocate_cursor_id.h new file mode 100644 index 00000000000..f7a204c5816 --- /dev/null +++ b/src/mongo/db/allocate_cursor_id.h @@ -0,0 +1,48 @@ +/** + * Copyright (C) 2021-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 <functional> + +#include "mongo/db/cursor_id.h" +#include "mongo/platform/random.h" + +namespace mongo::generic_cursor { + +/** + * Allocates a positive Cursor Id that satisfies 'pred', which checks that the CursorId is not + * already in use. + * + * The caller of this function is responsible for synchronization between the check of whether a + * cursor is already allocated in 'pred' and the creation of new cursors. + */ +CursorId allocateCursorId(const std::function<bool(CursorId)>& pred, PseudoRandom& random); + +} // namespace mongo::generic_cursor diff --git a/src/mongo/db/cursor_manager.cpp b/src/mongo/db/cursor_manager.cpp index c1728d78586..f9dbab4ad1a 100644 --- a/src/mongo/db/cursor_manager.cpp +++ b/src/mongo/db/cursor_manager.cpp @@ -37,6 +37,7 @@ #include "mongo/base/data_cursor.h" #include "mongo/base/init.h" +#include "mongo/db/allocate_cursor_id.h" #include "mongo/db/audit.h" #include "mongo/db/auth/authorization_checks.h" #include "mongo/db/auth/authorization_session.h" @@ -358,38 +359,6 @@ size_t CursorManager::numCursors() const { return _cursorMap->size(); } -CursorId CursorManager::allocateCursorId_inlock() { - for (int i = 0; i < 10000; i++) { - CursorId id = _random->nextInt64(); - - // A cursor id of zero is reserved to indicate that the cursor has been closed. If the - // random number generator gives us zero, then try again. - if (id == 0) { - continue; - } - - // Avoid negative cursor ids by taking the absolute value. If the cursor id is the minimum - // representable negative number, then just generate another random id. - if (id == std::numeric_limits<CursorId>::min()) { - continue; - } - id = std::abs(id); - - auto partition = _cursorMap->lockOnePartition(id); - if (partition->count(id) == 0) { - // The cursor id is not already in use, so return it. Even though we drop the lock on - // the '_cursorMap' partition, another thread cannot register a cursor with the same id - // because we still hold '_registrationLock'. - return id; - } - - // The cursor id is already in use. Generate another random id. - } - - // We failed to generate a unique cursor id. - fassertFailed(17360); -} - ClientCursorPin CursorManager::registerCursor(OperationContext* opCtx, ClientCursorParams&& cursorParams) { // Avoid computing the current time within the critical section. @@ -403,7 +372,15 @@ ClientCursorPin CursorManager::registerCursor(OperationContext* opCtx, // Note we must hold the registration lock from now until insertion into '_cursorMap' to ensure // we don't insert two cursors with the same cursor id. stdx::lock_guard<SimpleMutex> lock(_registrationLock); - CursorId cursorId = allocateCursorId_inlock(); + CursorId cursorId = generic_cursor::allocateCursorId( + [&](CursorId cursorId) -> bool { + // Even though we drop the lock on the '_cursorMap' partition, another thread cannot + // register a cursor with the same id because we still hold '_registrationLock'. + auto partition = _cursorMap->lockOnePartition(cursorId); + return partition->count(cursorId) == 0; + }, + *_random); + std::unique_ptr<ClientCursor, ClientCursor::Deleter> clientCursor( new ClientCursor(std::move(cursorParams), cursorId, opCtx, now)); diff --git a/src/mongo/s/commands/cluster_killcursors_cmd.cpp b/src/mongo/s/commands/cluster_killcursors_cmd.cpp index c05ac76d7fb..716107f43d1 100644 --- a/src/mongo/s/commands/cluster_killcursors_cmd.cpp +++ b/src/mongo/s/commands/cluster_killcursors_cmd.cpp @@ -50,12 +50,13 @@ struct ClusterKillCursorsCmd { }; return Grid::get(opCtx)->getCursorManager()->checkAuthForKillCursors( - opCtx, nss, cursorId, authChecker); + opCtx, cursorId, authChecker); } + static Status doKillCursor(OperationContext* opCtx, const NamespaceString& nss, CursorId cursorId) { - return Grid::get(opCtx)->getCursorManager()->killCursor(opCtx, nss, cursorId); + return Grid::get(opCtx)->getCursorManager()->killCursor(opCtx, cursorId); } }; KillCursorsCmdBase<ClusterKillCursorsCmd> clusterKillCursorsCmd; diff --git a/src/mongo/s/commands/cluster_killoperations_cmd.cpp b/src/mongo/s/commands/cluster_killoperations_cmd.cpp index d636fc58adc..0dfc1676438 100644 --- a/src/mongo/s/commands/cluster_killoperations_cmd.cpp +++ b/src/mongo/s/commands/cluster_killoperations_cmd.cpp @@ -32,6 +32,7 @@ #include "mongo/db/commands/killoperations_common.h" #include "mongo/s/grid.h" #include "mongo/s/query/cluster_cursor_manager.h" +#include "mongo/stdx/unordered_set.h" namespace mongo { @@ -39,24 +40,28 @@ class ClusterKillOperationsCmd : public KillOperationsCmdBase<ClusterKillOperati public: static void killCursors(OperationContext* opCtx, const std::vector<OperationKey>& opKeys) { auto clusterCursorManager = Grid::get(opCtx)->getCursorManager(); - for (auto& cursorId : clusterCursorManager->getCursorsForOpKeys(opKeys)) { - LOGV2(4664805, "Attempting to kill cursor", "cursorId"_attr = cursorId); - auto cursorNss = clusterCursorManager->getNamespaceForCursorId(cursorId); - if (!cursorNss) { - // The cursor must have already been killed. - continue; - } - - auto status = clusterCursorManager->killCursor(opCtx, cursorNss.get(), cursorId); - - if (!status.isOK()) { - LOGV2(4664806, - "Failed to kill the cursor ", - "error"_attr = redact(status.toString())); - } else { - LOGV2(4664807, "Killed cursor", "cursorId"_attr = cursorId); - } - } + stdx::unordered_set<UUID, UUID::Hash> opKeySet(opKeys.begin(), opKeys.end()); + + std::size_t numCursorsKilled = clusterCursorManager->killCursorsSatisfying( + opCtx, + [&opKeySet](CursorId cursorId, const ClusterCursorManager::CursorEntry& entry) -> bool { + if (!entry.getOperationKey()) { + return false; + } + + bool hasOpKey = opKeySet.find(*entry.getOperationKey()) != opKeySet.end(); + if (hasOpKey) { + LOGV2(4664805, + "Attempting to kill cursor", + "cursorId"_attr = cursorId, + "opKey"_attr = entry.getOperationKey()); + } + + return hasOpKey; + }); + + LOGV2( + 4664806, "_killOperations command killed cursors", "numKilled"_attr = numCursorsKilled); } } ClusterKillOperationsCmd; diff --git a/src/mongo/s/commands/cluster_map_reduce_agg.cpp b/src/mongo/s/commands/cluster_map_reduce_agg.cpp index 770e490648f..d478ef08d48 100644 --- a/src/mongo/s/commands/cluster_map_reduce_agg.cpp +++ b/src/mongo/s/commands/cluster_map_reduce_agg.cpp @@ -253,7 +253,7 @@ bool runAggregationMapReduce(OperationContext* opCtx, // response. if (aggResults["cursor"]["id"].Long() != 0) { uassertStatusOK(Grid::get(opCtx)->getCursorManager()->killCursor( - opCtx, parsedMr.getNamespace(), aggResults["cursor"]["id"].Long())); + opCtx, aggResults["cursor"]["id"].Long())); uasserted(31301, "MapReduce inline results are greater than the allowed 16MB limit"); } diff --git a/src/mongo/s/query/cluster_cursor_manager.cpp b/src/mongo/s/query/cluster_cursor_manager.cpp index 4087d33d74f..1209d709e22 100644 --- a/src/mongo/s/query/cluster_cursor_manager.cpp +++ b/src/mongo/s/query/cluster_cursor_manager.cpp @@ -36,6 +36,7 @@ #include <set> +#include "mongo/db/allocate_cursor_id.h" #include "mongo/db/curop.h" #include "mongo/db/kill_sessions_common.h" #include "mongo/db/logical_session_cache.h" @@ -49,32 +50,17 @@ namespace mongo { namespace { // -// Helpers to construct a user-friendly error Status from a (nss, cursorId) pair. +// Helpers to construct a user-friendly error Status from a cursorId. // -Status cursorNotFoundStatus(const NamespaceString& nss, CursorId cursorId) { +Status cursorNotFoundStatus(CursorId cursorId) { return {ErrorCodes::CursorNotFound, - str::stream() << "Cursor not found (namespace: '" << nss.ns() << "', id: " << cursorId - << ")."}; + str::stream() << "Cursor not found (id: " << cursorId << ")."}; } -Status cursorInUseStatus(const NamespaceString& nss, CursorId cursorId) { +Status cursorInUseStatus(CursorId cursorId) { return {ErrorCodes::CursorInUse, - str::stream() << "Cursor already in use (namespace: '" << nss.ns() - << "', id: " << cursorId << ")."}; -} - -// -// CursorId is a 64-bit type, made up of a 32-bit prefix and a 32-bit suffix. The below helpers -// convert between a CursorId and its prefix/suffix. -// - -CursorId createCursorId(uint32_t prefix, uint32_t suffix) { - return (static_cast<uint64_t>(prefix) << 32) | suffix; -} - -uint32_t extractPrefixFromCursorId(CursorId cursorId) { - return static_cast<uint64_t>(cursorId) >> 32; + str::stream() << "Cursor already in use (id: " << cursorId << ")."}; } } // namespace @@ -118,7 +104,7 @@ void ClusterCursorManager::PinnedCursor::returnCursor(CursorState cursorState) { invariant(_cursor); // Note that unpinning a cursor transfers ownership of the underlying ClusterClientCursor object // back to the manager. - _manager->checkInCursor(std::move(_cursor), _nss, _cursorId, cursorState); + _manager->checkInCursor(std::move(_cursor), _cursorId, cursorState); *this = PinnedCursor(); } @@ -157,8 +143,7 @@ ClusterCursorManager::ClusterCursorManager(ClockSource* clockSource) } ClusterCursorManager::~ClusterCursorManager() { - invariant(_cursorIdPrefixToNamespaceMap.empty()); - invariant(_namespaceToContainerMap.empty()); + invariant(_cursorEntryMap.empty()); } void ClusterCursorManager::shutdown(OperationContext* opCtx) { @@ -191,58 +176,26 @@ StatusWith<CursorId> ClusterCursorManager::registerCursor( invariant(cursor); cursor->setLeftoverMaxTimeMicros(opCtx->getRemainingMaxTimeMicros()); - // Find the CursorEntryContainer for this namespace. If none exists, create one. - auto nsToContainerIt = _namespaceToContainerMap.find(nss); - if (nsToContainerIt == _namespaceToContainerMap.end()) { - uint32_t containerPrefix = 0; - do { - // The server has always generated positive values for CursorId (which is a signed - // type), so we use std::abs() here on the prefix for consistency with this historical - // behavior. If the random number generated is INT_MIN, calling std::abs on it is - // undefined behavior on 2's complement systems so we need to generate a new number. - int32_t randomNumber = 0; - do { - randomNumber = _pseudoRandom.nextInt32(); - } while (randomNumber == std::numeric_limits<int32_t>::min()); - containerPrefix = static_cast<uint32_t>(std::abs(randomNumber)); - } while (_cursorIdPrefixToNamespaceMap.count(containerPrefix) > 0); - _cursorIdPrefixToNamespaceMap[containerPrefix] = nss; - - auto emplaceResult = - _namespaceToContainerMap.emplace(nss, CursorEntryContainer(containerPrefix)); - invariant(emplaceResult.second); - invariant(_namespaceToContainerMap.size() == _cursorIdPrefixToNamespaceMap.size()); - - nsToContainerIt = emplaceResult.first; - } else { - invariant(!nsToContainerIt->second.entryMap.empty()); // If exists, shouldn't be empty. - } - CursorEntryContainer& container = nsToContainerIt->second; - - // Generate a CursorId (which can't be the invalid value zero). - CursorEntryMap& entryMap = container.entryMap; - CursorId cursorId = 0; - do { - const uint32_t cursorSuffix = static_cast<uint32_t>(_pseudoRandom.nextInt32()); - cursorId = createCursorId(container.containerPrefix, cursorSuffix); - } while (cursorId == 0 || entryMap.count(cursorId) > 0); + auto cursorId = generic_cursor::allocateCursorId( + [&](CursorId cursorId) -> bool { return _cursorEntryMap.count(cursorId) == 0; }, + _pseudoRandom); // Create a new CursorEntry and register it in the CursorEntryContainer's map. - auto emplaceResult = entryMap.emplace(cursorId, - CursorEntry(std::move(cursor), - cursorType, - cursorLifetime, - now, - authenticatedUsers, - opCtx->getClient()->getUUID(), - opCtx->getOperationKey())); + auto emplaceResult = _cursorEntryMap.emplace(cursorId, + CursorEntry(std::move(cursor), + cursorType, + cursorLifetime, + now, + authenticatedUsers, + opCtx->getClient()->getUUID(), + opCtx->getOperationKey(), + nss)); invariant(emplaceResult.second); return cursorId; } StatusWith<ClusterCursorManager::PinnedCursor> ClusterCursorManager::checkOutCursor( - const NamespaceString& nss, CursorId cursorId, OperationContext* opCtx, AuthzCheckFn authChecker, @@ -255,9 +208,9 @@ StatusWith<ClusterCursorManager::PinnedCursor> ClusterCursorManager::checkOutCur "Cannot check out cursor as we are in the process of shutting down"); } - CursorEntry* entry = _getEntry(lk, nss, cursorId); + CursorEntry* entry = _getEntry(lk, cursorId); if (!entry) { - return cursorNotFoundStatus(nss, cursorId); + return cursorNotFoundStatus(cursorId); } // Check if the user is coauthorized to access this cursor. @@ -276,7 +229,7 @@ StatusWith<ClusterCursorManager::PinnedCursor> ClusterCursorManager::checkOutCur } if (entry->getOperationUsingCursor()) { - return cursorInUseStatus(nss, cursorId); + return cursorInUseStatus(cursorId); } auto cursorGuard = entry->releaseCursor(opCtx); @@ -294,11 +247,10 @@ StatusWith<ClusterCursorManager::PinnedCursor> ClusterCursorManager::checkOutCur CurOp::get(opCtx)->debug().queryHash = cursorGuard->getQueryHash(); - return PinnedCursor(this, std::move(cursorGuard), nss, cursorId); + return PinnedCursor(this, std::move(cursorGuard), entry->getNamespace(), cursorId); } void ClusterCursorManager::checkInCursor(std::unique_ptr<ClusterClientCursor> cursor, - const NamespaceString& nss, CursorId cursorId, CursorState cursorState) { invariant(cursor); @@ -313,7 +265,7 @@ void ClusterCursorManager::checkInCursor(std::unique_ptr<ClusterClientCursor> cu stdx::unique_lock<Latch> lk(_mutex); - CursorEntry* entry = _getEntry(lk, nss, cursorId); + CursorEntry* entry = _getEntry(lk, cursorId); invariant(entry); // killPending will be true if killCursor() was called while the cursor was in use. @@ -329,18 +281,17 @@ void ClusterCursorManager::checkInCursor(std::unique_ptr<ClusterClientCursor> cu // After detaching the cursor, the entry will be destroyed. entry = nullptr; - detachAndKillCursor(std::move(lk), opCtx, nss, cursorId); + detachAndKillCursor(std::move(lk), opCtx, cursorId); } Status ClusterCursorManager::checkAuthForKillCursors(OperationContext* opCtx, - const NamespaceString& nss, CursorId cursorId, AuthzCheckFn authChecker) { stdx::lock_guard<Latch> lk(_mutex); - auto entry = _getEntry(lk, nss, cursorId); + auto entry = _getEntry(lk, cursorId); if (!entry) { - return cursorNotFoundStatus(nss, cursorId); + return cursorNotFoundStatus(cursorId); } // Note that getAuthenticatedUsers() is thread-safe, so it's okay to call even if there's @@ -359,16 +310,14 @@ void ClusterCursorManager::killOperationUsingCursor(WithLock, CursorEntry* entry // operation is done. } -Status ClusterCursorManager::killCursor(OperationContext* opCtx, - const NamespaceString& nss, - CursorId cursorId) { +Status ClusterCursorManager::killCursor(OperationContext* opCtx, CursorId cursorId) { invariant(opCtx); stdx::unique_lock<Latch> lk(_mutex); - CursorEntry* entry = _getEntry(lk, nss, cursorId); + CursorEntry* entry = _getEntry(lk, cursorId); if (!entry) { - return cursorNotFoundStatus(nss, cursorId); + return cursorNotFoundStatus(cursorId); } // Interrupt any operation currently using the cursor, unless if it's the current operation. @@ -381,7 +330,7 @@ Status ClusterCursorManager::killCursor(OperationContext* opCtx, } // No one is using the cursor, so we destroy it. - detachAndKillCursor(std::move(lk), opCtx, nss, cursorId); + detachAndKillCursor(std::move(lk), opCtx, cursorId); // We no longer hold the lock here. @@ -390,9 +339,8 @@ Status ClusterCursorManager::killCursor(OperationContext* opCtx, void ClusterCursorManager::detachAndKillCursor(stdx::unique_lock<Latch> lk, OperationContext* opCtx, - const NamespaceString& nss, CursorId cursorId) { - auto detachedCursorGuard = _detachCursor(lk, opCtx, nss, cursorId); + auto detachedCursorGuard = _detachCursor(lk, opCtx, cursorId); invariant(detachedCursorGuard.getStatus()); // Deletion of the cursor can happen out of the lock. @@ -434,39 +382,29 @@ std::size_t ClusterCursorManager::killCursorsSatisfying( std::size_t nKilled = 0; std::vector<ClusterClientCursorGuard> cursorsToDestroy; - auto nsContainerIt = _namespaceToContainerMap.begin(); - while (nsContainerIt != _namespaceToContainerMap.end()) { - auto&& entryMap = nsContainerIt->second.entryMap; - auto cursorIdEntryIt = entryMap.begin(); - while (cursorIdEntryIt != entryMap.end()) { - auto cursorId = cursorIdEntryIt->first; - auto& entry = cursorIdEntryIt->second; - - if (!pred(cursorId, entry)) { - ++cursorIdEntryIt; - continue; - } - - ++nKilled; - - if (entry.getOperationUsingCursor()) { - // Mark the OperationContext using the cursor as killed, and move on. - killOperationUsingCursor(lk, &entry); - ++cursorIdEntryIt; - continue; - } + auto cursorIdEntryIt = _cursorEntryMap.begin(); + while (cursorIdEntryIt != _cursorEntryMap.end()) { + auto cursorId = cursorIdEntryIt->first; + auto& entry = cursorIdEntryIt->second; + + if (!pred(cursorId, entry)) { + ++cursorIdEntryIt; + continue; + } - cursorsToDestroy.push_back(entry.releaseCursor(opCtx)); + ++nKilled; - // Destroy the entry and set the iterator to the next element. - entryMap.erase(cursorIdEntryIt++); + if (entry.getOperationUsingCursor()) { + // Mark the OperationContext using the cursor as killed, and move on. + killOperationUsingCursor(lk, &entry); + ++cursorIdEntryIt; + continue; } - if (entryMap.empty()) { - nsContainerIt = eraseContainer(nsContainerIt); - } else { - ++nsContainerIt; - } + cursorsToDestroy.push_back(entry.releaseCursor(opCtx)); + + // Destroy the entry and set the iterator to the next element. + _cursorEntryMap.erase(cursorIdEntryIt++); } // Ensure cursors are killed outside the lock, as killing may require waiting for callbacks to @@ -486,28 +424,24 @@ ClusterCursorManager::Stats ClusterCursorManager::stats() const { Stats stats; - for (auto& nsContainerPair : _namespaceToContainerMap) { - for (auto& cursorIdEntryPair : nsContainerPair.second.entryMap) { - const CursorEntry& entry = cursorIdEntryPair.second; - - if (entry.isKillPending()) { - // Killed cursors do not count towards the number of pinned cursors or the number of - // open cursors. - continue; - } + for (auto&& [cursorId, entry] : _cursorEntryMap) { + if (entry.isKillPending()) { + // Killed cursors do not count towards the number of pinned cursors or the number of + // open cursors. + continue; + } - if (entry.getOperationUsingCursor()) { - ++stats.cursorsPinned; - } + if (entry.getOperationUsingCursor()) { + ++stats.cursorsPinned; + } - switch (entry.getCursorType()) { - case CursorType::SingleTarget: - ++stats.cursorsSingleTarget; - break; - case CursorType::MultiTarget: - ++stats.cursorsMultiTarget; - break; - } + switch (entry.getCursorType()) { + case CursorType::SingleTarget: + ++stats.cursorsSingleTarget; + break; + case CursorType::MultiTarget: + ++stats.cursorsMultiTarget; + break; } } @@ -517,19 +451,15 @@ ClusterCursorManager::Stats ClusterCursorManager::stats() const { void ClusterCursorManager::appendActiveSessions(LogicalSessionIdSet* lsids) const { stdx::lock_guard<Latch> lk(_mutex); - for (const auto& nsContainerPair : _namespaceToContainerMap) { - for (const auto& cursorIdEntryPair : nsContainerPair.second.entryMap) { - const CursorEntry& entry = cursorIdEntryPair.second; - - if (entry.isKillPending()) { - // Don't include sessions for killed cursors. - continue; - } + for (auto&& [cursorId, entry] : _cursorEntryMap) { + if (entry.isKillPending()) { + // Don't include sessions for killed cursors. + continue; + } - auto lsid = entry.getLsid(); - if (lsid) { - lsids->insert(*lsid); - } + auto lsid = entry.getLsid(); + if (lsid) { + lsids->insert(*lsid); } } } @@ -560,40 +490,30 @@ std::vector<GenericCursor> ClusterCursorManager::getIdleCursors( AuthorizationSession* ctxAuth = AuthorizationSession::get(opCtx->getClient()); - for (const auto& nsContainerPair : _namespaceToContainerMap) { - for (const auto& cursorIdEntryPair : nsContainerPair.second.entryMap) { - - const CursorEntry& entry = cursorIdEntryPair.second; - // If auth is enabled, and userMode is allUsers, check if the current user has - // permission to see this cursor. - if (ctxAuth->getAuthorizationManager().isAuthEnabled() && - userMode == MongoProcessInterface::CurrentOpUserMode::kExcludeOthers && - !ctxAuth->isCoauthorizedWith(entry.getAuthenticatedUsers())) { - continue; - } - if (entry.isKillPending() || entry.getOperationUsingCursor()) { - // Don't include sessions for killed or pinned cursors. - continue; - } - - cursors.emplace_back( - entry.cursorToGenericCursor(cursorIdEntryPair.first, nsContainerPair.first)); + for (auto&& [cursorId, entry] : _cursorEntryMap) { + // If auth is enabled, and userMode is allUsers, check if the current user has + // permission to see this cursor. + if (ctxAuth->getAuthorizationManager().isAuthEnabled() && + userMode == MongoProcessInterface::CurrentOpUserMode::kExcludeOthers && + !ctxAuth->isCoauthorizedWith(entry.getAuthenticatedUsers())) { + continue; + } + if (entry.isKillPending() || entry.getOperationUsingCursor()) { + // Don't include sessions for killed or pinned cursors. + continue; } + + cursors.emplace_back(entry.cursorToGenericCursor(cursorId, entry.getNamespace())); } + return cursors; } std::pair<Status, int> ClusterCursorManager::killCursorsWithMatchingSessions( OperationContext* opCtx, const SessionKiller::Matcher& matcher) { auto eraser = [&](ClusterCursorManager& mgr, CursorId id) { - auto cursorNss = getNamespaceForCursorId(id); - if (!cursorNss) { - // The cursor manager couldn't find a namespace associated with 'id'. This means the - // cursor must have already been killed, so we have no more work to do. - return; - } - uassertStatusOK(mgr.killCursor(opCtx, *cursorNss, id)); + uassertStatusOK(mgr.killCursor(opCtx, id)); LOGV2(22838, "Killing cursor as part of killing session(s)", "cursorId"_attr = id); }; @@ -609,131 +529,48 @@ stdx::unordered_set<CursorId> ClusterCursorManager::getCursorsForSession( stdx::unordered_set<CursorId> cursorIds; - for (auto&& nsContainerPair : _namespaceToContainerMap) { - for (auto&& [cursorId, entry] : nsContainerPair.second.entryMap) { - if (entry.isKillPending()) { - // Don't include sessions for killed cursors. - continue; - } - - auto cursorLsid = entry.getLsid(); - if (lsid == cursorLsid) { - cursorIds.insert(cursorId); - } + for (auto&& [cursorId, entry] : _cursorEntryMap) { + if (entry.isKillPending()) { + // Don't include sessions for killed cursors. + continue; } - } - - return cursorIds; -} - -stdx::unordered_set<CursorId> ClusterCursorManager::getCursorsForOpKeys( - std::vector<OperationKey> opKeys) const { - stdx::lock_guard<Latch> lk(_mutex); - - stdx::unordered_set<CursorId> cursorIds; - // While we could maintain a cached mapping of OperationKey to CursorID to increase performance, - // this approach was chosen given that 1) mongos will not have as many open cursors as a shard - // and 2) mongos performance has historically not been a bottleneck. - for (auto&& opKey : opKeys) { - for (auto&& nsContainerPair : _namespaceToContainerMap) { - for (auto&& [cursorId, entry] : nsContainerPair.second.entryMap) { - if (entry.isKillPending()) { - // Don't include any killed cursors. - continue; - } - - if (opKey == entry.getOperationKey()) { - cursorIds.insert(cursorId); - } - } + auto cursorLsid = entry.getLsid(); + if (lsid == cursorLsid) { + cursorIds.insert(cursorId); } } return cursorIds; } -boost::optional<NamespaceString> ClusterCursorManager::getNamespaceForCursorId( - CursorId cursorId) const { - stdx::lock_guard<Latch> lk(_mutex); - - const auto it = _cursorIdPrefixToNamespaceMap.find(extractPrefixFromCursorId(cursorId)); - if (it == _cursorIdPrefixToNamespaceMap.end()) { - return boost::none; - } - return it->second; -} - -auto ClusterCursorManager::_getEntry(WithLock, NamespaceString const& nss, CursorId cursorId) - -> CursorEntry* { - - auto nsToContainerIt = _namespaceToContainerMap.find(nss); - if (nsToContainerIt == _namespaceToContainerMap.end()) { - return nullptr; - } - CursorEntryMap& entryMap = nsToContainerIt->second.entryMap; - auto entryMapIt = entryMap.find(cursorId); - if (entryMapIt == entryMap.end()) { +auto ClusterCursorManager::_getEntry(WithLock, CursorId cursorId) -> CursorEntry* { + auto entryMapIt = _cursorEntryMap.find(cursorId); + if (entryMapIt == _cursorEntryMap.end()) { return nullptr; } return &entryMapIt->second; } -auto ClusterCursorManager::eraseContainer(NssToCursorContainerMap::iterator it) - -> NssToCursorContainerMap::iterator { - auto&& container = it->second; - auto&& entryMap = container.entryMap; - invariant(entryMap.empty()); - - // This was the last cursor remaining in the given namespace. Erase all state associated - // with this namespace. - size_t numDeleted = _cursorIdPrefixToNamespaceMap.erase(container.containerPrefix); - if (numDeleted != 1) { - LOGV2_ERROR( - 4786901, - "Error attempting to erase CursorEntryContainer for nss {nss} and containerPrefix" - "{prefix}. Could not find containerPrefix in map from cursor ID prefix to nss. " - "Expected 'numDeleted' to be 1, but got {actualNumDeleted}", - "Error attempting to erase CursorEntryContainer. Could not find containerPrefix in map " - "from cursor id prefix to namespace string.", - "nss"_attr = it->first, - "prefix"_attr = container.containerPrefix, - "actualNumDeleted"_attr = numDeleted); - MONGO_UNREACHABLE; - } - const auto nssRemoved = it->first; - _namespaceToContainerMap.erase(it++); - - invariant(_namespaceToContainerMap.size() == _cursorIdPrefixToNamespaceMap.size()); - return it; -} - StatusWith<ClusterClientCursorGuard> ClusterCursorManager::_detachCursor(WithLock lk, OperationContext* opCtx, - const NamespaceString& nss, CursorId cursorId) { - CursorEntry* entry = _getEntry(lk, nss, cursorId); + CursorEntry* entry = _getEntry(lk, cursorId); if (!entry) { - return cursorNotFoundStatus(nss, cursorId); + return cursorNotFoundStatus(cursorId); } if (entry->getOperationUsingCursor()) { - return cursorInUseStatus(nss, cursorId); + return cursorInUseStatus(cursorId); } // Transfer ownership away from the entry. ClusterClientCursorGuard cursor = entry->releaseCursor(opCtx); // Destroy the entry. - auto nsToContainerIt = _namespaceToContainerMap.find(nss); - invariant(nsToContainerIt != _namespaceToContainerMap.end()); - CursorEntryMap& entryMap = nsToContainerIt->second.entryMap; - size_t eraseResult = entryMap.erase(cursorId); + size_t eraseResult = _cursorEntryMap.erase(cursorId); invariant(1 == eraseResult); - if (entryMap.empty()) { - eraseContainer(nsToContainerIt); - } return std::move(cursor); } diff --git a/src/mongo/s/query/cluster_cursor_manager.h b/src/mongo/s/query/cluster_cursor_manager.h index 7df393e3403..be10b0d60bd 100644 --- a/src/mongo/s/query/cluster_cursor_manager.h +++ b/src/mongo/s/query/cluster_cursor_manager.h @@ -228,13 +228,15 @@ public: Date_t lastActive, UserNameIterator authenticatedUsersIter, UUID clientUUID, - boost::optional<OperationKey> opKey) + boost::optional<OperationKey> opKey, + NamespaceString nss) : _cursor(std::move(cursor)), _cursorType(cursorType), _cursorLifetime(cursorLifetime), _lastActive(lastActive), _lsid(_cursor->getLsid()), _opKey(std::move(opKey)), + _nss(std::move(nss)), _originatingClient(std::move(clientUUID)), _authenticatedUsers( userNameIteratorToContainer<std::vector<UserName>>(authenticatedUsersIter)) { @@ -279,6 +281,10 @@ public: return _opKey; } + const NamespaceString& getNamespace() const { + return _nss; + } + /** * Returns a cursor guard holding the cursor owned by this CursorEntry for an operation to * use. Only one operation may use the cursor at a time, so callers should check that @@ -344,6 +350,8 @@ public: */ boost::optional<OperationKey> _opKey; + NamespaceString _nss; + /** * Current operation using the cursor. Non-null if the cursor is checked out. */ @@ -429,8 +437,7 @@ public: * Does not block. */ enum AuthCheck { kCheckSession = true, kNoCheckSession = false }; - StatusWith<PinnedCursor> checkOutCursor(const NamespaceString& nss, - CursorId cursorId, + StatusWith<PinnedCursor> checkOutCursor(CursorId cursorId, OperationContext* opCtx, AuthzCheckFn authChecker, AuthCheck checkSessionAuth = kCheckSession); @@ -440,7 +447,6 @@ public: * list of users authorized to use the cursor. Will propagate the return value of authChecker. */ Status checkAuthForKillCursors(OperationContext* opCtx, - const NamespaceString& nss, CursorId cursorId, AuthzCheckFn authChecker); @@ -457,7 +463,7 @@ public: * * May block waiting for other threads to finish, but does not block on the network. */ - Status killCursor(OperationContext* opCtx, const NamespaceString& nss, CursorId cursorId); + Status killCursor(OperationContext* opCtx, CursorId cursorId); /** * Kill the cursors satisfying the given predicate. Returns the number of cursors killed. @@ -510,24 +516,6 @@ public: */ stdx::unordered_set<CursorId> getCursorsForSession(LogicalSessionId lsid) const; - /* - * Returns a list of all open cursors for the given set of OperationKeys. - */ - stdx::unordered_set<CursorId> getCursorsForOpKeys(std::vector<OperationKey>) const; - - /** - * Returns the namespace associated with the given cursor id, by examining the 'namespace - * prefix' portion of the cursor id. A cursor with the given cursor id need not actually exist. - * If no such namespace is associated with the 'namespace prefix' portion of the cursor id, - * returns boost::none. - * - * This method is deprecated. Use only when a cursor needs to be operated on in cases where a - * namespace is not available (e.g. OP_KILL_CURSORS). - * - * Does not block. - */ - boost::optional<NamespaceString> getNamespaceForCursorId(CursorId cursorId) const; - void incrementCursorsTimedOut(size_t inc) { _cursorsTimedOut += inc; } @@ -537,9 +525,7 @@ public: } private: - struct CursorEntryContainer; using CursorEntryMap = stdx::unordered_map<CursorId, CursorEntry>; - using NssToCursorContainerMap = stdx::unordered_map<NamespaceString, CursorEntryContainer>; /** * Transfers ownership of the given pinned cursor back to the manager, and moves the cursor to @@ -553,7 +539,6 @@ private: * back in. */ void checkInCursor(std::unique_ptr<ClusterClientCursor> cursor, - const NamespaceString& nss, CursorId cursorId, CursorState cursorState); @@ -562,7 +547,6 @@ private: */ void detachAndKillCursor(stdx::unique_lock<Latch> lk, OperationContext* opCtx, - const NamespaceString& nss, CursorId cursorId); /** @@ -571,7 +555,12 @@ private: * * Not thread-safe. */ - CursorEntry* _getEntry(WithLock, NamespaceString const& nss, CursorId cursorId); + CursorEntry* _getEntry(WithLock, CursorId cursorId); + + /** + * Allocates a new cursor id (a positive 64 bit number) that is not already in use. + */ + CursorId _allocateCursorId(); /** * De-registers the given cursor, and returns an owned pointer to the underlying @@ -584,7 +573,6 @@ private: */ StatusWith<ClusterClientCursorGuard> _detachCursor(WithLock, OperationContext* opCtx, - const NamespaceString& nss, CursorId cursorId); /** @@ -592,32 +580,6 @@ private: */ void killOperationUsingCursor(WithLock, CursorEntry* entry); - /** - * CursorEntryContainer is a moveable, non-copyable container for a set of cursors, where all - * contained cursors share the same 32-bit prefix of their cursor id. - */ - struct CursorEntryContainer { - CursorEntryContainer(const CursorEntryContainer&) = delete; - CursorEntryContainer& operator=(const CursorEntryContainer&) = delete; - - CursorEntryContainer(uint32_t containerPrefix) : containerPrefix(containerPrefix) {} - - CursorEntryContainer(CursorEntryContainer&& other) = default; - CursorEntryContainer& operator=(CursorEntryContainer&& other) = default; - - // Common cursor id prefix for all cursors in this container. - uint32_t containerPrefix; - - // Map from cursor id to cursor entry. - CursorEntryMap entryMap; - }; - - /** - * Erase the container that 'it' points to and return an iterator to the next one. Assumes 'it' - * is an iterator in '_namespaceToContainerMap'. - */ - NssToCursorContainerMap::iterator eraseContainer(NssToCursorContainerMap::iterator it); - // Clock source. Used when the 'last active' time for a cursor needs to be set/updated. May be // concurrently accessed by multiple threads. ClockSource* _clockSource; @@ -631,23 +593,8 @@ private: const int64_t _randomSeed; PseudoRandom _pseudoRandom; - // Map from cursor id prefix to associated namespace. Exists only to provide namespace lookup - // for (deprecated) getNamespaceForCursorId() method. - // - // A CursorId is a 64-bit type, made up of a 32-bit prefix and a 32-bit suffix. When the first - // cursor on a given namespace is registered, it is given a CursorId with a prefix that is - // unique to that namespace, and an arbitrary suffix. Cursors subsequently registered on that - // namespace will all share the same prefix. - // - // Entries are added when the first cursor on the given namespace is registered, and removed - // when the last cursor on the given namespace is destroyed. - stdx::unordered_map<uint32_t, NamespaceString> _cursorIdPrefixToNamespaceMap; - - // Map from namespace to the CursorEntryContainer for that namespace. - // - // Entries are added when the first cursor on the given namespace is registered, and removed - // when the last cursor on the given namespace is destroyed. - NssToCursorContainerMap _namespaceToContainerMap; + // Map from CursorId to CursorEntry. + CursorEntryMap _cursorEntryMap; size_t _cursorsTimedOut = 0; }; diff --git a/src/mongo/s/query/cluster_cursor_manager_test.cpp b/src/mongo/s/query/cluster_cursor_manager_test.cpp index e6704bddbd8..0176fe23947 100644 --- a/src/mongo/s/query/cluster_cursor_manager_test.cpp +++ b/src/mongo/s/query/cluster_cursor_manager_test.cpp @@ -116,12 +116,12 @@ protected: return *std::next(_cursorKilledFlags.begin(), i); } - void killCursorFromDifferentOpCtx(const NamespaceString& nss, CursorId cursorId) { + void killCursorFromDifferentOpCtx(CursorId cursorId) { // Set up another client to kill the cursor. auto killCursorClient = getServiceContext()->makeClient("killCursorClient"); auto killCursorOpCtx = killCursorClient->makeOperationContext(); AlternativeClientRegion acr(killCursorClient); - ASSERT_OK(getManager()->killCursor(killCursorOpCtx.get(), nss, cursorId)); + ASSERT_OK(getManager()->killCursor(killCursorOpCtx.get(), cursorId)); } @@ -149,7 +149,7 @@ TEST_F(ClusterCursorManagerTest, RegisterCursor) { ClusterCursorManager::CursorLifetime::Mortal, UserNameIterator())); auto pinnedCursor = - getManager()->checkOutCursor(nss, cursorId, getOperationContext(), successAuthChecker); + getManager()->checkOutCursor(cursorId, getOperationContext(), successAuthChecker); ASSERT_OK(pinnedCursor.getStatus()); auto nextResult = pinnedCursor.getValue()->next(); ASSERT_OK(nextResult.getStatus()); @@ -184,7 +184,7 @@ TEST_F(ClusterCursorManagerTest, CheckOutCursorBasic) { ClusterCursorManager::CursorLifetime::Mortal, UserNameIterator())); auto checkedOutCursor = - getManager()->checkOutCursor(nss, cursorId, getOperationContext(), successAuthChecker); + getManager()->checkOutCursor(cursorId, getOperationContext(), successAuthChecker); ASSERT_OK(checkedOutCursor.getStatus()); ASSERT_EQ(cursorId, checkedOutCursor.getValue().getCursorId()); auto nextResult = checkedOutCursor.getValue()->next(); @@ -213,8 +213,8 @@ TEST_F(ClusterCursorManagerTest, CheckOutCursorMultipleCursors) { UserNameIterator())); } for (int i = 0; i < numCursors; ++i) { - auto pinnedCursor = getManager()->checkOutCursor( - nss, cursorIds[i], getOperationContext(), successAuthChecker); + auto pinnedCursor = + getManager()->checkOutCursor(cursorIds[i], getOperationContext(), successAuthChecker); ASSERT_OK(pinnedCursor.getStatus()); auto nextResult = pinnedCursor.getValue()->next(); ASSERT_OK(nextResult.getStatus()); @@ -236,11 +236,11 @@ TEST_F(ClusterCursorManagerTest, CheckOutCursorPinned) { ClusterCursorManager::CursorLifetime::Mortal, UserNameIterator())); auto pinnedCursor = - getManager()->checkOutCursor(nss, cursorId, getOperationContext(), successAuthChecker); + getManager()->checkOutCursor(cursorId, getOperationContext(), successAuthChecker); ASSERT_OK(pinnedCursor.getStatus()); ASSERT_EQ(ErrorCodes::CursorInUse, getManager() - ->checkOutCursor(nss, cursorId, getOperationContext(), successAuthChecker) + ->checkOutCursor(cursorId, getOperationContext(), successAuthChecker) .getStatus()); } @@ -253,35 +253,17 @@ TEST_F(ClusterCursorManagerTest, CheckOutCursorKilled) { ClusterCursorManager::CursorType::SingleTarget, ClusterCursorManager::CursorLifetime::Mortal, UserNameIterator())); - killCursorFromDifferentOpCtx(nss, cursorId); + killCursorFromDifferentOpCtx(cursorId); ASSERT_EQ(ErrorCodes::CursorNotFound, getManager() - ->checkOutCursor(nss, cursorId, getOperationContext(), successAuthChecker) + ->checkOutCursor(cursorId, getOperationContext(), successAuthChecker) .getStatus()); } // Test that checking out an unknown cursor returns an error with code ErrorCodes::CursorNotFound. TEST_F(ClusterCursorManagerTest, CheckOutCursorUnknown) { ASSERT_EQ(ErrorCodes::CursorNotFound, - getManager()->checkOutCursor(nss, 5, nullptr, successAuthChecker).getStatus()); -} - -// Test that checking out a unknown cursor returns an error with code ErrorCodes::CursorNotFound, -// even if there is an existing cursor with the same cursor id but a different namespace. -TEST_F(ClusterCursorManagerTest, CheckOutCursorWrongNamespace) { - const NamespaceString correctNamespace("test.correct"); - const NamespaceString incorrectNamespace("test.incorrect"); - auto cursorId = - assertGet(getManager()->registerCursor(getOperationContext(), - allocateMockCursor(), - correctNamespace, - ClusterCursorManager::CursorType::SingleTarget, - ClusterCursorManager::CursorLifetime::Mortal, - UserNameIterator())); - ASSERT_EQ(ErrorCodes::CursorNotFound, - getManager() - ->checkOutCursor(incorrectNamespace, cursorId, nullptr, successAuthChecker) - .getStatus()); + getManager()->checkOutCursor(5, nullptr, successAuthChecker).getStatus()); } // Test that checking out a unknown cursor returns an error with code ErrorCodes::CursorNotFound, @@ -296,7 +278,7 @@ TEST_F(ClusterCursorManagerTest, CheckOutCursorWrongCursorId) { UserNameIterator())); ASSERT_EQ(ErrorCodes::CursorNotFound, getManager() - ->checkOutCursor(nss, cursorId + 1, getOperationContext(), successAuthChecker) + ->checkOutCursor(cursorId + 1, getOperationContext(), successAuthChecker) .getStatus()); } @@ -313,7 +295,7 @@ TEST_F(ClusterCursorManagerTest, CheckOutCursorUpdateActiveTime) { Date_t cursorRegistrationTime = getClockSource()->now(); getClockSource()->advance(Milliseconds(1)); auto checkedOutCursor = - getManager()->checkOutCursor(nss, cursorId, getOperationContext(), successAuthChecker); + getManager()->checkOutCursor(cursorId, getOperationContext(), successAuthChecker); ASSERT_OK(checkedOutCursor.getStatus()); checkedOutCursor.getValue().returnCursor(ClusterCursorManager::CursorState::NotExhausted); getManager()->killMortalCursorsInactiveSince(getOperationContext(), cursorRegistrationTime); @@ -329,7 +311,7 @@ TEST_F(ClusterCursorManagerTest, CheckOutCursorAuthFails) { ClusterCursorManager::CursorLifetime::Mortal, UserNameIterator())); auto checkedOutCursor = - getManager()->checkOutCursor(nss, cursorId, getOperationContext(), failAuthChecker); + getManager()->checkOutCursor(cursorId, getOperationContext(), failAuthChecker); ASSERT_EQ(checkedOutCursor.getStatus(), ErrorCodes::Unauthorized); } @@ -346,7 +328,7 @@ TEST_F(ClusterCursorManagerTest, ReturnCursorUpdateActiveTime) { UserNameIterator())); Date_t cursorCheckOutTime = getClockSource()->now(); auto checkedOutCursor = - getManager()->checkOutCursor(nss, cursorId, getOperationContext(), successAuthChecker); + getManager()->checkOutCursor(cursorId, getOperationContext(), successAuthChecker); ASSERT_OK(checkedOutCursor.getStatus()); getClockSource()->advance(Milliseconds(1)); checkedOutCursor.getValue().returnCursor(ClusterCursorManager::CursorState::NotExhausted); @@ -363,7 +345,7 @@ TEST_F(ClusterCursorManagerTest, KillUnpinnedCursorBasic) { ClusterCursorManager::CursorType::SingleTarget, ClusterCursorManager::CursorLifetime::Mortal, UserNameIterator())); - killCursorFromDifferentOpCtx(nss, cursorId); + killCursorFromDifferentOpCtx(cursorId); ASSERT(isMockCursorKilled(0)); } @@ -377,9 +359,9 @@ TEST_F(ClusterCursorManagerTest, KillPinnedCursorBasic) { ClusterCursorManager::CursorLifetime::Mortal, UserNameIterator())); auto pinnedCursor = - getManager()->checkOutCursor(nss, cursorId, getOperationContext(), successAuthChecker); + getManager()->checkOutCursor(cursorId, getOperationContext(), successAuthChecker); ASSERT_OK(pinnedCursor.getStatus()); - killCursorFromDifferentOpCtx(nss, pinnedCursor.getValue().getCursorId()); + killCursorFromDifferentOpCtx(pinnedCursor.getValue().getCursorId()); // When the cursor is pinned the operation which checked out the cursor should be interrupted. ASSERT_EQ(getOperationContext()->checkForInterruptNoAssert(), ErrorCodes::CursorKilled); @@ -406,31 +388,14 @@ TEST_F(ClusterCursorManagerTest, KillCursorMultipleCursors) { } // Kill each cursor and verify that it was successfully killed. for (size_t i = 0; i < numCursors; ++i) { - ASSERT_OK(getManager()->killCursor(getOperationContext(), nss, cursorIds[i])); + ASSERT_OK(getManager()->killCursor(getOperationContext(), cursorIds[i])); ASSERT(isMockCursorKilled(i)); } } // Test that killing an unknown cursor returns an error with code ErrorCodes::CursorNotFound. TEST_F(ClusterCursorManagerTest, KillCursorUnknown) { - Status killResult = getManager()->killCursor(getOperationContext(), nss, 5); - ASSERT_EQ(ErrorCodes::CursorNotFound, killResult); -} - -// Test that killing an unknown cursor returns an error with code ErrorCodes::CursorNotFound, -// even if there is an existing cursor with the same cursor id but a different namespace. -TEST_F(ClusterCursorManagerTest, KillCursorWrongNamespace) { - const NamespaceString correctNamespace("test.correct"); - const NamespaceString incorrectNamespace("test.incorrect"); - auto cursorId = - assertGet(getManager()->registerCursor(getOperationContext(), - allocateMockCursor(), - correctNamespace, - ClusterCursorManager::CursorType::SingleTarget, - ClusterCursorManager::CursorLifetime::Mortal, - UserNameIterator())); - Status killResult = - getManager()->killCursor(getOperationContext(), incorrectNamespace, cursorId); + Status killResult = getManager()->killCursor(getOperationContext(), 5); ASSERT_EQ(ErrorCodes::CursorNotFound, killResult); } @@ -444,7 +409,7 @@ TEST_F(ClusterCursorManagerTest, KillCursorWrongCursorId) { ClusterCursorManager::CursorType::SingleTarget, ClusterCursorManager::CursorLifetime::Mortal, UserNameIterator())); - Status killResult = getManager()->killCursor(getOperationContext(), nss, cursorId + 1); + Status killResult = getManager()->killCursor(getOperationContext(), cursorId + 1); ASSERT_EQ(ErrorCodes::CursorNotFound, killResult); } @@ -497,7 +462,7 @@ TEST_F(ClusterCursorManagerTest, ShouldNotKillPinnedCursors) { ClusterCursorManager::CursorLifetime::Mortal, UserNameIterator())); auto pin = assertGet( - getManager()->checkOutCursor(nss, cursorId, getOperationContext(), successAuthChecker)); + getManager()->checkOutCursor(cursorId, getOperationContext(), successAuthChecker)); getManager()->killMortalCursorsInactiveSince(getOperationContext(), getClockSource()->now()); ASSERT(!isMockCursorKilled(0)); pin.returnCursor(ClusterCursorManager::CursorState::NotExhausted); @@ -615,6 +580,27 @@ TEST_F(ClusterCursorManagerTest, KillCursorsSatisfyingOnlyKillsMatchingSubset) { } } +// Tests that we can kill cusors based on their opkey. +TEST_F(ClusterCursorManagerTest, KillCursorsSatisfyingBasedOnOpKey) { + const size_t numCursors = 10; + for (size_t i = 0; i < numCursors; ++i) { + ASSERT_OK(getManager()->registerCursor(getOperationContext(), + allocateMockCursor(), + nss, + ClusterCursorManager::CursorType::SingleTarget, + ClusterCursorManager::CursorLifetime::Mortal, + UserNameIterator())); + } + auto pred = [&](CursorId id, const ClusterCursorManager::CursorEntry& entry) { + return entry.getOperationKey() == getOperationContext()->getOperationKey(); + }; + auto nKilled = getManager()->killCursorsSatisfying(getOperationContext(), std::move(pred)); + ASSERT_EQ(nKilled, numCursors); + for (size_t i = 0; i < numCursors; ++i) { + ASSERT(isMockCursorKilled(i)); + } +} + // Test that the Client that registered a cursor is correctly recorded. TEST_F(ClusterCursorManagerTest, CorrectlyRecordsOriginatingClient) { ASSERT_OK(getManager()->registerCursor(getOperationContext(), @@ -691,7 +677,7 @@ TEST_F(ClusterCursorManagerTest, StatsPinCursor) { ClusterCursorManager::CursorLifetime::Mortal, UserNameIterator())); auto pinnedCursor = - getManager()->checkOutCursor(nss, cursorId, getOperationContext(), successAuthChecker); + getManager()->checkOutCursor(cursorId, getOperationContext(), successAuthChecker); ASSERT_EQ(1U, getManager()->stats().cursorsPinned); } @@ -732,7 +718,7 @@ TEST_F(ClusterCursorManagerTest, StatsKillShardedCursor) { ClusterCursorManager::CursorLifetime::Mortal, UserNameIterator())); ASSERT_EQ(1U, getManager()->stats().cursorsMultiTarget); - ASSERT_OK(getManager()->killCursor(getOperationContext(), nss, cursorId)); + ASSERT_OK(getManager()->killCursor(getOperationContext(), cursorId)); ASSERT_EQ(0U, getManager()->stats().cursorsMultiTarget); } @@ -746,7 +732,7 @@ TEST_F(ClusterCursorManagerTest, StatsKillNotShardedCursor) { ClusterCursorManager::CursorLifetime::Mortal, UserNameIterator())); ASSERT_EQ(1U, getManager()->stats().cursorsSingleTarget); - ASSERT_OK(getManager()->killCursor(getOperationContext(), nss, cursorId)); + ASSERT_OK(getManager()->killCursor(getOperationContext(), cursorId)); ASSERT_EQ(0U, getManager()->stats().cursorsSingleTarget); } @@ -760,10 +746,10 @@ TEST_F(ClusterCursorManagerTest, StatsKillPinnedCursor) { ClusterCursorManager::CursorLifetime::Mortal, UserNameIterator())); auto pinnedCursor = - getManager()->checkOutCursor(nss, cursorId, getOperationContext(), successAuthChecker); + getManager()->checkOutCursor(cursorId, getOperationContext(), successAuthChecker); ASSERT_EQ(1U, getManager()->stats().cursorsPinned); - killCursorFromDifferentOpCtx(nss, cursorId); + killCursorFromDifferentOpCtx(cursorId); ASSERT_EQ(getOperationContext()->checkForInterruptNoAssert(), ErrorCodes::CursorKilled); ASSERT_EQ(0U, getManager()->stats().cursorsPinned); @@ -779,7 +765,7 @@ TEST_F(ClusterCursorManagerTest, StatsExhaustShardedCursor) { ClusterCursorManager::CursorLifetime::Mortal, UserNameIterator())); auto pinnedCursor = - getManager()->checkOutCursor(nss, cursorId, getOperationContext(), successAuthChecker); + getManager()->checkOutCursor(cursorId, getOperationContext(), successAuthChecker); ASSERT_OK(pinnedCursor.getStatus()); ASSERT_OK(pinnedCursor.getValue()->next().getStatus()); ASSERT_EQ(1U, getManager()->stats().cursorsMultiTarget); @@ -797,7 +783,7 @@ TEST_F(ClusterCursorManagerTest, StatsExhaustNotShardedCursor) { ClusterCursorManager::CursorLifetime::Mortal, UserNameIterator())); auto pinnedCursor = - getManager()->checkOutCursor(nss, cursorId, getOperationContext(), successAuthChecker); + getManager()->checkOutCursor(cursorId, getOperationContext(), successAuthChecker); ASSERT_OK(pinnedCursor.getStatus()); ASSERT_OK(pinnedCursor.getValue()->next().getStatus()); ASSERT_EQ(1U, getManager()->stats().cursorsSingleTarget); @@ -816,7 +802,7 @@ TEST_F(ClusterCursorManagerTest, StatsExhaustPinnedCursor) { ClusterCursorManager::CursorLifetime::Mortal, UserNameIterator())); auto pinnedCursor = - getManager()->checkOutCursor(nss, cursorId, getOperationContext(), successAuthChecker); + getManager()->checkOutCursor(cursorId, getOperationContext(), successAuthChecker); ASSERT_OK(pinnedCursor.getStatus()); ASSERT_OK(pinnedCursor.getValue()->next().getStatus()); ASSERT_EQ(1U, getManager()->stats().cursorsPinned); @@ -835,7 +821,7 @@ TEST_F(ClusterCursorManagerTest, StatsCheckInWithoutExhaustingPinnedCursor) { ClusterCursorManager::CursorLifetime::Mortal, UserNameIterator())); auto pinnedCursor = - getManager()->checkOutCursor(nss, cursorId, getOperationContext(), successAuthChecker); + getManager()->checkOutCursor(cursorId, getOperationContext(), successAuthChecker); ASSERT_OK(pinnedCursor.getStatus()); ASSERT_OK(pinnedCursor.getValue()->next().getStatus()); ASSERT_EQ(1U, getManager()->stats().cursorsPinned); @@ -843,73 +829,6 @@ TEST_F(ClusterCursorManagerTest, StatsCheckInWithoutExhaustingPinnedCursor) { ASSERT_EQ(0U, getManager()->stats().cursorsPinned); } -// Test that getting the namespace for a cursor returns the correct namespace. -TEST_F(ClusterCursorManagerTest, GetNamespaceForCursorIdBasic) { - auto cursorId = - assertGet(getManager()->registerCursor(getOperationContext(), - allocateMockCursor(), - nss, - ClusterCursorManager::CursorType::SingleTarget, - ClusterCursorManager::CursorLifetime::Mortal, - UserNameIterator())); - boost::optional<NamespaceString> cursorNamespace = - getManager()->getNamespaceForCursorId(cursorId); - ASSERT(cursorNamespace); - ASSERT_EQ(nss.ns(), cursorNamespace->ns()); -} - -// Test that getting the namespace for a cursor returns the correct namespace, when there are -// multiple cursors registered on that namespace. -TEST_F(ClusterCursorManagerTest, GetNamespaceForCursorIdMultipleCursorsSameNamespace) { - const size_t numCursors = 10; - std::vector<CursorId> cursorIds(numCursors); - for (size_t i = 0; i < numCursors; ++i) { - cursorIds[i] = - assertGet(getManager()->registerCursor(getOperationContext(), - allocateMockCursor(), - nss, - ClusterCursorManager::CursorType::SingleTarget, - ClusterCursorManager::CursorLifetime::Mortal, - UserNameIterator())); - } - for (size_t i = 0; i < numCursors; ++i) { - boost::optional<NamespaceString> cursorNamespace = - getManager()->getNamespaceForCursorId(cursorIds[i]); - ASSERT(cursorNamespace); - ASSERT_EQ(nss.ns(), cursorNamespace->ns()); - } -} - -// Test that getting the namespace for a cursor returns the correct namespace, when there are -// multiple cursors registered on different namespaces. -TEST_F(ClusterCursorManagerTest, GetNamespaceForCursorIdMultipleCursorsDifferentNamespaces) { - const size_t numCursors = 10; - std::vector<std::pair<NamespaceString, CursorId>> cursors(numCursors); - for (size_t i = 0; i < numCursors; ++i) { - NamespaceString cursorNamespace(std::string(str::stream() << "test.collection" << i)); - auto cursorId = - assertGet(getManager()->registerCursor(getOperationContext(), - allocateMockCursor(), - cursorNamespace, - ClusterCursorManager::CursorType::SingleTarget, - ClusterCursorManager::CursorLifetime::Mortal, - UserNameIterator())); - cursors[i] = {cursorNamespace, cursorId}; - } - for (size_t i = 0; i < numCursors; ++i) { - boost::optional<NamespaceString> cursorNamespace = - getManager()->getNamespaceForCursorId(cursors[i].second); - ASSERT(cursorNamespace); - ASSERT_EQ(cursors[i].first.ns(), cursorNamespace->ns()); - } -} - -// Test that getting the namespace for an unknown cursor returns boost::none. -TEST_F(ClusterCursorManagerTest, GetNamespaceForCursorIdUnknown) { - boost::optional<NamespaceString> cursorNamespace = getManager()->getNamespaceForCursorId(5); - ASSERT_FALSE(cursorNamespace); -} - // Test that the PinnedCursor default constructor creates a pin that owns no cursor. TEST_F(ClusterCursorManagerTest, PinnedCursorDefaultConstructor) { ClusterCursorManager::PinnedCursor pinnedCursor; @@ -927,14 +846,14 @@ TEST_F(ClusterCursorManagerTest, PinnedCursorReturnCursorNotExhausted) { ClusterCursorManager::CursorLifetime::Mortal, UserNameIterator())); auto registeredCursor = - getManager()->checkOutCursor(nss, cursorId, getOperationContext(), successAuthChecker); + getManager()->checkOutCursor(cursorId, getOperationContext(), successAuthChecker); ASSERT_OK(registeredCursor.getStatus()); ASSERT_EQ(cursorId, registeredCursor.getValue().getCursorId()); ASSERT_NE(0, cursorId); registeredCursor.getValue().returnCursor(ClusterCursorManager::CursorState::NotExhausted); ASSERT_EQ(0, registeredCursor.getValue().getCursorId()); auto checkedOutCursor = - getManager()->checkOutCursor(nss, cursorId, getOperationContext(), successAuthChecker); + getManager()->checkOutCursor(cursorId, getOperationContext(), successAuthChecker); ASSERT_OK(checkedOutCursor.getStatus()); } @@ -949,7 +868,7 @@ TEST_F(ClusterCursorManagerTest, PinnedCursorReturnCursorExhausted) { ClusterCursorManager::CursorLifetime::Mortal, UserNameIterator())); auto registeredCursor = - getManager()->checkOutCursor(nss, cursorId, getOperationContext(), successAuthChecker); + getManager()->checkOutCursor(cursorId, getOperationContext(), successAuthChecker); ASSERT_OK(registeredCursor.getStatus()); ASSERT_EQ(cursorId, registeredCursor.getValue().getCursorId()); ASSERT_NE(0, cursorId); @@ -959,7 +878,7 @@ TEST_F(ClusterCursorManagerTest, PinnedCursorReturnCursorExhausted) { // Cursor should have been killed and destroyed. ASSERT_NOT_OK(getManager() - ->checkOutCursor(nss, cursorId, getOperationContext(), successAuthChecker) + ->checkOutCursor(cursorId, getOperationContext(), successAuthChecker) .getStatus()); ASSERT(isMockCursorKilled(0)); } @@ -977,7 +896,7 @@ TEST_F(ClusterCursorManagerTest, PinnedCursorReturnCursorExhaustedWithNonExhaust ClusterCursorManager::CursorLifetime::Mortal, UserNameIterator())); auto registeredCursor = - getManager()->checkOutCursor(nss, cursorId, getOperationContext(), successAuthChecker); + getManager()->checkOutCursor(cursorId, getOperationContext(), successAuthChecker); ASSERT_OK(registeredCursor.getStatus()); ASSERT_EQ(cursorId, registeredCursor.getValue().getCursorId()); ASSERT_NE(0, cursorId); @@ -988,7 +907,7 @@ TEST_F(ClusterCursorManagerTest, PinnedCursorReturnCursorExhaustedWithNonExhaust // Cursor should be killed as soon as it's checked in. ASSERT(isMockCursorKilled(0)); ASSERT_NOT_OK(getManager() - ->checkOutCursor(nss, cursorId, getOperationContext(), successAuthChecker) + ->checkOutCursor(cursorId, getOperationContext(), successAuthChecker) .getStatus()); } @@ -1003,7 +922,7 @@ TEST_F(ClusterCursorManagerTest, PinnedCursorMoveAssignmentKill) { ClusterCursorManager::CursorLifetime::Mortal, UserNameIterator())); auto pinnedCursor = - getManager()->checkOutCursor(nss, cursorId, getOperationContext(), successAuthChecker); + getManager()->checkOutCursor(cursorId, getOperationContext(), successAuthChecker); pinnedCursor = ClusterCursorManager::PinnedCursor(); ASSERT(isMockCursorKilled(0)); } @@ -1019,7 +938,7 @@ TEST_F(ClusterCursorManagerTest, PinnedCursorDestructorKill) { ClusterCursorManager::CursorLifetime::Mortal, UserNameIterator())); auto pinnedCursor = - getManager()->checkOutCursor(nss, cursorId, getOperationContext(), successAuthChecker); + getManager()->checkOutCursor(cursorId, getOperationContext(), successAuthChecker); } ASSERT(isMockCursorKilled(0)); } @@ -1037,7 +956,7 @@ TEST_F(ClusterCursorManagerTest, RemotesExhausted) { ClusterCursorManager::CursorLifetime::Mortal, UserNameIterator())); auto pinnedCursor = - getManager()->checkOutCursor(nss, cursorId, getOperationContext(), successAuthChecker); + getManager()->checkOutCursor(cursorId, getOperationContext(), successAuthChecker); ASSERT_OK(pinnedCursor.getStatus()); ASSERT_FALSE(pinnedCursor.getValue()->remotesExhausted()); } @@ -1053,10 +972,10 @@ TEST_F(ClusterCursorManagerTest, DoNotDestroyKilledPinnedCursors) { ClusterCursorManager::CursorLifetime::Mortal, UserNameIterator())); auto pinnedCursor = - getManager()->checkOutCursor(nss, cursorId, getOperationContext(), successAuthChecker); + getManager()->checkOutCursor(cursorId, getOperationContext(), successAuthChecker); ASSERT_OK(pinnedCursor.getStatus()); - killCursorFromDifferentOpCtx(nss, cursorId); + killCursorFromDifferentOpCtx(cursorId); ASSERT_EQ(getOperationContext()->checkForInterruptNoAssert(), ErrorCodes::CursorKilled); ASSERT(!isMockCursorKilled(0)); @@ -1091,7 +1010,7 @@ TEST_F(ClusterCursorManagerTest, CursorStoresAPIParameters) { ClusterCursorManager::CursorLifetime::Mortal, UserNameIterator())); auto pinnedCursor = assertGet( - getManager()->checkOutCursor(nss, cursorId, getOperationContext(), successAuthChecker)); + getManager()->checkOutCursor(cursorId, getOperationContext(), successAuthChecker)); auto storedAPIParams = pinnedCursor->getAPIParameters(); ASSERT_EQ("2", *storedAPIParams.getAPIVersion()); @@ -1131,7 +1050,7 @@ TEST_F(ClusterCursorManagerTest, PinnedCursorNotKilledOnShutdown) { UserNameIterator())); auto pinnedCursor = - getManager()->checkOutCursor(nss, cursorId, getOperationContext(), successAuthChecker); + getManager()->checkOutCursor(cursorId, getOperationContext(), successAuthChecker); getManager()->shutdown(getOperationContext()); ASSERT_EQ(getOperationContext()->checkForInterruptNoAssert(), ErrorCodes::CursorKilled); @@ -1159,7 +1078,7 @@ TEST_F(ClusterCursorManagerTest, CannotCheckoutCursorDuringShutdown) { ASSERT_EQUALS(ErrorCodes::ShutdownInProgress, getManager() - ->checkOutCursor(nss, cursorId, getOperationContext(), successAuthChecker) + ->checkOutCursor(cursorId, getOperationContext(), successAuthChecker) .getStatus()); } @@ -1207,7 +1126,7 @@ TEST_F(ClusterCursorManagerTest, OneCursorWithASession) { ASSERT(cursors.find(cursorId) != cursors.end()); // Remove the cursor from the manager. - ASSERT_OK(getManager()->killCursor(getOperationContext(), nss, cursorId)); + ASSERT_OK(getManager()->killCursor(getOperationContext(), cursorId)); // There should be no more cursor entries by session id. LogicalSessionIdSet sessions; @@ -1231,8 +1150,7 @@ TEST_F(ClusterCursorManagerTest, GetSessionIdsWhileCheckedOut) { UserNameIterator())); // Check the cursor out, then try to append cursors, see that we get one. - auto res = - getManager()->checkOutCursor(nss, cursorId, getOperationContext(), successAuthChecker); + auto res = getManager()->checkOutCursor(cursorId, getOperationContext(), successAuthChecker); ASSERT(res.isOK()); auto cursors = getManager()->getCursorsForSession(lsid); @@ -1273,7 +1191,7 @@ TEST_F(ClusterCursorManagerTest, MultipleCursorsWithSameSession) { ASSERT(cursors.find(cursorId2) != cursors.end()); // Remove one cursor from the manager. - ASSERT_OK(getManager()->killCursor(getOperationContext(), nss, cursorId1)); + ASSERT_OK(getManager()->killCursor(getOperationContext(), cursorId1)); // Should still be able to retrieve the session. lsids.clear(); @@ -1367,12 +1285,12 @@ TEST_F(ClusterCursorManagerTest, CheckAuthForKillCursors) { ASSERT_EQ(ErrorCodes::CursorNotFound, getManager()->checkAuthForKillCursors( - getOperationContext(), nss, cursorId + 1, successAuthChecker)); - ASSERT_EQ(ErrorCodes::Unauthorized, - getManager()->checkAuthForKillCursors( - getOperationContext(), nss, cursorId, failAuthChecker)); - ASSERT_OK(getManager()->checkAuthForKillCursors( - getOperationContext(), nss, cursorId, successAuthChecker)); + getOperationContext(), cursorId + 1, successAuthChecker)); + ASSERT_EQ( + ErrorCodes::Unauthorized, + getManager()->checkAuthForKillCursors(getOperationContext(), cursorId, failAuthChecker)); + ASSERT_OK( + getManager()->checkAuthForKillCursors(getOperationContext(), cursorId, successAuthChecker)); } TEST_F(ClusterCursorManagerTest, PinnedCursorReturnsUnderlyingCursorTxnNumber) { @@ -1386,7 +1304,7 @@ TEST_F(ClusterCursorManagerTest, PinnedCursorReturnsUnderlyingCursorTxnNumber) { UserNameIterator())); auto pinnedCursor = - getManager()->checkOutCursor(nss, cursorId, getOperationContext(), successAuthChecker); + getManager()->checkOutCursor(cursorId, getOperationContext(), successAuthChecker); ASSERT_OK(pinnedCursor.getStatus()); // The underlying cursor's txnNumber should be returned. @@ -1394,134 +1312,6 @@ TEST_F(ClusterCursorManagerTest, PinnedCursorReturnsUnderlyingCursorTxnNumber) { ASSERT_EQ(txnNumber, *pinnedCursor.getValue()->getTxnNumber()); } -TEST_F(ClusterCursorManagerTest, CursorsWithoutOperationKeys) { - ASSERT_OK(getManager()->registerCursor(getOperationContext(), - allocateMockCursor(), - nss, - ClusterCursorManager::CursorType::SingleTarget, - ClusterCursorManager::CursorLifetime::Mortal, - UserNameIterator())); - - ASSERT_EQ(getManager()->getCursorsForOpKeys({UUID::gen()}).size(), size_t(0)); -} - -TEST_F(ClusterCursorManagerTest, OneCursorWithAnOperationKey) { - auto opKey = UUID::gen(); - getOperationContext()->setOperationKey(opKey); - auto cursorId = - assertGet(getManager()->registerCursor(getOperationContext(), - allocateMockCursor(), - nss, - ClusterCursorManager::CursorType::SingleTarget, - ClusterCursorManager::CursorLifetime::Mortal, - UserNameIterator())); - - // Retrieve all cursors for this operation key - should be just ours. - auto cursors = getManager()->getCursorsForOpKeys({opKey}); - ASSERT_EQ(cursors.size(), size_t(1)); - ASSERT(cursors.find(cursorId) != cursors.end()); - - // Remove the cursor from the manager. - ASSERT_OK(getManager()->killCursor(getOperationContext(), nss, cursorId)); - - // There should be no more cursor entries for this operation key. - ASSERT(getManager()->getCursorsForOpKeys({opKey}).empty()); -} - -TEST_F(ClusterCursorManagerTest, GetCursorByOpKeyWhileCheckedOut) { - auto opKey = UUID::gen(); - getOperationContext()->setOperationKey(opKey); - auto cursorId = - assertGet(getManager()->registerCursor(getOperationContext(), - allocateMockCursor(), - nss, - ClusterCursorManager::CursorType::SingleTarget, - ClusterCursorManager::CursorLifetime::Mortal, - UserNameIterator())); - - // Check the cursor out then look it up by operation key. - auto res = - getManager()->checkOutCursor(nss, cursorId, getOperationContext(), successAuthChecker); - ASSERT(res.isOK()); - - auto cursors = getManager()->getCursorsForOpKeys({opKey}); - ASSERT_EQ(cursors.size(), size_t(1)); -} - -TEST_F(ClusterCursorManagerTest, MultipleCursorsWithSameOperationKey) { - auto opKey = UUID::gen(); - getOperationContext()->setOperationKey(opKey); - auto cursorId1 = - assertGet(getManager()->registerCursor(getOperationContext(), - allocateMockCursor(), - nss, - ClusterCursorManager::CursorType::SingleTarget, - ClusterCursorManager::CursorLifetime::Mortal, - UserNameIterator())); - auto cursorId2 = - assertGet(getManager()->registerCursor(getOperationContext(), - allocateMockCursor(), - nss, - ClusterCursorManager::CursorType::SingleTarget, - ClusterCursorManager::CursorLifetime::Mortal, - UserNameIterator())); - - // Retrieve all cursors for the operation key - should be both cursors. - auto cursors = getManager()->getCursorsForOpKeys({opKey}); - ASSERT_EQ(cursors.size(), size_t(2)); - ASSERT(cursors.find(cursorId1) != cursors.end()); - ASSERT(cursors.find(cursorId2) != cursors.end()); - - // Remove one cursor from the manager. - ASSERT_OK(getManager()->killCursor(getOperationContext(), nss, cursorId1)); - - // Should still be able to retrieve remaining cursor by session. - cursors = getManager()->getCursorsForOpKeys({opKey}); - ASSERT_EQ(cursors.size(), size_t(1)); - ASSERT(cursors.find(cursorId2) != cursors.end()); -} - -TEST_F(ClusterCursorManagerTest, MultipleCursorsMultipleOperationKeys) { - auto opKey1 = UUID::gen(); - auto opKey2 = UUID::gen(); - getOperationContext()->setOperationKey(opKey1); - - auto client2 = getServiceContext()->makeClient("client2"); - auto opCtx2 = client2->makeOperationContext(); - opCtx2->setOperationKey(opKey2); - - // Register two cursors with different operation keys. - CursorId cursor1 = - assertGet(getManager()->registerCursor(getOperationContext(), - allocateMockCursor(), - nss, - ClusterCursorManager::CursorType::SingleTarget, - ClusterCursorManager::CursorLifetime::Mortal, - UserNameIterator())); - - CursorId cursor2 = - assertGet(getManager()->registerCursor(opCtx2.get(), - allocateMockCursor(), - nss, - ClusterCursorManager::CursorType::SingleTarget, - ClusterCursorManager::CursorLifetime::Mortal, - UserNameIterator())); - - // Retrieve cursors for each operation key. - auto cursors1 = getManager()->getCursorsForOpKeys({opKey1}); - ASSERT_EQ(cursors1.size(), size_t(1)); - ASSERT(cursors1.find(cursor1) != cursors1.end()); - - auto cursors2 = getManager()->getCursorsForOpKeys({opKey2}); - ASSERT_EQ(cursors2.size(), size_t(1)); - - // Retrieve cursors for both operation keys. - auto cursors = getManager()->getCursorsForOpKeys({opKey1, opKey2}); - ASSERT_EQ(cursors.size(), size_t(2)); - ASSERT(cursors.find(cursor1) != cursors.end()); - ASSERT(cursors.find(cursor2) != cursors.end()); -} - } // namespace } // namespace mongo diff --git a/src/mongo/s/query/cluster_find.cpp b/src/mongo/s/query/cluster_find.cpp index 148a17f89b6..0bafa7b0fc2 100644 --- a/src/mongo/s/query/cluster_find.cpp +++ b/src/mongo/s/query/cluster_find.cpp @@ -681,7 +681,7 @@ StatusWith<CursorResponse> ClusterFind::runGetMore(OperationContext* opCtx, NamespaceString nss(cmd.getDbName(), cmd.getCollection()); int64_t cursorId = cmd.getCommandParameter(); - auto pinnedCursor = cursorManager->checkOutCursor(nss, cursorId, opCtx, authChecker); + auto pinnedCursor = cursorManager->checkOutCursor(cursorId, opCtx, authChecker); if (!pinnedCursor.isOK()) { return pinnedCursor.getStatus(); } |