diff options
Diffstat (limited to 'src/mongo/db/clientcursor.h')
| -rw-r--r-- | src/mongo/db/clientcursor.h | 545 |
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> {}; -} |
