2010-05-19 23:01:09 +02:00
|
|
|
// shardconnection.cpp
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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/>.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "pch.h"
|
|
|
|
#include "shard.h"
|
|
|
|
#include "config.h"
|
|
|
|
#include "request.h"
|
|
|
|
#include <set>
|
|
|
|
|
|
|
|
namespace mongo {
|
2011-01-04 06:40:41 +01:00
|
|
|
|
2010-10-01 19:28:19 +02:00
|
|
|
// The code in shardconnection may run not only in mongos context. When elsewhere, chunk shard versioning
|
|
|
|
// is disabled. To enable chunk shard versioning, provide the check/resetShardVerionCB's below
|
|
|
|
//
|
|
|
|
// TODO: better encapsulate this mechanism.
|
|
|
|
|
2011-01-04 06:40:41 +01:00
|
|
|
bool defaultCheckShardVersion( DBClientBase & conn , const string& ns , bool authoritative , int tryNumber ) {
|
2010-10-01 19:28:19 +02:00
|
|
|
// no-op in mongod
|
|
|
|
return false;
|
|
|
|
}
|
2011-01-04 06:40:41 +01:00
|
|
|
|
|
|
|
void defaultResetShardVersion( DBClientBase * conn ) {
|
2010-10-01 19:28:19 +02:00
|
|
|
// no-op in mongod
|
|
|
|
}
|
|
|
|
|
|
|
|
boost::function4<bool, DBClientBase&, const string&, bool, int> checkShardVersionCB = defaultCheckShardVersion;
|
|
|
|
boost::function1<void, DBClientBase*> resetShardVersionCB = defaultResetShardVersion;
|
|
|
|
|
2010-05-19 23:01:09 +02:00
|
|
|
/**
|
|
|
|
* holds all the actual db connections for a client to various servers
|
2010-08-04 16:11:49 +02:00
|
|
|
* 1 pre thread, so don't have to worry about thread safety
|
2010-05-19 23:01:09 +02:00
|
|
|
*/
|
|
|
|
class ClientConnections : boost::noncopyable {
|
|
|
|
public:
|
|
|
|
struct Status : boost::noncopyable {
|
2011-01-04 06:40:41 +01:00
|
|
|
Status() : created(0), avail(0) {}
|
2010-07-23 16:51:48 +02:00
|
|
|
|
2011-01-04 06:40:41 +01:00
|
|
|
long long created;
|
2010-07-23 16:51:48 +02:00
|
|
|
DBClientBase* avail;
|
2010-05-19 23:01:09 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
|
2011-01-04 06:40:41 +01:00
|
|
|
ClientConnections() {}
|
|
|
|
|
|
|
|
~ClientConnections() {
|
|
|
|
for ( map<string,Status*>::iterator i=_hosts.begin(); i!=_hosts.end(); ++i ) {
|
2010-05-19 23:01:09 +02:00
|
|
|
string addr = i->first;
|
|
|
|
Status* ss = i->second;
|
|
|
|
assert( ss );
|
2011-01-04 06:40:41 +01:00
|
|
|
if ( ss->avail ) {
|
|
|
|
/* if we're shutting down, don't want to initiate release mechanism as it is slow,
|
2010-09-07 19:00:30 +02:00
|
|
|
and isn't needed since all connections will be closed anyway */
|
|
|
|
if ( inShutdown() )
|
|
|
|
delete ss->avail;
|
|
|
|
else
|
|
|
|
release( addr , ss->avail );
|
2010-07-23 16:51:48 +02:00
|
|
|
ss->avail = 0;
|
2010-05-19 23:01:09 +02:00
|
|
|
}
|
|
|
|
delete ss;
|
|
|
|
}
|
|
|
|
_hosts.clear();
|
|
|
|
}
|
2011-01-04 06:40:41 +01:00
|
|
|
|
|
|
|
DBClientBase * get( const string& addr , const string& ns ) {
|
2010-08-04 16:11:49 +02:00
|
|
|
_check( ns );
|
2010-08-06 20:14:25 +02:00
|
|
|
|
2010-05-19 23:01:09 +02:00
|
|
|
Status* &s = _hosts[addr];
|
|
|
|
if ( ! s )
|
|
|
|
s = new Status();
|
2011-01-04 06:40:41 +01:00
|
|
|
|
|
|
|
if ( s->avail ) {
|
2010-07-23 16:51:48 +02:00
|
|
|
DBClientBase* c = s->avail;
|
|
|
|
s->avail = 0;
|
2010-05-19 23:01:09 +02:00
|
|
|
pool.onHandedOut( c );
|
|
|
|
return c;
|
|
|
|
}
|
2010-05-24 23:24:41 +02:00
|
|
|
|
2010-05-19 23:01:09 +02:00
|
|
|
s->created++;
|
|
|
|
return pool.get( addr );
|
|
|
|
}
|
2011-01-04 06:40:41 +01:00
|
|
|
|
|
|
|
void done( const string& addr , DBClientBase* conn ) {
|
2010-05-19 23:01:09 +02:00
|
|
|
Status* s = _hosts[addr];
|
|
|
|
assert( s );
|
2011-01-04 06:40:41 +01:00
|
|
|
if ( s->avail ) {
|
2010-08-04 00:12:04 +02:00
|
|
|
release( addr , conn );
|
2010-05-19 23:01:09 +02:00
|
|
|
return;
|
|
|
|
}
|
2010-07-23 16:51:48 +02:00
|
|
|
s->avail = conn;
|
2010-05-19 23:01:09 +02:00
|
|
|
}
|
2011-01-04 06:40:41 +01:00
|
|
|
|
|
|
|
void sync() {
|
|
|
|
for ( map<string,Status*>::iterator i=_hosts.begin(); i!=_hosts.end(); ++i ) {
|
2010-05-20 19:36:29 +02:00
|
|
|
string addr = i->first;
|
|
|
|
Status* ss = i->second;
|
2010-07-23 16:51:48 +02:00
|
|
|
|
2011-01-04 06:40:41 +01:00
|
|
|
if ( ss->avail ) {
|
2010-07-23 16:51:48 +02:00
|
|
|
ss->avail->getLastError();
|
2010-07-25 14:33:03 +02:00
|
|
|
release( addr , ss->avail );
|
2010-07-23 16:51:48 +02:00
|
|
|
ss->avail = 0;
|
2010-05-20 19:36:29 +02:00
|
|
|
}
|
2010-05-27 03:47:02 +02:00
|
|
|
delete ss;
|
2010-05-20 19:36:29 +02:00
|
|
|
}
|
|
|
|
_hosts.clear();
|
|
|
|
}
|
2010-07-23 16:51:48 +02:00
|
|
|
|
2011-01-04 06:40:41 +01:00
|
|
|
void checkVersions( const string& ns ) {
|
2010-08-04 00:12:04 +02:00
|
|
|
vector<Shard> all;
|
|
|
|
Shard::getAllShards( all );
|
2011-01-04 06:40:41 +01:00
|
|
|
for ( unsigned i=0; i<all.size(); i++ ) {
|
2010-08-04 00:12:04 +02:00
|
|
|
Status* &s = _hosts[all[i].getConnString()];
|
|
|
|
if ( ! s )
|
|
|
|
s = new Status();
|
|
|
|
}
|
|
|
|
|
2011-01-04 06:40:41 +01:00
|
|
|
for ( map<string,Status*>::iterator i=_hosts.begin(); i!=_hosts.end(); ++i ) {
|
2010-08-04 00:12:04 +02:00
|
|
|
if ( ! Shard::isAShard( i->first ) )
|
|
|
|
continue;
|
2010-07-23 16:51:48 +02:00
|
|
|
Status* ss = i->second;
|
|
|
|
assert( ss );
|
2010-08-04 00:12:04 +02:00
|
|
|
if ( ! ss->avail )
|
|
|
|
ss->avail = pool.get( i->first );
|
2010-10-01 19:28:19 +02:00
|
|
|
checkShardVersionCB( *ss->avail , ns , false , 1 );
|
2010-07-23 16:51:48 +02:00
|
|
|
}
|
|
|
|
}
|
2010-07-25 14:33:03 +02:00
|
|
|
|
2011-01-04 06:40:41 +01:00
|
|
|
void release( const string& addr , DBClientBase * conn ) {
|
2010-10-01 19:28:19 +02:00
|
|
|
resetShardVersionCB( conn );
|
2010-07-25 14:33:03 +02:00
|
|
|
BSONObj res;
|
2011-01-04 06:40:41 +01:00
|
|
|
|
2010-09-07 19:50:01 +02:00
|
|
|
try {
|
2011-01-04 06:40:41 +01:00
|
|
|
if ( conn->simpleCommand( "admin" , &res , "unsetSharding" ) ) {
|
2010-09-07 19:50:01 +02:00
|
|
|
pool.release( addr , conn );
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
log(LL_ERROR) << " couldn't unset sharding :( " << res << endl;
|
|
|
|
delete conn;
|
|
|
|
}
|
|
|
|
}
|
2011-01-04 06:40:41 +01:00
|
|
|
catch ( std::exception& e ) {
|
2010-10-18 04:29:04 +02:00
|
|
|
log(LL_ERROR) << "couldn't unset sharding : " << e.what() << endl;
|
2010-07-25 17:32:15 +02:00
|
|
|
delete conn;
|
|
|
|
}
|
2010-07-25 14:33:03 +02:00
|
|
|
}
|
2011-01-04 06:40:41 +01:00
|
|
|
|
|
|
|
void _check( const string& ns ) {
|
2010-08-04 16:11:49 +02:00
|
|
|
if ( ns.size() == 0 || _seenNS.count( ns ) )
|
|
|
|
return;
|
|
|
|
_seenNS.insert( ns );
|
|
|
|
checkVersions( ns );
|
|
|
|
}
|
|
|
|
|
2010-05-19 23:01:09 +02:00
|
|
|
map<string,Status*> _hosts;
|
2010-08-04 16:11:49 +02:00
|
|
|
set<string> _seenNS;
|
2010-05-19 23:01:09 +02:00
|
|
|
// -----
|
2011-01-04 06:40:41 +01:00
|
|
|
|
2010-05-19 23:01:09 +02:00
|
|
|
static thread_specific_ptr<ClientConnections> _perThread;
|
|
|
|
|
2011-01-04 06:40:41 +01:00
|
|
|
static ClientConnections* threadInstance() {
|
2010-05-19 23:01:09 +02:00
|
|
|
ClientConnections* cc = _perThread.get();
|
2011-01-04 06:40:41 +01:00
|
|
|
if ( ! cc ) {
|
2010-05-19 23:01:09 +02:00
|
|
|
cc = new ClientConnections();
|
|
|
|
_perThread.reset( cc );
|
|
|
|
}
|
|
|
|
return cc;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
thread_specific_ptr<ClientConnections> ClientConnections::_perThread;
|
|
|
|
|
2010-05-20 19:36:29 +02:00
|
|
|
ShardConnection::ShardConnection( const Shard * s , const string& ns )
|
|
|
|
: _addr( s->getConnString() ) , _ns( ns ) {
|
2010-05-19 23:01:09 +02:00
|
|
|
_init();
|
|
|
|
}
|
|
|
|
|
2010-05-20 19:36:29 +02:00
|
|
|
ShardConnection::ShardConnection( const Shard& s , const string& ns )
|
|
|
|
: _addr( s.getConnString() ) , _ns( ns ) {
|
2010-05-19 23:01:09 +02:00
|
|
|
_init();
|
|
|
|
}
|
2011-01-04 06:40:41 +01:00
|
|
|
|
2010-05-20 19:36:29 +02:00
|
|
|
ShardConnection::ShardConnection( const string& addr , const string& ns )
|
|
|
|
: _addr( addr ) , _ns( ns ) {
|
2010-05-19 23:01:09 +02:00
|
|
|
_init();
|
|
|
|
}
|
2011-01-04 06:40:41 +01:00
|
|
|
|
|
|
|
void ShardConnection::_init() {
|
2010-05-19 23:01:09 +02:00
|
|
|
assert( _addr.size() );
|
2010-08-06 20:14:25 +02:00
|
|
|
_conn = ClientConnections::threadInstance()->get( _addr , _ns );
|
2010-07-15 17:35:18 +02:00
|
|
|
_finishedInit = false;
|
|
|
|
}
|
|
|
|
|
2011-01-04 06:40:41 +01:00
|
|
|
void ShardConnection::_finishInit() {
|
2010-07-15 17:35:18 +02:00
|
|
|
if ( _finishedInit )
|
|
|
|
return;
|
|
|
|
_finishedInit = true;
|
2011-01-04 06:40:41 +01:00
|
|
|
|
|
|
|
if ( _ns.size() ) {
|
2010-10-01 19:28:19 +02:00
|
|
|
_setVersion = checkShardVersionCB( *_conn , _ns , false , 1 );
|
2010-07-01 23:44:13 +02:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
_setVersion = false;
|
2010-05-20 19:36:29 +02:00
|
|
|
}
|
2011-01-04 06:40:41 +01:00
|
|
|
|
2010-05-19 23:01:09 +02:00
|
|
|
}
|
|
|
|
|
2011-01-04 06:40:41 +01:00
|
|
|
void ShardConnection::done() {
|
|
|
|
if ( _conn ) {
|
2010-08-06 20:14:25 +02:00
|
|
|
ClientConnections::threadInstance()->done( _addr , _conn );
|
2010-05-19 23:01:09 +02:00
|
|
|
_conn = 0;
|
2010-07-15 17:35:18 +02:00
|
|
|
_finishedInit = true;
|
2010-05-19 23:01:09 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-01-04 06:40:41 +01:00
|
|
|
void ShardConnection::kill() {
|
|
|
|
if ( _conn ) {
|
2010-05-19 23:01:09 +02:00
|
|
|
delete _conn;
|
|
|
|
_conn = 0;
|
2010-07-15 17:35:18 +02:00
|
|
|
_finishedInit = true;
|
2010-05-19 23:01:09 +02:00
|
|
|
}
|
|
|
|
}
|
2010-05-20 19:36:29 +02:00
|
|
|
|
2011-01-04 06:40:41 +01:00
|
|
|
void ShardConnection::sync() {
|
2010-08-06 20:14:25 +02:00
|
|
|
ClientConnections::threadInstance()->sync();
|
2010-05-20 19:36:29 +02:00
|
|
|
}
|
2010-05-27 22:33:20 +02:00
|
|
|
|
2011-01-04 06:40:41 +01:00
|
|
|
bool ShardConnection::runCommand( const string& db , const BSONObj& cmd , BSONObj& res ) {
|
2010-07-23 16:51:48 +02:00
|
|
|
assert( _conn );
|
|
|
|
bool ok = _conn->runCommand( db , cmd , res );
|
2011-01-04 06:40:41 +01:00
|
|
|
if ( ! ok ) {
|
|
|
|
if ( res["code"].numberInt() == StaleConfigInContextCode ) {
|
2010-07-23 16:51:48 +02:00
|
|
|
string big = res["errmsg"].String();
|
|
|
|
string ns,raw;
|
|
|
|
massert( 13409 , (string)"can't parse ns from: " + big , StaleConfigException::parse( big , ns , raw ) );
|
|
|
|
done();
|
|
|
|
throw StaleConfigException( ns , raw );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return ok;
|
|
|
|
}
|
|
|
|
|
2011-01-04 06:40:41 +01:00
|
|
|
void ShardConnection::checkMyConnectionVersions( const string & ns ) {
|
2010-08-06 20:14:25 +02:00
|
|
|
ClientConnections::threadInstance()->checkVersions( ns );
|
2010-07-23 16:51:48 +02:00
|
|
|
}
|
|
|
|
|
2010-05-27 22:33:20 +02:00
|
|
|
ShardConnection::~ShardConnection() {
|
2011-01-04 06:40:41 +01:00
|
|
|
if ( _conn ) {
|
2010-05-27 22:33:20 +02:00
|
|
|
if ( ! _conn->isFailed() ) {
|
|
|
|
/* see done() comments above for why we log this line */
|
|
|
|
log() << "~ScopedDBConnection: _conn != null" << endl;
|
|
|
|
}
|
|
|
|
kill();
|
|
|
|
}
|
|
|
|
}
|
2010-05-19 23:01:09 +02:00
|
|
|
}
|