/**
* Copyright (C) 2008 10gen 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 .
*
* 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/cursor_id.h"
#include "mongo/db/jsobj.h"
#include "mongo/db/query/plan_executor.h"
#include "mongo/db/record_id.h"
#include "mongo/stdx/functional.h"
#include "mongo/util/net/message.h"
namespace mongo {
class Collection;
class CursorManager;
class RecoveryUnit;
/**
* Parameters used for constructing a ClientCursor. ClientCursors cannot be constructed in
* isolation, but rather must be constructed and managed using a CursorManager. See cursor_manager.h
* for more details.
*/
struct ClientCursorParams {
ClientCursorParams(PlanExecutor* exec,
std::string ns,
bool isReadCommitted,
int qopts = 0,
const BSONObj query = BSONObj(),
bool isAggCursor = false)
: exec(exec),
ns(std::move(ns)),
isReadCommitted(isReadCommitted),
qopts(qopts),
query(query),
isAggCursor(isAggCursor) {}
PlanExecutor* exec = nullptr;
const std::string ns;
bool isReadCommitted = false;
int qopts = 0;
const BSONObj query = BSONObj();
bool isAggCursor = false;
};
/**
* A ClientCursor is the server-side state associated with a particular cursor id. A cursor id is a
* handle that we return to the client for queries which require results to be returned in multiple
* batches. The client can manage the server-side cursor state by passing the cursor id back to the
* server for certain supported operations.
*
* For instance, a client can retrieve the next batch of results from the cursor by issuing a
* getMore on this cursor id. It can also request that server-side resources be freed by issuing a
* killCursors on a particular cursor id. This is useful if the client wishes to abandon the cursor
* without retrieving all results.
*
* ClientCursors cannot exist in isolation and must be created, accessed, and destroyed via a
* CursorManager. See cursor_manager.h for more details. Unless the ClientCursor is marked by the
* caller as "no timeout", it will be automatically destroyed by its cursor manager after a period
* of inactivity.
*/
class ClientCursor {
MONGO_DISALLOW_COPYING(ClientCursor);
public:
CursorId cursorid() const {
return _cursorid;
}
std::string ns() const {
return _ns;
}
bool isReadCommitted() const {
return _isReadCommitted;
}
bool isAggCursor() const {
return _isAggCursor;
}
PlanExecutor* getExecutor() const {
return _exec.get();
}
int queryOptions() const {
return _queryOptions;
}
const BSONObj& getQuery() const {
return _query;
}
/**
* Returns the total number of query results returned by the cursor so far.
*/
long long pos() const {
return _pos;
}
/**
* Increments the cursor's tracked number of query results returned so far by 'n'.
*/
void incPos(long long n) {
_pos += n;
}
/**
* Sets the cursor's tracked number of query results returned so far to 'n'.
*/
void setPos(long long n) {
_pos = n;
}
//
// Timing and timeouts.
//
/**
* Increments the amount of time for which this cursor believes it has been idle by 'millis'.
*
* After factoring in this additional idle time, returns whether or not this cursor now exceeds
* its idleness timeout and should be deleted.
*/
bool shouldTimeout(int millis);
/**
* Resets this cursor's idle time to zero. Only cursors whose idle time is sufficiently high
* will be deleted by the cursor manager's reaper thread.
*/
void resetIdleTime();
/**
* Returns the number of milliseconds for which this cursor believes it has been idle.
*/
int idleTime() const {
return _idleAgeMillis;
}
/**
* Returns the amount of time execution time available to this cursor. Only valid at the
* beginning of a getMore request, and only really for use by the maxTime tracking code.
*
* Microseconds::max() == infinity, values less than 1 mean no time left.
*/
Microseconds getLeftoverMaxTimeMicros() const {
return _leftoverMaxTimeMicros;
}
/**
* Sets the amount of execution time available to this cursor. This is only called when an
* operation that uses a cursor is finishing, to update its remaining time.
*
* Microseconds::max() == infinity, values less than 1 mean no time left.
*/
void setLeftoverMaxTimeMicros(Microseconds leftoverMaxTimeMicros) {
_leftoverMaxTimeMicros = leftoverMaxTimeMicros;
}
//
// Replication-related methods.
//
// Used to report replication position only in master-slave, so we keep them as TimeStamp rather
// than OpTime.
void updateSlaveLocation(OperationContext* txn);
void slaveReadTill(const Timestamp& t) {
_slaveReadTill = t;
}
/** Just for testing. */
Timestamp getSlaveReadTill() const {
return _slaveReadTill;
}
/**
* Returns the server-wide the count of living cursors. Such a cursor is called an "open
* cursor".
*/
static long long totalOpen();
private:
friend class CursorManager;
friend class ClientCursorPin;
/**
* Since the client cursor destructor is private, this is needed for using client cursors with
* smart pointers.
*/
struct Deleter {
void operator()(ClientCursor* cursor) {
delete cursor;
}
};
/**
* Constructs a ClientCursor. Since cursors must come into being registered and pinned, this is
* private. See cursor_manager.h for more details.
*/
ClientCursor(const ClientCursorParams& params, CursorManager* cursorManager, CursorId cursorId);
/**
* Constructs a special ClientCursor used to track sharding state for the given collection.
*/
ClientCursor(const Collection* collection, CursorManager* cursorManager, CursorId cursorId);
/**
* Destroys a ClientCursor. This is private, since only the CursorManager or the ClientCursorPin
* is allowed to destroy a cursor.
*
* Cursors must be unpinned and deregistered from the CursorManager before they can be
* destroyed.
*/
~ClientCursor();
void init();
/**
* Marks the cursor, and its underlying query plan, as killed.
*/
void kill();
// The ID of the ClientCursor. A value of 0 is used to mean that no cursor id has been assigned.
CursorId _cursorid = 0;
// The namespace we're operating on.
std::string _ns;
const bool _isReadCommitted = false;
// A pointer to the CursorManager which owns this cursor. This must be filled out when the
// cursor is constructed via the CursorManager.
//
// If '_cursorManager' is destroyed while this cursor is pinned, then ownership of the cursor is
// transferred to the ClientCursorPin. In this case, '_cursorManager' set back to null in order
// to indicate the ownership transfer.
CursorManager* _cursorManager = nullptr;
// Tracks the number of results returned by this cursor so far.
long long _pos = 0;
// If this cursor was created by a find operation, '_query' holds the query predicate for
// the find. If this cursor was created by a command (e.g. the aggregate command), then
// '_query' holds the command specification received from the client.
BSONObj _query;
// See the QueryOptions enum in dbclientinterface.h.
int _queryOptions = 0;
// Is this ClientCursor backed by an aggregation pipeline?
//
// Agg executors differ from others in that they manage their own locking internally and
// should not be killed or destroyed when the underlying collection is deleted.
//
// Note: This should *not* be set for the internal cursor used as input to an aggregation.
const bool _isAggCursor = false;
// While a cursor is being used by a client, it is marked as "pinned". See ClientCursorPin
// below.
//
// Cursors always come into existence in a pinned state.
bool _isPinned = true;
// Is the "no timeout" flag set on this cursor? If false, this cursor may be automatically
// deleted after an interval of inactivity.
bool _isNoTimeout = false;
// The replication position only used in master-slave.
Timestamp _slaveReadTill;
// How long has the cursor been idle?
int _idleAgeMillis = 0;
// Unused maxTime budget for this cursor.
Microseconds _leftoverMaxTimeMicros = Microseconds::max();
// The underlying query execution machinery.
std::unique_ptr _exec;
};
/**
* ClientCursorPin is an RAII class which must be used in order to access a cursor. On construction,
* the ClientCursorPin marks its cursor as in use, which is called "pinning" the cursor. On
* destructrution, the ClientCursorPin marks its cursor as no longer in use, which is called
* "unpinning" the cursor. Pinning is used to prevent multiple concurrent uses of the same cursor---
* pinned cursors cannot be killed or timed out and cannot be used concurrently by other operations
* such as getMore or killCursors. A pin is obtained using the CursorManager. See cursor_manager.h
* for more details.
*
* 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 killed by user
* kill requests. 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.
*
* Example usage:
* {
* StatusWith pin = cursorManager->pinCursor(cursorid);
* if (!pin.isOK()) {
* // No cursor with id 'cursorid' exists. Handle the error here. Pin automatically released
* // on block exit.
* return pin.getStatus();
* }
*
* ClientCursor* cursor = pin.getValue().getCursor();
* // 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.
*/
class ClientCursorPin {
MONGO_DISALLOW_COPYING(ClientCursorPin);
public:
/**
* Moves 'other' into 'this'. The 'other' pin must have a pinned cursor. Moving an empty pin
* into 'this' is illegal.
*/
ClientCursorPin(ClientCursorPin&& other);
/**
* Moves 'other' into 'this'. 'other' must have a pinned cursor and 'this' must have no pinned
* cursor.
*/
ClientCursorPin& operator=(ClientCursorPin&& other);
/**
* Calls release().
*/
~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.
*/
void release();
/**
* Deletes the underlying cursor. Cannot be called if release() or deleteUnderlying() have
* already been called on this pin.
*/
void deleteUnderlying();
/**
* Returns a pointer to the pinned cursor.
*/
ClientCursor* getCursor() const;
private:
friend class CursorManager;
ClientCursorPin(ClientCursor* cursor);
ClientCursor* _cursor = nullptr;
};
void startClientCursorMonitor();
} // namespace mongo