/**
* 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/jsobj.h"
#include "mongo/db/query/plan_executor.h"
#include "mongo/db/record_id.h"
#include "mongo/util/net/message.h"
namespace mongo {
class Collection;
class CursorManager;
class RecoveryUnit;
typedef long long CursorId; /* passed to the client so it can send back on getMore */
static const CursorId INVALID_CURSOR_ID = -1; // But see SERVER-5726.
/**
* ClientCursor is a wrapper that represents a cursorid from our database application's
* perspective.
*/
class ClientCursor {
MONGO_DISALLOW_COPYING(ClientCursor);
public:
/**
* This ClientCursor constructor creates a cursorid that can be used with getMore and
* killCursors. "cursorManager" is the object that will manage the lifetime of this
* cursor, and "ns" is the namespace string that should be associated with this cursor (e.g.
* "test.foo", "test.$cmd.listCollections", etc).
*/
ClientCursor(CursorManager* cursorManager,
PlanExecutor* exec,
const std::string& ns,
bool isReadCommitted,
int qopts = 0,
const BSONObj query = BSONObj(),
bool isAggCursor = false);
/**
* This ClientCursor is used to track sharding state for the given collection.
*
* Do not use outside of RangePreserver!
*/
explicit ClientCursor(const Collection* collection);
//
// Basic accessors
//
CursorId cursorid() const {
return _cursorid;
}
std::string ns() const {
return _ns;
}
CursorManager* cursorManager() const {
return _cursorManager;
}
bool isReadCommitted() const {
return _isReadCommitted;
}
bool isAggCursor() const {
return _isAggCursor;
}
//
// Pinning functionality.
//
/**
* Marks this ClientCursor as in use. unsetPinned() must be called before the destructor of
* this ClientCursor is invoked.
*/
void setPinned() {
_isPinned = true;
}
/**
* Marks this ClientCursor as no longer in use.
*/
void unsetPinned() {
_isPinned = false;
}
bool isPinned() const {
return _isPinned;
}
/**
* This is called when someone is dropping a collection or something else that
* goes through killing cursors.
* It removes the responsiilibty of de-registering from ClientCursor.
* Responsibility for deleting the ClientCursor doesn't change from this call
* see PlanExecutor::kill.
*/
void kill();
//
// Timing and timeouts
//
/**
* @param millis amount of idle passed time since last call
* note called outside of locks (other than ccmutex) so care must be exercised
*/
bool shouldTimeout(int millis);
void setIdleTime(int millis);
int idleTime() const {
return _idleAgeMillis;
}
uint64_t getLeftoverMaxTimeMicros() const {
return _leftoverMaxTimeMicros;
}
void setLeftoverMaxTimeMicros(uint64_t leftoverMaxTimeMicros) {
_leftoverMaxTimeMicros = leftoverMaxTimeMicros;
}
//
// Replication-related stuff. TODO: Document and clean.
//
// 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;
}
//
// Query-specific functionality that may be adapted for the PlanExecutor.
//
PlanExecutor* getExecutor() const {
return _exec.get();
}
int queryOptions() const {
return _queryOptions;
}
const BSONObj& getQuery() const {
return _query;
}
// Used by ops/query.cpp to stash how many results have been returned by a query.
long long pos() const {
return _pos;
}
void incPos(long long n) {
_pos += n;
}
void setPos(long long n) {
_pos = n;
}
static long long totalOpen();
private:
friend class CursorManager;
friend class ClientCursorPin;
/**
* Only friends are allowed to destroy ClientCursor objects.
*/
~ClientCursor();
/**
* Initialization common between both constructors for the ClientCursor. The database must
* be stable when this is called, because cursors hang off the collection.
*/
void init();
//
// ClientCursor-specific data, independent of the underlying execution type.
//
// The ID of the ClientCursor.
CursorId _cursorid;
// The namespace we're operating on.
std::string _ns;
const bool _isReadCommitted;
CursorManager* _cursorManager;
// if we've added it to the total open counter yet
bool _countedYet;
// How many objects have been returned by the find() so far?
long long _pos;
// 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 dbclient.h
int _queryOptions;
// Is this ClientCursor backed by an aggregation pipeline? Defaults to false.
//
// 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;
// Is this cursor in use? Defaults to false.
bool _isPinned;
// Is the "no timeout" flag set on this cursor? If false, this cursor may be targeted for
// deletion after an interval of inactivity. Defaults to false.
bool _isNoTimeout;
// The replication position only used in master-slave.
Timestamp _slaveReadTill;
// How long has the cursor been idle?
int _idleAgeMillis;
// TODO: Document.
uint64_t _leftoverMaxTimeMicros;
//
// The underlying execution machinery.
//
std::unique_ptr _exec;
};
/**
* ClientCursorPin is an RAII class that manages the pinned state of a ClientCursor.
* ClientCursorPin objects pin the given cursor upon construction, and release the pin upon
* destruction.
*
* 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:
* {
* ClientCursorPin pin(cursorManager, cursorid);
* ClientCursor* cursor = pin.c();
* if (cursor) {
* // 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 during pin acquisition and pin release. 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:
/**
* Asks "cursorManager" to set a pin on the ClientCursor associated with "cursorid". If no
* such cursor exists, does nothing. If the cursor is already pinned, throws a
* UserException.
*/
ClientCursorPin(CursorManager* cursorManager, long long cursorid);
/**
* 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();
ClientCursor* c() const;
private:
ClientCursor* _cursor;
};
void startClientCursorMonitor();
} // namespace mongo