summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Storch <david.storch@10gen.com>2019-01-17 10:36:29 -0500
committerDavid Storch <david.storch@10gen.com>2019-01-23 21:18:45 -0500
commit84b142d5123c06212ce7a2e495ae8afe48eb65cb (patch)
treee267b75425b6b61c863d73276a7be137f4109fc9
parent869b3b64a898076ff98405fa70403084417cb164 (diff)
downloadmongo-84b142d5123c06212ce7a2e495ae8afe48eb65cb.tar.gz
SERVER-37455 Delete per-collection cursor managers.
-rw-r--r--jstests/noPassthrough/cursor_timeout_interrupt.js61
-rw-r--r--src/mongo/db/catalog/SConscript1
-rw-r--r--src/mongo/db/catalog/catalog_control.cpp3
-rw-r--r--src/mongo/db/catalog/collection.h3
-rw-r--r--src/mongo/db/catalog/collection_impl.cpp11
-rw-r--r--src/mongo/db/catalog/collection_impl.h6
-rw-r--r--src/mongo/db/catalog/collection_mock.h4
-rw-r--r--src/mongo/db/catalog/database.h2
-rw-r--r--src/mongo/db/catalog/database_holder.h8
-rw-r--r--src/mongo/db/catalog/database_holder_impl.cpp10
-rw-r--r--src/mongo/db/catalog/database_holder_impl.h4
-rw-r--r--src/mongo/db/catalog/database_holder_mock.h4
-rw-r--r--src/mongo/db/catalog/database_impl.cpp44
-rw-r--r--src/mongo/db/catalog/database_impl.h12
-rw-r--r--src/mongo/db/catalog/index_catalog_impl.cpp23
-rw-r--r--src/mongo/db/catalog/index_catalog_impl.h4
-rw-r--r--src/mongo/db/clientcursor.cpp53
-rw-r--r--src/mongo/db/clientcursor.h44
-rw-r--r--src/mongo/db/commands/find_cmd.cpp1
-rw-r--r--src/mongo/db/commands/repair_cursor.cpp1
-rw-r--r--src/mongo/db/commands/run_aggregate.cpp1
-rw-r--r--src/mongo/db/cursor_manager.cpp99
-rw-r--r--src/mongo/db/cursor_manager.h130
-rw-r--r--src/mongo/db/pipeline/document_source_cursor.cpp30
-rw-r--r--src/mongo/db/pipeline/document_source_cursor.h5
-rw-r--r--src/mongo/db/pipeline/process_interface_standalone.cpp1
-rw-r--r--src/mongo/db/query/find.cpp1
-rw-r--r--src/mongo/db/query/plan_executor.h12
-rw-r--r--src/mongo/db/query/plan_executor_impl.cpp5
-rw-r--r--src/mongo/db/query/plan_executor_impl.h2
-rw-r--r--src/mongo/db/repair_database.cpp2
-rw-r--r--src/mongo/db/service_context_d_test_fixture.cpp2
-rw-r--r--src/mongo/dbtests/cursor_manager_test.cpp11
-rw-r--r--src/mongo/dbtests/querytests.cpp7
-rw-r--r--src/mongo/embedded/embedded.cpp2
35 files changed, 143 insertions, 466 deletions
diff --git a/jstests/noPassthrough/cursor_timeout_interrupt.js b/jstests/noPassthrough/cursor_timeout_interrupt.js
deleted file mode 100644
index a97142d4975..00000000000
--- a/jstests/noPassthrough/cursor_timeout_interrupt.js
+++ /dev/null
@@ -1,61 +0,0 @@
-// Tests the cursor timeout background thread is not subject to interrupt when acquiring locks. This
-// test was designed to reproduce SERVER-33785.
-(function() {
- "use strict";
-
- // We're testing the client cursor monitor thread, so make it run with a higher frequency.
- const options = {setParameter: {clientCursorMonitorFrequencySecs: 0}};
- const conn = MongoRunner.runMongod(options);
- assert.neq(conn, null, `Mongod failed to start up with options ${tojson(options)}`);
- const db = conn.getDB("test");
- const coll = db.cursor_timeout_interrupt;
-
- // Create a collection and open a cursor on the collection.
- const batchSize = 10;
- for (var i = 1; i < 2 * batchSize; ++i) {
- assert.writeOK(coll.insert({}));
- }
- const cursor = coll.find({}).batchSize(batchSize);
- cursor.next();
- assert.eq(db.serverStatus().metrics.cursor.open.total, 1);
- const originalTopTotal = db.adminCommand({top: 1}).totals[coll.getFullName()].total;
-
- // Start an operation which is holding the lock the timeout thread will need. At a minimum, we
- // need someone to be holding the lock in order for the thread to ever bother checking for
- // interrupt when acquiring locks.
- const awaitConflictingLock = startParallelShell(
- () => assert.commandFailedWithCode(db.adminCommand({sleep: 1, lock: "w", secs: 5 * 60}),
- ErrorCodes.Interrupted),
- conn.port);
-
- // Enable a failpoint that will cause a thread to always be interrupted. Test that the client
- // cursor monitor thread ignores interrupts, including during lock acquisitions.
- assert.commandWorked(db.adminCommand({
- configureFailPoint: "checkForInterruptFail",
- mode: "alwaysOn",
- data: {threadName: "clientcursormon", chance: 1}
- }));
-
- // Lower the cursor timeout threshold to make the newly created cursor eligible for timeout.
- assert.commandWorked(db.adminCommand({setParameter: 1, cursorTimeoutMillis: 1}));
-
- // Wait to verify that the client cursor is blocked waiting on the thread holding the lock.
- assert.soon(() => db.currentOp({desc: "clientcursormon", active: true, waitingForLock: true})
- .inprog.length === 1);
-
- const sleepOps = db.getSiblingDB("admin").currentOp({"command.sleep": 1, active: true}).inprog;
- assert.eq(sleepOps.length, 1);
- const sleepOpId = sleepOps[0].opid;
- assert.commandWorked(db.adminCommand({killOp: 1, op: sleepOpId}));
-
- assert.soon(() => db.serverStatus().metrics.cursor.open.total == 0);
- assert.eq(db.serverStatus().metrics.cursor.timedOut, 1);
- assert.commandWorked(
- db.adminCommand({configureFailPoint: "checkForInterruptFail", mode: "off"}));
-
- // Test that the client cursor monitor does not increment any stats for top.
- assert.eq(db.adminCommand({top: 1}).totals[coll.getFullName()].total, originalTopTotal);
- awaitConflictingLock();
-
- MongoRunner.stopMongod(conn);
-}());
diff --git a/src/mongo/db/catalog/SConscript b/src/mongo/db/catalog/SConscript
index 1d1a5868aae..6b8390933ee 100644
--- a/src/mongo/db/catalog/SConscript
+++ b/src/mongo/db/catalog/SConscript
@@ -364,7 +364,6 @@ env.Library(
'$BUILD_DIR/mongo/db/index/index_access_method',
'$BUILD_DIR/mongo/db/index_d',
'$BUILD_DIR/mongo/db/op_observer',
- '$BUILD_DIR/mongo/db/query_exec',
'$BUILD_DIR/mongo/db/repl/drop_pending_collection_reaper',
'$BUILD_DIR/mongo/db/repl/oplog',
'$BUILD_DIR/mongo/db/server_options_core',
diff --git a/src/mongo/db/catalog/catalog_control.cpp b/src/mongo/db/catalog/catalog_control.cpp
index fa6ed43e9e5..151886c033c 100644
--- a/src/mongo/db/catalog/catalog_control.cpp
+++ b/src/mongo/db/catalog/catalog_control.cpp
@@ -80,8 +80,7 @@ MinVisibleTimestampMap closeCatalog(OperationContext* opCtx) {
// Close all databases.
log() << "closeCatalog: closing all databases";
- constexpr auto reason = "closing databases for closeCatalog";
- databaseHolder->closeAll(opCtx, reason);
+ databaseHolder->closeAll(opCtx);
// Close the storage engine's catalog.
log() << "closeCatalog: closing storage engine catalog";
diff --git a/src/mongo/db/catalog/collection.h b/src/mongo/db/catalog/collection.h
index 41ce7a9fc74..b32c0e449b8 100644
--- a/src/mongo/db/catalog/collection.h
+++ b/src/mongo/db/catalog/collection.h
@@ -42,7 +42,6 @@
#include "mongo/db/catalog/collection_info_cache.h"
#include "mongo/db/catalog/collection_options.h"
#include "mongo/db/concurrency/d_concurrency.h"
-#include "mongo/db/cursor_manager.h"
#include "mongo/db/logical_session_id.h"
#include "mongo/db/namespace_string.h"
#include "mongo/db/query/collation/collator_interface.h"
@@ -198,8 +197,6 @@ public:
virtual const RecordStore* getRecordStore() const = 0;
virtual RecordStore* getRecordStore() = 0;
- virtual CursorManager* getCursorManager() const = 0;
-
virtual bool requiresIdIndex() const = 0;
virtual Snapshotted<BSONObj> docFor(OperationContext* const opCtx, RecordId loc) const = 0;
diff --git a/src/mongo/db/catalog/collection_impl.cpp b/src/mongo/db/catalog/collection_impl.cpp
index a1e0153720e..54c44632f46 100644
--- a/src/mongo/db/catalog/collection_impl.cpp
+++ b/src/mongo/db/catalog/collection_impl.cpp
@@ -218,7 +218,6 @@ CollectionImpl::CollectionImpl(OperationContext* opCtx,
_parseValidationAction(_details->getCollectionOptions(opCtx).validationAction))),
_validationLevel(uassertStatusOK(
_parseValidationLevel(_details->getCollectionOptions(opCtx).validationLevel))),
- _cursorManager(std::make_unique<CursorManager>(_ns)),
_cappedNotifier(_recordStore->isCapped() ? stdx::make_unique<CappedInsertNotifier>()
: nullptr) {
@@ -799,7 +798,6 @@ Status CollectionImpl::truncate(OperationContext* opCtx) {
// 2) drop indexes
_indexCatalog->dropAllIndexes(opCtx, true);
- _cursorManager->invalidateAll(opCtx, false, "collection truncated");
// 3) truncate record store
auto status = _recordStore->truncate(opCtx);
@@ -822,7 +820,6 @@ void CollectionImpl::cappedTruncateAfter(OperationContext* opCtx, RecordId end,
BackgroundOperation::assertNoBgOpInProgForNs(ns());
invariant(_indexCatalog->numIndexesInProgress(opCtx) == 0);
- _cursorManager->invalidateAll(opCtx, false, "capped collection truncated");
_recordStore->cappedTruncateAfter(opCtx, end, inclusive);
}
@@ -1329,14 +1326,6 @@ void CollectionImpl::setNs(NamespaceString nss) {
_indexCatalog->setNs(_ns);
_infoCache->setNs(_ns);
_recordStore->setNs(_ns);
-
- // Until the query layer is prepared for cursors to survive renames, all cursors are killed when
- // the name of a collection changes. Therefore, the CursorManager should be empty. This means it
- // is safe to re-establish it with a new namespace by tearing down the old one and allocating a
- // new manager associated with the new name. This is done in order to ensure that the
- // 'globalCursorIdCache' maintains the correct mapping from cursor id "prefix" (the high order
- // bits) to namespace.
- _cursorManager = std::make_unique<CursorManager>(_ns);
}
void CollectionImpl::indexBuildSuccess(OperationContext* opCtx, IndexCatalogEntry* index) {
diff --git a/src/mongo/db/catalog/collection_impl.h b/src/mongo/db/catalog/collection_impl.h
index 149c77d75f6..1260150ca49 100644
--- a/src/mongo/db/catalog/collection_impl.h
+++ b/src/mongo/db/catalog/collection_impl.h
@@ -101,10 +101,6 @@ public:
return _recordStore;
}
- CursorManager* getCursorManager() const final {
- return _cursorManager.get();
- }
-
bool requiresIdIndex() const final;
Snapshotted<BSONObj> docFor(OperationContext* opCtx, RecordId loc) const final {
@@ -415,8 +411,6 @@ private:
ValidationAction _validationAction;
ValidationLevel _validationLevel;
- std::unique_ptr<CursorManager> _cursorManager;
-
// Notifier object for awaitData. Threads polling a capped collection for new data can wait
// on this object until notified of the arrival of new data.
//
diff --git a/src/mongo/db/catalog/collection_mock.h b/src/mongo/db/catalog/collection_mock.h
index 30e2ee1fee6..9f294717914 100644
--- a/src/mongo/db/catalog/collection_mock.h
+++ b/src/mongo/db/catalog/collection_mock.h
@@ -87,10 +87,6 @@ public:
std::abort();
}
- CursorManager* getCursorManager() const {
- std::abort();
- }
-
bool requiresIdIndex() const {
std::abort();
}
diff --git a/src/mongo/db/catalog/database.h b/src/mongo/db/catalog/database.h
index 74ff3b6f42b..66d40285b6c 100644
--- a/src/mongo/db/catalog/database.h
+++ b/src/mongo/db/catalog/database.h
@@ -132,7 +132,7 @@ public:
virtual void init(OperationContext* opCtx) = 0;
// closes files and other cleanup see below.
- virtual void close(OperationContext* const opCtx, const std::string& reason) = 0;
+ virtual void close(OperationContext* const opCtx) = 0;
virtual const std::string& name() const = 0;
diff --git a/src/mongo/db/catalog/database_holder.h b/src/mongo/db/catalog/database_holder.h
index 825d413e6d9..07fe4a5dfd8 100644
--- a/src/mongo/db/catalog/database_holder.h
+++ b/src/mongo/db/catalog/database_holder.h
@@ -90,17 +90,13 @@ public:
* Closes the specified database. Must be called with the database locked in X-mode.
* No background jobs must be in progress on the database when this function is called.
*/
- virtual void close(OperationContext* const opCtx,
- const StringData ns,
- const std::string& reason) = 0;
+ virtual void close(OperationContext* opCtx, const StringData ns) = 0;
/**
* Closes all opened databases. Must be called with the global lock acquired in X-mode.
* Will uassert if any background jobs are running when this function is called.
- *
- * @param reason The reason for close.
*/
- virtual void closeAll(OperationContext* const opCtx, const std::string& reason) = 0;
+ virtual void closeAll(OperationContext* opCtx) = 0;
/**
* Returns the set of existing database names that differ only in casing.
diff --git a/src/mongo/db/catalog/database_holder_impl.cpp b/src/mongo/db/catalog/database_holder_impl.cpp
index 483e96e4f17..f72e495875c 100644
--- a/src/mongo/db/catalog/database_holder_impl.cpp
+++ b/src/mongo/db/catalog/database_holder_impl.cpp
@@ -181,7 +181,7 @@ void DatabaseHolderImpl::dropDb(OperationContext* opCtx, Database* db) {
Top::get(serviceContext).collectionDropped(coll->ns().ns(), true);
}
- close(opCtx, name, "database dropped");
+ close(opCtx, name);
auto const storageEngine = serviceContext->getStorageEngine();
writeConflictRetry(opCtx, "dropDatabase", name, [&] {
@@ -198,7 +198,7 @@ void evictDatabaseFromUUIDCatalog(OperationContext* opCtx, Database* db) {
}
} // namespace
-void DatabaseHolderImpl::close(OperationContext* opCtx, StringData ns, const std::string& reason) {
+void DatabaseHolderImpl::close(OperationContext* opCtx, StringData ns) {
invariant(opCtx->lockState()->isW());
const StringData dbName = _todb(ns);
@@ -214,7 +214,7 @@ void DatabaseHolderImpl::close(OperationContext* opCtx, StringData ns, const std
repl::oplogCheckCloseDatabase(opCtx, db);
evictDatabaseFromUUIDCatalog(opCtx, db);
- db->close(opCtx, reason);
+ db->close(opCtx);
delete db;
db = nullptr;
@@ -226,7 +226,7 @@ void DatabaseHolderImpl::close(OperationContext* opCtx, StringData ns, const std
.transitional_ignore();
}
-void DatabaseHolderImpl::closeAll(OperationContext* opCtx, const std::string& reason) {
+void DatabaseHolderImpl::closeAll(OperationContext* opCtx) {
invariant(opCtx->lockState()->isW());
stdx::lock_guard<SimpleMutex> lk(_m);
@@ -243,7 +243,7 @@ void DatabaseHolderImpl::closeAll(OperationContext* opCtx, const std::string& re
Database* db = _dbs[name];
repl::oplogCheckCloseDatabase(opCtx, db);
evictDatabaseFromUUIDCatalog(opCtx, db);
- db->close(opCtx, reason);
+ db->close(opCtx);
delete db;
_dbs.erase(name);
diff --git a/src/mongo/db/catalog/database_holder_impl.h b/src/mongo/db/catalog/database_holder_impl.h
index 29b63a8e8ef..cf5f3c290bc 100644
--- a/src/mongo/db/catalog/database_holder_impl.h
+++ b/src/mongo/db/catalog/database_holder_impl.h
@@ -47,9 +47,9 @@ public:
void dropDb(OperationContext* opCtx, Database* db) override;
- void close(OperationContext* opCtx, StringData ns, const std::string& reason) override;
+ void close(OperationContext* opCtx, StringData ns) override;
- void closeAll(OperationContext* opCtx, const std::string& reason) override;
+ void closeAll(OperationContext* opCtx) override;
std::set<std::string> getNamesWithConflictingCasing(StringData name) override;
diff --git a/src/mongo/db/catalog/database_holder_mock.h b/src/mongo/db/catalog/database_holder_mock.h
index dc04fffdf3d..4e50b1a768b 100644
--- a/src/mongo/db/catalog/database_holder_mock.h
+++ b/src/mongo/db/catalog/database_holder_mock.h
@@ -48,9 +48,9 @@ public:
void dropDb(OperationContext* opCtx, Database* db) override {}
- void close(OperationContext* opCtx, StringData ns, const std::string& reason) override {}
+ void close(OperationContext* opCtx, StringData ns) override {}
- void closeAll(OperationContext* opCtx, const std::string& reason) override {}
+ void closeAll(OperationContext* opCtx) override {}
std::set<std::string> getNamesWithConflictingCasing(StringData name) override {
return std::set<std::string>();
diff --git a/src/mongo/db/catalog/database_impl.cpp b/src/mongo/db/catalog/database_impl.cpp
index 3945f6b8e93..688f2adc935 100644
--- a/src/mongo/db/catalog/database_impl.cpp
+++ b/src/mongo/db/catalog/database_impl.cpp
@@ -184,16 +184,11 @@ DatabaseImpl::~DatabaseImpl() {
delete i->second;
}
-void DatabaseImpl::close(OperationContext* opCtx, const std::string& reason) {
+void DatabaseImpl::close(OperationContext* opCtx) {
invariant(opCtx->lockState()->isW());
// Clear cache of oplog Collection pointer.
repl::oplogCheckCloseDatabase(opCtx, this);
-
- for (auto&& pair : _collections) {
- auto* coll = pair.second;
- coll->getCursorManager()->invalidateAll(opCtx, true, reason);
- }
}
Status DatabaseImpl::validateDBName(StringData dbname) {
@@ -656,8 +651,14 @@ Status DatabaseImpl::_finishDropCollection(OperationContext* opCtx,
// We want to destroy the Collection object before telling the StorageEngine to destroy the
// RecordStore.
- _clearCollectionCache(
- opCtx, fullns.toString(), "collection dropped", /*collectionGoingAway*/ true);
+ invariant(_name == fullns.db());
+ auto it = _collections.find(fullns.toString());
+
+ if (it != _collections.end()) {
+ // Takes ownership of the collection
+ opCtx->recoveryUnit()->registerChange(new RemoveCollectionChange(this, it->second));
+ _collections.erase(it);
+ }
auto uuid = collection->uuid();
auto uuidString = uuid ? uuid.get().toString() : "no UUID";
@@ -666,23 +667,6 @@ Status DatabaseImpl::_finishDropCollection(OperationContext* opCtx,
return _dbEntry->dropCollection(opCtx, fullns.toString());
}
-void DatabaseImpl::_clearCollectionCache(OperationContext* opCtx,
- StringData fullns,
- const std::string& reason,
- bool collectionGoingAway) {
- verify(_name == nsToDatabaseSubstring(fullns));
- CollectionMap::const_iterator it = _collections.find(fullns.toString());
-
- if (it == _collections.end())
- return;
-
- // Takes ownership of the collection
- opCtx->recoveryUnit()->registerChange(new RemoveCollectionChange(this, it->second));
-
- it->second->getCursorManager()->invalidateAll(opCtx, collectionGoingAway, reason);
- _collections.erase(it);
-}
-
Collection* DatabaseImpl::getCollection(OperationContext* opCtx, StringData ns) const {
NamespaceString nss(ns);
invariant(_name == nss.db());
@@ -729,16 +713,6 @@ Status DatabaseImpl::renameCollection(OperationContext* opCtx,
return Status(ErrorCodes::NamespaceNotFound, "collection not found to rename");
}
- string clearCacheReason = str::stream() << "renamed collection '" << fromNS << "' to '" << toNS
- << "'";
-
- // Notify the cursor manager that it should kill all the cursors in the source and target
- // collections. This is currently necessary since the query layer is not prepared for cursors to
- // survive collection renames.
- auto sourceManager = collToRename->getCursorManager();
- invariant(sourceManager);
- sourceManager->invalidateAll(opCtx, /*collectionGoingAway*/ true, clearCacheReason);
-
log() << "renameCollection: renaming collection " << collToRename->uuid()->toString()
<< " from " << fromNS << " to " << toNS;
diff --git a/src/mongo/db/catalog/database_impl.h b/src/mongo/db/catalog/database_impl.h
index 2130cdd9ce5..57af3693296 100644
--- a/src/mongo/db/catalog/database_impl.h
+++ b/src/mongo/db/catalog/database_impl.h
@@ -47,7 +47,7 @@ public:
// closes files and other cleanup see below.
- void close(OperationContext* opCtx, const std::string& reason) final;
+ void close(OperationContext* opCtx) final;
const std::string& name() const final {
return _name;
@@ -187,16 +187,6 @@ private:
const CollectionOptions& options);
/**
- * Deregisters and invalidates all cursors on collection 'fullns'. Callers must specify
- * 'reason' for why the cache is being cleared. If 'collectionGoingAway' is false,
- * unpinned cursors will not be killed.
- */
- void _clearCollectionCache(OperationContext* opCtx,
- StringData fullns,
- const std::string& reason,
- bool collectionGoingAway);
-
- /**
* Completes a collection drop by removing all the indexes and removing the collection itself
* from the storage engine.
*
diff --git a/src/mongo/db/catalog/index_catalog_impl.cpp b/src/mongo/db/catalog/index_catalog_impl.cpp
index 08498dd3d8b..dabaee65ed0 100644
--- a/src/mongo/db/catalog/index_catalog_impl.cpp
+++ b/src/mongo/db/catalog/index_catalog_impl.cpp
@@ -742,11 +742,6 @@ void IndexCatalogImpl::dropAllIndexes(OperationContext* opCtx,
BackgroundOperation::assertNoBgOpInProgForNs(_collection->ns().ns());
- // there may be pointers pointing at keys in the btree(s). kill them.
- // TODO: can this can only clear cursors on this index?
- _collection->getCursorManager()->invalidateAll(
- opCtx, false, "all indexes on collection dropped");
-
// make sure nothing in progress
massert(17348,
"cannot dropAllIndexes when index builds in progress",
@@ -880,17 +875,6 @@ Status IndexCatalogImpl::_dropIndex(OperationContext* opCtx, IndexCatalogEntry*
string indexName = entry->descriptor()->indexName();
string indexNamespace = entry->descriptor()->indexNamespace();
- // If any cursors could be using this index, invalidate them. Note that we do not use indexes
- // until they are ready, so we do not need to invalidate anything if the index fails while it
- // is being built.
- //
- // TODO only kill cursors that are actually using the index rather than everything on this
- // collection.
- if (entry->isReady(opCtx)) {
- _collection->getCursorManager()->invalidateAll(
- opCtx, false, str::stream() << "index '" << indexName << "' dropped");
- }
-
// --------- START REAL WORK ----------
audit::logDropIndex(&cc(), indexName, _collection->ns().ns());
@@ -1137,13 +1121,6 @@ const IndexDescriptor* IndexCatalogImpl::refreshEntry(OperationContext* opCtx,
const std::string indexName = oldDesc->indexName();
invariant(_collection->getCatalogEntry()->isIndexReady(opCtx, indexName));
- // Notify other users of the IndexCatalog that we're about to invalidate 'oldDesc'.
- const bool collectionGoingAway = false;
- _collection->getCursorManager()->invalidateAll(
- opCtx,
- collectionGoingAway,
- str::stream() << "definition of index '" << indexName << "' changed");
-
// Delete the IndexCatalogEntry that owns this descriptor. After deletion, 'oldDesc' is
// invalid and should not be dereferenced.
auto oldEntry = _readyIndexes.release(oldDesc);
diff --git a/src/mongo/db/catalog/index_catalog_impl.h b/src/mongo/db/catalog/index_catalog_impl.h
index 36d9e8c41e4..57a59bd6e33 100644
--- a/src/mongo/db/catalog/index_catalog_impl.h
+++ b/src/mongo/db/catalog/index_catalog_impl.h
@@ -153,9 +153,7 @@ public:
*
* Use this method to notify the IndexCatalog that the spec for this index has changed.
*
- * It is invalid to dereference 'oldDesc' after calling this method. This method broadcasts
- * an invalidateAll() on the cursor manager to notify other users of the IndexCatalog that
- * this descriptor is now invalid.
+ * It is invalid to dereference 'oldDesc' after calling this method.
*/
const IndexDescriptor* refreshEntry(OperationContext* opCtx,
const IndexDescriptor* oldDesc) override;
diff --git a/src/mongo/db/clientcursor.cpp b/src/mongo/db/clientcursor.cpp
index ec988a1ce32..c7412752356 100644
--- a/src/mongo/db/clientcursor.cpp
+++ b/src/mongo/db/clientcursor.cpp
@@ -47,6 +47,7 @@
#include "mongo/db/commands.h"
#include "mongo/db/commands/server_status.h"
#include "mongo/db/commands/server_status_metric.h"
+#include "mongo/db/cursor_manager.h"
#include "mongo/db/cursor_server_params.h"
#include "mongo/db/jsobj.h"
#include "mongo/db/query/explain.h"
@@ -79,7 +80,6 @@ long long ClientCursor::totalOpen() {
}
ClientCursor::ClientCursor(ClientCursorParams params,
- CursorManager* cursorManager,
CursorId cursorId,
OperationContext* operationUsingCursor,
Date_t now)
@@ -89,7 +89,6 @@ ClientCursor::ClientCursor(ClientCursorParams params,
_lsid(operationUsingCursor->getLogicalSessionId()),
_txnNumber(operationUsingCursor->getTxnNumber()),
_readConcernArgs(params.readConcernArgs),
- _cursorManager(cursorManager),
_originatingCommand(params.originatingCommandObj),
_queryOptions(params.queryOptions),
_lockPolicy(params.lockPolicy),
@@ -98,7 +97,6 @@ ClientCursor::ClientCursor(ClientCursorParams params,
_lastUseDate(now),
_createdDate(now),
_planSummary(Explain::getPlanSummary(_exec.get())) {
- invariant(_cursorManager);
invariant(_exec);
invariant(_operationUsingCursor);
@@ -131,7 +129,7 @@ void ClientCursor::dispose(OperationContext* opCtx) {
return;
}
- _exec->dispose(opCtx, _cursorManager);
+ _exec->dispose(opCtx);
_disposed = true;
}
@@ -163,7 +161,6 @@ ClientCursorPin::ClientCursorPin(OperationContext* opCtx, ClientCursor* cursor)
: _opCtx(opCtx), _cursor(cursor) {
invariant(_cursor);
invariant(_cursor->_operationUsingCursor);
- invariant(_cursor->_cursorManager);
invariant(!_cursor->_disposed);
// We keep track of the number of cursors currently pinned. The cursor can become unpinned
@@ -216,24 +213,13 @@ void ClientCursorPin::release() {
if (!_cursor)
return;
- // Note it's not safe to dereference _cursor->_cursorManager unless we know we haven't been
- // killed. If we're not locked we assume we haven't been killed because we're working with the
- // global cursor manager which never kills cursors.
- dassert(_opCtx->lockState()->isCollectionLockedForMode(_cursor->_nss.ns(), MODE_IS) ||
- _cursor->_cursorManager->isGlobalManager());
-
invariant(_cursor->_operationUsingCursor);
- if (_cursor->getExecutor()->isMarkedAsKilled()) {
- // The ClientCursor was killed while we had it. Therefore, it is our responsibility to
- // call dispose() and delete it.
- deleteUnderlying();
- } else {
- // Unpin the cursor under the collection cursor manager lock.
- _cursor->_cursorManager->unpin(
- _opCtx, std::unique_ptr<ClientCursor, ClientCursor::Deleter>(_cursor));
- cursorStatsOpenPinned.decrement();
- }
+ // Unpin the cursor. This must be done by calling into the cursor manager, since the cursor
+ // manager must acquire the appropriate mutex in order to safely perform the unpin operation.
+ CursorManager::getGlobalCursorManager()->unpin(
+ _opCtx, std::unique_ptr<ClientCursor, ClientCursor::Deleter>(_cursor));
+ cursorStatsOpenPinned.decrement();
_cursor = nullptr;
}
@@ -242,22 +228,15 @@ void ClientCursorPin::deleteUnderlying() {
invariant(_cursor);
invariant(_cursor->_operationUsingCursor);
// Note the following subtleties of this method's implementation:
- // - We must unpin the cursor before destruction, since it is an error to delete a pinned
- // cursor.
- // - In addition, we must deregister the cursor before unpinning, since it is an
- // error to unpin a registered cursor without holding the cursor manager lock (note that
- // we can't simply unpin with the cursor manager lock here, since we need to guarantee
- // exclusive ownership of the cursor when we are deleting it).
-
- // Note it's not safe to dereference _cursor->_cursorManager unless we know we haven't been
- // killed. If we're not locked we assume we haven't been killed because we're working with the
- // global cursor manager which never kills cursors.
- dassert(_opCtx->lockState()->isCollectionLockedForMode(_cursor->_nss.ns(), MODE_IS) ||
- _cursor->_cursorManager->isGlobalManager());
-
- if (!_cursor->getExecutor()->isMarkedAsKilled()) {
- _cursor->_cursorManager->deregisterCursor(_cursor);
- }
+ // - We must unpin the cursor (by clearing the '_operationUsingCursor' field) before
+ // destruction, since it is an error to delete a pinned cursor.
+ // - In addition, we must deregister the cursor before clearing the '_operationUsingCursor'
+ // field, since it is an error to unpin a registered cursor without holding the appropriate
+ // cursor manager mutex. By first deregistering the cursor, we ensure that no other thread can
+ // access '_cursor', meaning that it is safe for us to write to '_operationUsingCursor'
+ // without holding the CursorManager mutex.
+
+ CursorManager::getGlobalCursorManager()->deregisterCursor(_cursor);
// Make sure the cursor is disposed and unpinned before being destroyed.
_cursor->dispose(_opCtx);
diff --git a/src/mongo/db/clientcursor.h b/src/mongo/db/clientcursor.h
index 8e17f0e0bec..45eb548e495 100644
--- a/src/mongo/db/clientcursor.h
+++ b/src/mongo/db/clientcursor.h
@@ -44,7 +44,6 @@
namespace mongo {
class Collection;
-class CursorManager;
class RecoveryUnit;
/**
@@ -305,7 +304,6 @@ private:
* private. See cursor_manager.h for more details.
*/
ClientCursor(ClientCursorParams params,
- CursorManager* cursorManager,
CursorId cursorId,
OperationContext* operationUsingCursor,
Date_t now);
@@ -357,8 +355,6 @@ private:
const repl::ReadConcernArgs _readConcernArgs;
- CursorManager* _cursorManager;
-
// Tracks whether dispose() has been called, to make sure it happens before destruction. It is
// an error to use a ClientCursor once it has been disposed.
bool _disposed = false;
@@ -383,28 +379,17 @@ private:
// The underlying query execution machinery. Must be non-null.
std::unique_ptr<PlanExecutor, PlanExecutor::Deleter> _exec;
- //
- // The following fields are used by the CursorManager and the ClientCursorPin. In most
- // conditions, they can only be used while holding the CursorManager's mutex. Exceptions
- // include:
- // - If the ClientCursor is pinned, the CursorManager will never change '_isPinned' until
- // asked to by the ClientCursorPin.
- // - It is safe to read '_killed' while holding a collection lock, which must be held when
- // interacting with a ClientCursorPin.
- // - A ClientCursorPin can access these members after deregistering the cursor from the
- // CursorManager, at which point it has sole ownership of the ClientCursor.
- //
-
// While a cursor is being used by a client, it is marked as "pinned" by setting
// _operationUsingCursor to the current OperationContext.
//
- // Cursors always come into existence in a pinned state (this must be non-null at construction).
+ // Cursors always come into existence in a pinned state ('_operationUsingCursor' must be
+ // non-null at construction).
//
// To write to this field one of the following must be true:
// 1) You have a lock on the appropriate partition in CursorManager and the cursor is unpinned
// (the field is null).
- // 2) You own the cursor and the cursor manager it was associated with is gone (this can only
- // happen in ClientCursorPin). In this case, nobody else will try to pin the cursor.
+ // 2) The cursor has already been deregistered from the CursorManager. In this case, nobody else
+ // will try to pin the cursor.
//
// To read this field one of the following must be true:
// 1) You have a lock on the appropriate partition in CursorManager.
@@ -431,9 +416,7 @@ private:
*
* A pin extends the lifetime of a ClientCursor object until the pin's release. Pinned ClientCursor
* objects cannot not be killed due to inactivity, and cannot be immediately erased by user kill
- * requests (though they can be marked as interrupted). When a CursorManager is destroyed (e.g. by
- * a collection drop), ownership of any still-pinned ClientCursor objects is transferred to their
- * managing ClientCursorPin objects.
+ * requests (though they can be marked as interrupted).
*
* Example usage:
* {
@@ -448,14 +431,10 @@ private:
* // Use cursor. Pin automatically released on block exit.
* }
*
- * Clients that wish to access ClientCursor objects owned by collection cursor managers must hold
- * the collection lock while calling any pin method, including pin acquisition by the RAII
- * constructor and pin release by the RAII destructor. This guards from a collection drop (which
- * requires an exclusive lock on the collection) occurring concurrently with the pin request or
- * unpin request.
- *
- * Clients that wish to access ClientCursor objects owned by the global cursor manager need not
- * hold any locks; the global cursor manager can only be destroyed by a process exit.
+ * Callers need not hold any lock manager locks in order to obtain or release a client cursor pin.
+ * However, in order to use the ClientCursor itself, locks may need to be acquired. Whether locks
+ * are needed to use the ClientCursor can be determined by consulting the ClientCursor's lock
+ * policy.
*/
class ClientCursorPin {
MONGO_DISALLOW_COPYING(ClientCursorPin);
@@ -479,9 +458,8 @@ public:
~ClientCursorPin();
/**
- * Releases the pin. It does not delete the underlying cursor unless ownership has passed
- * to us after kill. Turns into a no-op if release() or deleteUnderlying() have already
- * been called on this pin.
+ * Releases the pin without deleting the underlying cursor. Turns into a no-op if release() or
+ * deleteUnderlying() have already been called on this pin.
*/
void release();
diff --git a/src/mongo/db/commands/find_cmd.cpp b/src/mongo/db/commands/find_cmd.cpp
index 0febcfc38c3..ad5b9c4d1fd 100644
--- a/src/mongo/db/commands/find_cmd.cpp
+++ b/src/mongo/db/commands/find_cmd.cpp
@@ -38,6 +38,7 @@
#include "mongo/db/commands.h"
#include "mongo/db/commands/run_aggregate.h"
#include "mongo/db/curop_failpoint_helpers.h"
+#include "mongo/db/cursor_manager.h"
#include "mongo/db/db_raii.h"
#include "mongo/db/exec/working_set_common.h"
#include "mongo/db/matcher/extensions_callback_real.h"
diff --git a/src/mongo/db/commands/repair_cursor.cpp b/src/mongo/db/commands/repair_cursor.cpp
index a02645209d0..b21d2c2e23c 100644
--- a/src/mongo/db/commands/repair_cursor.cpp
+++ b/src/mongo/db/commands/repair_cursor.cpp
@@ -38,6 +38,7 @@
#include "mongo/db/catalog/collection.h"
#include "mongo/db/client.h"
#include "mongo/db/commands.h"
+#include "mongo/db/cursor_manager.h"
#include "mongo/db/db_raii.h"
#include "mongo/db/exec/multi_iterator.h"
#include "mongo/db/namespace_string.h"
diff --git a/src/mongo/db/commands/run_aggregate.cpp b/src/mongo/db/commands/run_aggregate.cpp
index bafb689df8f..78463d65c63 100644
--- a/src/mongo/db/commands/run_aggregate.cpp
+++ b/src/mongo/db/commands/run_aggregate.cpp
@@ -40,6 +40,7 @@
#include "mongo/db/auth/authorization_session.h"
#include "mongo/db/catalog/database.h"
#include "mongo/db/curop.h"
+#include "mongo/db/cursor_manager.h"
#include "mongo/db/db_raii.h"
#include "mongo/db/exec/change_stream_proxy.h"
#include "mongo/db/exec/working_set_common.h"
diff --git a/src/mongo/db/cursor_manager.cpp b/src/mongo/db/cursor_manager.cpp
index 071983c65b4..6c8fcf0c29a 100644
--- a/src/mongo/db/cursor_manager.cpp
+++ b/src/mongo/db/cursor_manager.cpp
@@ -135,7 +135,7 @@ MONGO_INITIALIZER(GlobalCursorIdCache)(InitializerContext* context) {
MONGO_INITIALIZER_WITH_PREREQUISITES(GlobalCursorManager, ("GlobalCursorIdCache"))
(InitializerContext* context) {
- globalCursorManager.reset(new CursorManager({}));
+ globalCursorManager.reset(new CursorManager());
return Status::OK();
}
@@ -228,51 +228,7 @@ bool GlobalCursorIdCache::killCursor(OperationContext* opCtx, CursorId id, bool
}
std::size_t GlobalCursorIdCache::timeoutCursors(OperationContext* opCtx, Date_t now) {
- size_t totalTimedOut = 0;
-
- // Time out the cursors from the global cursor manager.
- totalTimedOut += globalCursorManager->timeoutCursors(opCtx, now);
-
- // Compute the set of collection names that we have to time out cursors for.
- vector<NamespaceString> todo;
- {
- stdx::lock_guard<SimpleMutex> lk(_mutex);
- for (auto&& entry : _idToNss) {
- todo.push_back(entry.second);
- }
- }
-
- // For each collection, time out its cursors under the collection lock (to prevent the
- // collection from going away during the erase).
- for (const auto& nsTodo : todo) {
- // We need to be careful to not use an AutoGet* helper, since we only need the lock to
- // protect potential access to the Collection's CursorManager, and those helpers may
- // do things we don't want here, like check the shard version or throw an exception if this
- // namespace has since turned into a view. Using Database::getCollection() will simply
- // return nullptr if the collection has since turned into a view. In this case, the cursors
- // will already have been cleaned up when the collection was dropped, so there will be none
- // left to time out.
- //
- // Additionally, we need to use the UninterruptibleLockGuard to ensure the lock acquisition
- // will not throw due to an interrupt. This method can be called from a background thread so
- // we do not want to throw any exceptions.
- UninterruptibleLockGuard noInterrupt(opCtx->lockState());
- AutoGetDb dbLock(opCtx, nsTodo.db(), MODE_IS);
- Lock::CollectionLock collLock(opCtx->lockState(), nsTodo.ns(), MODE_IS);
- if (!dbLock.getDb()) {
- continue;
- }
-
- Collection* const collection = dbLock.getDb()->getCollection(opCtx, nsTodo);
- if (!collection) {
- // The 'nsTodo' collection has been dropped since we held _mutex. We can safely skip it.
- continue;
- }
-
- totalTimedOut += collection->getCursorManager()->timeoutCursors(opCtx, now);
- }
-
- return totalTimedOut;
+ return globalCursorManager->timeoutCursors(opCtx, now);
}
} // namespace
@@ -280,31 +236,6 @@ std::size_t GlobalCursorIdCache::timeoutCursors(OperationContext* opCtx, Date_t
template <typename Visitor>
void GlobalCursorIdCache::visitAllCursorManagers(OperationContext* opCtx, Visitor* visitor) {
(*visitor)(*globalCursorManager);
-
- // Compute the set of collection names that we have to get sessions for
- vector<NamespaceString> namespaces;
- {
- stdx::lock_guard<SimpleMutex> lk(_mutex);
- for (auto&& entry : _idToNss) {
- namespaces.push_back(entry.second);
- }
- }
-
- // For each collection, get its sessions under the collection lock (to prevent the
- // collection from going away during the erase).
- for (auto&& ns : namespaces) {
- AutoGetCollection ctx(opCtx, ns, MODE_IS);
- if (!ctx.getDb()) {
- continue;
- }
-
- Collection* collection = ctx.getCollection();
- if (!collection) {
- continue;
- }
-
- (*visitor)(*(collection->getCursorManager()));
- }
}
// ---
@@ -366,32 +297,14 @@ Status CursorManager::withCursorManager(OperationContext* opCtx,
CursorId id,
const NamespaceString& nss,
stdx::function<Status(CursorManager*)> callback) {
- boost::optional<AutoGetCollectionForReadCommand> readLock;
- CursorManager* cursorManager = nullptr;
-
- if (CursorManager::isGloballyManagedCursor(id)) {
- cursorManager = CursorManager::getGlobalCursorManager();
- } else {
- readLock.emplace(opCtx, nss);
- Collection* collection = readLock->getCollection();
- if (!collection) {
- return {ErrorCodes::CursorNotFound,
- str::stream() << "collection does not exist: " << nss.ns()};
- }
- cursorManager = collection->getCursorManager();
- }
- invariant(cursorManager);
-
+ auto cursorManager = CursorManager::getGlobalCursorManager();
return callback(cursorManager);
}
// --------------------------
-CursorManager::CursorManager(NamespaceString nss)
- : _nss(std::move(nss)),
- _collectionCacheRuntimeId(_nss.isEmpty() ? 0
- : globalCursorIdCache->registerCursorManager(_nss)),
- _random(stdx::make_unique<PseudoRandom>(globalCursorIdCache->nextSeed())),
+CursorManager::CursorManager()
+ : _random(stdx::make_unique<PseudoRandom>(globalCursorIdCache->nextSeed())),
_cursorMap(stdx::make_unique<Partitioned<stdx::unordered_map<CursorId, ClientCursor*>>>()) {}
CursorManager::~CursorManager() {
@@ -652,7 +565,7 @@ ClientCursorPin CursorManager::registerCursor(OperationContext* opCtx,
stdx::lock_guard<SimpleMutex> lock(_registrationLock);
CursorId cursorId = allocateCursorId_inlock();
std::unique_ptr<ClientCursor, ClientCursor::Deleter> clientCursor(
- new ClientCursor(std::move(cursorParams), this, cursorId, opCtx, now));
+ new ClientCursor(std::move(cursorParams), cursorId, opCtx, now));
// Register this cursor for lookup by transaction.
if (opCtx->getLogicalSessionId() && opCtx->getTxnNumber()) {
diff --git a/src/mongo/db/cursor_manager.h b/src/mongo/db/cursor_manager.h
index b1b8beb1fe6..d0e89e1e6de 100644
--- a/src/mongo/db/cursor_manager.h
+++ b/src/mongo/db/cursor_manager.h
@@ -57,68 +57,92 @@ class PlanExecutor;
* ClientCursors. It is also responsible for allocating the cursor ids that are passed back to
* clients.
*
- * There is a CursorManager per-collection and a global CursorManager. The global CursorManager owns
- * cursors whose lifetime is not tied to that of the collection and which do not need to receive
- * notifications about writes for a particular collection. In contrast, cursors owned by a
- * collection's CursorManager, unless pinned, are destroyed when the collection is destroyed.
- *
- * Callers must hold the collection lock in at least MODE_IS in order to access a collection's
- * CursorManager, which guards against the CursorManager being concurrently deleted due to a
- * catalog-level operation such as a collection drop. No locks are required to access the global
- * cursor manager.
- *
- * The CursorManager is internally synchronized; operations on a given collection may call methods
- * concurrently on that collection's CursorManager.
+ * There is a process-global CursorManager on every mongod which is responsible for managing all
+ * open cursors on the node. No lock manager locks are required to access this global cursor
+ * manager. The CursorManager is internally synchronized, and unless otherwise noted its public
+ * methods are thread-safe. For scalability in circumstances where many threads may be concurrently
+ * accessing the CursorManager (i.e. a workload which runs many concurrent queries), the cursor
+ * manager's underlying data structure is partitioned. Each partition is protected by its own latch.
*
* See clientcursor.h for more information.
*/
class CursorManager {
public:
/**
+ * Returns a pointer to the process-global cursor manager.
+ */
+ static CursorManager* getGlobalCursorManager();
+
+ /**
* Appends the sessions that have open cursors on the global cursor manager and across
* all collection-level cursor managers to the given set of lsids.
*/
static void appendAllActiveSessions(OperationContext* opCtx, LogicalSessionIdSet* lsids);
/**
- * Returns a list of GenericCursors for all idle cursors on the global cursor manager and across
- * all collection-level cursor managers. Does not include currently pinned cursors.
- * 'userMode': If auth is on, calling with userMode as kExcludeOthers will cause this function
- * to only return cursors owned by the caller. If auth is off, this argument does not matter.
+ * Returns a list of GenericCursors for all idle cursors on global cursor manager. Does not
+ * include currently pinned cursors. 'userMode': If auth is on, calling with userMode as
+ * kExcludeOthers will cause this function to only return cursors owned by the caller. If auth
+ * is off, this argument does not matter.
+ *
+ * TODO SERVER-37454: This method should become non-static now that there are no more
+ * per-collection cursor managers.
*/
static std::vector<GenericCursor> getIdleCursors(
OperationContext* opCtx, MongoProcessInterface::CurrentOpUserMode userMode);
/**
- * Kills cursors with matching logical sessions. Returns a pair with the overall
- * Status of the operation and the number of cursors successfully killed.
+ * Kills cursors with matching logical sessions. Returns a pair with the overall Status of the
+ * operation and the number of cursors successfully killed.
*/
static std::pair<Status, int> killCursorsWithMatchingSessions(
OperationContext* opCtx, const SessionKiller::Matcher& matcher);
- CursorManager(NamespaceString nss);
-
/**
- * Destroys the CursorManager. All cursors must be cleaned up via invalidateAll() before
- * destruction.
+ * This method is deprecated. Do not add new call sites.
+ *
+ * TODO SERVER-37452: Delete this method.
*/
- ~CursorManager();
+ static bool isGloballyManagedCursor(CursorId cursorId) {
+ // The first two bits are 01 for globally managed cursors, and 00 for cursors owned by a
+ // collection. The leading bit is always 0 so that CursorIds do not appear as negative.
+ const long long mask = static_cast<long long>(0b11) << 62;
+ return (cursorId & mask) == (static_cast<long long>(0b01) << 62);
+ }
+
+ static int killCursorGlobalIfAuthorized(OperationContext* opCtx, int n, const char* ids);
+
+ static bool killCursorGlobalIfAuthorized(OperationContext* opCtx, CursorId id);
+
+ static bool killCursorGlobal(OperationContext* opCtx, CursorId id);
/**
- * Kills all managed ClientCursors. Callers must have exclusive access to the collection (i.e.
- * must have the collection, database, or global resource locked in MODE_X).
+ * Deletes inactive cursors from the global cursor manager. Returns the number of cursors that
+ * were timed out.
*
- * Must be called before the CursorManger is destroyed.
- *
- * 'collectionGoingAway' indicates whether the Collection instance is being deleted. This could
- * be because the db is being closed, or the collection/db is being dropped.
+ * 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);
+
+ /**
+ * This method is deprecated. Do not add new call sites.
*
- * For any cursors that are in use by an active operation, ownership is transferred to the
- * respective cursor pin objects.
+ * TODO SERVER-39065: Delete this method.
+ */
+ static Status withCursorManager(OperationContext* opCtx,
+ CursorId id,
+ const NamespaceString& nss,
+ stdx::function<Status(CursorManager*)> callback);
+
+ CursorManager();
+
+ ~CursorManager();
+
+ /**
+ * This method is deprecated. Do not add new call sites.
*
- * The 'reason' is the motivation for invalidating all cursors. This will be used for error
- * reporting and logging when an operation finds that the cursor it was operating on has been
- * killed.
+ * TODO SERVER-38288: Delete this method.
*/
void invalidateAll(OperationContext* opCtx,
bool collectionGoingAway,
@@ -196,40 +220,6 @@ public:
*/
std::size_t numCursors() const;
- static CursorManager* getGlobalCursorManager();
-
- /**
- * Returns true if this CursorId would be registered with the global CursorManager. Note that if
- * this method returns true it does not imply the cursor exists.
- */
- static bool isGloballyManagedCursor(CursorId cursorId) {
- // The first two bits are 01 for globally managed cursors, and 00 for cursors owned by a
- // collection. The leading bit is always 0 so that CursorIds do not appear as negative.
- const long long mask = static_cast<long long>(0b11) << 62;
- return (cursorId & mask) == (static_cast<long long>(0b01) << 62);
- }
-
- 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 and from all per-collection cursor
- * managers. Returns the number of cursors that were timed out.
- */
- static std::size_t timeoutCursorsGlobal(OperationContext* opCtx, Date_t now);
-
- /**
- * Locate the correct cursor manager for a given cursorId and execute the provided callback.
- * Returns ErrorCodes::CursorNotFound if cursorId does not exist.
- */
- static Status withCursorManager(OperationContext* opCtx,
- CursorId id,
- const NamespaceString& nss,
- stdx::function<Status(CursorManager*)> callback);
-
private:
static constexpr int kNumPartitions = 16;
friend class ClientCursorPin;
@@ -255,8 +245,10 @@ private:
}
// No locks are needed to consult these data members.
+ //
+ // TODO SERVER-37452: Delete these data members.
const NamespaceString _nss;
- const uint32_t _collectionCacheRuntimeId;
+ const uint32_t _collectionCacheRuntimeId = 0;
// A CursorManager holds a pointer to all open ClientCursors. ClientCursors are owned by the
// CursorManager, except when they are in use by a ClientCursorPin. When in use by a pin, an
diff --git a/src/mongo/db/pipeline/document_source_cursor.cpp b/src/mongo/db/pipeline/document_source_cursor.cpp
index b019bbf8e35..978f62e4f2b 100644
--- a/src/mongo/db/pipeline/document_source_cursor.cpp
+++ b/src/mongo/db/pipeline/document_source_cursor.cpp
@@ -140,7 +140,7 @@ void DocumentSourceCursor::loadBatch() {
// still held if '_exec' did not end in an error. If '_exec' encountered an error during a
// yield, the locks might be yielded.
if (state != PlanExecutor::DEAD && state != PlanExecutor::FAILURE) {
- cleanupExecutor(autoColl);
+ cleanupExecutor();
}
}
@@ -266,33 +266,7 @@ void DocumentSourceCursor::doDispose() {
void DocumentSourceCursor::cleanupExecutor() {
invariant(_exec);
- auto* opCtx = pExpCtx->opCtx;
- // We need to be careful to not use AutoGetCollection here, since we only need the lock to
- // protect potential access to the Collection's CursorManager, and AutoGetCollection may throw
- // if this namespace has since turned into a view. Using Database::getCollection() will simply
- // return nullptr if the collection has since turned into a view. In this case, '_exec' will
- // already have been marked as killed when the collection was dropped, and we won't need to
- // access the CursorManager to properly dispose of it.
- UninterruptibleLockGuard noInterrupt(opCtx->lockState());
- auto lockMode = getLockModeForQuery(opCtx);
- AutoGetDb dbLock(opCtx, _exec->nss().db(), lockMode);
- Lock::CollectionLock collLock(opCtx->lockState(), _exec->nss().ns(), lockMode);
- auto collection = dbLock.getDb() ? dbLock.getDb()->getCollection(opCtx, _exec->nss()) : nullptr;
- auto cursorManager = collection ? collection->getCursorManager() : nullptr;
- _exec->dispose(opCtx, cursorManager);
-
- // Not freeing _exec if we're in explain mode since it will be used in serialize() to gather
- // execution stats.
- if (!pExpCtx->explain) {
- _exec.reset();
- }
-}
-
-void DocumentSourceCursor::cleanupExecutor(const AutoGetCollectionForRead& readLock) {
- invariant(_exec);
- auto cursorManager =
- readLock.getCollection() ? readLock.getCollection()->getCursorManager() : nullptr;
- _exec->dispose(pExpCtx->opCtx, cursorManager);
+ _exec->dispose(pExpCtx->opCtx);
// Not freeing _exec if we're in explain mode since it will be used in serialize() to gather
// execution stats.
diff --git a/src/mongo/db/pipeline/document_source_cursor.h b/src/mongo/db/pipeline/document_source_cursor.h
index d15c23c2849..90f3c6d4f1c 100644
--- a/src/mongo/db/pipeline/document_source_cursor.h
+++ b/src/mongo/db/pipeline/document_source_cursor.h
@@ -189,11 +189,6 @@ private:
void cleanupExecutor();
/**
- * Destroys and de-registers '_exec'. '_exec' must be non-null.
- */
- void cleanupExecutor(const AutoGetCollectionForRead& readLock);
-
- /**
* Reads a batch of data from '_exec'. Subclasses can specify custom behavior to be performed on
* each document by overloading transformBSONObjToDocument().
*/
diff --git a/src/mongo/db/pipeline/process_interface_standalone.cpp b/src/mongo/db/pipeline/process_interface_standalone.cpp
index 4c247333272..f7736cd41b1 100644
--- a/src/mongo/db/pipeline/process_interface_standalone.cpp
+++ b/src/mongo/db/pipeline/process_interface_standalone.cpp
@@ -41,6 +41,7 @@
#include "mongo/db/catalog/uuid_catalog.h"
#include "mongo/db/concurrency/d_concurrency.h"
#include "mongo/db/curop.h"
+#include "mongo/db/cursor_manager.h"
#include "mongo/db/db_raii.h"
#include "mongo/db/index/index_descriptor.h"
#include "mongo/db/pipeline/document_source_cursor.h"
diff --git a/src/mongo/db/query/find.cpp b/src/mongo/db/query/find.cpp
index 765e79a7183..d6e4ce7a1e0 100644
--- a/src/mongo/db/query/find.cpp
+++ b/src/mongo/db/query/find.cpp
@@ -42,6 +42,7 @@
#include "mongo/db/commands.h"
#include "mongo/db/curop.h"
#include "mongo/db/curop_failpoint_helpers.h"
+#include "mongo/db/cursor_manager.h"
#include "mongo/db/db_raii.h"
#include "mongo/db/exec/filter.h"
#include "mongo/db/exec/working_set_common.h"
diff --git a/src/mongo/db/query/plan_executor.h b/src/mongo/db/query/plan_executor.h
index 6c8d5defe74..3ecee6edb5a 100644
--- a/src/mongo/db/query/plan_executor.h
+++ b/src/mongo/db/query/plan_executor.h
@@ -44,7 +44,6 @@ class BSONObj;
class CappedInsertNotifier;
struct CappedInsertNotifierData;
class Collection;
-class CursorManager;
class PlanExecutor;
class PlanStage;
class PlanYieldPolicy;
@@ -164,8 +163,7 @@ public:
*/
Deleter() = default;
- inline Deleter(OperationContext* opCtx, CursorManager* cursorManager)
- : _opCtx(opCtx), _cursorManager(cursorManager) {}
+ inline Deleter(OperationContext* opCtx) : _opCtx(opCtx) {}
/**
* If an owner of a std::unique_ptr<PlanExecutor, PlanExecutor::Deleter> wants to assume
@@ -185,7 +183,7 @@ public:
// It is illegal to invoke operator() on a default constructed Deleter.
invariant(_opCtx);
if (!_dismissed) {
- execPtr->dispose(_opCtx, _cursorManager);
+ execPtr->dispose(_opCtx);
}
delete execPtr;
} catch (...) {
@@ -196,7 +194,6 @@ public:
private:
OperationContext* _opCtx = nullptr;
- CursorManager* _cursorManager = nullptr;
bool _dismissed = false;
};
@@ -404,8 +401,7 @@ public:
/**
* Cleans up any state associated with this PlanExecutor. Must be called before deleting this
- * PlanExecutor. It is illegal to use a PlanExecutor after calling dispose(). 'cursorManager'
- * may be null.
+ * PlanExecutor. It is illegal to use a PlanExecutor after calling dispose().
*
* There are multiple cleanup scenarios:
* - This PlanExecutor will only ever use one OperationContext. In this case the
@@ -415,7 +411,7 @@ public:
* is the owner's responsibility to call dispose() with a valid OperationContext before
* deleting the PlanExecutor.
*/
- virtual void dispose(OperationContext* opCtx, CursorManager* cursorManager) = 0;
+ virtual void dispose(OperationContext* opCtx) = 0;
/**
* Helper method to aid in displaying an ExecState for debug or other recreational purposes.
diff --git a/src/mongo/db/query/plan_executor_impl.cpp b/src/mongo/db/query/plan_executor_impl.cpp
index 53d00097fd1..2b5aad16259 100644
--- a/src/mongo/db/query/plan_executor_impl.cpp
+++ b/src/mongo/db/query/plan_executor_impl.cpp
@@ -1,4 +1,3 @@
-
/**
* Copyright (C) 2018-present MongoDB, Inc.
*
@@ -204,7 +203,7 @@ StatusWith<unique_ptr<PlanExecutor, PlanExecutor::Deleter>> PlanExecutorImpl::ma
collection,
std::move(nss),
yieldPolicy);
- PlanExecutor::Deleter planDeleter(opCtx, collection ? collection->getCursorManager() : nullptr);
+ PlanExecutor::Deleter planDeleter(opCtx);
std::unique_ptr<PlanExecutor, PlanExecutor::Deleter> exec(execImpl, std::move(planDeleter));
// Perform plan selection, if necessary.
@@ -654,7 +653,7 @@ void PlanExecutorImpl::markAsKilled(Status killStatus) {
}
}
-void PlanExecutorImpl::dispose(OperationContext* opCtx, CursorManager* cursorManager) {
+void PlanExecutorImpl::dispose(OperationContext* opCtx) {
if (_currentState == kDisposed) {
return;
}
diff --git a/src/mongo/db/query/plan_executor_impl.h b/src/mongo/db/query/plan_executor_impl.h
index 79a13f1086d..1fc8e52f94d 100644
--- a/src/mongo/db/query/plan_executor_impl.h
+++ b/src/mongo/db/query/plan_executor_impl.h
@@ -71,7 +71,7 @@ public:
bool isEOF() final;
Status executePlan() final;
void markAsKilled(Status killStatus) final;
- void dispose(OperationContext* opCtx, CursorManager* cursorManager) final;
+ void dispose(OperationContext* opCtx) final;
void enqueue(const BSONObj& obj) final;
BSONObjSet getOutputSorts() const final;
bool isMarkedAsKilled() const final;
diff --git a/src/mongo/db/repair_database.cpp b/src/mongo/db/repair_database.cpp
index 56f7dd433d2..2e4007b90e1 100644
--- a/src/mongo/db/repair_database.cpp
+++ b/src/mongo/db/repair_database.cpp
@@ -282,7 +282,7 @@ Status repairDatabase(OperationContext* opCtx, StorageEngine* engine, const std:
// Close the db and invalidate all current users and caches.
auto databaseHolder = DatabaseHolder::get(opCtx);
- databaseHolder->close(opCtx, dbName, "database closed for repair");
+ databaseHolder->close(opCtx, dbName);
ON_BLOCK_EXIT([databaseHolder, &dbName, &opCtx] {
try {
// Ensure that we don't trigger an exception when attempting to take locks.
diff --git a/src/mongo/db/service_context_d_test_fixture.cpp b/src/mongo/db/service_context_d_test_fixture.cpp
index 6a51301cdeb..ce78b540797 100644
--- a/src/mongo/db/service_context_d_test_fixture.cpp
+++ b/src/mongo/db/service_context_d_test_fixture.cpp
@@ -98,7 +98,7 @@ ServiceContextMongoDTest::~ServiceContextMongoDTest() {
auto opCtx = getClient()->makeOperationContext();
Lock::GlobalLock glk(opCtx.get(), MODE_X);
auto databaseHolder = DatabaseHolder::get(opCtx.get());
- databaseHolder->closeAll(opCtx.get(), "all databases dropped");
+ databaseHolder->closeAll(opCtx.get());
}
IndexBuildsCoordinator::get(getServiceContext())->shutdown();
diff --git a/src/mongo/dbtests/cursor_manager_test.cpp b/src/mongo/dbtests/cursor_manager_test.cpp
index 08c072dcde0..dd734401e9d 100644
--- a/src/mongo/dbtests/cursor_manager_test.cpp
+++ b/src/mongo/dbtests/cursor_manager_test.cpp
@@ -50,6 +50,7 @@
#include "mongo/dbtests/dbtests.h"
#include "mongo/unittest/unittest.h"
#include "mongo/util/clock_source_mock.h"
+#include "mongo/util/scopeguard.h"
namespace mongo {
namespace {
@@ -91,7 +92,7 @@ public:
}
virtual ~CursorManagerTest() {
- _cursorManager.invalidateAll(_opCtx.get(), true, "end of test");
+ useCursorManager()->invalidateAll(_opCtx.get(), true, "end of test");
}
std::unique_ptr<PlanExecutor, PlanExecutor::Deleter> makeFakePlanExecutor() {
@@ -119,7 +120,7 @@ public:
}
ClientCursorPin makeCursor(OperationContext* opCtx) {
- return _cursorManager.registerCursor(opCtx, makeParams(opCtx));
+ return useCursorManager()->registerCursor(opCtx, makeParams(opCtx));
}
ClockSourceMock* useClock() {
@@ -127,7 +128,7 @@ public:
}
CursorManager* useCursorManager() {
- return &_cursorManager;
+ return CursorManager::getGlobalCursorManager();
}
protected:
@@ -136,7 +137,6 @@ protected:
private:
ClockSourceMock* _clock;
- CursorManager _cursorManager{NamespaceString{}};
};
class CursorManagerTestCustomOpCtx : public CursorManagerTest {
@@ -240,6 +240,7 @@ TEST_F(CursorManagerTest, InvalidatePinnedCursor) {
repl::ReadConcernArgs(repl::ReadConcernLevel::kLocalReadConcern),
BSONObj(),
ClientCursorParams::LockPolicy::kLocksInternally});
+ auto cursorFreer = makeGuard([&] { cursorPin.deleteUnderlying(); });
// If the cursor is pinned, it sticks around, even after invalidation.
ASSERT_EQUALS(1U, cursorManager->numCursors());
@@ -254,7 +255,6 @@ TEST_F(CursorManagerTest, InvalidatePinnedCursor) {
const Status status = WorkingSetCommon::getMemberObjectStatus(objOut);
ASSERT(status.reason().find(invalidateReason) != std::string::npos);
- cursorPin.release();
ASSERT_EQUALS(0U, cursorManager->numCursors());
}
@@ -417,6 +417,7 @@ TEST_F(CursorManagerTest, InactiveKilledCursorsThatAreStillPinnedShouldNotTimeou
repl::ReadConcernArgs(repl::ReadConcernLevel::kLocalReadConcern),
BSONObj(),
ClientCursorParams::LockPolicy::kLocksInternally});
+ auto cursorFreer = makeGuard([&] { cursorPin.deleteUnderlying(); });
const bool collectionGoingAway = false;
cursorManager->invalidateAll(
_opCtx.get(), collectionGoingAway, "KilledCursorsShouldTimeoutTest");
diff --git a/src/mongo/dbtests/querytests.cpp b/src/mongo/dbtests/querytests.cpp
index 5c7441c13fc..1c74a747090 100644
--- a/src/mongo/dbtests/querytests.cpp
+++ b/src/mongo/dbtests/querytests.cpp
@@ -37,6 +37,7 @@
#include "mongo/db/catalog/collection.h"
#include "mongo/db/client.h"
#include "mongo/db/clientcursor.h"
+#include "mongo/db/cursor_manager.h"
#include "mongo/db/db_raii.h"
#include "mongo/db/dbdirectclient.h"
#include "mongo/db/dbhelpers.h"
@@ -1261,11 +1262,7 @@ public:
}
size_t numCursorsOpen() {
- AutoGetCollectionForReadCommand ctx(&_opCtx, NamespaceString(_ns));
- Collection* collection = ctx.getCollection();
- if (!collection)
- return 0;
- return collection->getCursorManager()->numCursors();
+ return CursorManager::getGlobalCursorManager()->numCursors();
}
const char* ns() {
diff --git a/src/mongo/embedded/embedded.cpp b/src/mongo/embedded/embedded.cpp
index c190620a0ae..712dac1b8de 100644
--- a/src/mongo/embedded/embedded.cpp
+++ b/src/mongo/embedded/embedded.cpp
@@ -160,7 +160,7 @@ void shutdown(ServiceContext* srvContext) {
UninterruptibleLockGuard noInterrupt(shutdownOpCtx->lockState());
Lock::GlobalLock lk(shutdownOpCtx.get(), MODE_X);
auto databaseHolder = DatabaseHolder::get(shutdownOpCtx.get());
- databaseHolder->closeAll(shutdownOpCtx.get(), "shutdown");
+ databaseHolder->closeAll(shutdownOpCtx.get());
LogicalSessionCache::set(serviceContext, nullptr);