summaryrefslogtreecommitdiff
path: root/db/repl/health.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'db/repl/health.cpp')
-rw-r--r--db/repl/health.cpp161
1 files changed, 92 insertions, 69 deletions
diff --git a/db/repl/health.cpp b/db/repl/health.cpp
index c75221c..762ca90 100644
--- a/db/repl/health.cpp
+++ b/db/repl/health.cpp
@@ -32,20 +32,22 @@
#include "../dbhelpers.h"
namespace mongo {
+
/* decls for connections.h */
- ScopedConn::M& ScopedConn::_map = *(new ScopedConn::M());
+ ScopedConn::M& ScopedConn::_map = *(new ScopedConn::M());
mutex ScopedConn::mapMutex("ScopedConn::mapMutex");
}
-namespace mongo {
+namespace mongo {
using namespace mongoutils::html;
using namespace bson;
static RamLog _rsLog;
Tee *rsLog = &_rsLog;
+ extern bool replSetBlind;
- string ago(time_t t) {
+ string ago(time_t t) {
if( t == 0 ) return "";
time_t x = time(0) - t;
@@ -58,14 +60,14 @@ namespace mongo {
s.precision(2);
s << x / 60.0 << " mins";
}
- else {
+ else {
s.precision(2);
s << x / 3600.0 << " hrs";
}
return s.str();
}
- void Member::summarizeMember(stringstream& s) const {
+ void Member::summarizeMember(stringstream& s) const {
s << tr();
{
stringstream u;
@@ -89,27 +91,29 @@ namespace mongo {
s << td(h);
}
s << td(config().votes);
- {
+ s << td(config().priority);
+ {
string stateText = state().toString();
if( _config.hidden )
stateText += " (hidden)";
- if( ok || stateText.empty() )
+ if( ok || stateText.empty() )
s << td(stateText); // text blank if we've never connected
else
s << td( grey(str::stream() << "(was " << state().toString() << ')', true) );
}
s << td( grey(hbinfo().lastHeartbeatMsg,!ok) );
stringstream q;
- q << "/_replSetOplog?" << id();
+ q << "/_replSetOplog?_id=" << id();
s << td( a(q.str(), "", never ? "?" : hbinfo().opTime.toString()) );
if( hbinfo().skew > INT_MIN ) {
s << td( grey(str::stream() << hbinfo().skew,!ok) );
- } else
+ }
+ else
s << td("");
s << _tr();
}
-
- string ReplSetImpl::stateAsHtml(MemberState s) {
+
+ string ReplSetImpl::stateAsHtml(MemberState s) {
if( s.s == MemberState::RS_STARTUP ) return a("", "serving still starting up, or still trying to initiate the set", "STARTUP");
if( s.s == MemberState::RS_PRIMARY ) return a("", "this server thinks it is primary", "PRIMARY");
if( s.s == MemberState::RS_SECONDARY ) return a("", "this server thinks it is a secondary (slave mode)", "SECONDARY");
@@ -122,7 +126,7 @@ namespace mongo {
return "";
}
- string MemberState::toString() const {
+ string MemberState::toString() const {
if( s == MemberState::RS_STARTUP ) return "STARTUP";
if( s == MemberState::RS_PRIMARY ) return "PRIMARY";
if( s == MemberState::RS_SECONDARY ) return "SECONDARY";
@@ -143,9 +147,9 @@ namespace mongo {
set<string> skip;
be e = op["ts"];
- if( e.type() == Date || e.type() == Timestamp ) {
+ if( e.type() == Date || e.type() == Timestamp ) {
OpTime ot = e._opTime();
- ss << td( time_t_to_String_short( ot.getSecs() ) );
+ ss << td( time_t_to_String_short( ot.getSecs() ) );
ss << td( ot.toString() );
skip.insert("ts");
}
@@ -155,7 +159,8 @@ namespace mongo {
if( e.type() == NumberLong ) {
ss << "<td>" << hex << e.Long() << "</td>\n";
skip.insert("h");
- } else
+ }
+ else
ss << td("?");
ss << td(op["op"].valuestrsafe());
@@ -164,20 +169,17 @@ namespace mongo {
skip.insert("ns");
ss << "<td>";
- for( bo::iterator i(op); i.more(); ) {
+ for( bo::iterator i(op); i.more(); ) {
be e = i.next();
if( skip.count(e.fieldName()) ) continue;
ss << e.toString() << ' ';
}
- ss << "</td>";
-
- ss << "</tr>";
- ss << '\n';
+ ss << "</td></tr>\n";
}
- void ReplSetImpl::_getOplogDiagsAsHtml(unsigned server_id, stringstream& ss) const {
+ void ReplSetImpl::_getOplogDiagsAsHtml(unsigned server_id, stringstream& ss) const {
const Member *m = findById(server_id);
- if( m == 0 ) {
+ if( m == 0 ) {
ss << "Error : can't find a member with id: " << server_id << '\n';
return;
}
@@ -187,21 +189,29 @@ namespace mongo {
//const bo fields = BSON( "o" << false << "o2" << false );
const bo fields;
- ScopedDbConnection conn(m->fullName());
+ /** todo fix we might want an so timeout here */
+ DBClientConnection conn(false, 0, /*timeout*/ 20);
+ {
+ string errmsg;
+ if( !conn.connect(m->fullName(), errmsg) ) {
+ ss << "couldn't connect to " << m->fullName() << ' ' << errmsg;
+ return;
+ }
+ }
- auto_ptr<DBClientCursor> c = conn->query(rsoplog, Query().sort("$natural",1), 20, 0, &fields);
- if( c.get() == 0 ) {
+ auto_ptr<DBClientCursor> c = conn.query(rsoplog, Query().sort("$natural",1), 20, 0, &fields);
+ if( c.get() == 0 ) {
ss << "couldn't query " << rsoplog;
return;
}
static const char *h[] = {"ts","optime", "h","op","ns","rest",0};
ss << "<style type=\"text/css\" media=\"screen\">"
- "table { font-size:75% }\n"
+ "table { font-size:75% }\n"
// "th { background-color:#bbb; color:#000 }\n"
// "td,th { padding:.25em }\n"
- "</style>\n";
-
+ "</style>\n";
+
ss << table(h, true);
//ss << "<pre>\n";
int n = 0;
@@ -211,17 +221,17 @@ namespace mongo {
while( c->more() ) {
bo o = c->next();
otLast = o["ts"]._opTime();
- if( otFirst.isNull() )
+ if( otFirst.isNull() )
otFirst = otLast;
say(ss, o);
- n++;
+ n++;
}
if( n == 0 ) {
ss << rsoplog << " is empty\n";
}
- else {
- auto_ptr<DBClientCursor> c = conn->query(rsoplog, Query().sort("$natural",-1), 20, 0, &fields);
- if( c.get() == 0 ) {
+ else {
+ auto_ptr<DBClientCursor> c = conn.query(rsoplog, Query().sort("$natural",-1), 20, 0, &fields);
+ if( c.get() == 0 ) {
ss << "couldn't query [2] " << rsoplog;
return;
}
@@ -230,7 +240,7 @@ namespace mongo {
otEnd = o["ts"]._opTime();
while( 1 ) {
stringstream z;
- if( o["ts"]._opTime() == otLast )
+ if( o["ts"]._opTime() == otLast )
break;
say(z, o);
x = z.str() + x;
@@ -253,32 +263,31 @@ namespace mongo {
ss.precision(3);
if( h < 72 )
ss << h << " hours";
- else
+ else
ss << h / 24.0 << " days";
ss << "</p>\n";
}
-
- conn.done();
}
- void ReplSetImpl::_summarizeAsHtml(stringstream& s) const {
+ void ReplSetImpl::_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",
- "<a title=\"member id in the replset config\">id</a>",
- "Up",
- "<a title=\"length of time we have been continuously connected to the other member with no reconnects (for self, shows uptime)\">cctime</a>",
- "<a title=\"when this server last received a heartbeat response - includes error code responses\">Last heartbeat</a>",
- "Votes", "State", "Status",
- "<a title=\"how up to date this server is. this value polled every few seconds so actually lag is typically much lower than value shown here.\">optime</a>",
- "<a title=\"Clock skew in seconds relative to this server. Informational; server clock variances will make the diagnostics hard to read, but otherwise are benign..\">skew</a>",
- 0};
+ const char *h[] = {"Member",
+ "<a title=\"member id in the replset config\">id</a>",
+ "Up",
+ "<a title=\"length of time we have been continuously connected to the other member with no reconnects (for self, shows uptime)\">cctime</a>",
+ "<a title=\"when this server last received a heartbeat response - includes error code responses\">Last heartbeat</a>",
+ "Votes", "Priority", "State", "Messages",
+ "<a title=\"how up to date this server is. this value polled every few seconds so actually lag is typically much lower than value shown here.\">optime</a>",
+ "<a title=\"Clock skew in seconds relative to this server. Informational; server clock variances will make the diagnostics hard to read, but otherwise are benign..\">skew</a>",
+ 0
+ };
s << table(h);
- /* this is to sort the member rows by their ordinal _id, so they show up in the same
+ /* this is to sort the member rows by their ordinal _id, so they show up in the same
order on all the different web ui's; that is less confusing for the operator. */
map<int,string> mp;
@@ -287,13 +296,13 @@ namespace mongo {
readlocktry lk("local.replset.minvalid", 300);
if( lk.got() ) {
BSONObj mv;
- if( Helpers::getSingleton("local.replset.minvalid", mv) ) {
+ if( Helpers::getSingleton("local.replset.minvalid", mv) ) {
myMinValid = "minvalid:" + mv["ts"]._opTime().toString();
}
}
else myMinValid = ".";
}
- catch(...) {
+ catch(...) {
myMinValid = "exception fetching minvalid";
}
@@ -301,25 +310,26 @@ namespace mongo {
stringstream s;
/* self row */
s << tr() << td(_self->fullName() + " (me)") <<
- td(_self->id()) <<
- td("1") << //up
- td(ago(started)) <<
- td("") << // last heartbeat
- td(ToString(_self->config().votes)) <<
- td( stateAsHtml(box.getState()) + (_self->config().hidden?" (hidden)":"") );
+ td(_self->id()) <<
+ td("1") << //up
+ td(ago(started)) <<
+ td("") << // last heartbeat
+ td(ToString(_self->config().votes)) <<
+ td(ToString(_self->config().priority)) <<
+ td( stateAsHtml(box.getState()) + (_self->config().hidden?" (hidden)":"") );
s << td( _hbmsg );
stringstream q;
- q << "/_replSetOplog?" << _self->id();
+ q << "/_replSetOplog?_id=" << _self->id();
s << td( a(q.str(), myMinValid, theReplSet->lastOpTimeWritten.toString()) );
s << td(""); // skew
s << _tr();
- mp[_self->hbinfo().id()] = s.str();
+ mp[_self->hbinfo().id()] = s.str();
}
Member *m = head();
while( m ) {
- stringstream s;
+ stringstream s;
m->summarizeMember(s);
- mp[m->hbinfo().id()] = s.str();
+ mp[m->hbinfo().id()] = s.str();
m = m->next();
}
@@ -333,26 +343,27 @@ namespace mongo {
_rsLog.toHTML( s );
}
- const Member* ReplSetImpl::findById(unsigned id) const {
+ const Member* ReplSetImpl::findById(unsigned id) const {
if( id == _self->id() ) return _self;
for( Member *m = head(); m; m = m->next() )
- if( m->id() == id )
+ if( m->id() == id )
return m;
return 0;
}
- void ReplSetImpl::_summarizeStatus(BSONObjBuilder& b) const {
+ void ReplSetImpl::_summarizeStatus(BSONObjBuilder& b) const {
vector<BSONObj> v;
// add self
{
- HostAndPort h(getHostName(), cmdLine.port);
-
BSONObjBuilder bb;
bb.append("_id", (int) _self->id());
- bb.append("name", h.toString());
+ bb.append("name", _self->fullName());
bb.append("health", 1.0);
bb.append("state", (int) box.getState().s);
+ bb.append("stateStr", box.getState().toString());
+ bb.appendTimestamp("optime", lastOpTimeWritten.asDate());
+ bb.appendDate("optimeDate", lastOpTimeWritten.getSecs() * 1000LL);
string s = _self->lhb();
if( !s.empty() )
bb.append("errmsg", s);
@@ -365,9 +376,19 @@ namespace mongo {
BSONObjBuilder bb;
bb.append("_id", (int) m->id());
bb.append("name", m->fullName());
- bb.append("health", m->hbinfo().health);
+ double h = m->hbinfo().health;
+ bb.append("health", h);
bb.append("state", (int) m->state().s);
+ if( h == 0 ) {
+ // if we can't connect the state info is from the past and could be confusing to show
+ bb.append("stateStr", "(not reachable/healthy)");
+ }
+ else {
+ bb.append("stateStr", m->state().toString());
+ }
bb.append("uptime", (unsigned) (m->hbinfo().upSince ? (time(0)-m->hbinfo().upSince) : 0));
+ bb.appendTimestamp("optime", m->hbinfo().opTime.asDate());
+ bb.appendDate("optimeDate", m->hbinfo().opTime.getSecs() * 1000LL);
bb.appendTimeT("lastHeartbeat", m->hbinfo().lastHeartbeat);
string s = m->lhb();
if( !s.empty() )
@@ -380,10 +401,12 @@ namespace mongo {
b.appendTimeT("date", time(0));
b.append("myState", box.getState().s);
b.append("members", v);
+ if( replSetBlind )
+ b.append("blind",true); // to avoid confusion if set...normally never set except for testing.
}
- static struct Test : public UnitTest {
- void run() {
+ static struct Test : public UnitTest {
+ void run() {
HealthOptions a,b;
assert( a == b );
assert( a.isDefault() );