summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--jstests/slowNightly/index_retry.js15
-rw-r--r--src/mongo/SConscript4
-rw-r--r--src/mongo/db/cloner.cpp28
-rw-r--r--src/mongo/db/commands.cpp5
-rw-r--r--src/mongo/db/commands.h4
-rw-r--r--src/mongo/db/compact.cpp16
-rw-r--r--src/mongo/db/curop.cpp38
-rw-r--r--src/mongo/db/curop.h9
-rw-r--r--src/mongo/db/db.cpp6
-rw-r--r--src/mongo/db/dbcommands.cpp121
-rw-r--r--src/mongo/db/index_builder.cpp74
-rw-r--r--src/mongo/db/index_builder.h59
-rw-r--r--src/mongo/db/index_rebuilder.cpp25
-rw-r--r--src/mongo/db/index_rebuilder.h9
-rw-r--r--src/mongo/db/index_update.cpp4
-rw-r--r--src/mongo/db/index_update.h1
-rw-r--r--src/mongo/db/pdfile.cpp10
-rw-r--r--src/mongo/db/repl/oplog.cpp19
-rw-r--r--src/mongo/dbtests/replsettests.cpp560
19 files changed, 964 insertions, 43 deletions
diff --git a/jstests/slowNightly/index_retry.js b/jstests/slowNightly/index_retry.js
index d6632c20543..64561d60f87 100644
--- a/jstests/slowNightly/index_retry.js
+++ b/jstests/slowNightly/index_retry.js
@@ -1,5 +1,3 @@
-
-if (0) { // SERVER-8536 / SERVER-8344
// Check index rebuild when MongoDB is killed
var ports = allocatePorts(1);
@@ -14,7 +12,7 @@ t.drop();
// Insert a large number of documents, enough to ensure that an index build on these documents can
// be interrupted before complete.
-for (i = 0; i < 1e6; ++i) {
+for (i = 0; i < 1e5; ++i) {
t.save( { a:i } );
if (i % 10000 == 0) {
print("i: " + i);
@@ -57,7 +55,8 @@ function indexBuildInProgress() {
function abortDuringIndexBuild(options) {
// Create an index asynchronously by using a new connection.
- new Mongo(test.getMongo().host ).getCollection( t.toString() ).createIndex( { a:1 }, options);
+ new Mongo(test.getMongo().host).getCollection(t.toString()).createIndex(
+ { a:1 }, { background:true } );
// Wait for the index build to start.
var times = 0;
@@ -71,13 +70,12 @@ function abortDuringIndexBuild(options) {
stopMongod(ports[0], /* signal */ 9);
}
-abortDuringIndexBuild({background:true});
-
-print("sleeping");
-sleep(2000);
+abortDuringIndexBuild();
conn = mongod.start(/* reuseData */ true);
+
+
assert.soon(
function() {
try {
@@ -96,4 +94,3 @@ print("Index built");
stopMongod(ports[0]);
print("SUCCESS!");
-}
diff --git a/src/mongo/SConscript b/src/mongo/SConscript
index 160db497a9d..e575cecb1fb 100644
--- a/src/mongo/SConscript
+++ b/src/mongo/SConscript
@@ -421,6 +421,7 @@ serverOnlyFiles = [ "db/curop.cpp",
"db/query_plan_selection_policy.cpp",
"db/extsort.cpp",
"db/index.cpp",
+ "db/index_builder.cpp",
"db/index_update.cpp",
"db/index_rebuilder.cpp",
"db/scanandorder.cpp",
@@ -440,6 +441,7 @@ serverOnlyFiles = [ "db/curop.cpp",
"db/ops/update_internal.cpp",
"db/parsed_query.cpp",
"db/dbcommands.cpp",
+ "db/compact.cpp",
"db/dbcommands_admin.cpp",
# most commands are only for mongod
@@ -603,7 +605,7 @@ mongodAndMongosFiles = [
]
env.StaticLibrary("mongodandmongos", mongodAndMongosFiles)
-mongodOnlyFiles = [ "db/db.cpp", "db/compact.cpp", "db/commands/touch.cpp" ]
+mongodOnlyFiles = [ "db/db.cpp", "db/commands/touch.cpp" ]
# ----- TARGETS ------
diff --git a/src/mongo/db/cloner.cpp b/src/mongo/db/cloner.cpp
index d22ef10d894..2dbacd03ddc 100644
--- a/src/mongo/db/cloner.cpp
+++ b/src/mongo/db/cloner.cpp
@@ -26,6 +26,7 @@
#include "mongo/db/commands/rename_collection.h"
#include "mongo/db/db.h"
#include "mongo/db/dbhelpers.h"
+#include "mongo/db/index_builder.h"
#include "mongo/db/instance.h"
#include "mongo/db/jsobj.h"
#include "mongo/db/kill_current_op.h"
@@ -848,6 +849,30 @@ namespace mongo {
virtual void help( stringstream &help ) const {
help << " example: { renameCollection: foo.a, to: bar.b }";
}
+
+ virtual std::vector<BSONObj> stopIndexBuilds(const std::string& dbname,
+ const BSONObj& cmdObj) {
+ string source = cmdObj.getStringField( name.c_str() );
+ string target = cmdObj.getStringField( "to" );
+
+ BSONObj criteria = BSON("op" << "insert" << "ns" << dbname+".system.indexes" <<
+ "insert.ns" << source);
+
+ std::vector<BSONObj> prelim = IndexBuilder::killMatchingIndexBuilds(criteria);
+ std::vector<BSONObj> indexes;
+
+ for (int i = 0; i < static_cast<int>(prelim.size()); i++) {
+ // Change the ns
+ BSONObj stripped = prelim[i].removeField("ns");
+ BSONObjBuilder builder;
+ builder.appendElements(stripped);
+ builder.append("ns", target);
+ indexes.push_back(builder.done());
+ }
+
+ return indexes;
+ }
+
virtual bool run(const string& dbname, BSONObj& cmdObj, int, string& errmsg, BSONObjBuilder& result, bool fromRepl) {
string source = cmdObj.getStringField( name.c_str() );
string target = cmdObj.getStringField( "to" );
@@ -881,10 +906,12 @@ namespace mongo {
bool capped = false;
long long size = 0;
+ std::vector<BSONObj> indexesInProg;
{
Client::Context ctx( source );
NamespaceDetails *nsd = nsdetails( source );
uassert( 10026 , "source namespace does not exist", nsd );
+ indexesInProg = stopIndexBuilds(dbname, cmdObj);
capped = nsd->isCapped();
if ( capped )
for( DiskLoc i = nsd->firstExtent; !i.isNull(); i = i.ext()->xnext )
@@ -970,6 +997,7 @@ namespace mongo {
{
Client::Context ctx( source );
dropCollection( source, errmsg, result );
+ IndexBuilder::restoreIndexes(targetIndexes, indexesInProg);
}
return true;
}
diff --git a/src/mongo/db/commands.cpp b/src/mongo/db/commands.cpp
index 87e9129e612..9616eca8650 100644
--- a/src/mongo/db/commands.cpp
+++ b/src/mongo/db/commands.cpp
@@ -166,6 +166,11 @@ namespace mongo {
help << "no help defined";
}
+ std::vector<BSONObj> Command::stopIndexBuilds(const std::string& dbname,
+ const BSONObj& cmdObj) {
+ return std::vector<BSONObj>();
+ }
+
Command* Command::findCommand( const string& name ) {
map<string,Command*>::iterator i = _commands->find( name );
if ( i == _commands->end() )
diff --git a/src/mongo/db/commands.h b/src/mongo/db/commands.h
index 3897308a720..d1049f78c1a 100644
--- a/src/mongo/db/commands.h
+++ b/src/mongo/db/commands.h
@@ -149,6 +149,10 @@ namespace mongo {
static map<string,Command*> * _webCommands;
public:
+ // Stop all index builds required to run this command and return index builds killed.
+ virtual std::vector<BSONObj> stopIndexBuilds(const std::string& dbname,
+ const BSONObj& cmdObj);
+
static const map<string,Command*>* commandsByBestName() { return _commandsByBestName; }
static const map<string,Command*>* webCommands() { return _webCommands; }
/** @return if command was found */
diff --git a/src/mongo/db/compact.cpp b/src/mongo/db/compact.cpp
index de684f0112b..31a091aad4d 100644
--- a/src/mongo/db/compact.cpp
+++ b/src/mongo/db/compact.cpp
@@ -33,6 +33,7 @@
#include "mongo/db/curop-inl.h"
#include "mongo/db/extsort.h"
#include "mongo/db/index.h"
+#include "mongo/db/index_builder.h"
#include "mongo/db/index_update.h"
#include "mongo/db/jsobj.h"
#include "mongo/db/kill_current_op.h"
@@ -364,6 +365,16 @@ namespace mongo {
}
CompactCmd() : Command("compact") { }
+ virtual std::vector<BSONObj> stopIndexBuilds(const std::string& dbname,
+ const BSONObj& cmdObj) {
+ std::string systemIndexes = dbname+".system.indexes";
+ std::string coll = cmdObj.firstElement().valuestr();
+ std::string ns = dbname + "." + coll;
+ BSONObj criteria = BSON("ns" << systemIndexes << "op" << "insert" << "insert.ns" << ns);
+
+ return IndexBuilder::killMatchingIndexBuilds(criteria);
+ }
+
virtual bool run(const string& db, BSONObj& cmdObj, int, string& errmsg, BSONObjBuilder& result, bool fromRepl) {
string coll = cmdObj.firstElement().valuestr();
if( coll.empty() || db.empty() ) {
@@ -414,8 +425,13 @@ namespace mongo {
verify( pb >= 0 && pb <= 1024 * 1024 );
}
+ std::vector<BSONObj> indexesInProg = stopIndexBuilds(db, cmdObj);
+
bool validate = !cmdObj.hasElement("validate") || cmdObj["validate"].trueValue(); // default is true at the moment
bool ok = compact(ns, errmsg, validate, result, pf, pb);
+
+ IndexBuilder::restoreIndexes(db+".system.indexes", indexesInProg);
+
return ok;
}
};
diff --git a/src/mongo/db/curop.cpp b/src/mongo/db/curop.cpp
index 68e24e2b171..66e0138ee0f 100644
--- a/src/mongo/db/curop.cpp
+++ b/src/mongo/db/curop.cpp
@@ -67,6 +67,44 @@ namespace mongo {
_active = true; // this should be last for ui clarity
}
+ CurOp* CurOp::getOp(const BSONObj& criteria) {
+ // Regarding Matcher: This is not quite the right hammer to use here.
+ // Future: use an actual property of CurOp to flag index builds
+ // and use that to filter.
+ // This will probably need refactoring once we change index builds
+ // to be a real command instead of an insert into system.indexes
+ Matcher matcher(criteria);
+
+ Client& me = cc();
+
+ scoped_lock client_lock(Client::clientsMutex);
+ for (std::set<Client*>::iterator it = Client::clients.begin();
+ it != Client::clients.end();
+ it++) {
+
+ Client *client = *it;
+ verify(client);
+
+ CurOp* curop = client->curop();
+ if (client == &me || curop == NULL) {
+ continue;
+ }
+
+ if ( !curop->active() )
+ continue;
+
+ if ( curop->killPendingStrict() )
+ continue;
+
+ BSONObj info = curop->info();
+ if (matcher.matches(info)) {
+ return curop;
+ }
+ }
+
+ return NULL;
+ }
+
void CurOp::reset( const HostAndPort& remote, int op ) {
reset();
if( _remote != remote ) {
diff --git a/src/mongo/db/curop.h b/src/mongo/db/curop.h
index 8721484b684..04c916119b9 100644
--- a/src/mongo/db/curop.h
+++ b/src/mongo/db/curop.h
@@ -245,6 +245,15 @@ namespace mongo {
LockStat& lockStat() { return _lockStat; }
void setKillWaiterFlags();
+
+ /**
+ * Find a currently running operation matching the given criteria. This assumes that you're
+ * going to kill the operation, so it must be called multiple times to get multiple matching
+ * operations.
+ * @param criteria the search to do against the infoNoauth() BSONObj
+ * @return a pointer to a matching op or NULL if no ops match
+ */
+ static CurOp* getOp(const BSONObj& criteria);
private:
friend class Client;
void _reset();
diff --git a/src/mongo/db/db.cpp b/src/mongo/db/db.cpp
index 7ff4bc7e49a..f7eec8cd35d 100644
--- a/src/mongo/db/db.cpp
+++ b/src/mongo/db/db.cpp
@@ -672,6 +672,9 @@ namespace mongo {
Client::WriteContext c("admin", dbpath);
}
+ // Starts a background thread that rebuilds all incomplete indices.
+ indexRebuilder.go();
+
listen(listenPort);
// listen() will return when exit code closes its socket.
@@ -778,8 +781,7 @@ static void buildOptionsDescriptions(po::options_description *pVisible,
("jsonp","allow JSONP access via http (has security implications)")
("noauth", "run without security")
("nohttpinterface", "disable http interface")
- // SERVER-8536
- // ("noIndexBuildRetry", "don't retry any index builds that were interrupted by shutdown")
+ ("noIndexBuildRetry", "don't retry any index builds that were interrupted by shutdown")
("nojournal", "disable journaling (journaling is on by default for 64 bit)")
("noprealloc", "disable data file preallocation - will often hurt performance")
("noscripting", "disable scripting engine")
diff --git a/src/mongo/db/dbcommands.cpp b/src/mongo/db/dbcommands.cpp
index a94dca2f8ef..94477e68b98 100644
--- a/src/mongo/db/dbcommands.cpp
+++ b/src/mongo/db/dbcommands.cpp
@@ -40,6 +40,7 @@
#include "mongo/db/commands/server_status.h"
#include "mongo/db/db.h"
#include "mongo/db/dur_stats.h"
+#include "mongo/db/index_builder.h"
#include "mongo/db/index_update.h"
#include "mongo/db/instance.h"
#include "mongo/db/introspect.h"
@@ -382,6 +383,17 @@ namespace mongo {
virtual bool lockGlobally() const { return true; }
virtual LockType locktype() const { return WRITE; }
+
+ virtual std::vector<BSONObj> stopIndexBuilds(const std::string& dbname,
+ const BSONObj& cmdObj) {
+ std::string systemIndexes = dbname+".system.indexes";
+ std::string toDeleteRegex = "^"+dbname+"\\.";
+ BSONObj criteria = BSON("ns" << systemIndexes <<
+ "op" << "insert" <<
+ "insert.ns" << BSON("$regex" << toDeleteRegex));
+ return IndexBuilder::killMatchingIndexBuilds(criteria);
+ }
+
CmdDropDatabase() : Command("dropDatabase") {}
bool run(const string& dbname, BSONObj& cmdObj, int, string& errmsg, BSONObjBuilder& result, bool fromRepl) {
// disallow dropping the config database
@@ -394,6 +406,7 @@ namespace mongo {
int p = (int) e.number();
if ( p != 1 )
return false;
+ stopIndexBuilds(dbname, cmdObj);
dropDatabase(dbname);
result.append( "dropped" , dbname );
log() << "dropDatabase " << dbname << " finished" << endl;
@@ -424,19 +437,37 @@ namespace mongo {
out->push_back(Privilege(dbname, actions));
}
CmdRepairDatabase() : Command("repairDatabase") {}
+
+ virtual std::vector<BSONObj> stopIndexBuilds(const std::string& dbname,
+ const BSONObj& cmdObj) {
+ std::string systemIndexes = dbname+".system.indexes";
+ BSONObj criteria = BSON("ns" << systemIndexes << "op" << "insert");
+ return IndexBuilder::killMatchingIndexBuilds(criteria);
+ }
+
bool run(const string& dbname , BSONObj& cmdObj, int, string& errmsg, BSONObjBuilder& result, bool fromRepl) {
BSONElement e = cmdObj.firstElement();
log() << "repairDatabase " << dbname << endl;
int p = (int) e.number();
+
+ std::vector<BSONObj> indexesInProg;
if ( p != 1 ) {
errmsg = "bad option";
return false;
}
+
+ std::vector<BSONObj> indexes = stopIndexBuilds(dbname, cmdObj);
+
e = cmdObj.getField( "preserveClonedFilesOnFailure" );
bool preserveClonedFilesOnFailure = e.isBoolean() && e.boolean();
e = cmdObj.getField( "backupOriginalFiles" );
bool backupOriginalFiles = e.isBoolean() && e.boolean();
- return repairDatabase( dbname, errmsg, preserveClonedFilesOnFailure, backupOriginalFiles );
+ bool ok =
+ repairDatabase( dbname, errmsg, preserveClonedFilesOnFailure, backupOriginalFiles );
+
+ IndexBuilder::restoreIndexes(dbname+".system.indexes", indexesInProg);
+
+ return ok;
}
} cmdRepairDatabase;
@@ -567,6 +598,17 @@ namespace mongo {
}
virtual void help( stringstream& help ) const { help << "drop a collection\n{drop : <collectionName>}"; }
virtual LockType locktype() const { return WRITE; }
+
+ virtual std::vector<BSONObj> stopIndexBuilds(const std::string& dbname,
+ const BSONObj& cmdObj) {
+ std::string nsToDrop = dbname + '.' + cmdObj.firstElement().valuestr();
+ std::string systemIndexes = dbname+".system.indexes";
+ BSONObj criteria = BSON("ns" << systemIndexes << "op" << "insert" <<
+ "insert.ns" << nsToDrop);
+
+ return IndexBuilder::killMatchingIndexBuilds(criteria);
+ }
+
virtual bool run(const string& dbname , BSONObj& cmdObj, int, string& errmsg, BSONObjBuilder& result, bool) {
string nsToDrop = dbname + '.' + cmdObj.firstElement().valuestr();
NamespaceDetails *d = nsdetails(nsToDrop);
@@ -576,7 +618,12 @@ namespace mongo {
errmsg = "ns not found";
return false;
}
- uassert( 10039 , "can't drop collection with reserved $ character in name", strchr(nsToDrop.c_str(), '$') == 0 );
+
+ uassert(10039, "can't drop collection with reserved $ character in name",
+ strchr(nsToDrop.c_str(), '$') == 0);
+
+ stopIndexBuilds(dbname, cmdObj);
+
dropCollection( nsToDrop, errmsg, result );
return true;
}
@@ -698,6 +745,42 @@ namespace mongo {
actions.addAction(ActionType::dropIndexes);
out->push_back(Privilege(parseNs(dbname, cmdObj), actions));
}
+
+ virtual std::vector<BSONObj> stopIndexBuilds(const std::string& dbname,
+ const BSONObj& cmdObj) {
+ std::string systemIndexes = dbname+".system.indexes";
+ std::string toDeleteNs = dbname+"."+cmdObj.firstElement().valuestr();
+ BSONObjBuilder builder;
+ builder.append("ns", systemIndexes);
+ builder.append("op", "insert");
+ builder.append("insert.ns", toDeleteNs);
+
+ // Get index name to drop
+ BSONElement toDrop = cmdObj.getField("index");
+
+ if (toDrop.type() == String) {
+ // Kill all in-progress indexes
+ if (strcmp("*", toDrop.valuestr()) == 0) {
+ BSONObj criteria = builder.done();
+ return IndexBuilder::killMatchingIndexBuilds(criteria);
+ }
+ // Kill an in-progress index by name
+ else {
+ builder.append("insert.name", toDrop.valuestr());
+ BSONObj criteria = builder.done();
+ return IndexBuilder::killMatchingIndexBuilds(criteria);
+ }
+ }
+ // Kill an in-progress index build by index key
+ else if (toDrop.type() == Object) {
+ builder.append("insert.key", toDrop.Obj());
+ BSONObj criteria = builder.done();
+ return IndexBuilder::killMatchingIndexBuilds(criteria);
+ }
+
+ return std::vector<BSONObj>();
+ }
+
CmdDropIndexes() : Command("dropIndexes", false, "deleteIndexes") { }
bool run(const string& dbname, BSONObj& jsobj, int, string& errmsg, BSONObjBuilder& anObjBuilder, bool /*fromRepl*/) {
BSONElement e = jsobj.firstElement();
@@ -706,6 +789,8 @@ namespace mongo {
if ( !cmdLine.quiet )
tlog() << "CMD: dropIndexes " << toDeleteNs << endl;
if ( d ) {
+ stopIndexBuilds(dbname, jsobj);
+
BSONElement f = jsobj.getField("index");
if ( f.type() == String ) {
return dropIndexes( d, toDeleteNs.c_str(), f.valuestr(), errmsg, anObjBuilder, false );
@@ -751,6 +836,16 @@ namespace mongo {
out->push_back(Privilege(parseNs(dbname, cmdObj), actions));
}
CmdReIndex() : Command("reIndex") { }
+
+ virtual std::vector<BSONObj> stopIndexBuilds(const std::string& dbname,
+ const BSONObj& cmdObj) {
+ std::string systemIndexes = dbname+".system.indexes";
+ std::string ns = dbname + '.' + cmdObj["reIndex"].valuestrsafe();
+ BSONObj criteria = BSON("ns" << systemIndexes << "op" << "insert" << "insert.ns" << ns);
+
+ return IndexBuilder::killMatchingIndexBuilds(criteria);
+ }
+
bool run(const string& dbname , BSONObj& jsobj, int, string& errmsg, BSONObjBuilder& result, bool /*fromRepl*/) {
static DBDirectClient db;
@@ -765,6 +860,9 @@ namespace mongo {
return false;
}
+ std::vector<BSONObj> indexesInProg;
+ stopIndexBuilds(dbname, jsobj);
+
list<BSONObj> all;
auto_ptr<DBClientCursor> i = db.query( dbname + ".system.indexes" , BSON( "ns" << toDeleteNs ) , 0 , 0 , 0 , QueryOption_SlaveOk );
BSONObjBuilder b;
@@ -791,6 +889,8 @@ namespace mongo {
result.append( "nIndexes" , (int)all.size() );
result.appendArray( "indexes" , b.obj() );
+
+ IndexBuilder::restoreIndexes(dbname+".system.indexes", indexesInProg);
return true;
}
} cmdReIndex;
@@ -1862,13 +1962,30 @@ namespace mongo {
virtual void addRequiredPrivileges(const std::string& dbname,
const BSONObj& cmdObj,
std::vector<Privilege>* out) {}
+
+ virtual std::vector<BSONObj> stopIndexBuilds(const std::string& dbname,
+ const BSONObj& cmdObj) {
+ std::string systemIndexes = dbname+".system.indexes";
+ std::string coll = cmdObj[ "emptycapped" ].valuestrsafe();
+ std::string ns = dbname + '.' + coll;
+ BSONObj criteria = BSON("ns" << systemIndexes << "op" << "insert" << "insert.ns" << ns);
+
+ return IndexBuilder::killMatchingIndexBuilds(criteria);
+ }
+
virtual bool run(const string& dbname , BSONObj& cmdObj, int, string& errmsg, BSONObjBuilder& result, bool) {
string coll = cmdObj[ "emptycapped" ].valuestrsafe();
uassert( 13428, "emptycapped must specify a collection", !coll.empty() );
string ns = dbname + "." + coll;
NamespaceDetails *nsd = nsdetails( ns );
massert( 13429, "emptycapped no such collection", nsd );
+
+ std::vector<BSONObj> indexes = stopIndexBuilds(dbname, cmdObj);
+
nsd->emptyCappedCollection( ns.c_str() );
+
+ IndexBuilder::restoreIndexes(dbname+".system.indexes", indexes);
+
return true;
}
};
diff --git a/src/mongo/db/index_builder.cpp b/src/mongo/db/index_builder.cpp
new file mode 100644
index 00000000000..090fbcd4508
--- /dev/null
+++ b/src/mongo/db/index_builder.cpp
@@ -0,0 +1,74 @@
+/**
+ * Copyright (C) 2012 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 "mongo/db/index_builder.h"
+
+#include "mongo/db/client.h"
+#include "mongo/db/kill_current_op.h"
+#include "mongo/db/repl/rs.h"
+#include "mongo/util/mongoutils/str.h"
+
+namespace mongo {
+
+ AtomicUInt IndexBuilder::_indexBuildCount = 0;
+
+ IndexBuilder::IndexBuilder(const std::string ns, const BSONObj index) :
+ BackgroundJob(true /* self-delete */), _ns(ns), _index(index.getOwned()),
+ _name(str::stream() << "repl index builder " << (_indexBuildCount++).get()) {
+ }
+
+ IndexBuilder::~IndexBuilder() {}
+
+ std::string IndexBuilder::name() const {
+ return _name;
+ }
+
+ void IndexBuilder::run() {
+ LOG(2) << "building index " << _index << " on " << _ns << endl;
+ Client::initThread(name().c_str());
+ replLocalAuth();
+
+ Client::WriteContext ctx(_ns);
+ build();
+
+ cc().shutdown();
+ }
+
+ void IndexBuilder::build() const {
+ theDataFileMgr.insert(_ns.c_str(), _index.objdata(), _index.objsize(),
+ true /* mayInterrupt */);
+ }
+
+ std::vector<BSONObj> IndexBuilder::killMatchingIndexBuilds(const BSONObj& criteria) {
+ std::vector<BSONObj> indexes;
+ CurOp* op = NULL;
+ while ((op = CurOp::getOp(criteria)) != NULL) {
+ BSONObj index = op->query();
+ killCurrentOp.blockingKill(op->opNum());
+ indexes.push_back(index);
+ }
+ return indexes;
+ }
+
+ void IndexBuilder::restoreIndexes(const std::string& ns, const std::vector<BSONObj>& indexes) {
+ for (int i = 0; i < static_cast<int>(indexes.size()); i++) {
+ IndexBuilder* indexBuilder = new IndexBuilder(ns, indexes[i]);
+ // This looks like a memory leak, but indexBuilder deletes itself when it finishes
+ indexBuilder->go();
+ }
+ }
+}
+
diff --git a/src/mongo/db/index_builder.h b/src/mongo/db/index_builder.h
new file mode 100644
index 00000000000..0b5de273c84
--- /dev/null
+++ b/src/mongo/db/index_builder.h
@@ -0,0 +1,59 @@
+/**
+ * Copyright (C) 2012 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/>.
+ */
+
+#pragma once
+
+#include <string>
+
+#include "mongo/db/jsobj.h"
+#include "mongo/util/background.h"
+
+/**
+ * Forks off a thread to build an index.
+ */
+namespace mongo {
+
+ class IndexBuilder : public BackgroundJob {
+ public:
+ IndexBuilder(const std::string ns, const BSONObj index);
+ virtual ~IndexBuilder();
+
+ virtual void run();
+ virtual std::string name() const;
+
+ void build() const;
+
+ /**
+ * Kill all in-progress indexes matching criteria and, optionally, store them in the
+ * indexes list.
+ */
+ static std::vector<BSONObj> killMatchingIndexBuilds(const BSONObj& criteria);
+
+ /**
+ * Retry all index builds in the list. Builds each index in a separate thread. If ns does
+ * not match the ns field in the indexes list, the BSONObj's ns field is changed before the
+ * index is built (to handle rename).
+ */
+ static void restoreIndexes(const std::string& ns, const std::vector<BSONObj>& indexes);
+
+ private:
+ const std::string _ns;
+ const BSONObj _index;
+ std::string _name;
+ static AtomicUInt _indexBuildCount;
+ };
+
+}
diff --git a/src/mongo/db/index_rebuilder.cpp b/src/mongo/db/index_rebuilder.cpp
index ac2132ed56e..924882c80c4 100644
--- a/src/mongo/db/index_rebuilder.cpp
+++ b/src/mongo/db/index_rebuilder.cpp
@@ -22,12 +22,14 @@
namespace mongo {
- // Disabled until locking at startup can be ironed out.
- // See SERVER-8344 and SERVER-8536
- //IndexRebuilder indexRebuilder;
+ IndexRebuilder indexRebuilder;
IndexRebuilder::IndexRebuilder() {}
+ std::string IndexRebuilder::name() const {
+ return "IndexRebuilder";
+ }
+
/**
* This resets memory tracking to its original value after all indexes are rebuilt.
*
@@ -46,8 +48,8 @@ namespace mongo {
ON_BLOCK_EXIT(resetMemoryTracking, Record::MemoryTrackingEnabled);
Record::MemoryTrackingEnabled = false;
+ Client::initThread(name().c_str());
Client::GodScope gs;
- Lock::GlobalWrite lk;
bool firstTime = true;
std::vector<std::string> dbNames;
@@ -72,6 +74,8 @@ namespace mongo {
BSONObj nsDoc = cursor->next();
const char* ns = nsDoc["name"].valuestrsafe();
+ // This write lock is held throughout the index building process
+ // for this namespace.
Client::WriteContext ctx(ns);
NamespaceDetails* nsd = nsdetails(ns);
@@ -79,9 +83,9 @@ namespace mongo {
continue;
}
- log() << "Found interrupted index build on " << ns << endl;
+ log() << "found interrupted index build(s) on " << ns << endl;
if (*firstTime) {
- log() << "Restart the server with --noIndexBuildRetry to skip index rebuilds"
+ log() << "note: restart the server with --noIndexBuildRetry to skip index rebuilds"
<< endl;
*firstTime = false;
}
@@ -91,6 +95,7 @@ namespace mongo {
// If we crash between unsetting the inProg flag and cleaning up the index, the
// index space will be lost.
int inProg = nsd->indexBuildsInProgress;
+
getDur().writingInt(nsd->indexBuildsInProgress) = 0;
for (int i = 0; i < inProg; i++) {
@@ -123,23 +128,19 @@ namespace mongo {
// Clean up the in-progress index build
getDur().writingInt(nsd->indexBuildsInProgress) -= 1;
details.kill_idx();
+
// The index has now been removed from system.indexes, so the only record of it is in-
// memory. If there is a journal commit between now and when insert() rewrites the entry and
// the db crashes before the new system.indexes entry is journalled, the index will be lost
// forever. Thus, we're assuming no journaling will happen between now and the entry being
// re-written.
- // We need to force a foreground index build to prevent replication from replaying an
- // incompatible op (like a drop) during a yield.
- // TODO: once commands can interrupt/wait for index builds, this can be removed.
- indexObj = indexObj.removeField("background");
-
try {
const std::string ns = dbName + ".system.indexes";
theDataFileMgr.insert(ns.c_str(), indexObj.objdata(), indexObj.objsize(), false, true);
}
catch (const DBException& e) {
- log() << "Rebuilding index failed: " << e.what() << " (" << e.getCode() << ")"
+ log() << "building index failed: " << e.what() << " (" << e.getCode() << ")"
<< endl;
}
}
diff --git a/src/mongo/db/index_rebuilder.h b/src/mongo/db/index_rebuilder.h
index 2b327fbc048..4abc2d133ee 100644
--- a/src/mongo/db/index_rebuilder.h
+++ b/src/mongo/db/index_rebuilder.h
@@ -17,10 +17,13 @@
#pragma once
#include "mongo/db/namespace_details.h"
+#include "mongo/util/background.h"
namespace mongo {
- class IndexRebuilder {
+ // This is a job that's only run at startup. It finds all incomplete indices and
+ // finishes rebuilding them. After they complete rebuilding, the thread terminates.
+ class IndexRebuilder : public BackgroundJob {
public:
IndexRebuilder();
@@ -40,7 +43,9 @@ namespace mongo {
* @param nsd the namespace details of the namespace building the index
* @param index the offset into nsd's index array of the partially-built index
*/
- void retryIndexBuild(const std::string& dbName, NamespaceDetails* nsd, const int index);
+ void retryIndexBuild(const std::string& dbName,
+ NamespaceDetails* nsd,
+ const int index);
};
extern IndexRebuilder indexRebuilder;
diff --git a/src/mongo/db/index_update.cpp b/src/mongo/db/index_update.cpp
index 741b1285c51..a6be38c6431 100644
--- a/src/mongo/db/index_update.cpp
+++ b/src/mongo/db/index_update.cpp
@@ -553,8 +553,10 @@ namespace mongo {
void buildAnIndex(const std::string& ns,
NamespaceDetails* d,
IndexDetails& idx,
- bool background,
bool mayInterrupt) {
+
+ bool background = idx.info.obj()["background"].trueValue();
+
tlog() << "build index " << ns << ' ' << idx.keyPattern() << ( background ? " background" : "" ) << endl;
Timer t;
unsigned long long n;
diff --git a/src/mongo/db/index_update.h b/src/mongo/db/index_update.h
index ad71d293ce0..d0f3bd0608e 100644
--- a/src/mongo/db/index_update.h
+++ b/src/mongo/db/index_update.h
@@ -34,7 +34,6 @@ namespace mongo {
void buildAnIndex(const std::string& ns,
NamespaceDetails *d,
IndexDetails& idx,
- bool background,
bool mayInterrupt);
// add index keys for a newly inserted record
diff --git a/src/mongo/db/pdfile.cpp b/src/mongo/db/pdfile.cpp
index 3f9e22c6af3..93a948ad666 100644
--- a/src/mongo/db/pdfile.cpp
+++ b/src/mongo/db/pdfile.cpp
@@ -1456,14 +1456,6 @@ namespace mongo {
NamespaceString(tabletoidxns).coll != "system.indexes");
BSONObj info = loc.obj();
- bool background = info["background"].trueValue();
- if (background && !isMasterNs(tabletoidxns.c_str())) {
- /* don't do background indexing on slaves. there are nuances. this could be added later
- but requires more code.
- */
- log() << "info: indexing in foreground on this replica; was a background index build on the primary" << endl;
- background = false;
- }
// The total number of indexes right before we write to the collection
int oldNIndexes = -1;
@@ -1485,7 +1477,7 @@ namespace mongo {
try {
getDur().writingInt(tableToIndex->indexBuildsInProgress) += 1;
- buildAnIndex(tabletoidxns, tableToIndex, idx, background, mayInterrupt);
+ buildAnIndex(tabletoidxns, tableToIndex, idx, mayInterrupt);
}
catch (DBException& e) {
// save our error msg string as an exception or dropIndexes will overwrite our message
diff --git a/src/mongo/db/repl/oplog.cpp b/src/mongo/db/repl/oplog.cpp
index 969c8ba530d..6e149994cdf 100644
--- a/src/mongo/db/repl/oplog.cpp
+++ b/src/mongo/db/repl/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/index_builder.h"
#include "mongo/db/index_update.h"
#include "mongo/db/instance.h"
#include "mongo/db/namespacestring.h"
@@ -95,6 +96,8 @@ namespace mongo {
theReplSet->lastOpTimeWritten = ts;
theReplSet->lastH = h;
ctx.getClient()->setLastOp( ts );
+
+ replset::BackgroundSync::notify();
}
}
@@ -439,10 +442,18 @@ namespace mongo {
if ( *opType == 'i' ) {
opCounters->gotInsert();
- if (NamespaceString(ns).coll == "system.indexes") {
- // updates aren't allowed for indexes -- so we will do a regular insert. if index already
- // exists, that is ok.
- theDataFileMgr.insert(ns, (void*) o.objdata(), o.objsize());
+ const char *p = strchr(ns, '.');
+ if ( p && strcmp(p, ".system.indexes") == 0 ) {
+ if (o["background"].trueValue()) {
+ IndexBuilder* builder = new IndexBuilder(ns, o);
+ // This spawns a new thread and returns immediately.
+ builder->go();
+ }
+ else {
+ IndexBuilder builder(ns, o);
+ // Finish the foreground build before returning
+ builder.build();
+ }
}
else {
// do upserts for inserts as we might get replayed more than once
diff --git a/src/mongo/dbtests/replsettests.cpp b/src/mongo/dbtests/replsettests.cpp
index 4c679628fb7..a11b0913219 100644
--- a/src/mongo/dbtests/replsettests.cpp
+++ b/src/mongo/dbtests/replsettests.cpp
@@ -20,12 +20,16 @@
#include "mongo/pch.h"
#include "mongo/db/db.h"
+#include "mongo/db/index_builder.h"
#include "mongo/db/instance.h"
#include "mongo/db/json.h"
+#include "mongo/db/kill_current_op.h"
#include "mongo/db/repl/bgsync.h"
#include "mongo/db/repl/oplog.h"
#include "mongo/db/repl/rs.h"
#include "mongo/dbtests/dbtests.h"
+#include "mongo/util/time_support.h"
+
namespace mongo {
void createOplog();
@@ -172,6 +176,555 @@ namespace ReplSetTests {
DBDirectClient Base::client_;
+ class IndexBuildThread : public BackgroundJob {
+ public:
+ IndexBuildThread(const BSONObj index) :
+ _done(false), _index(index.getOwned()), _client(NULL), _curop(NULL) {
+ }
+
+ std::string name() const {
+ return "index build helper";
+ }
+
+ void run() {
+ std::string ns = nsToDatabase(_index.getStringField("ns"))+".system.indexes";
+
+ Client::initThread("in progress idx build");
+ Client::WriteContext ctx(ns);
+ _client = currentClient.get();
+
+ // This spins to mimic the db building an index. Yield the read lock so that other
+ // dbs can be opened (which requires the write lock)
+ while (!_curop || !_curop->killPendingStrict()) {
+ dbtemprelease temp;
+ sleepmillis(0);
+ }
+
+ killCurrentOp.notifyAllWaiters();
+ cc().shutdown();
+ _done = true;
+ }
+
+ // Make sure the test doesn't end while this "index build" is still spinning
+ void finish() {
+ while (!_curop) {
+ sleepmillis(0);
+ }
+
+ _curop->kill();
+
+ while (!_done) {
+ sleepmillis(0);
+ }
+ }
+
+ bool finished() {
+ return _done;
+ }
+
+ CurOp* curop() {
+ if (_curop != NULL) {
+ return _curop;
+ }
+
+ while (_client == NULL) {
+ sleepmillis(0);
+ }
+
+ // On the first time through, make sure the curop is set up correctly
+ _client->curop()->reset(HostAndPort::me(), dbInsert);
+ _client->curop()->enter(_client->getContext());
+ _client->curop()->setQuery(_index);
+
+ _curop = _client->curop();
+ return _curop;
+ }
+
+ private:
+ bool _done;
+ BSONObj _index;
+ Client* _client;
+ CurOp* _curop;
+ };
+
+ class TestDropDB : public Base {
+ public:
+ void run() {
+ drop();
+
+ std::string dbname = nsToDatabase(ns());
+ Command *dropDB = Command::findCommand("dropDatabase");
+ BSONObj cmdObj = BSON("dropDatabase" << 1);
+
+ BSONObj indexOp1 = BSON("ns" << ns() << "name" << "foo_1" << "key" <<
+ BSON("foo" << 1));
+ BSONObj indexOp2 = BSON("ns" << (dbname+".something.else") << "name" << "baz_1" <<
+ "key" << BSON("baz" << 1));
+ // Different database - foo
+ BSONObj indexOp3 = BSON("ns" << "foo.something.else" << "name" << "baz_1" <<
+ "key" << BSON("baz" << 1));
+ // Different database - unittestsx
+ BSONObj indexOp4 = BSON("ns" << (dbname+"x.something.else") << "name" << "baz_1" <<
+ "key" << BSON("baz" << 1));
+ // Different database - xunittests
+ BSONObj indexOp5 = BSON("ns" << ("x"+dbname+".something.else") << "name" << "baz_1" <<
+ "key" << BSON("baz" << 1));
+
+ IndexBuildThread t1(indexOp1);
+ IndexBuildThread t2(indexOp2);
+ IndexBuildThread t3(indexOp3);
+ IndexBuildThread t4(indexOp4);
+ IndexBuildThread t5(indexOp5);
+
+ t1.go();
+ t2.go();
+ t3.go();
+ t4.go();
+ t5.go();
+
+ ASSERT(!t1.curop()->killPending());
+ ASSERT(!t2.curop()->killPending());
+ ASSERT(!t3.curop()->killPending());
+ ASSERT(!t4.curop()->killPending());
+ ASSERT(!t5.curop()->killPending());
+
+ dropDB->stopIndexBuilds(dbname, cmdObj);
+
+ sleepsecs(1);
+
+ ASSERT(t1.finished());
+ ASSERT(t2.finished());
+ ASSERT(!t3.finished());
+ ASSERT(!t4.finished());
+ ASSERT(!t5.finished());
+
+ t3.finish();
+ t4.finish();
+ t5.finish();
+ }
+ };
+
+ class TestDrop : public Base {
+ public:
+ void run() {
+ drop();
+
+ std::string dbname = nsToDatabase(ns());
+ Command *drop = Command::findCommand("drop");
+
+ BSONObj indexOp1 = BSON("ns" << ns() << "name" << "foo_1" <<
+ "key" << BSON("foo" << 1));
+ BSONObj indexOp2 = BSON("ns" << ns() << "name" << "bar_1" <<
+ "key" << BSON("bar" << 1) << "background" << true);
+ // Different collection
+ BSONObj indexOp3 = BSON("ns" << (dbname+".something.else") << "name" << "baz_1" <<
+ "key" << BSON("baz" << 1));
+
+ IndexBuildThread t1(indexOp1);
+ IndexBuildThread t2(indexOp2);
+ IndexBuildThread t3(indexOp3);
+
+ t1.go();
+ t2.go();
+ t3.go();
+
+ ASSERT(!t1.curop()->killPending());
+ ASSERT(!t2.curop()->killPending());
+ ASSERT(!t3.curop()->killPending());
+
+ std::string coll(strchr(ns(), '.')+1);
+ BSONObj cmdObj = BSON("drop" << coll);
+ drop->stopIndexBuilds(dbname, cmdObj);
+
+ sleepsecs(1);
+
+ ASSERT(t1.finished());
+ ASSERT(t2.finished());
+ ASSERT(!t3.finished());
+
+ t3.finish();
+ }
+ };
+
+ class TestDropIndexes : public Base {
+
+ void testDropIndexes1() {
+ drop();
+
+ std::string dbname = nsToDatabase(ns());
+ Command *c = Command::findCommand("dropIndexes");
+
+ BSONObj indexOp1 = BSON("ns" << ns() << "name" << "x_1" << "key" << BSON("x" << 1));
+ BSONObj indexOp2 = BSON("ns" << ns() << "name" << "y_1" << "key" << BSON("y" << 1));
+ BSONObj indexOp3 = BSON("ns" << ns() << "name" << "z_1" << "key" << BSON("z" << 1));
+
+ std::string coll(strchr(ns(), '.')+1);
+ BSONObj cmd1 = BSON("dropIndexes" << coll << "index" << "*");
+
+ IndexBuildThread t1(indexOp1);
+ IndexBuildThread t2(indexOp2);
+ IndexBuildThread t3(indexOp3);
+
+ t1.go();
+ t2.go();
+ t3.go();
+
+ ASSERT(!t1.curop()->killPending());
+ ASSERT(!t2.curop()->killPending());
+ ASSERT(!t3.curop()->killPending());
+
+ c->stopIndexBuilds(dbname, cmd1);
+
+ sleepsecs(1);
+
+ ASSERT(t1.finished());
+ ASSERT(t2.finished());
+ ASSERT(t3.finished());
+ }
+
+ void testDropIndexes2() {
+ drop();
+
+ std::string dbname = nsToDatabase(ns());
+ Command *c = Command::findCommand("dropIndexes");
+
+ BSONObj indexOp1 = BSON("ns" << ns() << "name" << "x_1" << "key" << BSON("x" << 1));
+ BSONObj indexOp2 = BSON("ns" << ns() << "name" << "y_1" << "key" << BSON("y" << 1));
+ BSONObj indexOp3 = BSON("ns" << ns() << "name" << "z_1" << "key" << BSON("z" << 1));
+
+ std::string coll(strchr(ns(), '.')+1);
+ BSONObj cmd2 = BSON("dropIndexes" << coll << "index" << "y_1");
+
+ IndexBuildThread t1(indexOp1);
+ IndexBuildThread t2(indexOp2);
+ IndexBuildThread t3(indexOp3);
+
+ t1.go();
+ t2.go();
+ t3.go();
+
+ ASSERT(!t1.curop()->killPending());
+ ASSERT(!t2.curop()->killPending());
+ ASSERT(!t3.curop()->killPending());
+
+ c->stopIndexBuilds(dbname, cmd2);
+
+ sleepsecs(1);
+
+ ASSERT(!t1.finished());
+ ASSERT(t2.finished());
+ ASSERT(!t3.finished());
+
+ t1.finish();
+ t3.finish();
+ }
+
+ void testDropIndexes3() {
+ drop();
+
+ std::string dbname = nsToDatabase(ns());
+ std::string coll(strchr(ns(), '.')+1);
+ BSONObj cmd3 = BSON("dropIndexes" << coll << "index" << BSON("z" << 1));
+ Command *c = Command::findCommand("dropIndexes");
+
+ BSONObj indexOp1 = BSON("ns" << ns() << "name" << "x_1" << "key" << BSON("x" << 1));
+ BSONObj indexOp2 = BSON("ns" << ns() << "name" << "y_1" << "key" << BSON("y" << 1));
+ BSONObj indexOp3 = BSON("ns" << ns() << "name" << "z_1" << "key" << BSON("z" << 1));
+
+ IndexBuildThread t1(indexOp1);
+ IndexBuildThread t2(indexOp2);
+ IndexBuildThread t3(indexOp3);
+
+ t1.go();
+ t2.go();
+ t3.go();
+
+ ASSERT(!t1.curop()->killPending());
+ ASSERT(!t2.curop()->killPending());
+ ASSERT(!t3.curop()->killPending());
+
+ c->stopIndexBuilds(dbname, cmd3);
+
+ sleepsecs(1);
+
+ ASSERT(!t1.finished());
+ ASSERT(!t2.finished());
+ ASSERT(t3.finished());
+
+ t1.finish();
+ t2.finish();
+ }
+
+ public:
+ void run() {
+ testDropIndexes1();
+ testDropIndexes2();
+ testDropIndexes3();
+ }
+ };
+
+ class TestRename : public Base {
+ public:
+ void run() {
+ drop();
+
+ std::string dbname = nsToDatabase(ns());
+ std::string coll(strchr(ns(), '.')+1);
+ BSONObj cmdObj = BSON("renameCollection" << ns() << "to" << dbname+".bar");
+ Command *c = Command::findCommand("renameCollection");
+
+ BSONObj indexOp1 = BSON("ns" << ns() << "name" << "x_1" << "key" << BSON("x" << 1));
+ BSONObj indexOp2 = BSON("ns" << ns() << "name" << "y_1" << "key" << BSON("y" << 1));
+ BSONObj indexOp3 = BSON("ns" << (dbname+".bar") << "name" << "z_1" << "key" <<
+ BSON("z" << 1));
+ BSONObj indexOp4 = BSON("ns" << ("x."+coll) << "name" << "z_1" << "key" <<
+ BSON("z" << 1));
+
+ IndexBuildThread t1(indexOp1);
+ IndexBuildThread t2(indexOp2);
+ IndexBuildThread t3(indexOp3);
+ IndexBuildThread t4(indexOp4);
+
+ t1.go();
+ t2.go();
+ t3.go();
+ t4.go();
+
+ ASSERT(!t1.curop()->killPending());
+ ASSERT(!t2.curop()->killPending());
+ ASSERT(!t3.curop()->killPending());
+ ASSERT(!t4.curop()->killPending());
+
+ std::vector<BSONObj> indexes = c->stopIndexBuilds(dbname, cmdObj);
+
+ ASSERT(t1.finished());
+ ASSERT(t2.finished());
+ ASSERT(!t3.finished());
+ ASSERT(!t4.finished());
+
+ t3.finish();
+ t4.finish();
+
+ // Build indexes
+ IndexBuilder::restoreIndexes(dbname+".system.indexes", indexes);
+
+ DBDirectClient cli;
+ time_t max = time(0)+10;
+ // assert.soon
+ while (time(0) < max) {
+ std::string ns = dbname+".system.indexes";
+ if (cli.count(ns) == 3) {
+ return;
+ }
+ };
+
+ ASSERT(false);
+ }
+ };
+
+ class TestReIndex : public Base {
+ public:
+ void run() {
+ drop();
+
+ std::string dbname = nsToDatabase(ns());
+ std::string coll(strchr(ns(), '.')+1);
+ BSONObj cmdObj = BSON("reIndex" << coll);
+ Command *c = Command::findCommand("reIndex");
+
+ DBDirectClient cli;
+ int originalIndexCount = cli.count(dbname+".system.indexes");
+
+ BSONObj indexOp1 = BSON("ns" << ns() << "name" << "foo_1" <<
+ "key" << BSON("foo" << 1));
+ BSONObj indexOp2 = BSON("ns" << (dbname+".something.else") << "name" << "baz_1" <<
+ "key" << BSON("baz" << 1));
+
+ IndexBuildThread t1(indexOp1);
+ IndexBuildThread t2(indexOp2);
+
+ t1.go();
+ t2.go();
+
+ ASSERT(!t1.curop()->killPending());
+ ASSERT(!t2.curop()->killPending());
+
+ std::vector<BSONObj> indexes = c->stopIndexBuilds(dbname, cmdObj);
+ ASSERT_EQUALS(1U, indexes.size());
+ IndexBuilder::restoreIndexes(dbname+".system.indexes", indexes);
+
+ ASSERT(!t2.finished());
+ t2.finish();
+
+ time_t max = time(0)+10;
+ // assert.soon(t1.finished())
+ while (time(0) < max) {
+ std::string ns = dbname+".system.indexes";
+ if (static_cast<int>(cli.count(ns)) == originalIndexCount+2) {
+ return;
+ }
+ };
+
+ ASSERT(false);
+ }
+ };
+
+ class TestTruncateCapped : public Base {
+ public:
+ void run() {
+ drop();
+
+ std::string dbname = nsToDatabase(ns());
+ std::string coll(strchr(ns(), '.')+1);
+ BSONObj cmdObj = BSON("emptycapped" << coll);
+ Command *c = Command::findCommand("emptycapped");
+
+ BSONObj indexOp1 = BSON("ns" << ns() << "name" << "foo_1" <<
+ "key" << BSON("foo" << 1));
+ BSONObj indexOp2 = BSON("ns" << (dbname+".something.else") << "name" << "baz_1" <<
+ "key" << BSON("baz" << 1));
+
+ IndexBuildThread t1(indexOp1);
+ IndexBuildThread t2(indexOp2);
+
+ t1.go();
+ t2.go();
+
+ ASSERT(!t1.curop()->killPending());
+ ASSERT(!t2.curop()->killPending());
+
+ c->stopIndexBuilds(dbname, cmdObj);
+
+ sleepsecs(1);
+
+ ASSERT(t1.finished());
+ ASSERT(!t2.finished());
+
+ t2.finish();
+ }
+ };
+
+ class TestCompact : public Base {
+ public:
+ void run() {
+ drop();
+
+ std::string dbname = nsToDatabase(ns());
+ std::string coll(strchr(ns(), '.')+1);
+ BSONObj cmdObj = BSON("compact" << coll);
+ Command *c = Command::findCommand("compact");
+ DBDirectClient cli;
+ int originalIndexCount = cli.count(dbname+".system.indexes");
+
+ BSONObj indexOp1 = BSON("ns" << ns() << "name" << "foo_1" <<
+ "key" << BSON("foo" << 1));
+ BSONObj indexOp2 = BSON("ns" << (dbname+".something.else") << "name" << "baz_1" <<
+ "key" << BSON("baz" << 1));
+
+ IndexBuildThread t1(indexOp1);
+ IndexBuildThread t2(indexOp2);
+
+ t1.go();
+ t2.go();
+
+ ASSERT(!t1.curop()->killPending());
+ ASSERT(!t2.curop()->killPending());
+
+ std::vector<BSONObj> indexes = c->stopIndexBuilds(dbname, cmdObj);
+ IndexBuilder::restoreIndexes(dbname+".system.indexes", indexes);
+
+ ASSERT(!t2.finished());
+ t2.finish();
+
+ time_t max = time(0)+10;
+ // assert.soon(t1.finished())
+ while (time(0) < max) {
+ std::string ns = dbname+".system.indexes";
+ if (static_cast<int>(cli.count(ns)) == originalIndexCount+2) {
+ return;
+ }
+ };
+
+ ASSERT(false);
+ }
+ };
+
+ class TestRepair : public Base {
+ public:
+ void run() {
+ drop();
+
+ std::string dbname = nsToDatabase(ns());
+ Command *c = Command::findCommand("repairDatabase");
+ BSONObj cmdObj = BSON("repairDatabase" << 1);
+ DBDirectClient cli;
+ int originalIndexCount = cli.count(dbname+".system.indexes");
+
+ BSONObj indexOp1 = BSON("ns" << ns() << "name" << "foo_1" << "key" <<
+ BSON("foo" << 1));
+ BSONObj indexOp2 = BSON("ns" << (dbname+".something.else") << "name" << "baz_1" <<
+ "key" << BSON("baz" << 1));
+ // Different database - foo
+ BSONObj indexOp3 = BSON("ns" << "foo.something.else" << "name" << "baz_1" <<
+ "key" << BSON("baz" << 1));
+ // Different database - unittestsx
+ BSONObj indexOp4 = BSON("ns" << (dbname+"x.something.else") << "name" << "baz_1" <<
+ "key" << BSON("baz" << 1));
+ // Different database - xunittests
+ BSONObj indexOp5 = BSON("ns" << ("x"+dbname+".something.else") << "name" << "baz_1" <<
+ "key" << BSON("baz" << 1));
+
+ IndexBuildThread t1(indexOp1);
+ IndexBuildThread t2(indexOp2);
+ IndexBuildThread t3(indexOp3);
+ IndexBuildThread t4(indexOp4);
+ IndexBuildThread t5(indexOp5);
+
+ t1.go();
+ t2.go();
+ t3.go();
+ t4.go();
+ t5.go();
+
+ ASSERT(!t1.curop()->killPending());
+ ASSERT(!t2.curop()->killPending());
+ ASSERT(!t3.curop()->killPending());
+ ASSERT(!t4.curop()->killPending());
+ ASSERT(!t5.curop()->killPending());
+
+ std::vector<BSONObj> indexes = c->stopIndexBuilds(dbname, cmdObj);
+
+ sleepsecs(1);
+
+ ASSERT(t1.finished());
+ ASSERT(t2.finished());
+ ASSERT(!t3.finished());
+ ASSERT(!t4.finished());
+ ASSERT(!t5.finished());
+
+ IndexBuilder::restoreIndexes(dbname+".system.indexes", indexes);
+
+ ASSERT(!t3.finished());
+ ASSERT(!t4.finished());
+ ASSERT(!t5.finished());
+
+ t3.finish();
+ t4.finish();
+ t5.finish();
+
+ time_t max = time(0)+10;
+ // assert.soon(t1.finished() && t2.finished())
+ while (time(0) < max) {
+ std::string ns = dbname+".system.indexes";
+ if (static_cast<int>(cli.count(ns)) == originalIndexCount+4) {
+ return;
+ }
+ };
+
+ ASSERT(false);
+ }
+ };
+
class MockInitialSync : public replset::InitialSync {
int step;
public:
@@ -545,6 +1098,13 @@ namespace ReplSetTests {
add< CappedUpdate >();
add< CappedInsert >();
add< TestRSSync >();
+ add< TestDropDB >();
+ add< TestDrop >();
+ add< TestDropIndexes >();
+ add< TestTruncateCapped >();
+ add< TestReIndex >();
+ add< TestRepair >();
+ add< TestCompact >();
}
} myall;
}