2010-04-14 17:25:03 -04:00
/**
* Copyright ( C ) 2008 10 gen 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 .
*
2010-04-18 12:30:40 -04:00
* This program is distributed in the hope that it will be useful , b
2010-04-14 17:25:03 -04:00
* 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/>.
*/
2010-04-27 15:27:52 -04:00
# include "pch.h"
2010-05-29 15:45:47 -04:00
# include "rs.h"
2010-04-14 17:25:03 -04:00
# include "health.h"
2010-04-18 12:30:40 -04:00
# include "../../util/background.h"
# include "../../client/dbclient.h"
# include "../commands.h"
# include "../../util/concurrency/value.h"
2010-05-18 14:47:11 -04:00
# include "../../util/concurrency/task.h"
2010-05-07 16:42:55 -04:00
# include "../../util/mongoutils/html.h"
2010-05-05 16:33:35 -04:00
# include "../../util/goodies.h"
2010-05-07 16:42:55 -04:00
# include "../../util/ramlog.h"
2010-05-06 10:05:48 -04:00
# include "../helpers/dblogger.h"
2010-05-06 16:48:07 -04:00
# include "connections.h"
namespace mongo {
/* decls for connections.h */
ScopedConn : : M & ScopedConn : : _map = * ( new ScopedConn : : M ( ) ) ;
2010-05-26 00:46:49 -04:00
mutex ScopedConn : : mapMutex ( " ScopedConn::mapMutex " ) ;
2010-05-06 16:48:07 -04:00
}
2010-04-14 17:25:03 -04:00
namespace mongo {
2010-05-05 16:33:35 -04:00
using namespace mongoutils : : html ;
2010-05-13 17:18:17 -04:00
using namespace bson ;
2010-05-05 16:33:35 -04:00
2010-05-07 16:42:55 -04:00
static RamLog _rsLog ;
Tee * rsLog = & _rsLog ;
2010-05-13 17:18:17 -04:00
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 ( ) ;
}
2010-06-01 16:25:47 -04:00
void Member : : summarizeAsHtml ( stringstream & s ) const {
2010-05-05 16:33:35 -04:00
s < < tr ( ) ;
{
stringstream u ;
2010-05-20 12:40:22 -04:00
u < < " http:// " < < h ( ) . host ( ) < < ' : ' < < ( h ( ) . port ( ) + 1000 ) < < " /_replSet " ;
2010-05-05 16:33:35 -04:00
s < < td ( a ( u . str ( ) , " " , fullName ( ) ) ) ;
}
2010-07-20 13:37:09 -04:00
s < < td ( id ( ) ) ;
2010-05-20 13:52:30 -04:00
double h = hbinfo ( ) . health ;
2010-05-11 15:58:44 -04:00
bool ok = h > 0 ;
2010-07-26 16:04:54 -04:00
s < < td ( red ( str : : stream ( ) < < h , h = = 0 ) ) ;
2010-05-20 13:52:30 -04:00
s < < td ( ago ( hbinfo ( ) . upSince ) ) ;
2010-07-19 17:23:11 -04:00
bool never = false ;
2010-05-05 16:33:35 -04:00
{
2010-05-13 17:18:17 -04:00
string h ;
2010-05-20 13:52:30 -04:00
time_t hb = hbinfo ( ) . lastHeartbeat ;
2010-07-19 17:23:11 -04:00
if ( hb = = 0 ) {
h = " never " ;
never = true ;
}
2010-05-13 17:18:17 -04:00
else h = ago ( hb ) + " ago " ;
s < < td ( h ) ;
2010-05-05 16:33:35 -04:00
}
s < < td ( config ( ) . votes ) ;
2010-07-26 20:09:57 -04:00
{
string stateText = ReplSet : : stateAsStr ( state ( ) ) ;
if ( ok | | stateText . empty ( ) )
s < < td ( stateText ) ; // text blank if we've never connected
else
s < < td ( grey ( str : : stream ( ) < < " (was " < < ReplSet : : stateAsStr ( state ( ) ) < < ' ) ' , true ) ) ;
}
2010-06-29 15:52:35 -04:00
s < < td ( grey ( hbinfo ( ) . lastHeartbeatMsg , ! ok ) ) ;
2010-06-04 11:19:40 -04:00
stringstream q ;
q < < " /_replSetOplog? " < < id ( ) ;
2010-07-19 17:23:11 -04:00
s < < td ( a ( q . str ( ) , " " , never ? " ? " : hbinfo ( ) . opTime . toString ( ) ) ) ;
2010-05-05 16:33:35 -04:00
s < < _tr ( ) ;
}
2010-07-20 11:05:27 -04:00
2010-06-01 16:25:47 -04:00
string ReplSetImpl : : stateAsHtml ( MemberState s ) {
2010-07-22 17:50:54 -04:00
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 " ) ;
if ( s . s = = MemberState : : RS_RECOVERING ) return a ( " " , " recovering/resyncing; after recovery usually auto-transitions to secondary " , " RECOVERING " ) ;
if ( s . s = = MemberState : : RS_FATAL ) return a ( " " , " something bad has occurred and server is not completely offline with regard to the replica set. fatal error. " , " RS_FATAL " ) ;
if ( s . s = = MemberState : : RS_STARTUP2 ) return a ( " " , " loaded config, still determining who is primary " , " RS_STARTUP2 " ) ;
if ( s . s = = MemberState : : RS_ARBITER ) return a ( " " , " this server is an arbiter only " , " ARBITER " ) ;
if ( s . s = = MemberState : : RS_DOWN ) return a ( " " , " member is down, slow, or unreachable " , " DOWN " ) ;
2010-05-11 15:58:44 -04:00
return " " ;
2010-05-07 15:35:16 -04:00
}
2010-06-01 16:25:47 -04:00
string ReplSetImpl : : stateAsStr ( MemberState s ) {
2010-07-22 17:50:54 -04:00
if ( s . s = = MemberState : : RS_STARTUP ) return " STARTUP " ;
if ( s . s = = MemberState : : RS_PRIMARY ) return " PRIMARY " ;
if ( s . s = = MemberState : : RS_SECONDARY ) return " SECONDARY " ;
if ( s . s = = MemberState : : RS_RECOVERING ) return " RECOVERING " ;
if ( s . s = = MemberState : : RS_FATAL ) return " FATAL " ;
if ( s . s = = MemberState : : RS_STARTUP2 ) return " STARTUP2 " ;
if ( s . s = = MemberState : : RS_ARBITER ) return " ARBITER " ;
if ( s . s = = MemberState : : RS_DOWN ) return " DOWN " ;
2010-05-11 15:58:44 -04:00
return " " ;
2010-05-07 15:35:16 -04:00
}
2010-05-13 17:18:17 -04:00
extern time_t started ;
2010-07-06 16:11:56 -04:00
// oplogdiags in web ui
2010-06-04 14:02:20 -04:00
static void say ( stringstream & ss , const bo & op ) {
2010-07-06 16:11:56 -04:00
ss < < " <tr> " ;
set < string > skip ;
be e = op [ " ts " ] ;
if ( e . type ( ) = = Date | | e . type ( ) = = Timestamp ) {
2010-07-12 08:54:37 -04:00
OpTime ot = e . _opTime ( ) ;
ss < < td ( a ( " " , ot . toString ( ) , ot . toStringPretty ( ) ) ) ;
2010-07-06 16:11:56 -04:00
skip . insert ( " ts " ) ;
}
else ss < < td ( " ? " ) ;
e = op [ " h " ] ;
if ( e . type ( ) = = NumberLong ) {
ss < < " <td> " < < hex < < e . Long ( ) < < " </td> \n " ;
skip . insert ( " h " ) ;
} else
ss < < td ( " ? " ) ;
2010-07-12 09:13:38 -04:00
ss < < td ( op [ " op " ] . valuestrsafe ( ) ) ;
ss < < td ( op [ " ns " ] . valuestrsafe ( ) ) ;
2010-07-06 16:11:56 -04:00
skip . insert ( " op " ) ;
skip . insert ( " ns " ) ;
ss < < " <td> " ;
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 ' ;
2010-06-04 14:02:20 -04:00
}
void ReplSetImpl : : _getOplogDiagsAsHtml ( unsigned server_id , stringstream & ss ) const {
Member * m = findById ( server_id ) ;
if ( m = = 0 ) {
ss < < " Error : can't find a member with id: " < < server_id < < ' \n ' ;
return ;
}
2010-07-06 22:16:40 -04:00
ss < < p ( " Server : " + m - > fullName ( ) + " <br>ns : " + rsoplog ) ;
2010-06-17 19:54:57 +01:00
2010-07-06 16:11:56 -04:00
//const bo fields = BSON( "o" << false << "o2" << false );
const bo fields ;
2010-06-04 14:02:20 -04:00
ScopedConn conn ( m - > fullName ( ) ) ;
auto_ptr < DBClientCursor > c = conn - > query ( rsoplog , Query ( ) . sort ( " $natural " , 1 ) , 20 , 0 , & fields ) ;
2010-07-06 16:11:56 -04:00
static const char * h [ ] = { " ts " , " h " , " op " , " ns " , " rest " , 0 } ;
2010-07-20 14:58:51 -04:00
ss < < " <style type= \" text/css \" media= \" screen \" > "
" table { font-size:75% } \n "
// "th { background-color:#bbb; color:#000 }\n"
// "td,th { padding:.25em }\n"
" </style> \n " ;
2010-07-06 16:11:56 -04:00
ss < < table ( h , true ) ;
//ss << "<pre>\n";
2010-06-04 14:02:20 -04:00
int n = 0 ;
2010-06-29 21:44:30 -04:00
OpTime otFirst ;
2010-06-29 18:27:09 -04:00
OpTime otLast ;
2010-06-29 21:44:30 -04:00
OpTime otEnd ;
2010-06-04 14:02:20 -04:00
while ( c - > more ( ) ) {
bo o = c - > next ( ) ;
2010-06-29 18:27:09 -04:00
otLast = o [ " ts " ] . _opTime ( ) ;
2010-06-29 21:44:30 -04:00
if ( otFirst . isNull ( ) )
otFirst = otLast ;
2010-06-04 14:02:20 -04:00
say ( ss , o ) ;
n + + ;
}
if ( n = = 0 ) {
ss < < rsoplog < < " is empty \n " ;
}
else {
auto_ptr < DBClientCursor > c = conn - > query ( rsoplog , Query ( ) . sort ( " $natural " , - 1 ) , 20 , 0 , & fields ) ;
string x ;
2010-06-29 21:44:30 -04:00
bo o = c - > next ( ) ;
otEnd = o [ " ts " ] . _opTime ( ) ;
while ( 1 ) {
2010-06-04 14:02:20 -04:00
stringstream z ;
2010-06-29 18:27:09 -04:00
if ( o [ " ts " ] . _opTime ( ) = = otLast )
2010-06-04 14:02:20 -04:00
break ;
say ( z , o ) ;
x = z . str ( ) + x ;
2010-06-29 21:44:30 -04:00
if ( ! c - > more ( ) )
break ;
bo o = c - > next ( ) ;
2010-06-04 14:02:20 -04:00
}
if ( ! x . empty ( ) ) {
2010-07-20 14:58:51 -04:00
ss < < " <tr><td>...</td><td>...</td><td>...</td><td>...</td><td>...</td></tr> \n " < < x ;
2010-07-20 13:37:09 -04:00
//ss << "\n...\n\n" << x;
2010-06-04 14:02:20 -04:00
}
}
2010-07-06 16:11:56 -04:00
ss < < _table ( ) ;
//ss << "</pre>\n";
if ( ! otEnd . isNull ( ) ) {
ss < < " <p>Log length in time: " ;
unsigned d = otEnd . getSecs ( ) - otFirst . getSecs ( ) ;
2010-07-20 13:37:09 -04:00
double h = d / 3600.0 ;
ss . precision ( 3 ) ;
if ( h < 72 )
ss < < h < < " hours " ;
else
ss < < h / 24.0 < < " days " ;
ss < < " </p> \n " ;
2010-07-06 16:11:56 -04:00
}
2010-07-12 08:54:37 -04:00
ss < < p ( " Current time: " + time_t_to_String_short ( time ( 0 ) ) ) ;
2010-06-04 14:02:20 -04:00
}
2010-06-01 16:25:47 -04:00
void ReplSetImpl : : _summarizeAsHtml ( stringstream & s ) const {
2010-05-06 10:05:48 -04:00
s < < table ( 0 , false ) ;
2010-05-06 16:48:07 -04:00
s < < tr ( " Set name: " , _name ) ;
2010-05-06 21:50:52 -04:00
s < < tr ( " Majority up: " , elect . aMajoritySeemsToBeUp ( ) ? " yes " : " no " ) ;
2010-05-06 10:05:48 -04:00
s < < _table ( ) ;
2010-07-20 13:37:09 -04:00
const char * h [ ] = { " Member " ,
" <a title= \" member id in the replset config \" >id</a> " ,
" Up " ,
2010-07-22 14:20:13 -04:00
" <a title= \" length of time we have been continuously connected to the other member with no reconnects (for self, shows uptime) \" >cctime</a> " ,
2010-05-05 16:33:35 -04:00
" <a title= \" when this server last received a heartbeat response - includes error code responses \" >Last heartbeat</a> " ,
2010-06-04 11:19:40 -04:00
" Votes " , " State " , " Status " ,
2010-07-20 11:34:23 -04:00
" <a title= \" how up to date this server is; write operations are sequentially numbered. this value polled every few seconds so actually lag is typically much lower than value shown here. \" >opord</a> " ,
2010-06-04 11:19:40 -04:00
0 } ;
2010-05-05 16:33:35 -04:00
s < < table ( h ) ;
2010-05-15 14:48:40 -04:00
/* 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 . */
2010-05-15 15:33:32 -04:00
map < int , string > mp ;
2010-05-15 14:48:40 -04:00
2010-05-12 17:43:21 -04:00
{
2010-05-15 15:33:32 -04:00
stringstream s ;
2010-05-12 17:43:21 -04:00
/* self row */
2010-07-20 11:34:23 -04:00
s < < tr ( ) < < td ( _self - > fullName ( ) + " (me) " ) < <
2010-07-20 13:37:09 -04:00
td ( _self - > id ( ) ) < <
2010-07-20 21:11:43 -04:00
td ( " 1 " ) < < //up
2010-05-13 17:18:17 -04:00
td ( ago ( started ) ) < <
2010-07-20 21:11:43 -04:00
td ( " " ) < < // last heartbeat
2010-05-12 17:43:21 -04:00
td ( ToString ( _self - > config ( ) . votes ) ) < <
2010-07-22 17:50:54 -04:00
td ( stateAsHtml ( box . getState ( ) ) ) ;
2010-07-20 21:11:43 -04:00
s < < td ( _hbmsg ) ;
2010-06-04 11:19:40 -04:00
stringstream q ;
q < < " /_replSetOplog? " < < _self - > id ( ) ;
2010-06-26 21:31:17 -04:00
s < < td ( a ( q . str ( ) , " " , theReplSet - > lastOpTimeWritten . toString ( ) ) ) ;
2010-05-12 17:43:21 -04:00
s < < _tr ( ) ;
2010-05-20 13:52:30 -04:00
mp [ _self - > hbinfo ( ) . id ( ) ] = s . str ( ) ;
2010-05-12 17:43:21 -04:00
}
2010-05-05 16:33:35 -04:00
Member * m = head ( ) ;
while ( m ) {
2010-05-15 15:33:32 -04:00
stringstream s ;
m - > summarizeAsHtml ( s ) ;
2010-05-20 13:52:30 -04:00
mp [ m - > hbinfo ( ) . id ( ) ] = s . str ( ) ;
2010-05-05 16:33:35 -04:00
m = m - > next ( ) ;
}
2010-05-15 14:48:40 -04:00
2010-05-15 15:33:32 -04:00
for ( map < int , string > : : const_iterator i = mp . begin ( ) ; i ! = mp . end ( ) ; i + + )
s < < i - > second ;
2010-05-05 16:33:35 -04:00
s < < _table ( ) ;
}
2010-05-10 11:26:02 -04:00
2010-05-07 16:42:55 -04:00
void fillRsLog ( stringstream & s ) {
2010-07-17 12:38:48 -04:00
_rsLog . toHTML ( s ) ;
2010-05-07 16:42:55 -04:00
}
2010-06-04 14:02:20 -04:00
Member * ReplSetImpl : : findById ( unsigned id ) const {
if ( id = = _self - > id ( ) ) return _self ;
for ( Member * m = head ( ) ; m ; m = m - > next ( ) )
if ( m - > id ( ) = = id )
return m ;
return 0 ;
}
2010-06-01 16:25:47 -04:00
void ReplSetImpl : : _summarizeStatus ( BSONObjBuilder & b ) const {
2010-05-05 14:57:49 -04:00
Member * m = _members . head ( ) ;
2010-04-20 12:29:00 -04:00
vector < BSONObj > v ;
// add self
{
HostAndPort h ( getHostName ( ) , cmdLine . port ) ;
2010-05-12 17:43:21 -04:00
v . push_back (
BSON ( " name " < < h . toString ( ) < < " self " < < true < <
2010-05-18 14:47:11 -04:00
" errmsg " < < _self - > lhb ( ) ) ) ;
2010-04-20 12:29:00 -04:00
}
while ( m ) {
2010-04-20 15:30:37 -04:00
BSONObjBuilder bb ;
bb . append ( " name " , m - > fullName ( ) ) ;
2010-05-20 13:52:30 -04:00
bb . append ( " health " , m - > hbinfo ( ) . health ) ;
bb . append ( " uptime " , ( unsigned ) ( m - > hbinfo ( ) . upSince ? ( time ( 0 ) - m - > hbinfo ( ) . upSince ) : 0 ) ) ;
2010-07-14 15:08:59 -04:00
bb . appendTimeT ( " lastHeartbeat " , m - > hbinfo ( ) . lastHeartbeat ) ;
2010-05-18 14:47:11 -04:00
bb . append ( " errmsg " , m - > lhb ( ) ) ;
2010-04-20 15:30:37 -04:00
v . push_back ( bb . obj ( ) ) ;
2010-04-20 12:29:00 -04:00
m = m - > next ( ) ;
}
2010-05-13 17:18:17 -04:00
b . append ( " set " , name ( ) ) ;
2010-07-14 15:08:59 -04:00
b . appendTimeT ( " date " , time ( 0 ) ) ;
2010-07-22 17:50:54 -04:00
b . append ( " myState " , box . getState ( ) . s ) ;
2010-04-20 12:29:00 -04:00
b . append ( " members " , v ) ;
}
2010-06-30 18:05:26 -04:00
}