/* 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 . */ /* Cursor -- and its derived classes -- are our internal cursors. ClientCursor is a wrapper that represents a cursorid from our database application's perspective. */ #pragma once #include "../stdafx.h" #include "cursor.h" #include "jsobj.h" #include "../util/message.h" #include "storage.h" #include "dbhelpers.h" #include "matcher.h" namespace mongo { typedef long long CursorId; /* passed to the client so it can send back on getMore */ class Cursor; /* internal server cursor base class */ class ClientCursor; typedef map CCById; extern CCById clientCursorsById; extern BSONObj id_obj; // utility class for de duping ids class IdSet_Deprecated { public: IdSet_Deprecated() : mySize_(), inMem_( true ) {} ~IdSet_Deprecated() { size_ -= mySize_; } bool get( const BSONObj &id ) const { if ( inMem_ ) return mem_.count( id ); else return db_.get( id ); } void put( const BSONObj &id ) { if ( inMem_ ) { if ( mem_.insert( id.getOwned() ).second ) { int sizeInc = id.objsize() + sizeof( BSONObj ); mySize_ += sizeInc; size_ += sizeInc; } } else { db_.set( id, true ); } } void mayUpgradeStorage( const string &name ) { if ( size_ > maxSize_ || mySize_ > maxSizePerSet() ) { if ( !inMem_ ) return; log() << "upgrading id set to collection backed storage, cursorid: " << name << endl; inMem_ = false; db_.reset( "local.temp.clientcursor." + name, id_obj ); for( BSONObjSetDefaultOrder::const_iterator i = mem_.begin(); i != mem_.end(); ++i ) db_.set( *i, true ); mem_.clear(); size_ -= mySize_; mySize_ = 0; } } bool inMem() const { return inMem_; } long long mySize() { return mySize_; } static long long aggregateSize() { return size_; } static long long maxSize_; private: string name_; BSONObjSetDefaultOrder mem_; DbSet db_; long long mySize_; static long long size_; long long maxSizePerSet() const { return maxSize_ / 2; } bool inMem_; }; class ClientCursor { DiskLoc _lastLoc; // use getter and setter not this. unsigned _idleAgeMillis; // how long has the cursor been around, relative to server idle time bool _liveForever; // if true, never time out cursor static CursorId allocCursorId(); public: ClientCursor() : _idleAgeMillis(0), _liveForever(false), cursorid( allocCursorId() ), pos(0) { clientCursorsById.insert( make_pair(cursorid, this) ); } ~ClientCursor(); const CursorId cursorid; string ns; auto_ptr matcher; auto_ptr c; // auto_ptr ids_; int pos; /* # objects into the cursor so far */ DiskLoc lastLoc() const { return _lastLoc; } void setLastLoc(DiskLoc); auto_ptr< FieldMatcher > filter; // which fields query wants returned Message originalMessage; // this is effectively an auto ptr for data the matcher points to /* Get rid of cursors for namespaces that begin with nsprefix. Used by drop, deleteIndexes, dropDatabase. */ static void invalidate(const char *nsPrefix); static bool erase(CursorId id) { ClientCursor *cc = find(id); if ( cc ) { delete cc; return true; } return false; } static ClientCursor* find(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)\n"; 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". */ void updateLocation(); void cleanupByLocation(DiskLoc loc); void mayUpgradeStorage() { /* if ( !ids_.get() ) return; stringstream ss; ss << ns << "." << cursorid; ids_->mayUpgradeStorage( ss.str() );*/ } /** * @param millis amount of idle passed time since last call */ bool shouldTimeout( unsigned millis ){ _idleAgeMillis += millis; return ! _liveForever && _idleAgeMillis > 600000; } unsigned idleTime(){ return _idleAgeMillis; } void liveForever(){ _liveForever = true; } }; } // namespace mongo