/**
* 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/pch.h"
#include
#include "mongo/db/cc_by_loc.h"
#include "mongo/db/cursor.h"
#include "mongo/db/diskloc.h"
#include "mongo/db/dbhelpers.h"
#include "mongo/db/jsobj.h"
#include "mongo/db/keypattern.h"
#include "mongo/db/matcher.h"
#include "mongo/db/projection.h"
#include "mongo/db/query/runner.h"
#include "mongo/s/collection_metadata.h"
#include "mongo/util/net/message.h"
#include "mongo/util/background.h"
#include "mongo/util/elapsed_tracker.h"
namespace mongo {
typedef boost::recursive_mutex::scoped_lock recursive_scoped_lock;
class ClientCursor;
class ParsedQuery;
/**
* ClientCursor is a wrapper that represents a cursorid from our database application's
* perspective.
*/
class ClientCursor : private boost::noncopyable {
public:
ClientCursor(int qopts, const shared_ptr& c, const StringData& ns,
BSONObj query = BSONObj());
ClientCursor(Runner* runner, int qopts = 0, const BSONObj query = BSONObj());
~ClientCursor();
/**
* Assert that there are no open cursors.
* Called from DatabaseHolder::closeAll.
*/
static void assertNoCursors();
//
// Basic accessors
//
CursorId cursorid() const { return _cursorid; }
string ns() const { return _ns; }
Database * db() const { return _db; }
//
// Invalidation of DiskLocs and dropping of namespaces
//
/**
* Get rid of cursors for namespaces 'ns'. When dropping a db, ns is "dbname." Used by drop,
* dropIndexes, dropDatabase.
*/
static void invalidate(const StringData& ns);
/**
* Called when the provided DiskLoc is about to change state via a deletion or an update.
* All runners/cursors that might be using that DiskLoc must adapt.
*/
static void aboutToDelete(const StringData& ns,
const NamespaceDetails* nsd,
const DiskLoc& dl);
/**
* Register a runner so that it can be notified of deletion/invalidation during yields.
* Must be called before a runner yields. If a runner is cached (inside a ClientCursor) it
* MUST NOT be registered; the two are mutually exclusive.
*/
static void registerRunner(Runner* runner);
/**
* Remove a runner from the runner registry.
*/
static void deregisterRunner(Runner* runner);
//
// Yielding.
//
static void staticYield(int micros, const StringData& ns, Record* rec);
//
// Static methods about all ClientCursors TODO: Document.
//
static void appendStats( BSONObjBuilder& result );
//
// ClientCursor creation/deletion.
//
static unsigned numCursors() { return clientCursorsById.size(); }
static void find( const string& ns , set& all );
static ClientCursor* find(CursorId id, bool warn = true);
// Same as erase but checks to make sure this thread has read permission on the cursor's
// namespace. This should be called when receiving killCursors from a client. This should
// not be called when ccmutex is held.
static int eraseIfAuthorized(int n, long long* ids);
static bool eraseIfAuthorized(CursorId id);
/**
* @return number of cursors found
*/
static int erase(int n, long long* ids);
/**
* Deletes the cursor with the provided @param 'id' if one exists.
* @throw if the cursor with the provided id is pinned.
* This does not do any auth checking and should be used only when erasing cursors as part
* of cleaning up internal operations.
*/
static bool erase(CursorId id);
//
// Timing and timeouts
//
/**
* called every 4 seconds. millis is amount of idle time passed since the last call --
* could be zero
*/
static void idleTimeReport(unsigned millis);
/**
* @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( unsigned millis );
unsigned idleTime() const { return _idleAgeMillis; }
uint64_t getLeftoverMaxTimeMicros() const { return _leftoverMaxTimeMicros; }
void setLeftoverMaxTimeMicros( uint64_t leftoverMaxTimeMicros ) {
_leftoverMaxTimeMicros = leftoverMaxTimeMicros;
}
//
// Sharding-specific data. TODO: Document.
//
// future getMore.
void setCollMetadata( CollectionMetadataPtr metadata ){ _collMetadata = metadata; }
CollectionMetadataPtr getCollMetadata(){ return _collMetadata; }
//
// Replication-related stuff. TODO: Document and clean.
//
void updateSlaveLocation( CurOp& curop );
void slaveReadTill( const OpTime& t ) { _slaveReadTill = t; }
/** Just for testing. */
OpTime getSlaveReadTill() const { return _slaveReadTill; }
//
// Query-specific functionality that may be adapted for the Runner.
//
Runner* getRunner() const { return _runner.get(); }
int queryOptions() const { return _queryOptions; }
// Used by ops/query.cpp to stash how many results have been returned by a query.
int pos() const { return _pos; }
void incPos(int n) { _pos += n; }
void setPos(int n) { _pos = n; }
//
// Yielding that is DEPRECATED. Will be removed when we use runners and they yield
// internally.
//
/**
* DEPRECATED
* @param microsToSleep -1 : ask client
* 0 : pthread_yield or equivilant
* >0 : sleep for that amount
* @param recordToLoad after yielding lock, load this record with only mmutex
* do a dbtemprelease
* note: caller should check matcher.docMatcher().atomic() first and not yield if atomic -
* we don't do herein as this->matcher (above) is only initialized for true queries/getmore.
* (ie not set for remote/update)
* @return if the cursor is still valid.
* if false is returned, then this ClientCursor should be considered deleted -
* in fact, the whole database could be gone.
*/
bool yield( int microsToSleep = -1, Record * recordToLoad = 0 );
enum RecordNeeds {
DontNeed = -1 , MaybeCovered = 0 , WillNeed = 100
};
/**
* @param needRecord whether or not the next record has to be read from disk for sure
* if this is true, will yield of next record isn't in memory
* @param yielded true if a yield occurred, and potentially if a yield did not occur
* @return same as yield()
*/
bool yieldSometimes( RecordNeeds need, bool *yielded = 0 );
struct YieldData { CursorId _id; bool _doingDeletes; };
bool prepareToYield( YieldData &data );
static bool recoverFromYield( const YieldData &data );
static int suggestYieldMicros();
//
// Cursor-only DEPRECATED methods.
//
void storeOpForSlave( DiskLoc last );
// Only used by ops/query.cpp, which will stop using them when queries are answered only by
// a runner.
const BSONObj& query() const { return _query; }
shared_ptr pq;
// This one is used also by pipeline/document_source_cursor.cpp
shared_ptr fields; // which fields query wants returned
DiskLoc lastLoc() const { return _lastLoc; }
Cursor* c() const { return _c.get(); }
bool ok() { return _c->ok(); }
bool advance() { return _c->advance(); }
BSONObj current() { return _c->current(); }
DiskLoc currLoc() { return _c->currLoc(); }
BSONObj currKey() const { return _c->currKey(); }
bool currentIsDup() { return _c->getsetdup( _c->currLoc() ); }
bool currentMatches() {
if ( ! _c->matcher() )
return true;
return _c->matcher()->matchesCurrent( _c.get() );
}
void setDoingDeletes( bool doingDeletes ) {_doingDeletes = doingDeletes; }
private:
friend class ClientCursorHolder;
friend class ClientCursorPin;
friend struct ClientCursorYieldLock;
friend class CmdCursorInfo;
// A map from the CursorId to the ClientCursor behind it.
// TODO: Consider making this per-connection.
typedef map CCById;
static CCById clientCursorsById;
// A list of NON-CACHED runners. Any runner that yields must be put into this map before
// yielding in order to be notified of invalidation and namespace deletion. Before the
// runner is deleted, it must be removed from this map.
//
// TODO: This is temporary and as such is highly NOT optimized.
static set nonCachedRunners;
// How many cursors have timed out?
static long long numberTimedOut;
// This must be held when modifying any static member.
static boost::recursive_mutex& ccmutex;
/**
* Initialization common between Cursor and Runner.
* TODO: Remove when we're all-runner.
*/
void init();
/**
* Allocates a new CursorId.
* Called from init(...). Assumes ccmutex held.
*/
static CursorId allocCursorId_inlock();
/**
* Find the ClientCursor with the provided ID. Optionally warn if it's not found.
* Assumes ccmutex is held.
*/
static ClientCursor* find_inlock(CursorId id, bool warn = true);
/**
* Delete the ClientCursor with the provided ID. masserts if the cursor is pinned.
*/
static void _erase_inlock(ClientCursor* cursor);
//
// ClientCursor-specific data, independent of the underlying execution type.
//
// The ID of the ClientCursor.
CursorId _cursorid;
// A variable indicating the state of the ClientCursor. Possible values:
// 0: Normal behavior. May time out.
// 1: No timing out of this ClientCursor.
// 100: Currently in use (via ClientCursorPin).
unsigned _pinValue;
// The namespace we're operating on.
string _ns;
// The database we're operating on.
Database* _db;
// How many objects have been returned by the find() so far?
int _pos;
// The query that prompted this ClientCursor. Only used for debugging.
BSONObj _query;
// See the QueryOptions enum in dbclient.h
int _queryOptions;
// TODO: document better.
OpTime _slaveReadTill;
// How long has the cursor been idle?
unsigned _idleAgeMillis;
// TODO: Document.
uint64_t _leftoverMaxTimeMicros;
// For chunks that are being migrated, there is a period of time when that chunks data is in
// two shards, the donor and the receiver one. That data is picked up by a cursor on the
// receiver side, even before the migration was decided. The CollectionMetadata allow one
// to inquiry if any given document of the collection belongs indeed to this shard or if it
// is coming from (or a vestige of) an ongoing migration.
CollectionMetadataPtr _collMetadata;
//
// The underlying execution machinery.
//
// The new world: a runner.
scoped_ptr _runner;
//
// Cursor-only private data and methods. DEPRECATED.
//
// The old world: a cursor. DEPRECATED.
const shared_ptr _c;
/**
* call when cursor's location changes so that we can update the cursorsbylocation map. if
* you are locked and internally iterating, only need to call when you are ready to
* "unlock".
*/
void updateLocation();
void setLastLoc_inlock(DiskLoc);
Record* _recordForYield( RecordNeeds need );
DiskLoc _lastLoc; // use getter and setter not this (important)
bool _doingDeletes; // when true we are the delete and aboutToDelete shouldn't manipulate us
// TODO: This will be moved into the runner.
ElapsedTracker _yieldSometimesTracker;
};
/**
* use this to assure we don't in the background time out cursor while it is under use. if you
* are using noTimeout() already, there is no risk anyway. Further, this mechanism guards
* against two getMore requests on the same cursor executing at the same time - which might be
* bad. That should never happen, but if a client driver had a bug, it could (or perhaps some
* sort of attack situation).
*/
class ClientCursorPin : boost::noncopyable {
public:
ClientCursorPin( long long cursorid );
~ClientCursorPin();
void release();
// Call this to delete the underlying ClientCursor.
void free();
ClientCursor *c() const;
private:
CursorId _cursorid;
};
/** Assures safe and reliable cleanup of a ClientCursor. */
class ClientCursorHolder : boost::noncopyable {
public:
ClientCursorHolder( ClientCursor *c = 0 );
~ClientCursorHolder();
void reset( ClientCursor *c = 0 );
ClientCursor* get();
operator bool() { return _c; }
ClientCursor * operator-> ();
const ClientCursor * operator-> () const;
/** Release ownership of the ClientCursor. */
void release();
private:
ClientCursor *_c;
CursorId _id;
};
/** thread for timing out old cursors */
class ClientCursorMonitor : public BackgroundJob {
public:
string name() const { return "ClientCursorMonitor"; }
void run();
};
struct ClientCursorYieldLock : boost::noncopyable {
explicit ClientCursorYieldLock( ptr cc );
~ClientCursorYieldLock();
/**
* @return if the cursor is still ok
* if it is, we also relock
*/
bool stillOk();
void relock();
private:
const bool _canYield;
ClientCursor::YieldData _data;
scoped_ptr _unlock;
};
} // namespace mongo
// ClientCursor should only be used with auto_ptr because it needs to be
// release()ed after a yield if stillOk() returns false and these pointer types
// do not support releasing. This will prevent them from being used accidentally
// Instead of auto_ptr<>, which still requires some degree of manual management
// of this, consider using ClientCursor::Holder which handles ClientCursor's
// unusual self-deletion mechanics.
namespace boost{
template<> class scoped_ptr {};
template<> class shared_ptr {};
}