Files
mongo/s/config.cpp

536 lines
16 KiB
C++
Raw Normal View History

2009-02-12 21:03:46 -05:00
// config.cpp
2008-10-13 17:58:51 -04:00
/**
* Copyright (C) 2008 10gen Inc.
2008-12-28 20:28:49 -05:00
*
2008-10-13 17:58:51 -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-10-13 17:58:51 -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-10-13 17:58:51 -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/>.
*/
#include "stdafx.h"
2009-02-03 13:30:28 -05:00
#include "../util/message.h"
2008-10-13 17:58:51 -04:00
#include "../util/unittest.h"
2008-10-31 19:17:54 -05:00
#include "../client/connpool.h"
2009-02-06 15:44:21 -05:00
#include "../client/model.h"
#include "../db/pdfile.h"
#include "../db/cmdline.h"
2009-02-12 21:09:06 -05:00
#include "server.h"
2009-02-12 21:03:46 -05:00
#include "config.h"
#include "chunk.h"
2008-10-13 17:58:51 -04:00
2009-01-14 17:09:51 -05:00
namespace mongo {
int ConfigServer::VERSION = 2;
2009-02-06 15:44:21 -05:00
/* --- DBConfig --- */
2008-10-19 17:46:53 -05:00
string DBConfig::modelServer() {
return configServer.modelServer();
}
2009-02-17 11:41:34 -05:00
2009-09-01 12:17:41 -04:00
bool DBConfig::isSharded( const string& ns ){
if ( ! _shardingEnabled )
2009-02-06 15:44:21 -05:00
return false;
return _sharded.find( ns ) != _sharded.end();
2009-02-06 15:44:21 -05:00
}
string DBConfig::getShard( const string& ns ){
2009-09-01 12:17:41 -04:00
if ( isSharded( ns ) )
2009-02-17 11:41:34 -05:00
return "";
2009-02-06 15:44:21 -05:00
uassert( 10178 , "no primary!" , _primary.size() );
2009-02-06 15:44:21 -05:00
return _primary;
}
2009-02-19 12:55:01 -05:00
2009-09-01 12:17:41 -04:00
void DBConfig::enableSharding(){
_shardingEnabled = true;
2009-02-19 12:55:01 -05:00
}
2009-09-03 16:48:34 -04:00
ChunkManager* DBConfig::shardCollection( const string& ns , ShardKeyPattern fieldsAndOrder , bool unique ){
2009-09-01 12:17:41 -04:00
if ( ! _shardingEnabled )
throw UserException( 8042 , "db doesn't have sharding enabled" );
2009-02-19 12:55:01 -05:00
ChunkManager * info = _shards[ns];
2009-02-19 12:55:01 -05:00
if ( info )
return info;
2009-09-01 12:17:41 -04:00
if ( isSharded( ns ) )
throw UserException( 8043 , "already sharded" );
2009-02-19 12:55:01 -05:00
2009-12-10 10:46:41 -05:00
log() << "enable sharding on: " << ns << " with shard key: " << fieldsAndOrder << endl;
2009-09-03 16:48:34 -04:00
_sharded[ns] = CollectionInfo( fieldsAndOrder , unique );
2009-02-19 12:55:01 -05:00
2009-09-03 16:48:34 -04:00
info = new ChunkManager( this , ns , fieldsAndOrder , unique );
2009-02-19 12:55:01 -05:00
_shards[ns] = info;
return info;
}
2009-02-06 15:44:21 -05:00
bool DBConfig::removeSharding( const string& ns ){
if ( ! _shardingEnabled ){
cout << "AAAA" << endl;
return false;
}
ChunkManager * info = _shards[ns];
map<string,CollectionInfo>::iterator i = _sharded.find( ns );
if ( info == 0 && i == _sharded.end() ){
cout << "BBBB" << endl;
return false;
}
uassert( 10179 , "_sharded but no info" , info );
uassert( 10180 , "info but no sharded" , i != _sharded.end() );
_sharded.erase( i );
_shards.erase( ns ); // TODO: clean this up, maybe switch to shared_ptr
return true;
}
ChunkManager* DBConfig::getChunkManager( const string& ns , bool reload ){
ChunkManager* m = _shards[ns];
2009-03-26 17:33:30 -04:00
if ( m && ! reload )
return m;
uassert( 10181 , (string)"not sharded:" + ns , isSharded( ns ) );
2009-04-11 10:27:45 -04:00
if ( m && reload )
log() << "reloading shard info for: " << ns << endl;
2009-09-03 16:48:34 -04:00
m = new ChunkManager( this , ns , _sharded[ ns ].key , _sharded[ns].unique );
_shards[ns] = m;
return m;
}
2009-02-06 15:44:21 -05:00
void DBConfig::serialize(BSONObjBuilder& to){
to.append("name", _name);
2009-09-01 12:17:41 -04:00
to.appendBool("partitioned", _shardingEnabled );
to.append("primary", _primary );
2009-02-19 12:55:01 -05:00
if ( _sharded.size() > 0 ){
BSONObjBuilder a;
2009-09-03 16:48:34 -04:00
for ( map<string,CollectionInfo>::reverse_iterator i=_sharded.rbegin(); i != _sharded.rend(); i++){
BSONObjBuilder temp;
temp.append( "key" , i->second.key.key() );
temp.appendBool( "unique" , i->second.unique );
a.append( i->first.c_str() , temp.obj() );
2009-02-19 12:55:01 -05:00
}
2009-05-15 13:02:49 -04:00
to.append( "sharded" , a.obj() );
2009-02-19 12:55:01 -05:00
}
2009-02-06 15:44:21 -05:00
}
2009-02-27 10:37:13 -05:00
void DBConfig::unserialize(const BSONObj& from){
2009-02-06 15:44:21 -05:00
_name = from.getStringField("name");
2009-09-01 12:17:41 -04:00
_shardingEnabled = from.getBoolField("partitioned");
_primary = from.getStringField("primary");
2009-02-19 12:55:01 -05:00
_sharded.clear();
BSONObj sharded = from.getObjectField( "sharded" );
if ( ! sharded.isEmpty() ){
BSONObjIterator i(sharded);
while ( i.more() ){
2009-02-19 12:55:01 -05:00
BSONElement e = i.next();
uassert( 10182 , "sharded things have to be objects" , e.type() == Object );
2009-09-03 16:48:34 -04:00
BSONObj c = e.embeddedObject();
uassert( 10183 , "key has to be an object" , c["key"].type() == Object );
2009-09-03 16:48:34 -04:00
_sharded[e.fieldName()] = CollectionInfo( c["key"].embeddedObject() ,
c["unique"].trueValue() );
2009-02-19 12:55:01 -05:00
}
}
2009-02-06 15:44:21 -05:00
}
2009-02-05 16:45:58 -05:00
void DBConfig::save( bool check ){
Model::save( check );
for ( map<string,ChunkManager*>::iterator i=_shards.begin(); i != _shards.end(); i++)
i->second->save();
}
bool DBConfig::reload(){
// TODO: i don't think is 100% correct
return doload();
}
bool DBConfig::doload(){
2009-02-06 15:44:21 -05:00
BSONObjBuilder b;
b.append("name", _name.c_str());
2009-02-06 15:44:21 -05:00
BSONObj q = b.done();
return load(q);
}
2009-12-02 16:36:46 -05:00
bool DBConfig::dropDatabase( string& errmsg ){
/**
* 1) make sure everything is up
* 2) update config server
* 3) drop and reset sharded collections
* 4) drop and reset primary
* 5) drop everywhere to clean up loose ends
*/
2009-12-10 10:46:41 -05:00
log() << "DBConfig::dropDatabase: " << _name << endl;
2009-12-02 16:36:46 -05:00
// 1
if ( ! configServer.allUp( errmsg ) ){
log(1) << "\t DBConfig::dropDatabase not all up" << endl;
return 0;
}
// 2
grid.removeDB( _name );
remove( true );
if ( ! configServer.allUp( errmsg ) ){
log() << "error removing from config server even after checking!" << endl;
return 0;
}
log(1) << "\t removed entry from config server for: " << _name << endl;
set<string> allServers;
// 3
while ( true ){
int num;
if ( ! _dropShardedCollections( num , allServers , errmsg ) )
return 0;
log() << " DBConfig::dropDatabase: " << _name << " dropped sharded collections: " << num << endl;
if ( num == 0 )
break;
}
// 4
{
ScopedDbConnection conn( _primary );
BSONObj res;
if ( ! conn->dropDatabase( _name , &res ) ){
errmsg = res.toString();
return 0;
}
conn.done();
}
// 5
for ( set<string>::iterator i=allServers.begin(); i!=allServers.end(); i++ ){
string s = *i;
ScopedDbConnection conn( s );
BSONObj res;
if ( ! conn->dropDatabase( _name , &res ) ){
errmsg = res.toString();
return 0;
}
conn.done();
}
log(1) << "\t dropped primary db for: " << _name << endl;
return true;
}
bool DBConfig::_dropShardedCollections( int& num, set<string>& allServers , string& errmsg ){
num = 0;
set<string> seen;
while ( true ){
map<string,ChunkManager*>::iterator i = _shards.begin();
if ( i == _shards.end() )
break;
if ( seen.count( i->first ) ){
errmsg = "seen a collection twice!";
return false;
}
seen.insert( i->first );
log(1) << "\t dropping sharded collection: " << i->first << endl;
i->second->getAllServers( allServers );
i->second->drop();
num++;
uassert( 10184 , "_dropShardedCollections too many collections - bailing" , num < 100000 );
2009-12-02 16:36:46 -05:00
log(2) << "\t\t dropped " << num << " so far" << endl;
}
return true;
}
2009-02-06 15:44:21 -05:00
/* --- Grid --- */
2009-12-02 16:36:46 -05:00
string Grid::pickShardForNewDB(){
2009-02-09 13:34:40 -05:00
ScopedDbConnection conn( configServer.getPrimary() );
2009-02-08 17:55:33 -05:00
// TODO: this is temporary
vector<string> all;
auto_ptr<DBClientCursor> c = conn->query( "config.shards" , Query() );
while ( c->more() ){
BSONObj s = c->next();
all.push_back( s["host"].valuestrsafe() );
// look at s["maxSize"] if exists
}
2009-02-09 13:34:40 -05:00
conn.done();
2009-02-14 09:42:18 -05:00
if ( all.size() == 0 )
return "";
return all[ rand() % all.size() ];
2009-02-08 17:55:33 -05:00
}
bool Grid::knowAboutShard( string name ) const{
ScopedDbConnection conn( configServer.getPrimary() );
BSONObj shard = conn->findOne( "config.shards" , BSON( "host" << name ) );
conn.done();
return ! shard.isEmpty();
}
DBConfig* Grid::getDBConfig( string database , bool create ){
2009-02-05 16:45:58 -05:00
{
string::size_type i = database.find( "." );
if ( i != string::npos )
database = database.substr( 0 , i );
}
if ( database == "config" )
2009-02-06 15:44:21 -05:00
return &configServer;
2009-12-02 16:36:46 -05:00
boostlock l( _lock );
2009-02-05 16:45:58 -05:00
DBConfig*& cc = _databases[database];
if ( cc == 0 ){
cc = new DBConfig( database );
if ( ! cc->doload() ){
if ( create ){
// note here that cc->primary == 0.
log() << "couldn't find database [" << database << "] in config db" << endl;
2009-02-14 09:42:18 -05:00
if ( database == "admin" )
cc->_primary = configServer.getPrimary();
else
cc->_primary = pickShardForNewDB();
2009-02-14 09:42:18 -05:00
if ( cc->_primary.size() ){
cc->save();
log() << "\t put [" << database << "] on: " << cc->_primary << endl;
}
else {
2009-11-19 21:49:25 -05:00
log() << "\t can't find a shard to put new db on" << endl;
uassert( 10185 , "can't find a shard to put new db on" , 0 );
2009-02-14 09:42:18 -05:00
}
}
else {
cc = 0;
}
}
2008-10-19 17:46:53 -05:00
}
2009-02-05 16:45:58 -05:00
return cc;
2008-10-19 17:46:53 -05:00
}
2009-02-12 21:09:06 -05:00
2009-12-02 16:36:46 -05:00
void Grid::removeDB( string database ){
uassert( 10186 , "removeDB expects db name" , database.find( '.' ) == string::npos );
2009-12-02 16:36:46 -05:00
boostlock l( _lock );
_databases.erase( database );
}
2009-03-30 13:28:49 -04:00
unsigned long long Grid::getNextOpTime() const {
ScopedDbConnection conn( configServer.getPrimary() );
BSONObj result;
massert( 10421 , "getoptime failed" , conn->simpleCommand( "admin" , &result , "getoptime" ) );
2009-03-30 13:28:49 -04:00
conn.done();
return result["optime"]._numberLong();
2009-03-30 13:28:49 -04:00
}
2009-02-12 21:09:06 -05:00
/* --- ConfigServer ---- */
ConfigServer::ConfigServer() {
2009-09-01 12:17:41 -04:00
_shardingEnabled = false;
2009-02-12 21:09:06 -05:00
_primary = "";
_name = "grid";
}
2009-02-05 16:45:58 -05:00
2009-02-12 21:09:06 -05:00
ConfigServer::~ConfigServer() {
}
bool ConfigServer::init( vector<string> configHosts ){
uassert( 10187 , "need configdbs" , configHosts.size() );
2009-02-12 21:09:06 -05:00
string hn = getHostName();
if ( hn.empty() ) {
sleepsecs(5);
2009-08-07 15:37:50 -04:00
dbexit( EXIT_BADOPTIONS );
2009-02-12 21:09:06 -05:00
}
ourHostname = hn;
set<string> hosts;
for ( size_t i=0; i<configHosts.size(); i++ ){
string host = configHosts[i];
hosts.insert( getHost( host , false ) );
configHosts[i] = getHost( host , true );
}
2009-02-12 21:09:06 -05:00
for ( set<string>::iterator i=hosts.begin(); i!=hosts.end(); i++ ){
string host = *i;
bool ok = false;
for ( int x=0; x<10; x++ ){
if ( ! hostbyname( host.c_str() ).empty() ){
ok = true;
break;
2009-02-12 21:09:06 -05:00
}
log() << "can't resolve DNS for [" << host << "] sleeping and trying " << (10-x) << " more times" << endl;
sleepsecs( 10 );
2009-02-12 21:09:06 -05:00
}
if ( ! ok )
return false;
2009-02-12 21:09:06 -05:00
}
uassert( 10188 , "can only hand 1 config db right now" , configHosts.size() == 1 );
_primary = configHosts[0];
2009-02-12 21:09:06 -05:00
return true;
}
bool ConfigServer::allUp(){
2009-12-02 16:36:46 -05:00
string errmsg;
return allUp( errmsg );
}
bool ConfigServer::allUp( string& errmsg ){
try {
ScopedDbConnection conn( _primary );
conn->getLastError();
conn.done();
return true;
}
2009-11-16 15:55:03 -05:00
catch ( DBException& ){
log() << "ConfigServer::allUp : " << _primary << " seems down!" << endl;
2009-12-02 16:36:46 -05:00
errmsg = _primary + " seems down";
return false;
}
}
int ConfigServer::dbConfigVersion(){
ScopedDbConnection conn( _primary );
int version = dbConfigVersion( conn.conn() );
conn.done();
return version;
}
int ConfigServer::dbConfigVersion( DBClientBase& conn ){
auto_ptr<DBClientCursor> c = conn.query( "config.version" , BSONObj() );
int version = 0;
if ( c->more() ){
BSONObj o = c->next();
version = o["version"].numberInt();
uassert( 10189 , "should only have 1 thing in config.version" , ! c->more() );
}
else {
if ( conn.count( "config.shard" ) || conn.count( "config.databases" ) ){
version = 1;
}
}
return version;
}
int ConfigServer::checkConfigVersion(){
int cur = dbConfigVersion();
if ( cur == VERSION )
return 0;
if ( cur == 0 ){
ScopedDbConnection conn( _primary );
conn->insert( "config.version" , BSON( "version" << VERSION ) );
pool.flush();
assert( VERSION == dbConfigVersion( conn.conn() ) );
conn.done();
return 0;
}
log() << "don't know how to upgrade " << cur << " to " << VERSION << endl;
return -8;
}
2009-01-14 17:09:51 -05:00
string ConfigServer::getHost( string name , bool withPort ){
if ( name.find( ":" ) ){
if ( withPort )
return name;
return name.substr( 0 , name.find( ":" ) );
}
if ( withPort ){
stringstream ss;
ss << name << ":" << CmdLine::ConfigServerPort;
return ss.str();
}
return name;
}
2009-02-12 21:09:06 -05:00
ConfigServer configServer;
2009-02-06 15:44:21 -05:00
Grid grid;
2009-02-19 12:55:01 -05:00
class DBConfigUnitTest : public UnitTest {
public:
void testInOut( DBConfig& c , BSONObj o ){
c.unserialize( o );
BSONObjBuilder b;
c.serialize( b );
BSONObj out = b.obj();
if ( o.toString() == out.toString() )
return;
log() << "DBConfig serialization broken\n"
<< "in : " << o.toString() << "\n"
<< "out : " << out.toString()
<< endl;
assert(0);
}
void a(){
BSONObjBuilder b;
b << "name" << "abc";
b.appendBool( "partitioned" , true );
b << "primary" << "myserver";
DBConfig c;
testInOut( c , b.obj() );
}
void b(){
BSONObjBuilder b;
b << "name" << "abc";
b.appendBool( "partitioned" , true );
b << "primary" << "myserver";
BSONObjBuilder a;
2009-09-03 16:48:34 -04:00
a << "abc.foo" << fromjson( "{ 'key' : { 'a' : 1 } , 'unique' : false }" );
a << "abc.bar" << fromjson( "{ 'key' : { 'kb' : -1 } , 'unique' : true }" );
2009-02-19 12:55:01 -05:00
b.appendArray( "sharded" , a.obj() );
DBConfig c;
testInOut( c , b.obj() );
2009-09-01 12:17:41 -04:00
assert( c.isSharded( "abc.foo" ) );
assert( ! c.isSharded( "abc.food" ) );
2009-02-19 12:55:01 -05:00
}
2009-02-19 12:55:01 -05:00
void run(){
a();
b();
}
} dbConfigUnitTest;
2009-02-05 16:45:58 -05:00
}