summaryrefslogtreecommitdiff
path: root/db/dbwebserver.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'db/dbwebserver.cpp')
-rw-r--r--db/dbwebserver.cpp305
1 files changed, 202 insertions, 103 deletions
diff --git a/db/dbwebserver.cpp b/db/dbwebserver.cpp
index 0e1483c..75d3a92 100644
--- a/db/dbwebserver.cpp
+++ b/db/dbwebserver.cpp
@@ -27,6 +27,9 @@
#include "replset.h"
#include "instance.h"
#include "security.h"
+#include "stats/snapshots.h"
+#include "background.h"
+#include "commands.h"
#include <pcrecpp.h>
#include <boost/date_time/posix_time/posix_time.hpp>
@@ -61,48 +64,6 @@ namespace mongo {
}
unsigned long long start, timeLocked;
};
- Timing tlast;
- const int NStats = 32;
- string lockStats[NStats];
- unsigned q = 0;
-
- void statsThread() {
- /*cout << "TEMP disabled statsthread" << endl;
- if( 1 )
- return;*/
- Client::initThread("stats");
- unsigned long long timeLastPass = 0;
- while ( 1 ) {
- {
- /* todo: do we even need readlock here? if so for what? */
- readlock lk("");
- Top::completeSnapshot();
- q = (q+1)%NStats;
- Timing timing;
- dbMutex.info().getTimingInfo(timing.start, timing.timeLocked);
- unsigned long long now = curTimeMicros64();
- if ( timeLastPass ) {
- unsigned long long dt = now - timeLastPass;
- unsigned long long dlocked = timing.timeLocked - tlast.timeLocked;
- {
- stringstream ss;
- ss << dt / 1000 << '\t';
- ss << dlocked / 1000 << '\t';
- if ( dt )
- ss << (dlocked*100)/dt << '%';
- string s = ss.str();
- if ( cmdLine.cpu )
- log() << "cpu: " << s << endl;
- lockStats[q] = s;
- ClientCursor::idleTimeReport( (unsigned) ((dt - dlocked)/1000) );
- }
- }
- timeLastPass = now;
- tlast = timing;
- }
- sleepsecs(4);
- }
- }
bool _bold;
string bold(bool x) {
@@ -118,14 +79,11 @@ namespace mongo {
// caller locks
void doLockedStuff(stringstream& ss) {
ss << "# databases: " << dbHolder.size() << '\n';
- if ( cc().database() ) {
- ss << "curclient: " << cc().database()->name; // TODO: isn't this useless?
- ss << '\n';
- }
+
ss << bold(ClientCursor::byLocSize()>10000) << "Cursors byLoc.size(): " << ClientCursor::byLocSize() << bold() << '\n';
ss << "\n<b>replication</b>\n";
- ss << "master: " << master << '\n';
- ss << "slave: " << slave << '\n';
+ ss << "master: " << replSettings.master << '\n';
+ ss << "slave: " << replSettings.slave << '\n';
if ( replPair ) {
ss << "replpair:\n";
ss << replPair->getInfo();
@@ -135,26 +93,76 @@ namespace mongo {
ss << "initialSyncCompleted: " << seemCaughtUp;
if ( !seemCaughtUp ) ss << "</b>";
ss << '\n';
-
- ss << "\n<b>DBTOP</b>\n";
- ss << "<table border=1><tr align='left'><th>Namespace</th><th>%</th><th>Reads</th><th>Writes</th><th>Calls</th><th>Time</th>";
- vector< Top::Usage > usage;
- Top::usage( usage );
- for( vector< Top::Usage >::iterator i = usage.begin(); i != usage.end(); ++i )
- ss << setprecision( 2 ) << fixed << "<tr><td>" << i->ns << "</td><td>" << i->pct << "</td><td>"
- << i->reads << "</td><td>" << i->writes << "</td><td>" << i->calls << "</td><td>" << i->time << "</td></tr>\n";
- ss << "</table>";
- ss << "\n<b>dt\ttlocked</b>\n";
- unsigned i = q;
- while ( 1 ) {
- ss << lockStats[i] << '\n';
- i = (i-1)%NStats;
- if ( i == q )
- break;
+ auto_ptr<SnapshotDelta> delta = statsSnapshots.computeDelta();
+ if ( delta.get() ){
+ ss << "\n<b>DBTOP (occurences|percent of elapsed)</b>\n";
+ ss << "<table border=1>";
+ ss << "<tr align='left'>";
+ ss << "<th>NS</th>"
+ "<th colspan=2>total</th>"
+ "<th colspan=2>Reads</th>"
+ "<th colspan=2>Writes</th>"
+ "<th colspan=2>Queries</th>"
+ "<th colspan=2>GetMores</th>"
+ "<th colspan=2>Inserts</th>"
+ "<th colspan=2>Updates</th>"
+ "<th colspan=2>Removes</th>";
+ ss << "</tr>";
+
+ display( ss , (double) delta->elapsed() , "GLOBAL" , delta->globalUsageDiff() );
+
+ Top::UsageMap usage = delta->collectionUsageDiff();
+ for ( Top::UsageMap::iterator i=usage.begin(); i != usage.end(); i++ ){
+ display( ss , (double) delta->elapsed() , i->first , i->second );
+ }
+
+ ss << "</table>";
}
+
+ statsSnapshots.outputLockInfoHTML( ss );
+
+ BackgroundOperation::dump(ss);
}
+ void display( stringstream& ss , double elapsed , const Top::UsageData& usage ){
+ ss << "<td>";
+ ss << usage.count;
+ ss << "</td><td>";
+ double per = 100 * ((double)usage.time)/elapsed;
+ ss << setprecision(2) << fixed << per << "%";
+ ss << "</td>";
+ }
+
+ void display( stringstream& ss , double elapsed , const string& ns , const Top::CollectionData& data ){
+ if ( ns != "GLOBAL" && data.total.count == 0 )
+ return;
+ ss << "<tr><th>" << ns << "</th>";
+
+ display( ss , elapsed , data.total );
+
+ display( ss , elapsed , data.readLock );
+ display( ss , elapsed , data.writeLock );
+
+ display( ss , elapsed , data.queries );
+ display( ss , elapsed , data.getmore );
+ display( ss , elapsed , data.insert );
+ display( ss , elapsed , data.update );
+ display( ss , elapsed , data.remove );
+
+ ss << "</tr>";
+ }
+
+ void tablecell( stringstream& ss , bool b ){
+ ss << "<td>" << (b ? "<b>X</b>" : "") << "</td>";
+ }
+
+
+ template< typename T>
+ void tablecell( stringstream& ss , const T& t ){
+ ss << "<td>" << t << "</td>";
+ }
+
void doUnlockedStuff(stringstream& ss) {
/* this is in the header already ss << "port: " << port << '\n'; */
ss << mongodVersion() << "\n";
@@ -178,21 +186,51 @@ namespace mongo {
ss << "\nreplInfo: " << replInfo << "\n\n";
ss << "Clients:\n";
- ss << "<table border=1><tr align='left'><th>Thread</th><th>Current op</th>\n";
+ ss << "<table border=1>";
+ ss << "<tr align='left'>"
+ << "<th>Thread</th>"
+
+ << "<th>OpId</th>"
+ << "<th>Active</th>"
+ << "<th>LockType</th>"
+ << "<th>Waiting</th>"
+ << "<th>SecsRunning</th>"
+ << "<th>Op</th>"
+ << "<th>NameSpace</th>"
+ << "<th>Query</th>"
+ << "<th>client</th>"
+ << "<th>msg</th>"
+ << "<th>progress</th>"
+
+ << "</tr>\n";
{
- boostlock bl(Client::clientsMutex);
+ scoped_lock bl(Client::clientsMutex);
for( set<Client*>::iterator i = Client::clients.begin(); i != Client::clients.end(); i++ ) {
Client *c = *i;
CurOp& co = *(c->curop());
- ss << "<tr><td>" << c->desc() << "</td><td";
- BSONObj info = co.infoNoauth();
- /*
- if( info.getIntField("inLock") > 0 )
- ss << "style='color:red'";
- else if( info.getIntField("inLock") < 0 )
- ss << "style='color:green'";
- */
- ss << ">" << info << "</td></tr>\n";
+ ss << "<tr><td>" << c->desc() << "</td>";
+
+ tablecell( ss , co.opNum() );
+ tablecell( ss , co.active() );
+ tablecell( ss , co.getLockType() );
+ tablecell( ss , co.isWaitingForLock() );
+ if ( co.active() )
+ tablecell( ss , co.elapsedSeconds() );
+ else
+ tablecell( ss , "" );
+ tablecell( ss , co.getOp() );
+ tablecell( ss , co.getNS() );
+ if ( co.haveQuery() )
+ tablecell( ss , co.query() );
+ else
+ tablecell( ss , "" );
+ tablecell( ss , co.getRemoteString() );
+
+ tablecell( ss , co.getMessage() );
+ tablecell( ss , co.getProgressMeter().toString() );
+
+
+ ss << "</tr>";
}
}
ss << "</table>\n";
@@ -203,7 +241,7 @@ namespace mongo {
if ( from.localhost() )
return true;
- if ( db.findOne( "admin.system.users" , BSONObj() ).isEmpty() )
+ if ( db.findOne( "admin.system.users" , BSONObj() , 0 , QueryOption_SlaveOk ).isEmpty() )
return true;
string auth = getHeader( rq , "Authorization" );
@@ -270,6 +308,23 @@ namespace mongo {
//out() << "url [" << url << "]" << endl;
if ( url.size() > 1 ) {
+
+ if ( url.find( "/_status" ) == 0 ){
+ if ( ! allowed( rq , headers, from ) ){
+ responseCode = 401;
+ responseMsg = "not allowed\n";
+ return;
+ }
+ generateServerStatus( url , responseMsg );
+ responseCode = 200;
+ return;
+ }
+
+ if ( ! cmdLine.rest ){
+ responseCode = 403;
+ responseMsg = "rest is not enabled. use --rest to turn on";
+ return;
+ }
if ( ! allowed( rq , headers, from ) ){
responseCode = 401;
responseMsg = "not allowed\n";
@@ -294,23 +349,18 @@ namespace mongo {
doUnlockedStuff(ss);
- int n = 2000;
- Timer t;
- while ( 1 ) {
- if ( !dbMutex.info().isLocked() ) {
- {
- readlock lk("");
- ss << "time to get dblock: " << t.millis() << "ms\n";
- doLockedStuff(ss);
- }
- break;
+ {
+ Timer t;
+ readlocktry lk( "" , 2000 );
+ if ( lk.got() ){
+ ss << "time to get dblock: " << t.millis() << "ms\n";
+ doLockedStuff(ss);
}
- sleepmillis(1);
- if ( --n < 0 ) {
+ else {
ss << "\n<b>timed out getting dblock</b>\n";
- break;
}
}
+
ss << "</pre></body></html>";
responseMsg = ss.str();
@@ -323,6 +373,51 @@ namespace mongo {
}
}
+ void generateServerStatus( string url , string& responseMsg ){
+ static vector<string> commands;
+ if ( commands.size() == 0 ){
+ commands.push_back( "serverStatus" );
+ commands.push_back( "buildinfo" );
+ }
+
+ BSONObj params;
+ if ( url.find( "?" ) != string::npos ) {
+ parseParams( params , url.substr( url.find( "?" ) + 1 ) );
+ }
+
+ BSONObjBuilder buf(1024);
+
+ for ( unsigned i=0; i<commands.size(); i++ ){
+ string cmd = commands[i];
+
+ Command * c = Command::findCommand( cmd );
+ assert( c );
+ assert( c->locktype() == 0 );
+
+ BSONObj co;
+ {
+ BSONObjBuilder b;
+ b.append( cmd.c_str() , 1 );
+
+ if ( cmd == "serverStatus" && params["repl"].type() ){
+ b.append( "repl" , atoi( params["repl"].valuestr() ) );
+ }
+
+ co = b.obj();
+ }
+
+ string errmsg;
+
+ BSONObjBuilder sub;
+ if ( ! c->run( "admin.$cmd" , co , errmsg , sub , false ) )
+ buf.append( cmd.c_str() , errmsg );
+ else
+ buf.append( cmd.c_str() , sub.obj() );
+ }
+
+ responseMsg = buf.obj().jsonString();
+ }
+
void handleRESTRequest( const char *rq, // the full request
string url,
string& responseMsg,
@@ -341,7 +436,7 @@ namespace mongo {
string coll = url.substr( first + 1 );
string action = "";
- map<string,string> params;
+ BSONObj params;
if ( coll.find( "?" ) != string::npos ) {
parseParams( params , coll.substr( coll.find( "?" ) + 1 ) );
coll = coll.substr( 0 , coll.find( "?" ) );
@@ -361,7 +456,7 @@ namespace mongo {
if ( coll[i] == '/' )
coll[i] = '.';
- string fullns = dbname + "." + coll;
+ string fullns = urlDecode(dbname + "." + coll);
headers.push_back( (string)"x-action: " + action );
headers.push_back( (string)"x-ns: " + fullns );
@@ -387,26 +482,29 @@ namespace mongo {
responseMsg = ss.str();
}
- void handleRESTQuery( string ns , string action , map<string,string> & params , int & responseCode , stringstream & out ) {
+ void handleRESTQuery( string ns , string action , BSONObj & params , int & responseCode , stringstream & out ) {
Timer t;
int skip = _getOption( params["skip"] , 0 );
int num = _getOption( params["limit"] , _getOption( params["count" ] , 1000 ) ); // count is old, limit is new
int one = 0;
- if ( params["one"].size() > 0 && tolower( params["one"][0] ) == 't' ) {
+ if ( params["one"].type() == String && tolower( params["one"].valuestr()[0] ) == 't' ) {
num = 1;
one = 1;
}
BSONObjBuilder queryBuilder;
- for ( map<string,string>::iterator i = params.begin(); i != params.end(); i++ ) {
- if ( ! i->first.find( "filter_" ) == 0 )
+ BSONObjIterator i(params);
+ while ( i.more() ){
+ BSONElement e = i.next();
+ string name = e.fieldName();
+ if ( ! name.find( "filter_" ) == 0 )
continue;
- const char * field = i->first.substr( 7 ).c_str();
- const char * val = i->second.c_str();
+ const char * field = name.substr( 7 ).c_str();
+ const char * val = e.valuestr();
char * temp;
@@ -454,7 +552,7 @@ namespace mongo {
}
// TODO Generate id and revision per couch POST spec
- void handlePost( string ns, const char *body, map<string,string> & params, int & responseCode, stringstream & out ) {
+ void handlePost( string ns, const char *body, BSONObj& params, int & responseCode, stringstream & out ) {
try {
BSONObj obj = fromjson( body );
db.insert( ns.c_str(), obj );
@@ -468,10 +566,12 @@ namespace mongo {
out << "{ \"ok\" : true }";
}
- int _getOption( string val , int def ) {
- if ( val.size() == 0 )
- return def;
- return atoi( val.c_str() );
+ int _getOption( BSONElement e , int def ) {
+ if ( e.isNumber() )
+ return e.numberInt();
+ if ( e.type() == String )
+ return atoi( e.valuestr() );
+ return def;
}
private:
@@ -481,7 +581,6 @@ namespace mongo {
DBDirectClient DbWebServer::db;
void webServerThread() {
- boost::thread thr(statsThread);
Client::initThread("websvr");
DbWebServer mini;
int p = cmdLine.port + 1000;