mirror of
https://github.com/mongodb/mongo.git
synced 2024-12-01 09:32:32 +01:00
229 lines
7.6 KiB
C++
229 lines
7.6 KiB
C++
// snapshots.cpp
|
|
|
|
/**
|
|
* Copyright (C) 2008 10gen 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.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* 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/>.
|
|
*/
|
|
|
|
#include "pch.h"
|
|
#include "snapshots.h"
|
|
#include "../client.h"
|
|
#include "../clientcursor.h"
|
|
#include "../dbwebserver.h"
|
|
#include "../../util/mongoutils/html.h"
|
|
|
|
/**
|
|
handles snapshotting performance metrics and other such things
|
|
*/
|
|
namespace mongo {
|
|
void SnapshotData::takeSnapshot(){
|
|
_created = curTimeMicros64();
|
|
_globalUsage = Top::global.getGlobalData();
|
|
_totalWriteLockedTime = dbMutex.info().getTimeLocked();
|
|
Top::global.cloneMap(_usage);
|
|
}
|
|
|
|
SnapshotDelta::SnapshotDelta( const SnapshotData& older , const SnapshotData& newer )
|
|
: _older( older ) , _newer( newer )
|
|
{
|
|
assert( _newer._created > _older._created );
|
|
_elapsed = _newer._created - _older._created;
|
|
|
|
}
|
|
|
|
Top::CollectionData SnapshotDelta::globalUsageDiff(){
|
|
return Top::CollectionData( _older._globalUsage , _newer._globalUsage );
|
|
}
|
|
Top::UsageMap SnapshotDelta::collectionUsageDiff(){
|
|
Top::UsageMap u;
|
|
|
|
for ( Top::UsageMap::const_iterator i=_newer._usage.begin(); i != _newer._usage.end(); i++ ){
|
|
Top::UsageMap::const_iterator j = _older._usage.find(i->first);
|
|
if (j != _older._usage.end())
|
|
u[i->first] = Top::CollectionData( j->second , i->second );
|
|
}
|
|
return u;
|
|
}
|
|
|
|
Snapshots::Snapshots(int n)
|
|
: _lock("Snapshots"), _n(n)
|
|
, _snapshots(new SnapshotData[n])
|
|
, _loc(0)
|
|
, _stored(0)
|
|
{}
|
|
|
|
const SnapshotData* Snapshots::takeSnapshot(){
|
|
scoped_lock lk(_lock);
|
|
_loc = ( _loc + 1 ) % _n;
|
|
_snapshots[_loc].takeSnapshot();
|
|
if ( _stored < _n )
|
|
_stored++;
|
|
return &_snapshots[_loc];
|
|
}
|
|
|
|
auto_ptr<SnapshotDelta> Snapshots::computeDelta( int numBack ){
|
|
scoped_lock lk(_lock);
|
|
auto_ptr<SnapshotDelta> p;
|
|
if ( numBack < numDeltas() )
|
|
p.reset( new SnapshotDelta( getPrev(numBack+1) , getPrev(numBack) ) );
|
|
return p;
|
|
}
|
|
|
|
const SnapshotData& Snapshots::getPrev( int numBack ){
|
|
int x = _loc - numBack;
|
|
if ( x < 0 )
|
|
x += _n;
|
|
return _snapshots[x];
|
|
}
|
|
|
|
void Snapshots::outputLockInfoHTML( stringstream& ss ){
|
|
scoped_lock lk(_lock);
|
|
ss << "\n<div>";
|
|
for ( int i=0; i<numDeltas(); i++ ){
|
|
SnapshotDelta d( getPrev(i+1) , getPrev(i) );
|
|
unsigned e = (unsigned) d.elapsed() / 1000;
|
|
ss << (unsigned)(100*d.percentWriteLocked());
|
|
if( e < 3900 || e > 4100 )
|
|
ss << '(' << e / 1000.0 << "s)";
|
|
ss << ' ';
|
|
}
|
|
ss << "</div>\n";
|
|
}
|
|
|
|
void SnapshotThread::run(){
|
|
Client::initThread("snapshotthread");
|
|
Client& client = cc();
|
|
|
|
long long numLoops = 0;
|
|
|
|
const SnapshotData* prev = 0;
|
|
|
|
while ( ! inShutdown() ){
|
|
try {
|
|
const SnapshotData* s = statsSnapshots.takeSnapshot();
|
|
|
|
if ( prev ){
|
|
unsigned long long elapsed = s->_created - prev->_created;
|
|
|
|
if ( cmdLine.cpu ){
|
|
SnapshotDelta d( *prev , *s );
|
|
log() << "cpu: elapsed:" << (elapsed/1000) <<" writelock: " << (int)(100*d.percentWriteLocked()) << "%" << endl;
|
|
}
|
|
|
|
}
|
|
|
|
prev = s;
|
|
}
|
|
catch ( std::exception& e ){
|
|
log() << "ERROR in SnapshotThread: " << e.what() << endl;
|
|
}
|
|
|
|
numLoops++;
|
|
sleepsecs(4);
|
|
}
|
|
|
|
client.shutdown();
|
|
}
|
|
|
|
using namespace mongoutils::html;
|
|
|
|
class WriteLockStatus : public WebStatusPlugin {
|
|
public:
|
|
WriteLockStatus() : WebStatusPlugin( "write lock" , 51 , "% time in write lock, by 4 sec periods" ){}
|
|
virtual void init(){}
|
|
|
|
virtual void run( stringstream& ss ){
|
|
statsSnapshots.outputLockInfoHTML( ss );
|
|
|
|
ss << "<a "
|
|
"href=\"http://www.mongodb.org/pages/viewpage.action?pageId=7209296\" "
|
|
"title=\"snapshot: was the db in the write lock when this page was generated?\">";
|
|
ss << "write locked now:</a> " << (dbMutex.info().isLocked() ? "true" : "false") << "\n";
|
|
}
|
|
|
|
} writeLockStatus;
|
|
|
|
class DBTopStatus : public WebStatusPlugin {
|
|
public:
|
|
DBTopStatus() : WebStatusPlugin( "dbtop" , 50 , "(occurences|percent of elapsed)" ){}
|
|
|
|
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;
|
|
if( per == (int) per )
|
|
ss << (int) per;
|
|
else
|
|
ss << setprecision(1) << fixed << per;
|
|
ss << '%';
|
|
ss << "</td>";
|
|
}
|
|
|
|
void display( stringstream& ss , double elapsed , const string& ns , const Top::CollectionData& data ){
|
|
if ( ns != "TOTAL" && 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>\n";
|
|
}
|
|
|
|
void run( stringstream& ss ){
|
|
auto_ptr<SnapshotDelta> delta = statsSnapshots.computeDelta();
|
|
if ( ! delta.get() )
|
|
return;
|
|
|
|
ss << "<table border=1 cellpadding=2 cellspacing=0>";
|
|
ss << "<tr align='left'><th>";
|
|
ss << a("http://www.mongodb.org/display/DOCS/Developer+FAQ#DeveloperFAQ-What%27sa%22namespace%22%3F", "namespace") <<
|
|
"NS</a></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>\n";
|
|
|
|
display( ss , (double) delta->elapsed() , "TOTAL" , 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>";
|
|
|
|
}
|
|
|
|
virtual void init(){}
|
|
} dbtopStatus;
|
|
|
|
Snapshots statsSnapshots;
|
|
SnapshotThread snapshotThread;
|
|
}
|