summaryrefslogtreecommitdiff
path: root/src/mongo/db/clientcursor.h
diff options
context:
space:
mode:
Diffstat (limited to 'src/mongo/db/clientcursor.h')
-rw-r--r--src/mongo/db/clientcursor.h545
1 files changed, 169 insertions, 376 deletions
diff --git a/src/mongo/db/clientcursor.h b/src/mongo/db/clientcursor.h
index f2077282c66..26aed634556 100644
--- a/src/mongo/db/clientcursor.h
+++ b/src/mongo/db/clientcursor.h
@@ -1,436 +1,240 @@
-/* clientcursor.h */
-
/**
-* 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 <http://www.gnu.org/licenses/>.
-*/
-
-/* Cursor -- and its derived classes -- are our internal cursors.
-
- ClientCursor is a wrapper that represents a cursorid from our database
- application's perspective.
-*/
+ * 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 <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/pch.h"
-
#include <boost/thread/recursive_mutex.hpp>
-#include "cursor.h"
-#include "jsobj.h"
-#include "../util/net/message.h"
-#include "../util/background.h"
-#include "cc_by_loc.h"
-#include "diskloc.h"
-#include "dbhelpers.h"
-#include "matcher.h"
-#include "projection.h"
-#include "s/d_chunk_manager.h"
+#include "mongo/db/diskloc.h"
+#include "mongo/db/jsobj.h"
#include "mongo/db/keypattern.h"
-#include "mongo/util/elapsed_tracker.h"
+#include "mongo/db/query/runner.h"
+#include "mongo/s/collection_metadata.h"
+#include "mongo/util/background.h"
+#include "mongo/util/net/message.h"
namespace mongo {
typedef boost::recursive_mutex::scoped_lock recursive_scoped_lock;
- class Cursor; /* internal server cursor base class */
class ClientCursor;
+ class Collection;
+ class CurOp;
+ class Database;
+ class NamespaceDetails;
class ParsedQuery;
- /* todo: make this map be per connection. this will prevent cursor hijacking security attacks perhaps.
- * ERH: 9/2010 this may not work since some drivers send getMore over a different connection
- */
- typedef map<CursorId, ClientCursor*> CCById;
-
- extern BSONObj id_obj;
+ 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 : private boost::noncopyable {
- friend class CmdCursorInfo;
public:
- static void assertNoCursors();
-
- /* 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 Pin : boost::noncopyable {
- public:
- Pin( long long cursorid ) :
- _cursorid( INVALID_CURSOR_ID ) {
- recursive_scoped_lock lock( ccmutex );
- ClientCursor *cursor = ClientCursor::find_inlock( cursorid, true );
- if ( cursor ) {
- uassert( 12051, "clientcursor already in use? driver problem?",
- cursor->_pinValue < 100 );
- cursor->_pinValue += 100;
- _cursorid = cursorid;
- }
- }
- void release() {
- if ( _cursorid == INVALID_CURSOR_ID ) {
- return;
- }
- ClientCursor *cursor = c();
- _cursorid = INVALID_CURSOR_ID;
- if ( cursor ) {
- verify( cursor->_pinValue >= 100 );
- cursor->_pinValue -= 100;
- }
- }
- ~Pin() { DESTRUCTOR_GUARD( release(); ) }
- ClientCursor *c() const { return ClientCursor::find( _cursorid ); }
- private:
- CursorId _cursorid;
- };
-
- /** Assures safe and reliable cleanup of a ClientCursor. */
- class Holder : boost::noncopyable {
- public:
- Holder( ClientCursor *c = 0 ) :
- _c( 0 ),
- _id( INVALID_CURSOR_ID ) {
- reset( c );
- }
- void reset( ClientCursor *c = 0 ) {
- if ( c == _c )
- return;
- if ( _c ) {
- // be careful in case cursor was deleted by someone else
- ClientCursor::erase( _id );
- }
- if ( c ) {
- _c = c;
- _id = c->_cursorid;
- }
- else {
- _c = 0;
- _id = INVALID_CURSOR_ID;
- }
- }
- ~Holder() {
- DESTRUCTOR_GUARD ( reset(); );
- }
- ClientCursor* get() { return _c; }
- operator bool() { return _c; }
- ClientCursor * operator-> () { return _c; }
- const ClientCursor * operator-> () const { return _c; }
- /** Release ownership of the ClientCursor. */
- void release() {
- _c = 0;
- _id = INVALID_CURSOR_ID;
- }
- private:
- ClientCursor *_c;
- CursorId _id;
- };
+ ClientCursor(const Collection* collection, Runner* runner,
+ int qopts = 0, const BSONObj query = BSONObj());
- /**
- * Iterates through all ClientCursors, under its own ccmutex lock.
- * Also supports deletion on the fly.
- */
- class LockedIterator : boost::noncopyable {
- public:
- LockedIterator() : _lock( ccmutex ), _i( clientCursorsById.begin() ) {}
- bool ok() const { return _i != clientCursorsById.end(); }
- ClientCursor *current() const { return _i->second; }
- void advance() { ++_i; }
- /**
- * Delete 'current' and advance. Properly handles cascading deletions that may occur
- * when one ClientCursor is directly deleted.
- */
- void deleteAndAdvance();
- private:
- recursive_scoped_lock _lock;
- CCById::const_iterator _i;
- };
-
- ClientCursor(int queryOptions, const shared_ptr<Cursor>& c, const string& ns, BSONObj query = BSONObj() );
+ ClientCursor(const Collection* collection);
~ClientCursor();
- // *************** basic accessors *******************
+ //
+ // Basic accessors
+ //
CursorId cursorid() const { return _cursorid; }
string ns() const { return _ns; }
- Database * db() const { return _db; }
- const BSONObj& query() const { return _query; }
- int queryOptions() const { return _queryOptions; }
-
- DiskLoc lastLoc() const { return _lastLoc; }
-
- /* Get rid of cursors for namespaces 'ns'. When dropping a db, ns is "dbname."
- Used by drop, dropIndexes, dropDatabase.
- */
- static void invalidate(const char *ns);
+ const Collection* collection() const { return _collection; }
/**
- * @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.
+ * 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 Runner::kill.
*/
- bool yield( int microsToSleep = -1, Record * recordToLoad = 0 );
+ void kill();
- 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 );
+ //
+ // Yielding.
+ //
+ static void staticYield(int micros, const StringData& ns, const Record* rec);
static int suggestYieldMicros();
- static void staticYield( int micros , const StringData& ns , Record * rec );
-
- struct YieldData { CursorId _id; bool _doingDeletes; };
- bool prepareToYield( YieldData &data );
- static bool recoverFromYield( const YieldData &data );
-
- struct YieldLock : boost::noncopyable {
-
- explicit YieldLock( ptr<ClientCursor> cc );
-
- ~YieldLock();
-
- /**
- * @return if the cursor is still ok
- * if it is, we also relock
- */
- bool stillOk();
-
- void relock();
-
- private:
- const bool _canYield;
- YieldData _data;
- scoped_ptr<dbtempreleasecond> _unlock;
- };
-
- // --- some pass through helpers for Cursor ---
-
- Cursor* c() const { return _c.get(); }
- int pos() const { return _pos; }
- void incPos( int n ) { _pos += n; } // TODO: this is bad
- void setPos( int n ) { _pos = n; } // TODO : this is bad too
-
- BSONObj indexKeyPattern() { return _c->indexKeyPattern(); }
- bool modifiedKeys() const { return _c->modifiedKeys(); }
- bool isMultiKey() const { return _c->isMultiKey(); }
-
- 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(); }
+ //
+ // Timing and timeouts
+ //
/**
- * same as BSONObj::getFieldsDotted
- * if it can be retrieved from key, it is
- * @param holder keeps the currKey in scope by keeping a reference to it here. generally you'll want
- * holder and ret to destruct about the same time.
- * @return if this was retrieved from key
- */
- bool getFieldsDotted( const string& name, BSONElementSet &ret, BSONObj& holder );
-
- /**
- * same as BSONObj::getFieldDotted
- * if it can be retrieved from key, it is
- * @return if this was retrieved from key
- */
- BSONElement getFieldDotted( const string& name , BSONObj& holder , bool * fromKey = 0 ) ;
-
- /** extract items from object which match a pattern object.
- * e.g., if pattern is { x : 1, y : 1 }, builds an object with
- * x and y elements of this object, if they are present.
- * returns elements with original field names
- * NOTE: copied from BSONObj::extractFields
- */
- BSONObj extractFields(const BSONObj &pattern , bool fillWithNull = false) ;
-
- /** Extract elements from the object this cursor currently points to, using the expression
- * specified in KeyPattern. Will use a covered index if the one in this cursor is usable.
- * TODO: there are some cases where a covered index could be used but is not, for instance
- * if both this index and the keyPattern are {a : "hashed"}
- */
- BSONObj extractKey( const KeyPattern& usingKeyPattern ) const;
-
- void fillQueryResultFromObj( BufBuilder &b, const MatchDetails* details = NULL ) const;
-
- bool currentIsDup() { return _c->getsetdup( _c->currLoc() ); }
-
- bool currentMatches() {
- if ( ! _c->matcher() )
- return true;
- return _c->matcher()->matchesCurrent( _c.get() );
- }
-
- void setChunkManager( ShardChunkManagerPtr manager ){ _chunkManager = manager; }
- ShardChunkManagerPtr getChunkManager(){ return _chunkManager; }
-
- private:
- void setLastLoc_inlock(DiskLoc);
-
- static ClientCursor* find_inlock(CursorId id, bool warn = true) {
- CCById::iterator it = clientCursorsById.find(id);
- if ( it == clientCursorsById.end() ) {
- if ( warn ) {
- OCCASIONALLY out() << "ClientCursor::find(): cursor not found in map '" << id
- << "' (ok after a drop)" << endl;
- }
- return 0;
- }
- return it->second;
- }
-
- /* 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".
+ * @param millis amount of idle passed time since last call
+ * note called outside of locks (other than ccmutex) so care must be exercised
*/
- void updateLocation();
+ bool shouldTimeout( unsigned millis );
+ void setIdleTime( unsigned millis );
+ unsigned idleTime() const { return _idleAgeMillis; }
- public:
- static ClientCursor* find(CursorId id, bool warn = true) {
- recursive_scoped_lock lock(ccmutex);
- ClientCursor *c = find_inlock(id, warn);
- // if this asserts, your code was not thread safe - you either need to set no timeout
- // for the cursor or keep a ClientCursor::Pointer in scope for it.
- massert( 12521, "internal error: use of an unlocked ClientCursor", c == 0 || c->_pinValue );
- return c;
+ uint64_t getLeftoverMaxTimeMicros() const { return _leftoverMaxTimeMicros; }
+ void setLeftoverMaxTimeMicros( uint64_t leftoverMaxTimeMicros ) {
+ _leftoverMaxTimeMicros = leftoverMaxTimeMicros;
}
- /**
- * 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);
- // 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 bool eraseIfAuthorized(CursorId id);
+ //
+ // Sharding-specific data. TODO: Document.
+ //
- /**
- * @return number of cursors found
- */
- static int erase(int n, long long* ids);
- static int eraseIfAuthorized(int n, long long* ids);
-
- void mayUpgradeStorage() {
- /* if ( !ids_.get() )
- return;
- stringstream ss;
- ss << ns << "." << cursorid;
- ids_->mayUpgradeStorage( ss.str() );*/
- }
+ void setCollMetadata( CollectionMetadataPtr metadata ){ _collMetadata = metadata; }
+ CollectionMetadataPtr getCollMetadata(){ return _collMetadata; }
- /**
- * @param millis amount of idle passed time since last call
- */
- bool shouldTimeout( unsigned millis );
+ //
+ // Replication-related stuff. TODO: Document and clean.
+ //
- void storeOpForSlave( DiskLoc last );
void updateSlaveLocation( CurOp& curop );
-
- unsigned idleTime() const { return _idleAgeMillis; }
-
- void setDoingDeletes( bool doingDeletes ) {_doingDeletes = doingDeletes; }
-
void slaveReadTill( const OpTime& t ) { _slaveReadTill = t; }
-
/** Just for testing. */
OpTime getSlaveReadTill() const { return _slaveReadTill; }
- public: // static methods
-
- static void idleTimeReport(unsigned millis);
+ //
+ // Query-specific functionality that may be adapted for the Runner.
+ //
- static void appendStats( BSONObjBuilder& result );
- static unsigned numCursors() { return clientCursorsById.size(); }
- static void informAboutToDeleteBucket(const DiskLoc& b);
- static void aboutToDelete(const NamespaceDetails* nsd, const DiskLoc& dl);
- static void find( const string& ns , set<CursorId>& all );
+ Runner* getRunner() const { return _runner.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.
+ int pos() const { return _pos; }
+ void incPos(int n) { _pos += n; }
+ void setPos(int n) { _pos = n; }
- private: // methods
+ /**
+ * Is this ClientCursor backed by an aggregation pipeline. Defaults to false.
+ *
+ * Agg Runners 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.
+ */
+ bool isAggCursor;
- // cursors normally timeout after an inactivity period to prevent excess memory use
- // setting this prevents timeout of the cursor in question.
- void noTimeout() { _pinValue++; }
+ unsigned pinValue() const { return _pinValue; }
- CCByLoc& byLoc() { return _db->ccByLoc; }
-
- Record* _recordForYield( RecordNeeds need );
- static bool _erase_inlock(ClientCursor* cursor);
+ static long long totalOpen();
private:
+ friend class ClientCursorMonitor;
+ friend class CmdCursorInfo;
+ friend class CollectionCursorCache;
+
+ /**
+ * Initialization common between both constructors for the ClientCursor.
+ */
+ void init();
+ //
+ // ClientCursor-specific data, independent of the underlying execution type.
+ //
+
+ // The ID of the ClientCursor.
CursorId _cursorid;
- const string _ns;
- Database * _db;
+ // 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;
- const shared_ptr<Cursor> _c;
- map<string,int> _indexedFields; // map from indexed field to offset in key object
- int _pos; // # objects into the cursor so far
+ // The namespace we're operating on.
+ string _ns;
- const BSONObj _query; // used for logging diags only; optional in constructor
- int _queryOptions; // see enum QueryOptions dbclient.h
+ const Collection* _collection;
- OpTime _slaveReadTill;
+ // if we've added it to the total open counter yet
+ bool _countedYet;
- DiskLoc _lastLoc; // use getter and setter not this (important)
- unsigned _idleAgeMillis; // how long has the cursor been around, relative to server idle time
+ // How many objects have been returned by the find() so far?
+ int _pos;
- /* 0 = normal
- 1 = no timeout allowed
- 100 = in use (pinned) -- see Pointer class
- */
- unsigned _pinValue;
+ // 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;
- bool _doingDeletes; // when true we are the delete and aboutToDelete shouldn't manipulate us
- ElapsedTracker _yieldSometimesTracker;
+ // See the QueryOptions enum in dbclient.h
+ int _queryOptions;
- ShardChunkManagerPtr _chunkManager;
+ // TODO: document better.
+ OpTime _slaveReadTill;
- public:
- shared_ptr<ParsedQuery> pq;
- shared_ptr<Projection> fields; // which fields query wants returned
+ // How long has the cursor been idle?
+ unsigned _idleAgeMillis;
- private: // static members
+ // TODO: Document.
+ uint64_t _leftoverMaxTimeMicros;
- static CCById clientCursorsById;
- static long long numberTimedOut;
- static boost::recursive_mutex& ccmutex; // must use this for all statics above!
- static CursorId allocCursorId_inlock();
+ // 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.
+ //
+ scoped_ptr<Runner> _runner;
+ };
+ /**
+ * 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).
+ * Must have a read lock on the collection already
+ */
+ class ClientCursorPin : boost::noncopyable {
+ public:
+ ClientCursorPin( const Collection* collection, long long cursorid );
+ ~ClientCursorPin();
+ // This just releases the pin, does not delete the underlying
+ // unless ownership has passed to us after kill
+ void release();
+ // Call this to delete the underlying ClientCursor.
+ void deleteUnderlying();
+ ClientCursor *c() const;
+ private:
+ ClientCursor* _cursor;
};
+ /** thread for timing out old cursors */
class ClientCursorMonitor : public BackgroundJob {
public:
string name() const { return "ClientCursorMonitor"; }
@@ -438,14 +242,3 @@ namespace mongo {
};
} // 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<mongo::ClientCursor> {};
- template<> class shared_ptr<mongo::ClientCursor> {};
-}