summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEliot Horowitz <eliot@10gen.com>2013-10-04 07:52:01 -0400
committerEliot Horowitz <eliot@10gen.com>2013-10-04 07:52:01 -0400
commit577fe667c612de137251b34f96535e3afc9ad4bb (patch)
treee32e865a13ac70c160e6bf9a97aa0544754eab0a
parent92f7c00614dbc90627cb3a356498df9e7e09b88c (diff)
downloadmongo-577fe667c612de137251b34f96535e3afc9ad4bb.tar.gz
SERVER-11021: backport to 2.4
-rw-r--r--jstests/dbhash2.js22
-rw-r--r--src/mongo/SConscript1
-rw-r--r--src/mongo/db/commands/dbhash.cpp216
-rw-r--r--src/mongo/db/commands/dbhash.h67
-rw-r--r--src/mongo/db/dbcommands.cpp116
-rw-r--r--src/mongo/db/oplog.cpp2
6 files changed, 308 insertions, 116 deletions
diff --git a/jstests/dbhash2.js b/jstests/dbhash2.js
new file mode 100644
index 00000000000..ac491291c2b
--- /dev/null
+++ b/jstests/dbhash2.js
@@ -0,0 +1,22 @@
+
+mydb = db.getSisterDB( "config" );
+
+t = mydb.foo;
+t.drop();
+
+t.insert( { x : 1 } );
+res1 = mydb.runCommand( "dbhash" );
+assert( res1.fromCache.indexOf( "config.foo" ) == -1 );
+
+res2 = mydb.runCommand( "dbhash" );
+assert( res2.fromCache.indexOf( "config.foo" ) >= 0 );
+assert.eq( res1.collections.foo, res2.collections.foo );
+
+t.insert( { x : 2 } );
+res3 = mydb.runCommand( "dbhash" );
+assert( res3.fromCache.indexOf( "config.foo" ) < 0 );
+assert.neq( res1.collections.foo, res3.collections.foo );
+
+
+
+
diff --git a/src/mongo/SConscript b/src/mongo/SConscript
index 816a6bfacf8..265cd4800e0 100644
--- a/src/mongo/SConscript
+++ b/src/mongo/SConscript
@@ -433,6 +433,7 @@ serverOnlyFiles = [ "db/curop.cpp",
"db/dbcommands_admin.cpp",
# most commands are only for mongod
+ "db/commands/dbhash.cpp",
"db/commands/fsync.cpp",
"db/commands/distinct.cpp",
"db/commands/find_and_modify.cpp",
diff --git a/src/mongo/db/commands/dbhash.cpp b/src/mongo/db/commands/dbhash.cpp
new file mode 100644
index 00000000000..38a07cace56
--- /dev/null
+++ b/src/mongo/db/commands/dbhash.cpp
@@ -0,0 +1,216 @@
+// dbhash.cpp
+
+/**
+* Copyright (C) 2013 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/>.
+*
+* As a special exception, the copyright holders give permission to link the
+* code of portions of this program with the OpenSSL library under certain
+* conditions as described in each individual source file and distribute
+* linked combinations including the program with the OpenSSL library. You
+* must comply with the GNU Affero General Public License in all respects for
+* all of the code used other than as permitted herein. If you modify file(s)
+* with this exception, you may extend this exception to your version of the
+* file(s), but you are not obligated to do so. If you do not wish to do so,
+* delete this exception statement from your version. If you delete this
+* exception statement from all source files in the program, then also delete
+* it in the license file.
+*/
+
+#include "mongo/db/commands/dbhash.h"
+
+#include "mongo/db/btreecursor.h"
+#include "mongo/db/client.h"
+#include "mongo/db/commands.h"
+#include "mongo/db/database.h"
+#include "mongo/db/pdfile.h"
+#include "mongo/util/md5.hpp"
+#include "mongo/util/timer.h"
+
+namespace mongo {
+
+ DBHashCmd dbhashCmd;
+
+
+ void logOpForDbHash( const char* opstr,
+ const char* ns,
+ const BSONObj& obj,
+ BSONObj* patt ) {
+ dbhashCmd.wipeCacheForCollection( ns );
+ }
+
+ // ----
+
+ DBHashCmd::DBHashCmd()
+ : Command( "dbHash", false, "dbhash" ),
+ _cachedHashedMutex( "_cachedHashedMutex" ){
+ }
+
+ void DBHashCmd::addRequiredPrivileges(const std::string& dbname,
+ const BSONObj& cmdObj,
+ std::vector<Privilege>* out) {
+ ActionSet actions;
+ actions.addAction(ActionType::dbHash);
+ out->push_back(Privilege(dbname, actions));
+ }
+
+ string DBHashCmd::hashCollection( const string& fullCollectionName, bool* fromCache ) {
+
+ scoped_ptr<scoped_lock> cachedHashedLock;
+
+ if ( isCachable( fullCollectionName ) ) {
+ cachedHashedLock.reset( new scoped_lock( _cachedHashedMutex ) );
+ string hash = _cachedHashed[fullCollectionName];
+ if ( hash.size() > 0 ) {
+ *fromCache = true;
+ return hash;
+ }
+ }
+
+ *fromCache = false;
+ NamespaceDetails * nsd = nsdetails( fullCollectionName );
+ verify( nsd );
+
+ // debug SERVER-761
+ NamespaceDetails::IndexIterator ii = nsd->ii();
+ while( ii.more() ) {
+ const IndexDetails &idx = ii.next();
+ if ( !idx.head.isValid() || !idx.info.isValid() ) {
+ log() << "invalid index for ns: " << fullCollectionName << " " << idx.head << " " << idx.info;
+ if ( idx.info.isValid() )
+ log() << " " << idx.info.obj();
+ log() << endl;
+ }
+ }
+
+ int idNum = nsd->findIdIndex();
+
+ shared_ptr<Cursor> cursor;
+
+ if ( idNum >= 0 ) {
+ cursor.reset( BtreeCursor::make( nsd,
+ nsd->idx( idNum ),
+ BSONObj(),
+ BSONObj(),
+ false,
+ 1 ) );
+ }
+ else if ( nsd->isCapped() ) {
+ cursor = findTableScan( fullCollectionName.c_str() , BSONObj() );
+ }
+ else {
+ log() << "can't find _id index for: " << fullCollectionName << endl;
+ return "no _id _index";
+ }
+
+ md5_state_t st;
+ md5_init(&st);
+
+ long long n = 0;
+
+ while ( cursor->ok() ) {
+ BSONObj c = cursor->current();
+ md5_append( &st , (const md5_byte_t*)c.objdata() , c.objsize() );
+ n++;
+ cursor->advance();
+ }
+
+ md5digest d;
+ md5_finish(&st, d);
+ string hash = digestToString( d );
+
+ if ( cachedHashedLock.get() ) {
+ _cachedHashed[fullCollectionName] = hash;
+ }
+
+ return hash;
+ }
+
+ bool DBHashCmd::run(const string& dbname , BSONObj& cmdObj, int, string& errmsg, BSONObjBuilder& result, bool) {
+ Timer timer;
+
+ set<string> desiredCollections;
+ if ( cmdObj["collections"].type() == Array ) {
+ BSONObjIterator i( cmdObj["collections"].Obj() );
+ while ( i.more() ) {
+ BSONElement e = i.next();
+ if ( e.type() != String ) {
+ errmsg = "collections entries have to be strings";
+ return false;
+ }
+ desiredCollections.insert( e.String() );
+ }
+ }
+
+ list<string> colls;
+ Database* db = cc().database();
+ if ( db )
+ db->namespaceIndex.getNamespaces( colls );
+ colls.sort();
+
+ result.appendNumber( "numCollections" , (long long)colls.size() );
+ result.append( "host" , prettyHostName() );
+
+ md5_state_t globalState;
+ md5_init(&globalState);
+
+ vector<string> cached;
+
+ BSONObjBuilder bb( result.subobjStart( "collections" ) );
+ for ( list<string>::iterator i=colls.begin(); i != colls.end(); i++ ) {
+ string fullCollectionName = *i;
+ string shortCollectionName = fullCollectionName.substr( dbname.size() + 1 );
+
+ if ( shortCollectionName.find( "system." ) == 0 )
+ continue;
+
+ if ( desiredCollections.size() > 0 &&
+ desiredCollections.count( shortCollectionName ) == 0 )
+ continue;
+
+ bool fromCache = false;
+ string hash = hashCollection( fullCollectionName, &fromCache );
+
+ bb.append( shortCollectionName, hash );
+
+ md5_append( &globalState , (const md5_byte_t*)hash.c_str() , hash.size() );
+ if ( fromCache )
+ cached.push_back( fullCollectionName );
+ }
+ bb.done();
+
+ md5digest d;
+ md5_finish(&globalState, d);
+ string hash = digestToString( d );
+
+ result.append( "md5" , hash );
+ result.appendNumber( "timeMillis", timer.millis() );
+
+ result.append( "fromCache", cached );
+
+ return 1;
+ }
+
+ void DBHashCmd::wipeCacheForCollection( const StringData& ns ) {
+ if ( !isCachable( ns ) )
+ return;
+ scoped_lock lk( _cachedHashedMutex );
+ _cachedHashed.erase( ns.toString() );
+ }
+
+ bool DBHashCmd::isCachable( const StringData& ns ) const {
+ return ns.startsWith( "config." );
+ }
+
+}
diff --git a/src/mongo/db/commands/dbhash.h b/src/mongo/db/commands/dbhash.h
new file mode 100644
index 00000000000..262c6609868
--- /dev/null
+++ b/src/mongo/db/commands/dbhash.h
@@ -0,0 +1,67 @@
+// dbhash.h
+
+/**
+* Copyright (C) 2013 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/>.
+*
+* As a special exception, the copyright holders give permission to link the
+* code of portions of this program with the OpenSSL library under certain
+* conditions as described in each individual source file and distribute
+* linked combinations including the program with the OpenSSL library. You
+* must comply with the GNU Affero General Public License in all respects for
+* all of the code used other than as permitted herein. If you modify file(s)
+* with this exception, you may extend this exception to your version of the
+* file(s), but you are not obligated to do so. If you do not wish to do so,
+* delete this exception statement from your version. If you delete this
+* exception statement from all source files in the program, then also delete
+* it in the license file.
+*/
+
+#pragma once
+
+#include "mongo/db/commands.h"
+
+namespace mongo {
+
+ void logOpForDbHash( const char* opstr,
+ const char* ns,
+ const BSONObj& obj,
+ BSONObj* patt );
+
+ class DBHashCmd : public Command {
+ public:
+ DBHashCmd();
+
+ virtual bool slaveOk() const { return true; }
+ virtual LockType locktype() const { return READ; }
+ virtual void addRequiredPrivileges(const std::string& dbname,
+ const BSONObj& cmdObj,
+ std::vector<Privilege>* out);
+
+ virtual bool run(const string& dbname , BSONObj& cmdObj, int, string& errmsg, BSONObjBuilder& result, bool);
+
+ void wipeCacheForCollection( const StringData& ns );
+
+ private:
+
+ bool isCachable( const StringData& ns ) const;
+
+ string hashCollection( const string& fullCollectionName, bool* fromCache );
+
+ map<string,string> _cachedHashed;
+ mutex _cachedHashedMutex;
+
+ };
+
+}
diff --git a/src/mongo/db/dbcommands.cpp b/src/mongo/db/dbcommands.cpp
index 33b77f455d6..8b8252f68a6 100644
--- a/src/mongo/db/dbcommands.cpp
+++ b/src/mongo/db/dbcommands.cpp
@@ -1689,122 +1689,6 @@ namespace mongo {
}
return Status::OK();
}
-
- class DBHashCmd : public Command {
- public:
- DBHashCmd() : Command( "dbHash", false, "dbhash" ) {}
- virtual bool slaveOk() const { return true; }
- virtual LockType locktype() const { return READ; }
- virtual void addRequiredPrivileges(const std::string& dbname,
- const BSONObj& cmdObj,
- std::vector<Privilege>* out) {
- ActionSet actions;
- actions.addAction(ActionType::dbHash);
- out->push_back(Privilege(dbname, actions));
- }
- virtual bool run(const string& dbname , BSONObj& cmdObj, int, string& errmsg, BSONObjBuilder& result, bool) {
- Timer timer;
-
- set<string> desiredCollections;
- if ( cmdObj["collections"].type() == Array ) {
- BSONObjIterator i( cmdObj["collections"].Obj() );
- while ( i.more() ) {
- BSONElement e = i.next();
- if ( e.type() != String ) {
- errmsg = "collections entries have to be strings";
- return false;
- }
- desiredCollections.insert( e.String() );
- }
- }
-
- list<string> colls;
- Database* db = cc().database();
- if ( db )
- db->namespaceIndex.getNamespaces( colls );
- colls.sort();
-
- result.appendNumber( "numCollections" , (long long)colls.size() );
- result.append( "host" , prettyHostName() );
-
- md5_state_t globalState;
- md5_init(&globalState);
-
- BSONObjBuilder bb( result.subobjStart( "collections" ) );
- for ( list<string>::iterator i=colls.begin(); i != colls.end(); i++ ) {
- string fullCollectionName = *i;
- string shortCollectionName = fullCollectionName.substr( dbname.size() + 1 );
-
- if ( shortCollectionName.find( "system." ) == 0 )
- continue;
-
- if ( desiredCollections.size() > 0 &&
- desiredCollections.count( shortCollectionName ) == 0 )
- continue;
-
- shared_ptr<Cursor> cursor;
-
- NamespaceDetails * nsd = nsdetails( fullCollectionName );
-
- // debug SERVER-761
- NamespaceDetails::IndexIterator ii = nsd->ii();
- while( ii.more() ) {
- const IndexDetails &idx = ii.next();
- if ( !idx.head.isValid() || !idx.info.isValid() ) {
- log() << "invalid index for ns: " << fullCollectionName << " " << idx.head << " " << idx.info;
- if ( idx.info.isValid() )
- log() << " " << idx.info.obj();
- log() << endl;
- }
- }
-
- int idNum = nsd->findIdIndex();
- if ( idNum >= 0 ) {
- cursor.reset( BtreeCursor::make( nsd,
- nsd->idx( idNum ),
- BSONObj(),
- BSONObj(),
- false,
- 1 ) );
- }
- else if ( nsd->isCapped() ) {
- cursor = findTableScan( fullCollectionName.c_str() , BSONObj() );
- }
- else {
- log() << "can't find _id index for: " << fullCollectionName << endl;
- continue;
- }
-
- md5_state_t st;
- md5_init(&st);
-
- long long n = 0;
- while ( cursor->ok() ) {
- BSONObj c = cursor->current();
- md5_append( &st , (const md5_byte_t*)c.objdata() , c.objsize() );
- n++;
- cursor->advance();
- }
- md5digest d;
- md5_finish(&st, d);
- string hash = digestToString( d );
-
- bb.append( shortCollectionName, hash );
-
- md5_append( &globalState , (const md5_byte_t*)hash.c_str() , hash.size() );
- }
- bb.done();
-
- md5digest d;
- md5_finish(&globalState, d);
- string hash = digestToString( d );
-
- result.append( "md5" , hash );
- result.appendNumber( "timeMillis", timer.millis() );
- return 1;
- }
-
- } dbhashCmd;
/* for diagnostic / testing purposes. Enabled via command line. */
class CmdSleep : public Command {
diff --git a/src/mongo/db/oplog.cpp b/src/mongo/db/oplog.cpp
index 12f4742c256..86a2dcba6d5 100644
--- a/src/mongo/db/oplog.cpp
+++ b/src/mongo/db/oplog.cpp
@@ -26,6 +26,7 @@
#include "mongo/db/auth/action_type.h"
#include "mongo/db/auth/privilege.h"
#include "mongo/db/commands.h"
+#include "mongo/db/commands/dbhash.h"
#include "mongo/db/index_update.h"
#include "mongo/db/instance.h"
#include "mongo/db/namespacestring.h"
@@ -334,6 +335,7 @@ namespace mongo {
}
logOpForSharding( opstr , ns , obj , patt );
+ logOpForDbHash( opstr , ns , obj , patt );
}
void createOplog() {