diff --git a/db/client.h b/db/client.h index 71fe74ee775..57005ab9545 100644 --- a/db/client.h +++ b/db/client.h @@ -172,24 +172,18 @@ namespace mongo { AuthenticationInfo * getAuthenticationInfo(){ return &_ai; } bool isAdmin() { return _ai.isAuthorized( "admin" ); } - - CurOp* curop() { return _curOp; } - + CurOp* curop() { return _curOp; } Context* getContext(){ return _context; } Database* database() { return _context ? _context->db() : 0; } - const char *ns() { return _context->ns(); } + const char *ns() const { return _context->ns(); } + const char *desc() const { return _desc; } Client(const char *desc); ~Client(); - const char *desc() const { return _desc; } - - void addTempCollection( const string& ns ){ - _tempCollections.push_back( ns ); - } - - void dropTempCollectionsInDB(const string db); - void dropAllTempCollectionsInDB(const string db); + void addTempCollection( const string& ns ) { _tempCollections.push_back( ns ); } + void dropTempCollectionsInDB(const string db); + void dropAllTempCollectionsInDB(const string db); void setLastOp( const OpTime& op ){ _lastOp = op; diff --git a/db/dbwebserver.cpp b/db/dbwebserver.cpp index b687ed15c81..b16f6e1bd29 100644 --- a/db/dbwebserver.cpp +++ b/db/dbwebserver.cpp @@ -269,8 +269,10 @@ namespace mongo { if( theReplSet == 0 ) { if( cmdLine.replSet.empty() ) s << p("Not using --replSet"); - else - s << p("Still starting up, or else set is not yet " + a("http://www.mongodb.org/display/DOCS/Replica+Set+Configuration#InitialSetup", "", "initialized") + '.'); + else { + s << p("Still starting up, or else set is not yet " + a("http://www.mongodb.org/display/DOCS/Replica+Set+Configuration#InitialSetup", "", "initiated") + + ".
" + ReplSet::startupStatusMsg); + } } else { try { diff --git a/db/repl/consensus.cpp b/db/repl/consensus.cpp index 981d9fba834..a404c068329 100644 --- a/db/repl/consensus.cpp +++ b/db/repl/consensus.cpp @@ -70,7 +70,7 @@ namespace mongo { ReplSet::Member& me = *rs._self; electCmd = BSON( "replSetElect" << 1 << - "set" << rs.getName() << + "set" << rs.name() << "who" << me.fullName() << "whoid" << me._id << "cfgver" << rs._cfg->version diff --git a/db/repl/health.cpp b/db/repl/health.cpp index eebb16ab65d..b8748c2fcba 100644 --- a/db/repl/health.cpp +++ b/db/repl/health.cpp @@ -36,6 +36,7 @@ namespace mongo { namespace mongo { using namespace mongoutils::html; + using namespace bson; static RamLog _rsLog; Tee *rsLog = &_rsLog; @@ -58,7 +59,6 @@ namespace mongo { errmsg = "incompatible replset protocol version"; return false; } - result.append("rs", true); string s = string(cmdObj.getStringField("replSetHeartbeat"))+'/'; if( !startsWith(cmdLine.replSet, s ) ) { errmsg = "repl set names do not match"; @@ -67,18 +67,23 @@ namespace mongo { result.append("mismatch", true); return false; } + result.append("rs", true); if( theReplSet == 0 ) { errmsg = "still initializing"; return false; } - if( theReplSet->getName() != cmdObj.getStringField("replSetHeartbeat") ) { + + if( theReplSet->name() != cmdObj.getStringField("replSetHeartbeat") ) { errmsg = "repl set names do not match (2)"; result.append("mismatch", true); return false; } - /* todo: send our state*/ - result.append("set", theReplSet->getName()); - result.append("v", theReplSet->config().version); + result.append("set", theReplSet->name()); + result.append("state", theReplSet->state()); + int v = theReplSet->config().version; + result.append("v", v); +// if( v > cmdObj["v"].Int() ) +// result << "config" << theReplSet->config().asBson(); return true; } } cmdReplSetHeartbeat; @@ -111,8 +116,13 @@ namespace mongo { try { BSONObj info; int theirConfigVersion = -10000; - bool ok = requestHeartbeat(theReplSet->getName(), m->fullName(), info, theReplSet->config().version, theirConfigVersion); + bool ok = requestHeartbeat(theReplSet->name(), m->fullName(), info, theReplSet->config().version, theirConfigVersion); m->_lastHeartbeat = time(0); // we set this on any response - we don't get this far if couldn't connect because exception is thrown + { + be state = info["state"]; + if( state.ok() ) + m->_state = (ReplSet::State) state.Int(); + } if( ok ) { if( m->_upSince == 0 ) { log() << "replSet " << m->fullName() << " is now up" << rsLog; @@ -120,6 +130,11 @@ namespace mongo { } m->_health = 1.0; m->_lastHeartbeatErrMsg.set(""); + + be cfg = info["config"]; + if( cfg.ok() ) { + //theReplSet->receivedNewConfig(cfg.Obj()); + } } else { down(); @@ -136,6 +151,26 @@ namespace mongo { } }; + string ago(time_t t) { + if( t == 0 ) return ""; + + time_t x = time(0) - t; + stringstream s; + if( x < 180 ) { + s << x << " sec"; + if( x != 1 ) s << 's'; + } + else if( x < 3600 ) { + s.precision(2); + s << x / 60.0 << " mins"; + } + else { + s.precision(2); + s << x / 3600.0 << " hrs"; + } + return s.str(); + } + void ReplSet::Member::summarizeAsHtml(stringstream& s) const { s << tr(); { @@ -146,20 +181,13 @@ namespace mongo { double h = health(); bool ok = h > 0; s << td(h); - s << td(upSince()); + s << td(ago(upSince())); { - stringstream h; + string h; time_t hb = lastHeartbeat(); - time_t now = time(0); - if( hb == 0 ) h << "never"; - else { - time_t diff = now-hb; - if( now > hb ) h << diff; - else h << 0; - if( diff == 1 ) h << "sec ago"; - else h << "secs ago"; - } - s << td(h.str()); + if( hb == 0 ) h = "never"; + else h = ago(hb) + " ago"; + s << td(h); } s << td(config().votes); s << td(ReplSet::stateAsStr(state())); @@ -187,13 +215,16 @@ namespace mongo { return ""; } + extern time_t started; + void ReplSet::summarizeAsHtml(stringstream& s) const { s << table(0, false); s << tr("Set name:", _name); s << tr("Majority up:", elect.aMajoritySeemsToBeUp()?"yes":"no" ); s << _table(); - const char *h[] = {"Member", "Up", "Uptime", + const char *h[] = {"Member", "Up", + "cctime", "Last heartbeat", "Votes", "State", "Status", 0}; s << table(h); @@ -201,8 +232,8 @@ namespace mongo { /* self row */ s << tr() << td(_self->fullName()) << td("1") << - td("self") << - td("") << + td(ago(started)) << + td("(self)") << td(ToString(_self->config().votes)) << td(stateAsHtml(_myState)); s << td( _self->_lastHeartbeatErrMsg.get() ); @@ -297,7 +328,7 @@ namespace mongo { v.push_back(bb.obj()); m = m->next(); } - b.append("set", getName()); + b.append("set", name()); b.appendDate("date", time(0)); b.append("myState", _myState); b.append("members", v); diff --git a/db/repl/manager.cpp b/db/repl/manager.cpp index 058cd3935c0..c5ec2f700c7 100644 --- a/db/repl/manager.cpp +++ b/db/repl/manager.cpp @@ -96,6 +96,7 @@ namespace mongo { } log() << "replSet todo elect self as primary primary" << rsLog; + _rs->_self->_lastHeartbeatErrMsg.set("todo code #2"); } diff --git a/db/repl/replset.cpp b/db/repl/replset.cpp index 933ec147f71..0ae528fa9d9 100644 --- a/db/repl/replset.cpp +++ b/db/repl/replset.cpp @@ -18,6 +18,7 @@ #include "../cmdline.h" #include "../../util/sock.h" #include "replset.h" +#include "../db/client.h" namespace mongo { @@ -96,8 +97,8 @@ namespace mongo { ReplSet::StartupStatus ReplSet::startupStatus = PRESTART; string ReplSet::startupStatusMsg; - void ReplSet::setFrom(ReplSetConfig& c) { - _cfg.reset( new ReplSetConfig(c) ); + void ReplSet::setFrom(ReplSetConfig& c, bool save) { + _cfg = new ReplSetConfig(c); assert( _cfg->ok() ); assert( _name.empty() || _name == _cfg->_id ); _name = _cfg->_id; @@ -117,20 +118,27 @@ namespace mongo { } } assert( me == 1 ); + + if( save ) { + _cfg->save(); + } } void ReplSet::finishLoadingConfig(vector& cfgs) { int v = -1; ReplSetConfig *highest = 0; + int myVersion = -2000; + int n = 0; for( vector::iterator i = cfgs.begin(); i != cfgs.end(); i++ ) { ReplSetConfig& cfg = *i; + if( ++n == 1 ) myVersion = cfg.version; if( cfg.ok() && cfg.version > v ) { highest = &cfg; v = cfg.version; } } assert( highest ); - setFrom(*highest); + setFrom(*highest, highest->version > myVersion && highest->version >= 0); } void ReplSet::loadConfig() { @@ -155,7 +163,7 @@ namespace mongo { if( nempty == (int) configs.size() ) { startupStatus = EMPTYCONFIG; - startupStatusMsg = "can't get " + rsConfigNs + " config from self or any seed (uninitialized?)"; + startupStatusMsg = "can't get " + rsConfigNs + " config from self or any seed (EMPTYCONFIG)"; log() << "replSet can't get " << rsConfigNs << " config from self or any seed (EMPTYCONFIG)" << rsLog; log() << "replSet have you ran replSetInitiate yet?" << rsLog; log() << "replSet sleeping 1 minute and will try again." << rsLog; @@ -186,8 +194,9 @@ namespace mongo { startupStatus = STARTED; } - /* called at startup */ + /* forked as a thread during startup */ void startReplSets() { + Client::initThread("startReplSets"); mongo::lastError.reset( new LastError() ); try { assert( theReplSet == 0 ); @@ -202,6 +211,7 @@ namespace mongo { if( theReplSet ) theReplSet->fatal(); } + cc().shutdown(); } } diff --git a/db/repl/replset.h b/db/repl/replset.h index cd757fd1d52..931767c58b8 100644 --- a/db/repl/replset.h +++ b/db/repl/replset.h @@ -37,15 +37,30 @@ namespace mongo { */ class ReplSet { public: - static enum StartupStatus { PRESTART=0, LOADINGCONFIG=1, BADCONFIG=2, EMPTYCONFIG=3, EMPTYUNREACHABLE=4, STARTED=5 } startupStatus; + static enum StartupStatus { PRESTART=0, LOADINGCONFIG=1, BADCONFIG=2, EMPTYCONFIG=3, EMPTYUNREACHABLE=4, STARTED=5, SOON=6 } startupStatus; static string startupStatusMsg; + enum State { + STARTUP, + PRIMARY, + SECONDARY, + RECOVERING, + FATAL, + STARTUP2, + UNKNOWN /* remote node not yet reached */ + }; + + private: + State _myState; + + public: + void fatal(); bool isMaster(const char *client); void fillIsMaster(BSONObjBuilder&); bool ok() const { return _myState != FATAL; } - - string getName() const { return _name; } /* @return replica set's logical name */ + State state() const { return _myState; } + string name() const { return _name; } /* @return replica set's logical name */ /* cfgString format is replsetname/host1,host2:port,... @@ -61,19 +76,20 @@ namespace mongo { // for replSetGetStatus command void summarizeStatus(BSONObjBuilder&) const; void summarizeAsHtml(stringstream&) const; - const ReplSetConfig& config() { return *_cfg.get(); } + const ReplSetConfig& config() { return *_cfg; } + void receivedNewConfig(BSONObj); private: string _name; const vector *_seeds; - auto_ptr _cfg; + ReplSetConfig *_cfg; /** load our configuration from admin.replset. try seed machines too. throws exception if a problem. */ void loadConfig(); void finishLoadingConfig(vector& v); - void setFrom(ReplSetConfig& c); + void setFrom(ReplSetConfig& c, bool save); struct Consensus { ReplSet &rs; @@ -83,17 +99,6 @@ namespace mongo { bool electSelf(); } elect; - private: - enum State { - STARTUP, - PRIMARY, - SECONDARY, - RECOVERING, - FATAL, - STARTUP2, - UNKNOWN /* remote node not yet reached */ - } _myState; - public: struct Member : public List1::Base { Member(HostAndPort h, int ord, const ReplSetConfig::MemberCfg *c); diff --git a/db/repl/rs_config.cpp b/db/repl/rs_config.cpp index 4f4ad679c97..cb30cce6366 100644 --- a/db/repl/rs_config.cpp +++ b/db/repl/rs_config.cpp @@ -29,8 +29,14 @@ namespace mongo { void ReplSetConfig::save() { check(); - BSONObj o = asBson(); - Helpers::putSingletonGod(rsConfigNs.c_str(), o, false/*logOp=false; local db so would work regardless...*/); + log() << "replSet info saving a newer config version to local.system.replset" << rsLog; + MemoryMappedFile::flushAll(true); + { + writelock lk("admin."); + BSONObj o = asBson(); + Helpers::putSingletonGod(rsConfigNs.c_str(), o, false/*logOp=false; local db so would work regardless...*/); + MemoryMappedFile::flushAll(true); + } } bo ReplSetConfig::MemberCfg::asBson() const { @@ -177,9 +183,8 @@ namespace mongo { //log(0) << "replSet load config from: " << h.toString() << rsLog; auto_ptr c; + int v = -5; try { - version = -5; - if( h.isSelf() ) { ; } @@ -192,33 +197,40 @@ namespace mongo { int theirVersion; BSONObj info; bool ok = requestHeartbeat(setname, h.toString(), info, -2, theirVersion); - if( !ok ) { - log() << "replSet TEMP !ok heartbeating " << h.toString() << " on cfg load" << rsLog; - if( !info.isEmpty() ) log() << "replSet TEMP response was: " << info.toString() << rsLog; - return; + if( info["rs"].trueValue() ) { + // yes, it is a replicate set, although perhaps not yet initialized } - if( !info["rs"].trueValue() ) { - stringstream ss; - ss << "replSet error: member " << h.toString() << " is not in --replSet mode"; - cout << "TEMP " << info.toString() << endl; - msgassertedNoTrace(13260, ss.str().c_str()); // not caught as not a user exception - we want it not caught - //for python: uassert(13260, "", false); + else { + if( !ok ) { + log() << "replSet TEMP !ok heartbeating " << h.toString() << " on cfg load" << rsLog; + if( !info.isEmpty() ) log() << "replSet TEMP response was: " << info.toString() << rsLog; + return; + } + { + stringstream ss; + ss << "replSet error: member " << h.toString() << " is not in --replSet mode"; + cout << "TEMP " << info.toString() << endl; + msgassertedNoTrace(13260, ss.str().c_str()); // not caught as not a user exception - we want it not caught + //for python err# checker: uassert(13260, "", false); + } } } - version = -4; + v = -4; ScopedConn conn(h.toString()); - version = -3; + v = -3; c = conn->query(rsConfigNs); - if( c.get() == 0 ) - return; + if( c.get() == 0 ) { + version = v; return; + } if( !c->more() ) { - version = -2; /* -2 is a sentinel - see ReplSetConfig::empty() */ + version = EMPTYCONFIG; return; } version = -1; } catch( DBException& e) { + version = v; log(level) << "replSet load config couldn't load " << h.toString() << ' ' << e.what() << rsLog; return; } diff --git a/db/repl/rs_config.h b/db/repl/rs_config.h index df9914f2e59..e9f3d64171a 100644 --- a/db/repl/rs_config.h +++ b/db/repl/rs_config.h @@ -25,17 +25,10 @@ namespace mongo { - /** - " + rsConfigNs + " - - This collection has one object per server in the set. - - See "Replical Sets Configuration" on mongodb.org wiki for details on the format. - */ - const string rsConfigNs = "local.system.replset"; class ReplSetConfig { + enum { EMPTYCONFIG = -2 }; public: /* if something is misconfigured, throws an exception. if couldn't be queried or is just blank, ok() will be false. @@ -63,8 +56,8 @@ namespace mongo { string md5; BSONObj getLastErrorDefaults; - // true if could connect, and there is no cfg object there at all - bool empty() const { return version == -2; } + /** @return true if could connect, and there is no cfg object there at all */ + bool empty() const { return version == EMPTYCONFIG; } string toString() const { return asBson().toString(); } @@ -72,12 +65,12 @@ namespace mongo { void check() const; void save(); // to local db + BSONObj asBson() const; private: bool _ok; void from(BSONObj); void clear(); - BSONObj asBson() const; }; } diff --git a/db/repl/rs_mod.cpp b/db/repl/rs_mod.cpp index 80b4695bda9..0885a4f709f 100644 --- a/db/repl/rs_mod.cpp +++ b/db/repl/rs_mod.cpp @@ -46,7 +46,7 @@ namespace mongo { } } catch(...) { } - if( !ok ) { + if( !ok && !res["rs"].trueValue() ) { if( !res.isEmpty() ) log() << "replSet warning " << i->h.toString() << " replied: " << res.toString() << rsLog; uasserted(13144, "need all members up to initiate, not ok: " + i->h.toString()); @@ -89,6 +89,7 @@ namespace mongo { } if( ReplSet::startupStatus != ReplSet::EMPTYCONFIG ) { result.append("startupStatus", ReplSet::startupStatus); + result.append("startupStatusMsg", ReplSet::startupStatusMsg); errmsg = "all members and seeds must be reachable to initiate set"; result.append("info", cmdLine.replSet); return false; @@ -108,11 +109,9 @@ namespace mongo { log() << "replSet replSetInitiate all members seem up" << rsLog; - log() << newConfig.toString() << rsLog; + //log() << newConfig.toString() << rsLog; - MemoryMappedFile::flushAll(true); newConfig.save(); - MemoryMappedFile::flushAll(true); } catch( DBException& e ) { log() << "replSet replSetInitiate exception: " << e.what() << rsLog; @@ -121,9 +120,20 @@ namespace mongo { log() << "replSet replSetInitiate Config now saved locally. Should come online in about a minute." << rsLog; result.append("info", "Config now saved locally. Should come online in about a minute."); + ReplSet::startupStatus = ReplSet::SOON; + ReplSet::startupStatusMsg = "Received replSetInitiate - should come online shortly."; return true; } } cmdReplSetInitiate; + /*void ReplSet::receivedNewConfig(BSONObj cfg) { + writelock lk("admin."); + ReplSetConfig c(cfg); + if( c.version <= config().version ) { + log() << "replSet info received new config v" << c.version << " but our v" << config().version << " is already newer" << rsLog; + return; + } + }*/ + } diff --git a/db/repl/testing.js b/db/repl/testing.js index d93747a37d9..e174470f11a 100644 --- a/db/repl/testing.js +++ b/db/repl/testing.js @@ -1,6 +1,6 @@ // helpers for testing repl sets // run -// mongo --shell testing.js +// mongo --shell testing.js cfg = { _id: 'asdf', @@ -13,6 +13,8 @@ cfg = { db = db.getSisterDB("admin"); local = db.getSisterDB("local"); -print("\nswitched to admin db\n"); +print("\nswitched to admin db"); +print("cfg = samp replset config"); +print("rc = runCommand\n"); function rc(c) { return db.runCommand(c); }