summaryrefslogtreecommitdiff
path: root/src/mongo/s/d_writeback.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/mongo/s/d_writeback.cpp')
-rw-r--r--src/mongo/s/d_writeback.cpp207
1 files changed, 207 insertions, 0 deletions
diff --git a/src/mongo/s/d_writeback.cpp b/src/mongo/s/d_writeback.cpp
new file mode 100644
index 00000000000..0278e40f712
--- /dev/null
+++ b/src/mongo/s/d_writeback.cpp
@@ -0,0 +1,207 @@
+// d_writeback.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 "../db/commands.h"
+#include "../util/queue.h"
+#include "../util/net/listen.h"
+#include "../db/curop.h"
+#include "../db/client.h"
+#include "mongo/util/stacktrace.h"
+
+#include "d_writeback.h"
+
+using namespace std;
+
+namespace mongo {
+
+ // ---------- WriteBackManager class ----------
+
+ // TODO init at mongod startup
+ WriteBackManager writeBackManager;
+
+ WriteBackManager::WriteBackManager() : _writebackQueueLock("sharding:writebackQueueLock") {
+ }
+
+ WriteBackManager::~WriteBackManager() {
+ }
+
+ void WriteBackManager::queueWriteBack( const string& remote , const BSONObj& o ) {
+ static mongo::mutex xxx( "WriteBackManager::queueWriteBack tmp" );
+ static OID lastOID;
+
+ scoped_lock lk( xxx );
+ const BSONElement& e = o["id"];
+
+ if ( lastOID.isSet() ) {
+ if ( e.OID() < lastOID ) {
+ log() << "this could fail" << endl;
+ printStackTrace();
+ }
+ }
+ lastOID = e.OID();
+ getWritebackQueue( remote )->queue.push( o );
+ }
+
+ shared_ptr<WriteBackManager::QueueInfo> WriteBackManager::getWritebackQueue( const string& remote ) {
+ scoped_lock lk ( _writebackQueueLock );
+ shared_ptr<QueueInfo>& q = _writebackQueues[remote];
+ if ( ! q )
+ q.reset( new QueueInfo() );
+ q->lastCall = Listener::getElapsedTimeMillis();
+ return q;
+ }
+
+ bool WriteBackManager::queuesEmpty() const {
+ scoped_lock lk( _writebackQueueLock );
+ for ( WriteBackQueuesMap::const_iterator it = _writebackQueues.begin(); it != _writebackQueues.end(); ++it ) {
+ const shared_ptr<QueueInfo> queue = it->second;
+ if (! queue->queue.empty() ) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ void WriteBackManager::appendStats( BSONObjBuilder& b ) const {
+ BSONObjBuilder sub;
+ long long totalQueued = 0;
+ long long now = Listener::getElapsedTimeMillis();
+ {
+ scoped_lock lk( _writebackQueueLock );
+ for ( WriteBackQueuesMap::const_iterator it = _writebackQueues.begin(); it != _writebackQueues.end(); ++it ) {
+ const shared_ptr<QueueInfo> queue = it->second;
+
+ BSONObjBuilder t( sub.subobjStart( it->first ) );
+ t.appendNumber( "n" , queue->queue.size() );
+ t.appendNumber( "minutesSinceLastCall" , ( now - queue->lastCall ) / ( 1000 * 60 ) );
+ t.done();
+
+ totalQueued += queue->queue.size();
+ }
+ }
+
+ b.appendBool( "hasOpsQueued" , totalQueued > 0 );
+ b.appendNumber( "totalOpsQueued" , totalQueued );
+ b.append( "queues" , sub.obj() );
+ }
+
+ bool WriteBackManager::cleanupOldQueues() {
+ long long now = Listener::getElapsedTimeMillis();
+
+ scoped_lock lk( _writebackQueueLock );
+ for ( WriteBackQueuesMap::iterator it = _writebackQueues.begin(); it != _writebackQueues.end(); ++it ) {
+ const shared_ptr<QueueInfo> queue = it->second;
+ long long sinceMinutes = ( now - queue->lastCall ) / ( 1000 * 60 );
+
+ if ( sinceMinutes < 60 ) // minutes of inactivity.
+ continue;
+
+ log() << "deleting queue from: " << it->first
+ << " of size: " << queue->queue.size()
+ << " after " << sinceMinutes << " inactivity"
+ << " (normal if any mongos has restarted)"
+ << endl;
+
+ _writebackQueues.erase( it );
+ return true;
+ }
+ return false;
+ }
+
+ void WriteBackManager::Cleaner::taskDoWork() {
+ for ( int i=0; i<1000; i++ ) {
+ if ( ! writeBackManager.cleanupOldQueues() )
+ break;
+ }
+ }
+
+ // ---------- admin commands ----------
+
+ // Note, this command will block until there is something to WriteBack
+ class WriteBackCommand : public Command {
+ public:
+ virtual LockType locktype() const { return NONE; }
+ virtual bool slaveOk() const { return true; }
+ virtual bool adminOnly() const { return true; }
+
+ WriteBackCommand() : Command( "writebacklisten" ) {}
+
+ void help(stringstream& h) const { h<<"internal"; }
+
+ bool run(const string& , BSONObj& cmdObj, int, string& errmsg, BSONObjBuilder& result, bool) {
+
+ cc().curop()->suppressFromCurop();
+ cc().curop()->setExpectedLatencyMs( 30000 );
+
+ BSONElement e = cmdObj.firstElement();
+ if ( e.type() != jstOID ) {
+ errmsg = "need oid as first value";
+ return 0;
+ }
+
+ // get the command issuer's (a mongos) serverID
+ const OID id = e.__oid();
+
+ // the command issuer is blocked awaiting a response
+ // we want to do return at least at every 5 minutes so sockets don't timeout
+ BSONObj z;
+ if ( writeBackManager.getWritebackQueue(id.str())->queue.blockingPop( z, 5 * 60 /* 5 minutes */ ) ) {
+ LOG(1) << "WriteBackCommand got : " << z << endl;
+ result.append( "data" , z );
+ }
+ else {
+ result.appendBool( "noop" , true );
+ }
+
+#ifdef _DEBUG
+ // Sleep a short amount of time usually
+ int sleepFor = rand() % 10;
+ sleepmillis( sleepFor );
+
+ // Sleep a longer amount of time every once and awhile
+ int sleepLong = rand() % 50;
+ if( sleepLong == 0 ) sleepsecs( 2 );
+#endif
+
+ return true;
+ }
+ } writeBackCommand;
+
+ class WriteBacksQueuedCommand : public Command {
+ public:
+ virtual LockType locktype() const { return NONE; }
+ virtual bool slaveOk() const { return true; }
+ virtual bool adminOnly() const { return true; }
+
+ WriteBacksQueuedCommand() : Command( "writeBacksQueued" ) {}
+
+ void help(stringstream& help) const {
+ help << "Returns whether there are operations in the writeback queue at the time the command was called. "
+ << "This is an internal command";
+ }
+
+ bool run(const string& , BSONObj& cmdObj, int, string& errmsg, BSONObjBuilder& result, bool) {
+ writeBackManager.appendStats( result );
+ return true;
+ }
+
+ } writeBacksQueuedCommand;
+
+} // namespace mongo