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); }