summaryrefslogtreecommitdiff
path: root/src/mongo/db/cursor_manager.h
diff options
context:
space:
mode:
authorADAM David Alan Martin <adam.martin@10gen.com>2017-06-05 11:07:00 -0400
committerADAM David Alan Martin <adam.martin@10gen.com>2017-06-05 11:07:00 -0400
commit7226d27bb57301fcfb995b22bcb3b0eee23ba540 (patch)
tree84f3b91260b4835a5c62488d66a39538f25a7794 /src/mongo/db/cursor_manager.h
parent6e41f26f639689ae55284af07abf033f8c7b3e7b (diff)
downloadmongo-7226d27bb57301fcfb995b22bcb3b0eee23ba540.tar.gz
SERVER-29253 Migrate cursor manager to the client cursor lib.
Putting `CursorManager` and `ClientCursor` in the same library allows us to remove a cycle exemption in cursor and to fully resolve mmap_v1 against dependencies.
Diffstat (limited to 'src/mongo/db/cursor_manager.h')
-rw-r--r--src/mongo/db/cursor_manager.h243
1 files changed, 243 insertions, 0 deletions
diff --git a/src/mongo/db/cursor_manager.h b/src/mongo/db/cursor_manager.h
new file mode 100644
index 00000000000..40408c48830
--- /dev/null
+++ b/src/mongo/db/cursor_manager.h
@@ -0,0 +1,243 @@
+/**
+ * Copyright (C) 2013 MongoDB Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the GNU Affero General Public License in all respects for
+ * all of the code used other than as permitted herein. If you modify file(s)
+ * with this exception, you may extend this exception to your version of the
+ * file(s), but you are not obligated to do so. If you do not wish to do so,
+ * delete this exception statement from your version. If you delete this
+ * exception statement from all source files in the program, then also delete
+ * it in the license file.
+ */
+
+#pragma once
+
+#include "mongo/db/catalog/util/partitioned.h"
+#include "mongo/db/clientcursor.h"
+#include "mongo/db/cursor_id.h"
+#include "mongo/db/invalidation_type.h"
+#include "mongo/db/namespace_string.h"
+#include "mongo/db/record_id.h"
+#include "mongo/platform/unordered_map.h"
+#include "mongo/platform/unordered_set.h"
+#include "mongo/util/concurrency/mutex.h"
+#include "mongo/util/duration.h"
+
+namespace mongo {
+
+class OperationContext;
+class PseudoRandom;
+class PlanExecutor;
+
+/**
+ * A container which owns ClientCursor objects. This class is used to create, access, and delete
+ * ClientCursors. It is also responsible for allocating the cursor ids that are passed back to
+ * clients.
+ *
+ * In addition to managing the lifetime of ClientCursors, the CursorManager is responsible for
+ * notifying yielded queries of write operations and collection drops. For this reason, query
+ * PlanExecutor objects which are not contained within a ClientCursor are also registered with the
+ * CursorManager. Query executors must be registered with the CursorManager, either as a bare
+ * PlanExecutor or inside a ClientCursor (but cannot be registered in both ways).
+ *
+ * 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. Such
+ * cursors receive notifications about writes to the collection.
+ *
+ * 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.
+ *
+ * See clientcursor.h for more information.
+ */
+class CursorManager {
+public:
+ // The number of minutes a cursor is allowed to be idle before timing out.
+ static constexpr Minutes kDefaultCursorTimeoutMinutes{10};
+ using RegistrationToken = Partitioned<unordered_set<PlanExecutor*>>::PartitionId;
+
+ CursorManager(NamespaceString nss);
+
+ /**
+ * Destroys the CursorManager. All cursors and PlanExecutors must be cleaned up via
+ * invalidateAll() before destruction.
+ */
+ ~CursorManager();
+
+ /**
+ * Kills all managed query executors and ClientCursors. Callers must have exclusive access to
+ * the collection (i.e. must have the collection, databse, or global resource locked in MODE_X).
+ *
+ * '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.
+ *
+ * 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.
+ */
+ void invalidateAll(OperationContext* opCtx,
+ bool collectionGoingAway,
+ const std::string& reason);
+
+ /**
+ * Broadcast a document invalidation to all relevant PlanExecutor(s). invalidateDocument
+ * must called *before* the provided RecordId is about to be deleted or mutated.
+ */
+ void invalidateDocument(OperationContext* opCtx, const RecordId& dl, InvalidationType type);
+
+ /**
+ * Destroys cursors that have been inactive for too long.
+ *
+ * Returns the number of cursors that were timed out.
+ */
+ std::size_t timeoutCursors(OperationContext* opCtx, Date_t now);
+
+ /**
+ * Register an executor so that it can be notified of deletions, invalidations, collection
+ * drops, or the like during yields. Must be called before an executor yields. Registration
+ * happens automatically for yielding PlanExecutors, so this should only be called by a
+ * PlanExecutor itself. Returns a token that must be stored for use during deregistration.
+ */
+ Partitioned<unordered_set<PlanExecutor*>>::PartitionId registerExecutor(PlanExecutor* exec);
+
+ /**
+ * Remove an executor from the registry. It is legal to call this even if 'exec' is not
+ * registered.
+ */
+ void deregisterExecutor(PlanExecutor* exec);
+
+ /**
+ * Constructs a new ClientCursor according to the given 'cursorParams'. The cursor is atomically
+ * registered with the manager and returned in pinned state.
+ */
+ ClientCursorPin registerCursor(OperationContext* opCtx, ClientCursorParams&& cursorParams);
+
+ /**
+ * Pins and returns the cursor with the given id.
+ *
+ * Returns ErrorCodes::CursorNotFound if the cursor does not exist or
+ * ErrorCodes::QueryPlanKilled if the cursor was killed in between uses.
+ *
+ * Throws a UserException if the cursor is already pinned. Callers need not specially handle
+ * this error, as it should only happen if a misbehaving client attempts to simultaneously issue
+ * two operations against the same cursor id.
+ */
+ StatusWith<ClientCursorPin> pinCursor(OperationContext* opCtx, CursorId id);
+
+ /**
+ * Returns an OK status if the cursor was successfully erased.
+ *
+ * Returns ErrorCodes::CursorNotFound if the cursor id is not owned by this manager. Returns
+ * ErrorCodes::OperationFailed if attempting to erase a pinned cursor.
+ *
+ * If 'shouldAudit' is true, will perform audit logging.
+ */
+ Status eraseCursor(OperationContext* opCtx, CursorId id, bool shouldAudit);
+
+ void getCursorIds(std::set<CursorId>* openCursors) const;
+
+ /**
+ * Returns the number of ClientCursors currently registered. Excludes any registered bare
+ * PlanExecutors.
+ */
+ 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 eraseCursorGlobalIfAuthorized(OperationContext* opCtx, int n, const char* ids);
+
+ static bool eraseCursorGlobalIfAuthorized(OperationContext* opCtx, CursorId id);
+
+ static bool eraseCursorGlobal(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);
+
+private:
+ static constexpr int kNumPartitions = 16;
+ friend class ClientCursorPin;
+
+ struct PlanExecutorPartitioner {
+ std::size_t operator()(const PlanExecutor* exec, std::size_t nPartitions);
+ };
+ CursorId allocateCursorId_inlock();
+
+ ClientCursorPin _registerCursor(
+ OperationContext* opCtx, std::unique_ptr<ClientCursor, ClientCursor::Deleter> clientCursor);
+
+ void deregisterCursor(ClientCursor* cc);
+
+ void unpin(OperationContext* opCtx, ClientCursor* cursor);
+
+ bool cursorShouldTimeout_inlock(const ClientCursor* cursor, Date_t now);
+
+ bool isGlobalManager() const {
+ return _nss.isEmpty();
+ }
+
+ // No locks are needed to consult these data members.
+ const NamespaceString _nss;
+ const uint32_t _collectionCacheRuntimeId;
+
+ // A CursorManager holds a pointer to all open PlanExecutors and all open ClientCursors. All
+ // pointers to PlanExecutors are unowned, and a PlanExecutor will notify the CursorManager when
+ // it is being destroyed. ClientCursors are owned by the CursorManager, except when they are in
+ // use by a ClientCursorPin. When in use by a pin, an unowned pointer remains to ensure they
+ // still receive invalidations while in use.
+ //
+ // There are several mutexes at work to protect concurrent access to data structures managed by
+ // this cursor manager. The two registration data structures '_registeredPlanExecutors' and
+ // '_cursorMap' are partitioned to decrease contention, and each partition of the structure is
+ // protected by its own mutex. Separately, there is a '_registrationLock' which protects
+ // concurrent access to '_random' for cursor id generation, and must be held from cursor id
+ // generation until insertion into '_cursorMap'. If you ever need to acquire more than one of
+ // these mutexes at once, you must follow the following rules:
+ // - '_registrationLock' must be acquired first, if at all.
+ // - Mutex(es) for '_registeredPlanExecutors' must be acquired next.
+ // - Mutex(es) for '_cursorMap' must be acquired next.
+ // - If you need to access multiple partitions within '_registeredPlanExecutors' or '_cursorMap'
+ // at once, you must acquire the mutexes for those partitions in ascending order, or use the
+ // partition helpers to acquire mutexes for all partitions.
+ mutable SimpleMutex _registrationLock;
+ std::unique_ptr<PseudoRandom> _random;
+ Partitioned<unordered_set<PlanExecutor*>, kNumPartitions, PlanExecutorPartitioner>
+ _registeredPlanExecutors;
+ std::unique_ptr<Partitioned<unordered_map<CursorId, ClientCursor*>, kNumPartitions>> _cursorMap;
+};
+} // namespace mongo