summaryrefslogtreecommitdiff
path: root/src/mongo/db/cursor_manager.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/mongo/db/cursor_manager.cpp')
-rw-r--r--src/mongo/db/cursor_manager.cpp118
1 files changed, 55 insertions, 63 deletions
diff --git a/src/mongo/db/cursor_manager.cpp b/src/mongo/db/cursor_manager.cpp
index f57c67a6c75..d80a33e62ae 100644
--- a/src/mongo/db/cursor_manager.cpp
+++ b/src/mongo/db/cursor_manager.cpp
@@ -53,7 +53,6 @@
#include "mongo/db/namespace_string.h"
#include "mongo/db/operation_context.h"
#include "mongo/db/query/plan_executor.h"
-#include "mongo/db/query/query_feature_flags_gen.h"
#include "mongo/db/query/query_knobs_gen.h"
#include "mongo/db/service_context.h"
#include "mongo/logv2/log.h"
@@ -62,6 +61,29 @@
namespace mongo {
+static Counter64 cursorStatsLifespanLessThan1Second;
+static Counter64 cursorStatsLifespanLessThan5Seconds;
+static Counter64 cursorStatsLifespanLessThan15Seconds;
+static Counter64 cursorStatsLifespanLessThan30Seconds;
+static Counter64 cursorStatsLifespanLessThan1Minute;
+static Counter64 cursorStatsLifespanLessThan10Minutes;
+static Counter64 cursorStatsLifespanGreaterThanOrEqual10Minutes;
+
+static ServerStatusMetricField<Counter64> dCursorStatsLifespanLessThan1Second(
+ "cursor.lifespan.lessThan1Second", &cursorStatsLifespanLessThan1Second);
+static ServerStatusMetricField<Counter64> dCursorStatsLifespanLessThan5Seconds(
+ "cursor.lifespan.lessThan5Seconds", &cursorStatsLifespanLessThan5Seconds);
+static ServerStatusMetricField<Counter64> dCursorStatsLifespanLessThan15Seconds(
+ "cursor.lifespan.lessThan15Seconds", &cursorStatsLifespanLessThan15Seconds);
+static ServerStatusMetricField<Counter64> dCursorStatsLifespanLessThan30Seconds(
+ "cursor.lifespan.lessThan30Seconds", &cursorStatsLifespanLessThan30Seconds);
+static ServerStatusMetricField<Counter64> dCursorStatsLifespanLessThan1Minute(
+ "cursor.lifespan.lessThan1Minute", &cursorStatsLifespanLessThan1Minute);
+static ServerStatusMetricField<Counter64> dCursorStatsLifespanLessThan10Minutes(
+ "cursor.lifespan.lessThan10Minutes", &cursorStatsLifespanLessThan10Minutes);
+static ServerStatusMetricField<Counter64> dCursorStatsLifespanGreaterThanOrEqual10Minutes(
+ "cursor.lifespan.greaterThanOrEqual10Minutes", &cursorStatsLifespanGreaterThanOrEqual10Minutes);
+
constexpr int CursorManager::kNumPartitions;
namespace {
@@ -74,6 +96,26 @@ ServiceContext::ConstructorActionRegisterer cursorManagerRegisterer{
auto cursorManager = std::make_unique<CursorManager>(svcCtx->getPreciseClockSource());
CursorManager::set(svcCtx, std::move(cursorManager));
}};
+
+void incrementCursorLifespanMetric(Date_t birth, Date_t death) {
+ auto elapsed = death - birth;
+
+ if (elapsed < Seconds(1)) {
+ cursorStatsLifespanLessThan1Second.increment();
+ } else if (elapsed < Seconds(5)) {
+ cursorStatsLifespanLessThan5Seconds.increment();
+ } else if (elapsed < Seconds(15)) {
+ cursorStatsLifespanLessThan15Seconds.increment();
+ } else if (elapsed < Seconds(30)) {
+ cursorStatsLifespanLessThan30Seconds.increment();
+ } else if (elapsed < Minutes(1)) {
+ cursorStatsLifespanLessThan1Minute.increment();
+ } else if (elapsed < Minutes(10)) {
+ cursorStatsLifespanLessThan10Minutes.increment();
+ } else {
+ cursorStatsLifespanGreaterThanOrEqual10Minutes.increment();
+ }
+}
} // namespace
CursorManager* CursorManager::get(ServiceContext* svcCtx) {
@@ -119,7 +161,7 @@ CursorManager::~CursorManager() {
for (auto&& cursor : *partition) {
// Callers must ensure that no cursors are in use.
invariant(!cursor.second->_operationUsingCursor);
- cursor.second->dispose(nullptr, boost::none);
+ cursor.second->dispose(nullptr);
delete cursor.second;
}
}
@@ -159,29 +201,11 @@ std::size_t CursorManager::timeoutCursors(OperationContext* opCtx, Date_t now) {
"Cursor timed out",
"cursorId"_attr = cursor->cursorid(),
"idleSince"_attr = cursor->getLastUseDate());
- cursor->dispose(opCtx, boost::none);
+ cursor->dispose(opCtx);
}
return toDisposeWithoutMutex.size();
}
-std::vector<CursorId> CursorManager::getCursorIdsForNamespace(const NamespaceString& nss) {
- std::vector<CursorId> cursorIds;
-
- // Lock and inspect one partition at a time in order to avoid contention. It is acceptable for
- // the output not to include info about cursors opened/closed while iterating.
- for (size_t partitionId = 0; partitionId < kNumPartitions; ++partitionId) {
- auto lockedPartition = _cursorMap->lockOnePartitionById(partitionId);
- for (auto it = lockedPartition->begin(); it != lockedPartition->end(); ++it) {
- auto* cursor = it->second;
- if (cursor->nss() == nss) {
- cursorIds.push_back(cursor->cursorid());
- }
- }
- }
-
- return cursorIds;
-}
-
StatusWith<ClientCursorPin> CursorManager::pinCursor(
OperationContext* opCtx,
CursorId id,
@@ -221,11 +245,6 @@ StatusWith<ClientCursorPin> CursorManager::pinCursor(
CurOp::get(opCtx)->debug().queryHash = cursor->_queryHash;
CurOp::get(opCtx)->debug().planCacheKey = cursor->_planCacheKey;
- // Pass along queryStats context so it is retrievable after query execution for storing metrics.
- CurOp::get(opCtx)->debug().queryStatsInfo.keyHash = cursor->_queryStatsKeyHash;
- CurOp::get(opCtx)->debug().queryStatsInfo.willNeverExhaust =
- cursor->_queryStatsWillNeverExhaust;
-
cursor->_operationUsingCursor = opCtx;
// We use pinning of a cursor as a proxy for active, user-initiated use of a cursor. Therefore,
@@ -262,7 +281,7 @@ void CursorManager::unpin(OperationContext* opCtx,
// interesting in proactively cleaning up that cursor's resources. In these cases, we
// proactively delete the cursor. In other cases we preserve the error code so that the client
// will see the reason the cursor was killed when asking for the next batch.
- if (interruptStatus == ErrorCodes::Interrupted || cursor->isKillPending()) {
+ if (interruptStatus == ErrorCodes::Interrupted || interruptStatus == ErrorCodes::CursorKilled) {
LOGV2(20530,
"removing cursor {cursor_cursorid} after completing batch: {error}",
"Removing cursor after completing batch",
@@ -270,7 +289,7 @@ void CursorManager::unpin(OperationContext* opCtx,
"error"_attr = interruptStatus);
return deregisterAndDestroyCursor(std::move(partition), opCtx, std::move(cursor));
} else if (!interruptStatus.isOK()) {
- cursor->getExecutor()->markAsKilled(interruptStatus);
+ cursor->markAsKilled(interruptStatus);
}
// The cursor will stay around in '_cursorMap', so release the unique pointer to avoid deleting
@@ -339,11 +358,8 @@ stdx::unordered_set<CursorId> CursorManager::getCursorsForOpKeys(
stdx::lock_guard<Latch> lk(_opKeyMutex);
for (auto opKey : opKeys) {
- if (auto it = _opKeyMap.find(opKey); it != _opKeyMap.end()) {
- for (auto cursor : it->second) {
- cursors.insert(cursor);
- }
- }
+ if (auto it = _opKeyMap.find(opKey); it != _opKeyMap.end())
+ cursors.insert(it->second);
}
return cursors;
}
@@ -390,12 +406,7 @@ ClientCursorPin CursorManager::registerCursor(OperationContext* opCtx,
// If set, store the mapping of OperationKey to the generated CursorID.
if (auto opKey = opCtx->getOperationKey()) {
stdx::lock_guard<Latch> lk(_opKeyMutex);
- auto it = _opKeyMap.find(*opKey);
- if (it != _opKeyMap.end()) {
- it->second.insert(cursorId);
- } else {
- _opKeyMap.emplace(*opKey, std::set<CursorId>{cursorId});
- }
+ _opKeyMap.emplace(*opKey, cursorId);
}
// Restores the maxTimeMS provided in the cursor generating command in the case it used
@@ -406,41 +417,26 @@ ClientCursorPin CursorManager::registerCursor(OperationContext* opCtx,
return ClientCursorPin(opCtx, unownedCursor, this);
}
-// Note the following subleties of the implementations of deregisterAndDestroyCursor:
-// - We must make sure the cursor is unpinned (by clearing the '_operationUsingCursor' field) before
-// destruction, since it is an error to delete a pinned cursor.
-// - In addition, we must deregister the cursor from the manager's map before clearing the
-// '_operationUsingCursor' field, since it is an error to unpin a registered cursor without
-// holidng 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.
-void CursorManager::deregisterAndDestroyCursor(
- OperationContext* opCtx, std::unique_ptr<ClientCursor, ClientCursor::Deleter> cursor) {
- removeCursorFromMap(_cursorMap, cursor.get());
- _destroyCursor(opCtx, std::move(cursor));
+void CursorManager::deregisterCursor(ClientCursor* cursor) {
+ removeCursorFromMap(_cursorMap, cursor);
+ incrementCursorLifespanMetric(cursor->_createdDate, _preciseClockSource->now());
}
void CursorManager::deregisterAndDestroyCursor(
Partitioned<stdx::unordered_map<CursorId, ClientCursor*>>::OnePartition&& lk,
OperationContext* opCtx,
std::unique_ptr<ClientCursor, ClientCursor::Deleter> cursor) {
- // Restrict the scope of the lock so we can destroy the cursor without holding any cursor
- // manager mutexes.
{
auto lockWithRestrictedScope = std::move(lk);
removeCursorFromMap(lockWithRestrictedScope, cursor.get());
}
- _destroyCursor(opCtx, std::move(cursor));
-}
-void CursorManager::_destroyCursor(OperationContext* opCtx,
- std::unique_ptr<ClientCursor, ClientCursor::Deleter> cursor) {
+ incrementCursorLifespanMetric(cursor->_createdDate, _preciseClockSource->now());
// Dispose of the cursor without holding any cursor manager mutexes. Disposal of a cursor can
// require taking lock manager locks, which we want to avoid while holding a mutex. If we did
// so, any caller of a CursorManager method which already held a lock manager lock could induce
// a deadlock when trying to acquire a CursorManager lock.
- cursor->dispose(opCtx, _preciseClockSource->now());
- cursor->_operationUsingCursor = nullptr;
+ cursor->dispose(opCtx);
}
Status CursorManager::killCursor(OperationContext* opCtx, CursorId id) {
@@ -460,10 +456,6 @@ Status CursorManager::killCursor(OperationContext* opCtx, CursorId id) {
cursor->_operationUsingCursor->getServiceContext()->killOperation(
lk, cursor->_operationUsingCursor, ErrorCodes::CursorKilled);
}
-
- // Mark that the cursor has been killed on the cursor object itself as well, as other errors
- // e.g. MaxTimeMSExpired may override the CursorKilled status.
- cursor->setKillPending(true);
return Status::OK();
}
std::unique_ptr<ClientCursor, ClientCursor::Deleter> ownedCursor(cursor);