2008-07-20 17:37:33 -04:00
/**
* Copyright ( C ) 2008 10 gen Inc .
2008-12-28 20:28:49 -05:00
*
2008-07-20 17:37:33 -04:00
* 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 .
2008-12-28 20:28:49 -05:00
*
2008-07-20 17:37:33 -04:00
* 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 .
2008-12-28 20:28:49 -05:00
*
2008-07-20 17:37:33 -04:00
* 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/>.
*/
2008-06-08 10:58:19 -04:00
/* clientcursor.cpp
2008-06-06 09:43:15 -04:00
2008-12-28 20:28:49 -05:00
ClientCursor is a wrapper that represents a cursorid from our database
2008-06-06 09:43:15 -04:00
application ' s perspective .
2008-06-08 10:58:19 -04:00
Cursor - - and its derived classes - - are our internal cursors .
2008-06-06 09:43:15 -04:00
*/
2010-04-27 15:27:52 -04:00
# include "pch.h"
2008-06-06 09:43:15 -04:00
# include "query.h"
# include "introspect.h"
# include <time.h>
2008-12-17 15:47:25 -05:00
# include "db.h"
2009-02-23 17:55:13 -05:00
# include "commands.h"
2010-04-13 11:45:01 -04:00
# include "repl_block.h"
2011-04-18 12:37:00 -04:00
# include "../util/processinfo.h"
2008-06-06 09:43:15 -04:00
2009-01-14 17:09:51 -05:00
namespace mongo {
2009-10-07 15:34:14 -04:00
CCById ClientCursor : : clientCursorsById ;
2009-10-09 11:24:37 -04:00
boost : : recursive_mutex ClientCursor : : ccmutex ;
2010-08-18 09:48:02 -04:00
long long ClientCursor : : numberTimedOut = 0 ;
2008-06-06 09:43:15 -04:00
2010-09-22 14:56:10 -04:00
void aboutToDeleteForSharding ( const Database * db , const DiskLoc & dl ) ; // from s/d_logic.h
2011-01-04 00:40:41 -05:00
/*static*/ void ClientCursor : : assertNoCursors ( ) {
2010-03-15 09:42:01 -07:00
recursive_scoped_lock lock ( ccmutex ) ;
2011-01-04 00:40:41 -05:00
if ( clientCursorsById . size ( ) ) {
2010-08-23 11:12:35 -04:00
log ( ) < < " ERROR clientcursors exist but should not at this point " < < endl ;
ClientCursor * cc = clientCursorsById . begin ( ) - > second ;
2010-10-29 11:00:38 -04:00
log ( ) < < " first one: " < < cc - > _cursorid < < ' ' < < cc - > _ns < < endl ;
2010-08-23 11:12:35 -04:00
clientCursorsById . clear ( ) ;
assert ( false ) ;
}
2009-01-15 10:17:11 -05:00
}
2008-06-08 10:58:19 -04:00
2010-08-23 11:12:35 -04:00
2009-10-09 11:24:37 -04:00
void ClientCursor : : setLastLoc_inlock ( DiskLoc L ) {
2010-10-29 11:00:38 -04:00
assert ( _pos ! = - 2 ) ; // defensive - see ~ClientCursor
2010-08-25 16:01:32 -04:00
2009-01-15 10:17:11 -05:00
if ( L = = _lastLoc )
return ;
2008-06-08 10:58:19 -04:00
2010-08-23 11:12:35 -04:00
CCByLoc & bl = byLoc ( ) ;
2010-09-30 12:19:09 -04:00
2009-01-15 10:17:11 -05:00
if ( ! _lastLoc . isNull ( ) ) {
2010-10-29 11:00:38 -04:00
bl . erase ( ByLocKey ( _lastLoc , _cursorid ) ) ;
2009-01-15 10:17:11 -05:00
}
2008-06-08 10:58:19 -04:00
2009-01-15 10:17:11 -05:00
if ( ! L . isNull ( ) )
2010-10-29 11:00:38 -04:00
bl [ ByLocKey ( L , _cursorid ) ] = this ;
2009-01-15 10:17:11 -05:00
_lastLoc = L ;
}
2008-06-08 10:58:19 -04:00
2009-01-15 10:17:11 -05:00
/* ------------------------------------------- */
2008-06-08 10:58:19 -04:00
2009-01-15 10:17:11 -05:00
/* must call this when a btree node is updated */
2009-10-07 15:47:08 -04:00
//void removedKey(const DiskLoc& btreeLoc, int keyPos) {
//}
2008-06-06 09:43:15 -04:00
2011-04-07 17:04:32 -04:00
// ns is either a full namespace or "dbname." when invalidating for a whole db
void ClientCursor : : invalidate ( const char * ns ) {
int len = strlen ( ns ) ;
const char * dot = strchr ( ns , ' . ' ) ;
assert ( len > 0 & & dot ) ;
bool isDB = ( dot = = & ns [ len - 1 ] ) ; // first (and only) dot is the last char
2008-12-28 20:28:49 -05:00
2009-10-09 11:24:37 -04:00
{
2011-04-07 17:04:32 -04:00
//cout << "\nTEMP invalidate " << ns << endl;
2010-03-15 09:42:01 -07:00
recursive_scoped_lock lock ( ccmutex ) ;
2009-10-09 11:24:37 -04:00
2010-08-23 11:12:35 -04:00
Database * db = cc ( ) . database ( ) ;
assert ( db ) ;
2011-04-07 17:04:32 -04:00
assert ( str : : startsWith ( ns , db - > name ) ) ;
2010-08-23 11:12:35 -04:00
2011-04-07 17:16:24 -04:00
for ( CCById : : iterator i = clientCursorsById . begin ( ) ; i ! = clientCursorsById . end ( ) ; /*++i*/ ) {
2009-10-09 11:24:37 -04:00
ClientCursor * cc = i - > second ;
2011-04-07 17:16:24 -04:00
+ + i ; // we may be removing this node
2011-01-04 00:40:41 -05:00
if ( cc - > _db ! = db )
2010-08-23 11:12:35 -04:00
continue ;
2011-04-07 17:04:32 -04:00
if ( isDB ) {
// already checked that db matched above
dassert ( str : : startsWith ( cc - > _ns . c_str ( ) , ns ) ) ;
2011-04-07 17:16:24 -04:00
delete cc ; //removes self from ccByID
2010-08-23 11:12:35 -04:00
}
2011-04-07 17:04:32 -04:00
else {
if ( str : : equals ( cc - > _ns . c_str ( ) , ns ) )
2011-04-07 17:16:24 -04:00
delete cc ; //removes self from ccByID
2011-04-07 17:04:32 -04:00
}
2009-10-09 11:24:37 -04:00
}
2010-08-23 11:12:35 -04:00
/*
note : we can ' t iterate byloc because clientcursors may exist with a loc of null in which case
2011-01-04 00:40:41 -05:00
they are not in the map . perhaps they should not exist though in the future ? something to
2010-08-23 11:12:35 -04:00
change ? ? ?
2011-01-04 00:40:41 -05:00
2010-08-23 11:12:35 -04:00
CCByLoc & bl = db - > ccByLoc ;
for ( CCByLoc : : iterator i = bl . begin ( ) ; i ! = bl . end ( ) ; + + i ) {
ClientCursor * cc = i - > second ;
2011-04-07 17:04:32 -04:00
if ( strncmp ( ns , cc - > ns . c_str ( ) , len ) = = 0 ) {
2010-08-23 11:12:35 -04:00
assert ( cc - > _db = = db ) ;
toDelete . push_back ( i - > second ) ;
}
} */
2010-08-23 12:44:40 -04:00
/*cout << "TEMP after invalidate " << endl;
2011-01-04 00:40:41 -05:00
for ( auto i = clientCursorsById . begin ( ) ; i ! = clientCursorsById . end ( ) ; + + i ) {
2010-08-23 12:44:40 -04:00
cout < < " " < < i - > second - > ns < < endl ;
}
cout < < " TEMP after invalidate done " < < endl ; */
2009-10-09 11:24:37 -04:00
}
2009-01-15 10:17:11 -05:00
}
2008-07-24 16:07:18 -04:00
2011-01-04 00:40:41 -05:00
bool ClientCursor : : shouldTimeout ( unsigned millis ) {
2010-08-18 09:48:02 -04:00
_idleAgeMillis + = millis ;
return _idleAgeMillis > 600000 & & _pinValue = = 0 ;
}
2009-03-27 16:27:35 -04:00
/* called every 4 seconds. millis is amount of idle time passed since the last call -- could be zero */
2009-10-08 12:18:56 -04:00
void ClientCursor : : idleTimeReport ( unsigned millis ) {
2010-08-23 11:12:35 -04:00
readlock lk ( " " ) ;
2010-03-15 09:42:01 -07:00
recursive_scoped_lock lock ( ccmutex ) ;
2010-08-23 11:12:35 -04:00
for ( CCById : : iterator i = clientCursorsById . begin ( ) ; i ! = clientCursorsById . end ( ) ; ) {
CCById : : iterator j = i ;
2009-03-27 16:27:35 -04:00
i + + ;
2011-01-04 00:40:41 -05:00
if ( j - > second - > shouldTimeout ( millis ) ) {
2010-08-18 09:48:02 -04:00
numberTimedOut + + ;
2011-01-04 00:40:41 -05:00
log ( 1 ) < < " killing old cursor " < < j - > second - > _cursorid < < ' ' < < j - > second - > _ns
2009-07-29 12:09:38 -04:00
< < " idle: " < < j - > second - > idleTime ( ) < < " ms \n " ;
2009-03-27 16:27:35 -04:00
delete j - > second ;
}
}
}
2009-01-15 10:17:11 -05:00
/* must call when a btree bucket going away.
note this is potentially slow
*/
2009-12-20 07:19:40 -05:00
void ClientCursor : : informAboutToDeleteBucket ( const DiskLoc & b ) {
2010-03-15 09:42:01 -07:00
recursive_scoped_lock lock ( ccmutex ) ;
2010-08-23 11:12:35 -04:00
Database * db = cc ( ) . database ( ) ;
CCByLoc & bl = db - > ccByLoc ;
RARELY if ( bl . size ( ) > 70 ) {
log ( ) < < " perf warning: byLoc.size= " < < bl . size ( ) < < " in aboutToDeleteBucket \n " ;
2009-01-15 10:17:11 -05:00
}
2010-08-23 11:12:35 -04:00
for ( CCByLoc : : iterator i = bl . begin ( ) ; i ! = bl . end ( ) ; i + + )
2010-10-29 11:00:38 -04:00
i - > second - > _c - > aboutToDeleteBucket ( b ) ;
2008-12-28 20:28:49 -05:00
}
2009-10-07 15:47:08 -04:00
void aboutToDeleteBucket ( const DiskLoc & b ) {
2011-01-04 00:40:41 -05:00
ClientCursor : : informAboutToDeleteBucket ( b ) ;
2009-10-07 15:47:08 -04:00
}
2009-01-15 10:17:11 -05:00
/* must call this on a delete so we clean up the cursors. */
2009-10-08 12:04:27 -04:00
void ClientCursor : : aboutToDelete ( const DiskLoc & dl ) {
2010-03-15 09:42:01 -07:00
recursive_scoped_lock lock ( ccmutex ) ;
2009-10-09 11:24:37 -04:00
2010-08-23 11:12:35 -04:00
Database * db = cc ( ) . database ( ) ;
assert ( db ) ;
2010-09-22 14:56:10 -04:00
aboutToDeleteForSharding ( db , dl ) ;
2010-08-23 11:12:35 -04:00
CCByLoc & bl = db - > ccByLoc ;
2010-09-30 12:19:09 -04:00
CCByLoc : : iterator j = bl . lower_bound ( ByLocKey : : min ( dl ) ) ;
CCByLoc : : iterator stop = bl . upper_bound ( ByLocKey : : max ( dl ) ) ;
2008-12-28 20:28:49 -05:00
if ( j = = stop )
2009-01-15 10:17:11 -05:00
return ;
2008-06-06 09:43:15 -04:00
2009-01-15 10:17:11 -05:00
vector < ClientCursor * > toAdvance ;
2008-12-17 15:47:25 -05:00
2009-01-15 10:17:11 -05:00
while ( 1 ) {
toAdvance . push_back ( j - > second ) ;
2010-09-30 12:19:09 -04:00
DEV assert ( j - > first . loc = = dl ) ;
2009-01-15 10:17:11 -05:00
+ + j ;
if ( j = = stop )
break ;
2008-12-28 20:28:49 -05:00
}
2009-01-15 10:17:11 -05:00
2011-01-04 00:40:41 -05:00
if ( toAdvance . size ( ) > = 3000 ) {
log ( ) < < " perf warning MPW101: " < < toAdvance . size ( ) < < " cursors for one diskloc "
< < dl . toString ( )
< < ' ' < < toAdvance [ 1000 ] - > _ns
< < ' ' < < toAdvance [ 2000 ] - > _ns
< < ' ' < < toAdvance [ 1000 ] - > _pinValue
< < ' ' < < toAdvance [ 2000 ] - > _pinValue
< < ' ' < < toAdvance [ 1000 ] - > _pos
< < ' ' < < toAdvance [ 2000 ] - > _pos
< < ' ' < < toAdvance [ 1000 ] - > _idleAgeMillis
< < ' ' < < toAdvance [ 2000 ] - > _idleAgeMillis
< < ' ' < < toAdvance [ 1000 ] - > _doingDeletes
< < ' ' < < toAdvance [ 2000 ] - > _doingDeletes
< < endl ;
2010-10-19 09:29:22 -04:00
//wassert( toAdvance.size() < 5000 );
}
2011-01-04 00:40:41 -05:00
for ( vector < ClientCursor * > : : iterator i = toAdvance . begin ( ) ; i ! = toAdvance . end ( ) ; + + i ) {
2009-12-17 16:54:40 -05:00
ClientCursor * cc = * i ;
2010-08-23 11:12:35 -04:00
wassert ( cc - > _db = = db ) ;
2011-01-04 00:40:41 -05:00
2009-12-17 16:54:40 -05:00
if ( cc - > _doingDeletes ) continue ;
2009-01-15 10:17:11 -05:00
2010-10-29 11:00:38 -04:00
Cursor * c = cc - > _c . get ( ) ;
2011-01-04 00:40:41 -05:00
if ( c - > capped ( ) ) {
/* note we cannot advance here. if this condition occurs, writes to the oplog
have " caught " the reader . skipping ahead , the reader would miss postentially
2010-10-30 13:22:21 -04:00
important data .
*/
2009-12-17 16:54:40 -05:00
delete cc ;
2009-10-13 16:44:52 -04:00
continue ;
}
2011-01-04 00:40:41 -05:00
2009-05-19 11:48:56 -04:00
c - > checkLocation ( ) ;
2009-03-11 17:05:50 -04:00
DiskLoc tmp1 = c - > refLoc ( ) ;
2009-01-15 10:17:11 -05:00
if ( tmp1 ! = dl ) {
/* this might indicate a failure to call ClientCursor::updateLocation() */
2009-05-19 11:48:56 -04:00
problem ( ) < < " warning: cursor loc " < < tmp1 < < " does not match byLoc position " < < dl < < " ! " < < endl ;
2009-01-15 10:17:11 -05:00
}
c - > advance ( ) ;
2009-03-11 17:05:50 -04:00
if ( c - > eof ( ) ) {
2010-08-01 19:53:25 -04:00
// advanced to end
2010-08-20 16:36:19 -04:00
// leave ClientCursor in place so next getMore doesn't fail
// still need to mark new location though
cc - > updateLocation ( ) ;
2009-01-15 10:17:11 -05:00
}
else {
2009-03-11 17:05:50 -04:00
wassert ( c - > refLoc ( ) ! = dl ) ;
2009-12-17 16:54:40 -05:00
cc - > updateLocation ( ) ;
2009-01-15 10:17:11 -05:00
}
2008-12-28 20:28:49 -05:00
}
}
2009-10-09 12:10:28 -04:00
void aboutToDelete ( const DiskLoc & dl ) { ClientCursor : : aboutToDelete ( dl ) ; }
2009-01-15 10:17:11 -05:00
2010-11-01 23:58:00 -04:00
ClientCursor : : ClientCursor ( int queryOptions , const shared_ptr < Cursor > & c , const string & ns , BSONObj query ) :
_ns ( ns ) , _db ( cc ( ) . database ( ) ) ,
2011-01-04 00:40:41 -05:00
_c ( c ) , _pos ( 0 ) ,
_query ( query ) , _queryOptions ( queryOptions ) ,
_idleAgeMillis ( 0 ) , _pinValue ( 0 ) ,
_doingDeletes ( false ) , _yieldSometimesTracker ( 128 , 10 ) {
2010-11-01 23:58:00 -04:00
assert ( _db ) ;
assert ( str : : startsWith ( _ns , _db - > name ) ) ;
if ( queryOptions & QueryOption_NoCursorTimeout )
noTimeout ( ) ;
recursive_scoped_lock lock ( ccmutex ) ;
_cursorid = allocCursorId_inlock ( ) ;
clientCursorsById . insert ( make_pair ( _cursorid , this ) ) ;
2011-01-04 00:40:41 -05:00
if ( ! _c - > modifiedKeys ( ) ) {
// store index information so we can decide if we can
2010-11-02 00:09:00 -04:00
// get something out of the index key rather than full object
2011-01-04 00:40:41 -05:00
2010-11-02 00:09:00 -04:00
int x = 0 ;
BSONObjIterator i ( _c - > indexKeyPattern ( ) ) ;
2011-01-04 00:40:41 -05:00
while ( i . more ( ) ) {
2010-11-02 00:09:00 -04:00
BSONElement e = i . next ( ) ;
2011-01-04 00:40:41 -05:00
if ( e . isNumber ( ) ) {
2010-11-02 00:09:00 -04:00
// only want basic index fields, not "2d" etc
_indexedFields [ e . fieldName ( ) ] = x ;
}
x + + ;
}
}
2010-11-14 01:42:36 -05:00
2010-11-01 23:58:00 -04:00
}
2011-01-04 00:40:41 -05:00
2010-11-01 23:58:00 -04:00
2009-01-15 10:17:11 -05:00
ClientCursor : : ~ ClientCursor ( ) {
2010-10-29 11:00:38 -04:00
assert ( _pos ! = - 2 ) ;
2009-10-09 11:24:37 -04:00
{
2010-03-15 09:42:01 -07:00
recursive_scoped_lock lock ( ccmutex ) ;
2009-10-09 11:24:37 -04:00
setLastLoc_inlock ( DiskLoc ( ) ) ; // removes us from bylocation multimap
2010-10-29 11:00:38 -04:00
clientCursorsById . erase ( _cursorid ) ;
2009-10-09 11:24:37 -04:00
// defensive:
2010-10-29 11:00:38 -04:00
( CursorId & ) _cursorid = - 1 ;
_pos = - 2 ;
2009-10-09 11:24:37 -04:00
}
2008-12-28 20:28:49 -05:00
}
2009-01-15 10:17:11 -05:00
2010-11-14 01:42:36 -05:00
bool ClientCursor : : getFieldsDotted ( const string & name , BSONElementSet & ret ) {
2011-01-04 00:40:41 -05:00
2010-11-14 01:42:36 -05:00
map < string , int > : : const_iterator i = _indexedFields . find ( name ) ;
2011-01-04 00:40:41 -05:00
if ( i = = _indexedFields . end ( ) ) {
2010-11-14 01:42:36 -05:00
current ( ) . getFieldsDotted ( name , ret ) ;
return false ;
}
int x = i - > second ;
2011-01-04 00:40:41 -05:00
2010-11-14 01:42:36 -05:00
BSONObjIterator it ( currKey ( ) ) ;
2011-01-05 00:32:30 -05:00
while ( x & & it . more ( ) ) {
2010-11-14 01:42:36 -05:00
it . next ( ) ;
2011-01-05 00:31:35 -05:00
x - - ;
}
2010-11-14 01:42:36 -05:00
assert ( x = = 0 ) ;
ret . insert ( it . next ( ) ) ;
return true ;
}
2011-02-05 00:41:55 -05:00
BSONElement ClientCursor : : getFieldDotted ( const string & name , bool * fromKey ) {
map < string , int > : : const_iterator i = _indexedFields . find ( name ) ;
if ( i = = _indexedFields . end ( ) ) {
if ( fromKey )
* fromKey = false ;
return current ( ) . getFieldDotted ( name ) ;
}
int x = i - > second ;
BSONObjIterator it ( currKey ( ) ) ;
while ( x & & it . more ( ) ) {
it . next ( ) ;
x - - ;
}
assert ( x = = 0 ) ;
if ( fromKey )
* fromKey = true ;
return it . next ( ) ;
}
2009-01-15 10:17:11 -05:00
/* 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 ClientCursor : : updateLocation ( ) {
2010-10-29 11:00:38 -04:00
assert ( _cursorid ) ;
2009-07-29 12:09:38 -04:00
_idleAgeMillis = 0 ;
2010-10-29 11:00:38 -04:00
DiskLoc cl = _c - > refLoc ( ) ;
2009-01-15 10:17:11 -05:00
if ( lastLoc ( ) = = cl ) {
//log() << "info: lastloc==curloc " << ns << '\n';
2011-01-04 00:40:41 -05:00
}
else {
2010-03-15 09:42:01 -07:00
recursive_scoped_lock lock ( ccmutex ) ;
2009-10-09 11:24:37 -04:00
setLastLoc_inlock ( cl ) ;
}
2010-06-07 14:13:57 -07:00
// may be necessary for MultiCursor even when cl hasn't changed
2010-10-29 11:00:38 -04:00
_c - > noteLocation ( ) ;
2008-12-28 20:28:49 -05:00
}
2011-01-04 00:40:41 -05:00
2010-07-07 19:35:26 -07:00
int ClientCursor : : yieldSuggest ( ) {
2010-06-22 05:27:56 -04:00
int writers = 0 ;
int readers = 0 ;
2011-01-04 00:40:41 -05:00
2010-06-22 05:27:56 -04:00
int micros = Client : : recommendedYieldMicros ( & writers , & readers ) ;
2011-01-04 00:40:41 -05:00
if ( micros > 0 & & writers = = 0 & & dbMutex . getState ( ) < = 0 ) {
2010-06-22 05:27:56 -04:00
// we have a read lock, and only reads are coming on, so why bother unlocking
2010-07-07 19:35:26 -07:00
micros = 0 ;
2010-06-22 05:16:52 -04:00
}
2011-01-04 00:40:41 -05:00
2010-07-07 19:35:26 -07:00
return micros ;
2010-06-19 20:20:19 -04:00
}
2011-01-04 00:40:41 -05:00
bool ClientCursor : : yieldSometimes ( ) {
2010-07-07 19:35:26 -07:00
if ( ! _yieldSometimesTracker . ping ( ) )
return true ;
2010-06-19 20:20:19 -04:00
2010-07-07 19:35:26 -07:00
int micros = yieldSuggest ( ) ;
return ( micros > 0 ) ? yield ( micros ) : true ;
}
2009-12-18 14:10:24 -05:00
2010-12-22 02:48:47 -05:00
void ClientCursor : : staticYield ( int micros , const StringData & ns ) {
2010-10-04 22:10:58 -07:00
killCurrentOp . checkForInterrupt ( false ) ;
2010-07-07 19:35:26 -07:00
{
dbtempreleasecond unlock ;
2011-01-04 00:40:41 -05:00
if ( unlock . unlocked ( ) ) {
2010-07-07 19:35:26 -07:00
if ( micros = = - 1 )
micros = Client : : recommendedYieldMicros ( ) ;
if ( micros > 0 )
2011-01-04 00:40:41 -05:00
sleepmicros ( micros ) ;
2010-07-07 19:35:26 -07:00
}
else {
2011-02-25 00:01:52 -05:00
CurOp * c = cc ( ) . curop ( ) ;
while ( c - > parent ( ) )
c = c - > parent ( ) ;
warning ( ) < < " ClientCursor::yield can't unlock b/c of recursive lock "
< < " ns: " < < ns
< < " top: " < < c - > info ( )
< < endl ;
2010-07-07 19:35:26 -07:00
}
2011-01-04 00:40:41 -05:00
}
2010-07-07 19:35:26 -07:00
}
2011-01-04 00:40:41 -05:00
2010-08-02 15:43:53 -07:00
bool ClientCursor : : prepareToYield ( YieldData & data ) {
2010-10-29 11:00:38 -04:00
if ( ! _c - > supportYields ( ) )
2010-08-02 15:43:53 -07:00
return false ;
2010-07-07 19:35:26 -07:00
// need to store in case 'this' gets deleted
2010-10-29 11:00:38 -04:00
data . _id = _cursorid ;
2011-01-04 00:40:41 -05:00
2010-07-07 19:35:26 -07:00
data . _doingDeletes = _doingDeletes ;
2009-12-18 14:10:24 -05:00
_doingDeletes = false ;
2011-01-04 00:40:41 -05:00
2009-12-18 14:10:24 -05:00
updateLocation ( ) ;
2011-01-04 00:40:41 -05:00
2009-12-18 14:10:24 -05:00
{
2011-01-04 00:40:41 -05:00
/* a quick test that our temprelease is safe.
todo : make a YieldingCursor class
2010-07-07 19:35:26 -07:00
and then make the following code part of a unit test .
*/
2009-12-18 14:10:24 -05:00
const int test = 0 ;
static bool inEmpty = false ;
2011-01-04 00:40:41 -05:00
if ( test & & ! inEmpty ) {
2009-12-18 14:10:24 -05:00
inEmpty = true ;
2010-01-20 17:11:25 -05:00
log ( ) < < " TEST: manipulate collection during cc:yield " < < endl ;
2011-01-04 00:40:41 -05:00
if ( test = = 1 )
2010-10-29 11:00:38 -04:00
Helpers : : emptyCollection ( _ns . c_str ( ) ) ;
2009-12-18 14:10:24 -05:00
else if ( test = = 2 ) {
BSONObjBuilder b ; string m ;
2010-10-29 11:00:38 -04:00
dropCollection ( _ns . c_str ( ) , m , b ) ;
2009-12-18 14:10:24 -05:00
}
2011-01-04 00:40:41 -05:00
else {
2010-10-29 11:00:38 -04:00
dropDatabase ( _ns . c_str ( ) ) ;
2009-12-18 14:10:24 -05:00
}
}
2011-01-04 00:40:41 -05:00
}
2010-08-02 15:43:53 -07:00
return true ;
2010-07-07 19:35:26 -07:00
}
2011-01-04 00:40:41 -05:00
2010-07-07 19:35:26 -07:00
bool ClientCursor : : recoverFromYield ( const YieldData & data ) {
ClientCursor * cc = ClientCursor : : find ( data . _id , false ) ;
2011-01-04 00:40:41 -05:00
if ( cc = = 0 ) {
2010-07-07 19:35:26 -07:00
// id was deleted
2009-12-18 14:10:24 -05:00
return false ;
}
2011-01-04 00:40:41 -05:00
2010-07-07 19:35:26 -07:00
cc - > _doingDeletes = data . _doingDeletes ;
2010-10-29 11:00:38 -04:00
cc - > _c - > checkLocation ( ) ;
2011-01-04 00:40:41 -05:00
return true ;
2010-07-07 19:35:26 -07:00
}
2011-01-04 00:40:41 -05:00
2010-07-07 19:35:26 -07:00
bool ClientCursor : : yield ( int micros ) {
2010-10-29 11:00:38 -04:00
if ( ! _c - > supportYields ( ) )
2010-07-24 21:58:39 -04:00
return true ;
2011-01-04 00:40:41 -05:00
YieldData data ;
2010-07-07 19:35:26 -07:00
prepareToYield ( data ) ;
2011-01-04 00:40:41 -05:00
2010-12-22 02:48:47 -05:00
staticYield ( micros , _ns ) ;
2009-12-18 14:10:24 -05:00
2010-07-07 19:35:26 -07:00
return ClientCursor : : recoverFromYield ( data ) ;
2009-12-18 14:10:24 -05:00
}
2009-01-15 10:17:11 -05:00
int ctmLast = 0 ; // so we don't have to do find() which is a little slow very often.
2009-10-09 11:24:37 -04:00
long long ClientCursor : : allocCursorId_inlock ( ) {
2011-01-04 00:40:41 -05:00
if ( 0 ) {
2010-08-23 11:12:35 -04:00
static long long z ;
+ + z ;
cout < < " TEMP alloccursorid " < < z < < endl ;
return z ;
}
2009-01-15 10:17:11 -05:00
long long x ;
int ctm = ( int ) curTimeMillis ( ) ;
while ( 1 ) {
x = ( ( ( long long ) rand ( ) ) < < 32 ) ;
x = x | ctm | 0x80000000 ; // OR to make sure not zero
2009-10-09 11:24:37 -04:00
if ( ctm ! = ctmLast | | ClientCursor : : find_inlock ( x , false ) = = 0 )
2009-01-15 10:17:11 -05:00
break ;
}
ctmLast = ctm ;
2010-05-22 16:23:09 -04:00
//DEV tlog() << " alloccursorid " << x << endl;
2009-01-15 10:17:11 -05:00
return x ;
2008-12-28 20:28:49 -05:00
}
2009-01-15 10:17:11 -05:00
2011-01-04 00:40:41 -05:00
void ClientCursor : : storeOpForSlave ( DiskLoc last ) {
2010-04-02 13:18:06 -04:00
if ( ! ( _queryOptions & QueryOption_OplogReplay ) )
return ;
if ( last . isNull ( ) )
return ;
2011-01-04 00:40:41 -05:00
2010-04-02 13:18:06 -04:00
BSONElement e = last . obj ( ) [ " ts " ] ;
if ( e . type ( ) = = Date | | e . type ( ) = = Timestamp )
2010-04-24 17:14:44 -04:00
_slaveReadTill = e . _opTime ( ) ;
2010-04-02 13:18:06 -04:00
}
2011-01-04 00:40:41 -05:00
void ClientCursor : : updateSlaveLocation ( CurOp & curop ) {
2010-04-02 13:18:06 -04:00
if ( _slaveReadTill . isNull ( ) )
return ;
2010-10-29 11:00:38 -04:00
mongo : : updateSlaveLocation ( curop , _ns . c_str ( ) , _slaveReadTill ) ;
2010-04-02 13:18:06 -04:00
}
2011-01-04 00:40:41 -05:00
void ClientCursor : : appendStats ( BSONObjBuilder & result ) {
2010-08-18 09:48:02 -04:00
recursive_scoped_lock lock ( ccmutex ) ;
result . appendNumber ( " totalOpen " , clientCursorsById . size ( ) ) ;
2010-08-23 11:31:06 -04:00
result . appendNumber ( " clientCursors_size " , ( int ) numCursors ( ) ) ;
2010-08-23 11:24:34 -04:00
result . appendNumber ( " timedOut " , numberTimedOut ) ;
2010-08-18 09:48:02 -04:00
}
2011-01-04 00:40:41 -05:00
2009-02-23 17:55:13 -05:00
// QUESTION: Restrict to the namespace from which this command was issued?
// Alternatively, make this command admin-only?
class CmdCursorInfo : public Command {
2009-01-15 10:17:11 -05:00
public :
2010-04-19 21:05:46 -04:00
CmdCursorInfo ( ) : Command ( " cursorInfo " , true ) { }
2010-04-23 15:50:49 -04:00
virtual bool slaveOk ( ) const { return true ; }
2009-02-23 17:55:13 -05:00
virtual void help ( stringstream & help ) const {
help < < " example: { cursorInfo : 1 } " ;
2009-01-15 10:17:11 -05:00
}
2010-04-23 15:50:49 -04:00
virtual LockType locktype ( ) const { return NONE ; }
2011-01-04 00:40:41 -05:00
bool run ( const string & dbname , BSONObj & jsobj , string & errmsg , BSONObjBuilder & result , bool fromRepl ) {
2010-08-18 09:48:02 -04:00
ClientCursor : : appendStats ( result ) ;
2009-02-23 17:55:13 -05:00
return true ;
}
} cmdCursorInfo ;
2011-01-04 00:40:41 -05:00
2011-04-18 12:37:00 -04:00
struct Mem {
Mem ( ) { res = virt = mapped = 0 ; }
int res ;
int virt ;
int mapped ;
bool grew ( const Mem & r ) {
return ( r . res & & ( ( ( double ) res ) / r . res ) > 1.1 ) | |
( r . virt & & ( ( ( double ) virt ) / r . virt ) > 1.1 ) | |
( r . mapped & & ( ( ( double ) mapped ) / r . mapped ) > 1.1 ) ;
}
} ;
/** called once a minute from killcursors thread */
void sayMemoryStatus ( ) {
static time_t last ;
static Mem mlast ;
try {
ProcessInfo p ;
if ( p . supported ( ) ) {
Mem m ;
m . res = p . getResidentSize ( ) ;
m . virt = p . getVirtualMemorySize ( ) ;
m . mapped = ( int ) ( MemoryMappedFile : : totalMappedLength ( ) / ( 1024 * 1024 ) ) ;
if ( time ( 0 ) - last > = 5000 | | m . grew ( mlast ) ) {
log ( ) < < " mem (MB) res: " < < m . res < < " virt: " < < m . virt < < " mapped: " < < m . mapped < < endl ;
if ( m . virt - m . mapped > 5000 ) {
ONCE log ( ) < < " warning mapped-virtual memory is large. You must have significantly more physical ram than the mapped-virtual value. Large values could indicate a large number of open client connections or possibly a memory leak. " < < endl ;
}
last = time ( 0 ) ;
mlast = m ;
}
}
}
catch ( . . . ) {
log ( ) < < " ProcessInfo exception " < < endl ;
}
}
/** thread for timing out old cursors */
2011-01-04 00:40:41 -05:00
void ClientCursorMonitor : : run ( ) {
2010-04-22 18:43:37 -04:00
Client : : initThread ( " clientcursormon " ) ;
2010-04-20 12:58:04 -04:00
Client & client = cc ( ) ;
2011-01-04 00:40:41 -05:00
2010-04-20 12:58:04 -04:00
unsigned old = curTimeMillis ( ) ;
2011-04-18 12:37:00 -04:00
const int Secs = 4 ;
unsigned n = 0 ;
2011-01-04 00:40:41 -05:00
while ( ! inShutdown ( ) ) {
2010-04-20 12:58:04 -04:00
unsigned now = curTimeMillis ( ) ;
ClientCursor : : idleTimeReport ( now - old ) ;
old = now ;
2011-04-18 12:37:00 -04:00
sleepsecs ( Secs ) ;
if ( + + n % ( 60 / 4 ) = = 0 /*once a minute*/ ) {
sayMemoryStatus ( ) ;
}
2010-04-20 12:58:04 -04:00
}
client . shutdown ( ) ;
}
2011-01-04 00:40:41 -05:00
void ClientCursor : : find ( const string & ns , set < CursorId > & all ) {
2010-07-22 17:24:51 -04:00
recursive_scoped_lock lock ( ccmutex ) ;
2011-01-04 00:40:41 -05:00
for ( CCById : : iterator i = clientCursorsById . begin ( ) ; i ! = clientCursorsById . end ( ) ; + + i ) {
2010-10-29 11:00:38 -04:00
if ( i - > second - > _ns = = ns )
2010-07-22 17:24:51 -04:00
all . insert ( i - > first ) ;
}
}
2010-09-30 10:07:17 -04:00
int ClientCursor : : erase ( int n , long long * ids ) {
int found = 0 ;
for ( int i = 0 ; i < n ; i + + ) {
if ( erase ( ids [ i ] ) )
found + + ;
if ( inShutdown ( ) )
break ;
}
return found ;
}
2010-07-22 17:24:51 -04:00
2010-04-20 12:58:04 -04:00
ClientCursorMonitor clientCursorMonitor ;
2009-05-06 20:24:01 -04:00
2009-01-14 17:09:51 -05:00
} // namespace mongo