Files
mongo/s/cursors.cpp

311 lines
9.4 KiB
C++
Raw Normal View History

2009-02-25 00:57:31 -05:00
// cursors.cpp
/*
* Copyright (C) 2010 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/>.
*/
2009-02-25 00:57:31 -05:00
2010-04-27 15:27:52 -04:00
#include "pch.h"
2009-02-25 00:57:31 -05:00
#include "cursors.h"
#include "../client/connpool.h"
#include "../db/queryutil.h"
2010-05-28 14:23:37 -04:00
#include "../db/commands.h"
#include "../util/concurrency/task.h"
2009-02-25 00:57:31 -05:00
namespace mongo {
2011-01-04 00:40:41 -05:00
2009-02-25 00:57:31 -05:00
// -------- ShardedCursor -----------
2011-01-04 00:40:41 -05:00
ShardedClientCursor::ShardedClientCursor( QueryMessage& q , ClusteredCursor * cursor ) {
2009-11-03 10:35:48 -05:00
assert( cursor );
_cursor = cursor;
2011-01-04 00:40:41 -05:00
2009-02-25 00:57:31 -05:00
_skip = q.ntoskip;
_ntoreturn = q.ntoreturn;
2011-01-04 00:40:41 -05:00
2009-02-25 00:57:31 -05:00
_totalSent = 0;
2009-02-25 01:23:58 -05:00
_done = false;
2009-02-25 00:57:31 -05:00
_id = 0;
2011-01-04 00:40:41 -05:00
if ( q.queryOptions & QueryOption_NoCursorTimeout ) {
2010-08-02 14:50:12 -04:00
_lastAccessMillis = 0;
}
2011-01-04 00:40:41 -05:00
else
2010-08-02 14:50:12 -04:00
_lastAccessMillis = Listener::getElapsedTimeMillis();
2009-02-25 00:57:31 -05:00
}
2011-01-04 00:40:41 -05:00
ShardedClientCursor::~ShardedClientCursor() {
2009-11-03 10:35:48 -05:00
assert( _cursor );
delete _cursor;
_cursor = 0;
2009-02-25 00:57:31 -05:00
}
2009-03-25 17:35:38 -04:00
2011-01-04 00:40:41 -05:00
long long ShardedClientCursor::getId() {
if ( _id <= 0 ) {
_id = cursorCache.genId();
assert( _id >= 0 );
}
return _id;
}
2011-01-04 00:40:41 -05:00
void ShardedClientCursor::accessed() {
2010-08-02 14:50:12 -04:00
if ( _lastAccessMillis > 0 )
_lastAccessMillis = Listener::getElapsedTimeMillis();
}
2011-01-04 00:40:41 -05:00
long long ShardedClientCursor::idleTime( long long now ) {
2010-08-02 14:50:12 -04:00
if ( _lastAccessMillis == 0 )
return 0;
return now - _lastAccessMillis;
}
2011-01-04 00:40:41 -05:00
bool ShardedClientCursor::sendNextBatch( Request& r , int ntoreturn ) {
uassert( 10191 , "cursor already done" , ! _done );
2011-01-04 00:40:41 -05:00
2009-02-25 00:57:31 -05:00
int maxSize = 1024 * 1024;
if ( _totalSent > 0 )
maxSize *= 3;
2011-01-04 00:40:41 -05:00
2009-02-25 00:57:31 -05:00
BufBuilder b(32768);
2011-01-04 00:40:41 -05:00
2009-02-25 00:57:31 -05:00
int num = 0;
bool sendMore = true;
2011-01-04 00:40:41 -05:00
while ( _cursor->more() ) {
2009-11-03 10:35:48 -05:00
BSONObj o = _cursor->next();
2009-02-25 00:57:31 -05:00
b.appendBuf( (void*)o.objdata() , o.objsize() );
2009-02-25 00:57:31 -05:00
num++;
2011-01-04 00:40:41 -05:00
if ( b.len() > maxSize ) {
2009-02-25 00:57:31 -05:00
break;
}
2009-02-25 00:57:31 -05:00
2011-01-04 00:40:41 -05:00
if ( num == ntoreturn ) {
2009-02-25 00:57:31 -05:00
// soft limit aka batch size
break;
}
2011-01-04 00:40:41 -05:00
if ( ntoreturn != 0 && ( -1 * num + _totalSent ) == ntoreturn ) {
2009-02-25 00:57:31 -05:00
// hard limit - total to send
sendMore = false;
break;
}
2011-01-04 00:40:41 -05:00
if ( ntoreturn == 0 && _totalSent == 0 && num > 100 ) {
// first batch should be max 100 unless batch size specified
break;
}
2009-02-25 00:57:31 -05:00
}
2009-11-03 10:35:48 -05:00
bool hasMore = sendMore && _cursor->more();
log(6) << "\t hasMore:" << hasMore << " wouldSendMoreIfHad: " << sendMore << " id:" << getId() << " totalSent: " << _totalSent << endl;
2011-01-04 00:40:41 -05:00
replyToQuery( 0 , r.p() , r.m() , b.buf() , b.len() , num , _totalSent , hasMore ? getId() : 0 );
2009-02-25 00:57:31 -05:00
_totalSent += num;
2009-02-25 01:23:58 -05:00
_done = ! hasMore;
2011-01-04 00:40:41 -05:00
2009-02-25 00:57:31 -05:00
return hasMore;
}
2010-08-02 14:50:12 -04:00
// ---- CursorCache -----
2011-01-04 00:40:41 -05:00
2010-08-02 14:50:12 -04:00
long long CursorCache::TIMEOUT = 600000;
2009-02-25 00:57:31 -05:00
2010-05-28 14:23:37 -04:00
CursorCache::CursorCache()
2011-01-04 00:40:41 -05:00
:_mutex( "CursorCache" ), _shardedTotal(0) {
2009-02-25 01:23:58 -05:00
}
2011-01-04 00:40:41 -05:00
CursorCache::~CursorCache() {
2009-02-25 01:23:58 -05:00
// TODO: delete old cursors?
2010-08-01 01:36:17 -04:00
int logLevel = 1;
if ( _cursors.size() || _refs.size() )
logLevel = 0;
log( logLevel ) << " CursorCache at shutdown - "
2011-01-04 00:40:41 -05:00
<< " sharded: " << _cursors.size()
2010-08-01 01:36:17 -04:00
<< " passthrough: " << _refs.size()
<< endl;
2009-02-25 01:23:58 -05:00
}
ShardedClientCursorPtr CursorCache::get( long long id ) const {
2011-02-16 14:40:18 -05:00
LOG(_myLogLevel) << "CursorCache::get id: " << id << endl;
2010-05-28 14:23:37 -04:00
scoped_lock lk( _mutex );
MapSharded::const_iterator i = _cursors.find( id );
2011-01-04 00:40:41 -05:00
if ( i == _cursors.end() ) {
2009-02-25 01:23:58 -05:00
OCCASIONALLY log() << "Sharded CursorCache missing cursor id: " << id << endl;
2010-05-28 19:29:44 -04:00
return ShardedClientCursorPtr();
2009-02-25 01:23:58 -05:00
}
2010-08-02 14:50:12 -04:00
i->second->accessed();
2009-02-25 01:23:58 -05:00
return i->second;
}
2011-01-04 00:40:41 -05:00
void CursorCache::store( ShardedClientCursorPtr cursor ) {
2011-02-16 14:40:18 -05:00
LOG(_myLogLevel) << "CursorCache::store cursor " << " id: " << cursor->getId() << endl;
assert( cursor->getId() );
2010-05-28 14:23:37 -04:00
scoped_lock lk( _mutex );
2009-02-25 01:23:58 -05:00
_cursors[cursor->getId()] = cursor;
_shardedTotal++;
2009-02-25 01:23:58 -05:00
}
2011-01-04 00:40:41 -05:00
void CursorCache::remove( long long id ) {
assert( id );
2010-05-28 14:23:37 -04:00
scoped_lock lk( _mutex );
2009-02-25 01:23:58 -05:00
_cursors.erase( id );
}
2011-02-16 14:40:18 -05:00
2011-01-04 00:40:41 -05:00
void CursorCache::storeRef( const string& server , long long id ) {
LOG(_myLogLevel) << "CursorCache::storeRef server: " << server << " id: " << id << endl;
assert( id );
2010-05-28 14:23:37 -04:00
scoped_lock lk( _mutex );
_refs[id] = server;
}
2011-01-04 00:40:41 -05:00
string CursorCache::getRef( long long id ) const {
LOG(_myLogLevel) << "CursorCache::getRef id: " << id << endl;
assert( id );
scoped_lock lk( _mutex );
MapNormal::const_iterator i = _refs.find( id );
if ( i == _refs.end() )
return "";
return i->second;
}
2011-01-04 00:40:41 -05:00
long long CursorCache::genId() {
while ( true ) {
long long x = security.getNonce();
if ( x == 0 )
continue;
if ( x < 0 )
x *= -1;
2011-01-04 00:40:41 -05:00
scoped_lock lk( _mutex );
MapSharded::iterator i = _cursors.find( x );
if ( i != _cursors.end() )
continue;
2011-01-04 00:40:41 -05:00
MapNormal::iterator j = _refs.find( x );
if ( j != _refs.end() )
continue;
2011-01-04 00:40:41 -05:00
return x;
}
}
2011-01-04 00:40:41 -05:00
void CursorCache::gotKillCursors(Message& m ) {
2010-05-28 14:23:37 -04:00
int *x = (int *) m.singleData()->_data;
x++; // reserved
int n = *x++;
2011-01-04 00:40:41 -05:00
if ( n > 2000 ) {
log( n < 30000 ? LL_WARNING : LL_ERROR ) << "receivedKillCursors, n=" << n << endl;
}
2010-05-28 14:23:37 -04:00
uassert( 13286 , "sent 0 cursors to kill" , n >= 1 );
uassert( 13287 , "too many cursors to kill" , n < 30000 );
2011-01-04 00:40:41 -05:00
2010-05-28 14:23:37 -04:00
long long * cursors = (long long *)x;
2011-01-04 00:40:41 -05:00
for ( int i=0; i<n; i++ ) {
2010-05-28 14:23:37 -04:00
long long id = cursors[i];
LOG(_myLogLevel) << "CursorCache::gotKillCursors id: " << id << endl;
2011-01-04 00:40:41 -05:00
if ( ! id ) {
log( LL_WARNING ) << " got cursor id of 0 to kill" << endl;
continue;
}
2011-01-04 00:40:41 -05:00
string server;
2010-05-28 14:23:37 -04:00
{
scoped_lock lk( _mutex );
MapSharded::iterator i = _cursors.find( id );
2011-01-04 00:40:41 -05:00
if ( i != _cursors.end() ) {
2010-05-28 14:23:37 -04:00
_cursors.erase( i );
continue;
}
2011-01-04 00:40:41 -05:00
2010-05-28 14:23:37 -04:00
MapNormal::iterator j = _refs.find( id );
2011-01-04 00:40:41 -05:00
if ( j == _refs.end() ) {
log( LL_WARNING ) << "can't find cursor: " << id << endl;
2010-05-28 14:23:37 -04:00
continue;
}
server = j->second;
_refs.erase( j );
}
2011-01-04 00:40:41 -05:00
LOG(_myLogLevel) << "CursorCache::found gotKillCursors id: " << id << " server: " << server << endl;
2010-05-28 14:23:37 -04:00
assert( server.size() );
ScopedDbConnection conn( server );
conn->killCursor( id );
conn.done();
}
}
void CursorCache::appendInfo( BSONObjBuilder& result ) const {
2010-05-28 14:23:37 -04:00
scoped_lock lk( _mutex );
result.append( "sharded" , (int)_cursors.size() );
result.appendNumber( "shardedEver" , _shardedTotal );
2010-05-28 14:23:37 -04:00
result.append( "refs" , (int)_refs.size() );
2010-08-02 09:45:19 -04:00
result.append( "totalOpen" , (int)(_cursors.size() + _refs.size() ) );
2010-05-28 14:23:37 -04:00
}
2011-01-04 00:40:41 -05:00
void CursorCache::doTimeouts() {
2010-08-02 14:50:12 -04:00
long long now = Listener::getElapsedTimeMillis();
scoped_lock lk( _mutex );
2011-01-04 00:40:41 -05:00
for ( MapSharded::iterator i=_cursors.begin(); i!=_cursors.end(); ++i ) {
2010-08-02 14:50:12 -04:00
long long idleFor = i->second->idleTime( now );
2011-01-04 00:40:41 -05:00
if ( idleFor < TIMEOUT ) {
2010-08-02 14:50:12 -04:00
continue;
}
log() << "killing old cursor " << i->second->getId() << " idle for: " << idleFor << "ms" << endl; // TODO: make log(1)
_cursors.erase( i );
}
}
2009-02-25 01:23:58 -05:00
CursorCache cursorCache;
2011-01-04 00:40:41 -05:00
int CursorCache::_myLogLevel = 3;
class CursorTimeoutTask : public task::Task {
2010-08-02 14:50:12 -04:00
public:
2010-10-13 17:38:09 -04:00
virtual string name() const { return "cursorTimeout"; }
2011-01-04 00:40:41 -05:00
virtual void doWork() {
2010-08-02 14:50:12 -04:00
cursorCache.doTimeouts();
}
} cursorTimeoutTask;
2010-08-02 14:50:12 -04:00
2011-01-04 00:40:41 -05:00
void CursorCache::startTimeoutThread() {
task::repeat( &cursorTimeoutTask , 400 );
2010-08-02 14:50:12 -04:00
}
2010-05-28 14:23:37 -04:00
class CmdCursorInfo : public Command {
public:
CmdCursorInfo() : Command( "cursorInfo", true ) {}
virtual bool slaveOk() const { return true; }
virtual void help( stringstream& help ) const {
help << " example: { cursorInfo : 1 }";
}
virtual LockType locktype() const { return NONE; }
2011-01-04 00:40:41 -05:00
bool run(const string&, BSONObj& jsobj, string& errmsg, BSONObjBuilder& result, bool fromRepl ) {
2010-05-28 14:23:37 -04:00
cursorCache.appendInfo( result );
2010-08-02 14:50:12 -04:00
if ( jsobj["setTimeout"].isNumber() )
CursorCache::TIMEOUT = jsobj["setTimeout"].numberLong();
2010-05-28 14:23:37 -04:00
return true;
}
} cmdCursorInfo;
2009-02-25 00:57:31 -05:00
}