Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

SERVER-2771 Retry index builds on startup

  • Loading branch information...
commit ab1bceba74845666aaad957cab256cadf2a0c8de 1 parent 3abcc53
@kchodorow kchodorow authored
View
88 jstests/slowNightly/index_retry.js
@@ -0,0 +1,88 @@
+// Check index rebuild when MongoDB is killed
+
+var ports = allocatePorts(1);
+mongod = new MongodRunner(ports[0], "/data/db/index_retry", null, null, ["--journal"]);
+var conn = mongod.start();
+
+var test = conn.getDB("test");
+
+var name = 'jstests_slownightly_index_retry';
+t = test.getCollection(name);
+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) {
+ t.save( { a:i } );
+ if (i % 10000 == 0) {
+ print("i: " + i);
+ }
+}
+test.getLastError();
+
+function debug(x) {
+ printjson(x);
+}
+
+/**
+ * @return if there's a current running index build
+ */
+function indexBuildInProgress() {
+ inprog = test.currentOp().inprog;
+ debug(inprog);
+ indexBuildOpId = -1;
+ inprog.forEach(
+ function( op ) {
+ // Identify the index build as an insert into the 'test.system.indexes'
+ // namespace. It is assumed that no other clients are concurrently
+ // accessing the 'test' database.
+ if ( op.op == 'insert' && op.ns == 'test.system.indexes' ) {
+ debug(op.opid);
+ indexBuildOpId = op.opid;
+ }
+ }
+ );
+ return indexBuildOpId != -1;
+}
+
+function abortDuringIndexBuild(options) {
+
+ // Create an index asynchronously by using a new connection.
+ new Mongo(test.getMongo().host ).getCollection( t.toString() ).createIndex( { a:1 }, options);
+
+ // Wait for the index build to start.
+ var times = 0;
+ assert.soon(
+ function() {
+ return indexBuildInProgress() && times++ >= 2;
+ }
+ );
+
+ print("killing the mongod");
+ stopMongod(ports[0], /* signal */ 9);
+}
+
+abortDuringIndexBuild({background:true});
+
+print("sleeping");
+sleep(2000);
+
+conn = mongod.start(/* reuseData */ true);
+
+assert.soon(
+ function() {
+ try {
+ printjson(conn.getDB("test").getCollection(name).find({a:42}).hint({a:1}).next());
+ } catch (e) {
+ print(e);
+ return false;
+ }
+ return true;
+ },
+ 'index builds successfully'
+);
+
+print("Index built");
+
+stopMongod(ports[0]);
+print("SUCCESS!");
View
1  src/mongo/SConscript
@@ -344,6 +344,7 @@ serverOnlyFiles = [ "db/curop.cpp",
"db/extsort.cpp",
"db/index.cpp",
"db/index_update.cpp",
+ "db/index_rebuilder.cpp",
"db/scanandorder.cpp",
"db/explain.cpp",
"db/geo/2d.cpp",
View
5 src/mongo/db/cmdline.h
@@ -72,6 +72,7 @@ namespace mongo {
bool usingReplSets() const { return !_replSet.empty(); }
std::string rsIndexPrefetch;// --indexPrefetch
+ bool indexBuildRetry; // --noIndexBuildRetry
// for master/slave replication
std::string source; // --source
@@ -141,7 +142,7 @@ namespace mongo {
SSLManager* sslServerManager; // currently leaks on close
#endif
-
+
static void launchOk();
static void addGlobalOptions( boost::program_options::options_description& general ,
@@ -176,7 +177,7 @@ namespace mongo {
// todo move to cmdline.cpp?
inline CmdLine::CmdLine() :
- port(DefaultDBPort), rest(false), jsonp(false), quiet(false),
+ port(DefaultDBPort), rest(false), jsonp(false), indexBuildRetry(true), quiet(false),
noTableScan(false), prealloc(true), preallocj(true), smallfiles(sizeof(int*) == 4),
configsvr(false), quota(false), quotaFiles(8), cpu(false),
durOptions(0), objcheck(false), oplogSize(0), defaultProfile(0),
View
26 src/mongo/db/db.cpp
@@ -34,6 +34,7 @@
#include "mongo/db/dbmessage.h"
#include "mongo/db/dbwebserver.h"
#include "mongo/db/dur.h"
+#include "mongo/db/index_rebuilder.h"
#include "mongo/db/initialize_server_global_state.h"
#include "mongo/db/instance.h"
#include "mongo/db/introspect.h"
@@ -78,7 +79,6 @@ namespace mongo {
extern int diagLogging;
extern unsigned lenForNewNsFiles;
extern int lockFile;
- extern bool checkNsFilesOnLoad;
extern string repairpath;
void setupSignals( bool inFork );
@@ -294,9 +294,6 @@ namespace mongo {
Client::GodScope gs;
LOG(1) << "enter repairDatabases (to check pdfile version #)" << endl;
- //verify(checkNsFilesOnLoad);
- checkNsFilesOnLoad = false; // we are mainly just checking the header - don't scan the whole .ns file for every db here.
-
Lock::GlobalWrite lk;
vector< string > dbNames;
getDatabaseNames( dbNames );
@@ -345,8 +342,6 @@ namespace mongo {
cc().shutdown();
dbexit( EXIT_CLEAN );
}
-
- checkNsFilesOnLoad = true;
}
void clearTmpFiles() {
@@ -403,7 +398,7 @@ namespace mongo {
*/
class DataFileSync : public BackgroundJob , public ServerStatusSection {
public:
- DataFileSync()
+ DataFileSync()
: ServerStatusSection( "backgroundFlushing" ),
_total_time( 0 ),
_flushes( 0 ),
@@ -412,7 +407,7 @@ namespace mongo {
virtual bool includeByDefault() const { return true; }
virtual bool adminOnly() const { return false; }
-
+
string name() const { return "DataFileSync"; }
void run() {
@@ -469,7 +464,7 @@ namespace mongo {
_last_time = ms;
_last = jsTime();
}
-
+
long long _total_time;
long long _flushes;
int _last_time;
@@ -485,16 +480,16 @@ namespace mongo {
virtual void appendAtLeaf( BSONObjBuilder& b ) const {
int m = static_cast<int>(MemoryMappedFile::totalMappedLength() / ( 1024 * 1024 ));
b.appendNumber( "mapped" , m );
-
+
if ( cmdLine.dur ) {
m *= 2;
b.appendNumber( "mappedWithJournal" , m );
}
-
+
}
} memJournalServerStatusMetric;
}
-
+
const char * jsInterruptCallback() {
// should be safe to interrupt in js code, even if we have a write lock
@@ -640,6 +635,8 @@ namespace mongo {
/* this is for security on certain platforms (nonce generation) */
srand((unsigned) (curTimeMicros() ^ startupSrandTimer.micros()));
+ indexRebuilder.go();
+
snapshotThread.go();
d.clientCursorMonitor.go();
PeriodicTask::theRunner->go();
@@ -754,6 +751,8 @@ static void buildOptionsDescriptions(po::options_description *pVisible,
("jsonp","allow JSONP access via http (has security implications)")
("noauth", "run without security")
("nohttpinterface", "disable http interface")
+ ("noIndexBuildRetry", po::value<int>(),
+ "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")
@@ -1037,6 +1036,9 @@ static void processCommandLineOptions(const std::vector<std::string>& argv) {
if (params.count("replIndexPrefetch")) {
cmdLine.rsIndexPrefetch = params["replIndexPrefetch"].as<std::string>();
}
+ if (params.count("noIndexBuildRetry")) {
+ cmdLine.indexBuildRetry = false;
+ }
if (params.count("only")) {
cmdLine.only = params["only"].as<string>().c_str();
}
View
112 src/mongo/db/index_rebuilder.cpp
@@ -0,0 +1,112 @@
+/**
+ * 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_rebuilder.h"
+
+#include "mongo/db/instance.h"
+#include "mongo/db/pdfile.h"
+
+namespace mongo {
+
+ IndexRebuilder indexRebuilder;
+
+ IndexRebuilder::IndexRebuilder() {}
+
+ std::string IndexRebuilder::name() const {
+ return "IndexRebuilder";
+ }
+
+ void IndexRebuilder::run() {
+ Client::initThread(name().c_str());
+ Lock::GlobalWrite lk;
+ Client::GodScope gs;
+ std::vector<std::string> dbNames;
+ getDatabaseNames(dbNames);
+
+ for (std::vector<std::string>::const_iterator it = dbNames.begin();
+ it < dbNames.end();
+ it++) {
+ checkDB(*it);
+ }
+
+ cc().shutdown();
+ }
+
+ void IndexRebuilder::checkDB(const std::string& dbName) {
+ const std::string systemNS = dbName + ".system.namespaces";
+ DBDirectClient cli;
+ scoped_ptr<DBClientCursor> cursor(cli.query(systemNS, Query()));
+
+ while (cursor->more()) {
+ BSONObj nsDoc = cursor->next();
+ const char* ns = nsDoc["name"].valuestrsafe();
+
+ Client::Context ctx(ns, dbpath, false, false);
+ NamespaceDetails* nsd = nsdetails(ns);
+
+ if (!nsd || !nsd->indexBuildInProgress) {
+ continue;
+ }
+
+ log() << "Found interrupted index build on " << ns << endl;
+
+ // If the indexBuildRetry flag isn't set, just clear the inProg flag
+ if (!cmdLine.indexBuildRetry) {
+ // If we crash between unsetting the inProg flag and cleaning up the index, the
+ // index space will be lost.
+ getDur().writingInt(nsd->indexBuildInProgress) = 0;
+ nsd->idx(nsd->nIndexes).kill_idx();
+ continue;
+ }
+
+ retryIndexBuild(dbName, nsd);
+ }
+ }
+
+ void IndexRebuilder::retryIndexBuild(const std::string& dbName, NamespaceDetails* nsd) {
+ // details.info is always a valid system.indexes entry because DataFileMgr::insert journals
+ // creating the index doc and then insert_makeIndex durably assigns its DiskLoc to info.
+ // indexBuildInProgress is set after that, so if it is set, info must be set.
+ IndexDetails& details = nsd->idx(nsd->nIndexes);
+
+ // First, clean up the in progress index build. Save the system.indexes entry so that we
+ // can add it again afterwards.
+ BSONObj indexObj = details.info.obj().getOwned();
+
+ // Clean up the in-progress index build
+ getDur().writingInt(nsd->indexBuildInProgress) = 0;
+ 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() << ")"
+ << endl;
+ }
+ }
+}
View
45 src/mongo/db/index_rebuilder.h
@@ -0,0 +1,45 @@
+/**
+ * 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 "mongo/db/namespace_details.h"
+#include "mongo/util/background.h"
+
+namespace mongo {
+
+ class IndexRebuilder : public BackgroundJob {
+ public:
+ IndexRebuilder();
+
+ std::string name() const;
+ void run();
+
+ private:
+ /**
+ * Check each collection in a database to see if it has any in-progress index builds that
+ * need to be retried. If so, calls retryIndexBuild.
+ */
+ void checkDB(const std::string& dbname);
+
+ /**
+ * Actually retry the index build on a given namespace.
+ */
+ void retryIndexBuild(const std::string& dbName, NamespaceDetails* nsd);
+ };
+
+ extern IndexRebuilder indexRebuilder;
+}
View
28 src/mongo/db/index_update.cpp
@@ -515,29 +515,6 @@ namespace mongo {
}
};
- /**
- * For the lifetime of this object, an index build is indicated on the specified
- * namespace and the newest index is marked as absent. This simplifies
- * the cleanup required on recovery.
- */
- class RecoverableIndexState {
- public:
- RecoverableIndexState( NamespaceDetails *d ) : _d( d ) {
- indexBuildInProgress() = 1;
- nIndexes()--;
- }
- ~RecoverableIndexState() {
- DESTRUCTOR_GUARD (
- nIndexes()++;
- indexBuildInProgress() = 0;
- )
- }
- private:
- int &nIndexes() { return getDur().writingInt( _d->nIndexes ); }
- int &indexBuildInProgress() { return getDur().writingInt( _d->indexBuildInProgress ); }
- NamespaceDetails *_d;
- };
-
// throws DBException
void buildAnIndex(const std::string& ns,
NamespaceDetails* d,
@@ -550,9 +527,7 @@ namespace mongo {
unsigned long long n;
verify( !BackgroundOperation::inProgForNs(ns.c_str()) ); // should have been checked earlier, better not be...
- verify( d->indexBuildInProgress == 0 );
verify( Lock::isWriteLocked(ns) );
- RecoverableIndexState recoverable( d );
// Build index spec here in case the collection is empty and the index details are invalid
idx.getSpec();
@@ -676,7 +651,8 @@ namespace mongo {
d->nIndexes = 0;
}
if ( idIndex ) {
- d->addIndex(ns) = *idIndex;
+ d->getNextIndexDetails(ns) = *idIndex;
+ d->addIndex(ns);
wassert( d->nIndexes == 1 );
}
/* assuming here that id index is not multikey: */
View
35 src/mongo/db/namespace_details.cpp
@@ -120,28 +120,6 @@ namespace mongo {
}
#endif
- void NamespaceDetails::onLoad(const Namespace& k) {
-
- if( k.isExtra() ) {
- /* overflow storage for indexes - so don't treat as a NamespaceDetails object. */
- return;
- }
-
- if( indexBuildInProgress ) {
- verify( Lock::isW() ); // TODO(erh) should this be per db?
- if( indexBuildInProgress ) {
- log() << "indexBuildInProgress was " << indexBuildInProgress << " for " << k << ", indicating an abnormal db shutdown" << endl;
- getDur().writingInt( indexBuildInProgress ) = 0;
- }
- }
- }
-
- static void namespaceOnLoadCallback(const Namespace& k, NamespaceDetails& v) {
- v.onLoad(k);
- }
-
- bool checkNsFilesOnLoad = true;
-
NOINLINE_DECL void NamespaceIndex::_init() {
verify( !ht );
@@ -194,8 +172,6 @@ namespace mongo {
verify( len <= 0x7fffffff );
ht = new HashTable<Namespace,NamespaceDetails>(p, (int) len, "namespace index");
- if( checkNsFilesOnLoad )
- ht->iterAll(namespaceOnLoadCallback);
}
static void namespaceGetNamespacesCallback( const Namespace& k , NamespaceDetails& v , void * extra ) {
@@ -523,8 +499,7 @@ namespace mongo {
NamespaceDetailsTransient::get(thisns).clearQueryCache();
}
- /* you MUST call when adding an index. see pdfile.cpp */
- IndexDetails& NamespaceDetails::addIndex(const char *thisns, bool resetTransient) {
+ IndexDetails& NamespaceDetails::getNextIndexDetails(const char* thisns) {
IndexDetails *id;
try {
id = &idx(nIndexes,true);
@@ -533,11 +508,13 @@ namespace mongo {
allocExtra(thisns, nIndexes);
id = &idx(nIndexes,false);
}
+ return *id;
+ }
+ /* you MUST call when adding an index. see pdfile.cpp */
+ void NamespaceDetails::addIndex(const char* thisns) {
(*getDur().writing(&nIndexes))++;
- if ( resetTransient )
- NamespaceDetailsTransient::get(thisns).addedIndex();
- return *id;
+ NamespaceDetailsTransient::get(thisns).addedIndex();
}
// must be called when renaming a NS to fix up extra
View
16 src/mongo/db/namespace_details.h
@@ -138,9 +138,6 @@ namespace mongo {
Extra* allocExtra(const char *ns, int nindexessofar);
void copyingFrom(const char *thisns, NamespaceDetails *src); // must be called when renaming a NS to fix up extra
- /* called when loaded from disk */
- void onLoad(const Namespace& k);
-
/* dump info on this namespace. for debugging. */
void dump(const Namespace& k);
@@ -226,10 +223,17 @@ namespace mongo {
bool isMultikey(int i) const { return (multiKeyIndexBits & (((unsigned long long) 1) << i)) != 0; }
void setIndexIsMultikey(const char *thisns, int i);
- /* add a new index. does not add to system.indexes etc. - just to NamespaceDetails.
- caller must populate returned object.
+ /**
+ * This fetches the IndexDetails for the next empty index slot. The caller must populate
+ * returned object. This handles allocating extra index space, if necessary.
+ */
+ IndexDetails& getNextIndexDetails(const char* thisns);
+
+ /**
+ * Add a new index. This does not add it to system.indexes etc. - just to NamespaceDetails.
+ * This resets the transient namespace details.
*/
- IndexDetails& addIndex(const char *thisns, bool resetTransient=true);
+ void addIndex(const char* thisns);
void aboutToDeleteAnIndex() {
clearSystemFlag( Flag_HaveIdIndex );
View
70 src/mongo/db/pdfile.cpp
@@ -1404,36 +1404,58 @@ namespace mongo {
}
int idxNo = tableToIndex->nIndexes;
- IndexDetails& idx = tableToIndex->addIndex(tabletoidxns.c_str(), !background); // clear transient info caches so they refresh; increments nIndexes
- getDur().writingDiskLoc(idx.info) = loc;
+
try {
- buildAnIndex(tabletoidxns, tableToIndex, idx, idxNo, background, mayInterrupt);
- }
- catch( DBException& e ) {
- // save our error msg string as an exception or dropIndexes will overwrite our message
- LastError *le = lastError.get();
- int savecode = 0;
- string saveerrmsg;
- if ( le ) {
- savecode = le->code;
- saveerrmsg = le->msg;
+ IndexDetails& idx = tableToIndex->getNextIndexDetails(tabletoidxns.c_str());
+ // It's important that this is outside the inner try/catch so that we never try to call
+ // kill_idx on a half-formed disk loc (if this asserts).
+ getDur().writingDiskLoc(idx.info) = loc;
+
+ try {
+ getDur().writingInt(tableToIndex->indexBuildInProgress) = 1;
+ buildAnIndex(tabletoidxns, tableToIndex, idx, idxNo, background, mayInterrupt);
}
- else {
- savecode = e.getCode();
- saveerrmsg = e.what();
+ catch (DBException& e) {
+ // save our error msg string as an exception or dropIndexes will overwrite our message
+ LastError *le = lastError.get();
+ int savecode = 0;
+ string saveerrmsg;
+ if ( le ) {
+ savecode = le->code;
+ saveerrmsg = le->msg;
+ }
+ else {
+ savecode = e.getCode();
+ saveerrmsg = e.what();
+ }
+
+ // roll back this index
+ idx.kill_idx();
+
+ verify(le && !saveerrmsg.empty());
+ setLastError(savecode,saveerrmsg.c_str());
+ throw;
}
- // roll back this index
- string name = idx.indexName();
- BSONObjBuilder b;
- string errmsg;
- bool ok = dropIndexes(tableToIndex, tabletoidxns.c_str(), name.c_str(), errmsg, b, true);
- if( !ok ) {
- log() << "failed to drop index after a unique key error building it: " << errmsg << ' ' << tabletoidxns << ' ' << name << endl;
+ // clear transient info caches so they refresh; increments nIndexes
+ tableToIndex->addIndex(tabletoidxns.c_str());
+ getDur().writingInt(tableToIndex->indexBuildInProgress) = 0;
+ }
+ catch (...) {
+ // Generally, this will be called as an exception from building the index bubbles up.
+ // Thus, the index will have already been cleaned up. This catch just ensures that the
+ // metadata is consistent on any exception. It may leak like a sieve if the index
+ // successfully finished building and addIndex or kill_idx threw.
+
+ // Check if nIndexes was incremented
+ if (idxNo < tableToIndex->nIndexes) {
+ // TODO: this will have to change when we can have multiple simultanious index
+ // builds
+ getDur().writingInt(tableToIndex->nIndexes) -= 1;
}
- verify( le && !saveerrmsg.empty() );
- setLastError(savecode,saveerrmsg.c_str());
+ getDur().writingInt(tableToIndex->indexBuildInProgress) = 0;
+
throw;
}
}
View
20 src/mongo/db/repl/rs_rollback.cpp
@@ -16,12 +16,14 @@
*/
#include "pch.h"
-#include "../client.h"
-#include "rs.h"
-#include "../repl.h"
-#include "../cloner.h"
-#include "../ops/update.h"
-#include "../ops/delete.h"
+
+#include "mongo/db/client.h"
+#include "mongo/db/cloner.h"
+#include "mongo/db/index_rebuilder.h"
+#include "mongo/db/ops/update.h"
+#include "mongo/db/ops/delete.h"
+#include "mongo/db/repl/rs.h"
+#include "mongo/db/repl.h"
/* Scenarios
@@ -573,6 +575,12 @@ namespace mongo {
}
void ReplSetImpl::syncRollback(OplogReader&r) {
+ // If this is startup, wait for any index build retries to finish first
+ while (indexRebuilder.getState() != BackgroundJob::Done) {
+ OCCASIONALLY LOG(0) << "replSet rollback waiting for index rebuild to finish" << endl;
+ indexRebuilder.wait(1000);
+ }
+
// check that we are at minvalid, otherwise we cannot rollback as we may be in an
// inconsistent state
{
View
43 src/mongo/dbtests/clienttests.cpp
@@ -17,9 +17,11 @@
// client.cpp
#include "pch.h"
+
#include "dbtests.h"
-#include "../db/d_concurrency.h"
#include "mongo/client/dbclientcursor.h"
+#include "mongo/db/d_concurrency.h"
+#include "mongo/db/pdfile.h"
namespace ClientTests {
@@ -27,6 +29,7 @@ namespace ClientTests {
public:
Base( string coll ) {
+ db.dropDatabase("test");
_ns = (string)"test." + coll;
}
@@ -98,6 +101,43 @@ namespace ClientTests {
};
+ /**
+ * Check that nIndexes is incremented correctly when an index builds (and that it is not
+ * incremented when an index fails to build), system.indexes has an entry added (or not), and
+ * system.namespaces has a doc added (or not).
+ */
+ class BuildIndex : public Base {
+ public:
+ BuildIndex() : Base("buildIndex") {}
+ void run() {
+ Lock::DBWrite lock(ns());
+ Client::WriteContext ctx(ns());
+
+ db.insert(ns(), BSON("x" << 1 << "y" << 2));
+ db.insert(ns(), BSON("x" << 2 << "y" << 2));
+
+ ASSERT_EQUALS(1, nsdetails(ns())->nIndexes);
+ // _id index
+ ASSERT_EQUALS(1U, db.count("test.system.indexes"));
+ // test.buildindex
+ // test.buildindex_$id
+ // test.system.indexes
+ ASSERT_EQUALS(3U, db.count("test.system.namespaces"));
+
+ db.ensureIndex(ns(), BSON("y" << 1), true);
+
+ ASSERT_EQUALS(1, nsdetails(ns())->nIndexes);
+ ASSERT_EQUALS(1U, db.count("test.system.indexes"));
+ ASSERT_EQUALS(3U, db.count("test.system.namespaces"));
+
+ db.ensureIndex(ns(), BSON("x" << 1), true);
+
+ ASSERT_EQUALS(2, nsdetails(ns())->nIndexes);
+ ASSERT_EQUALS(2U, db.count("test.system.indexes"));
+ ASSERT_EQUALS(4U, db.count("test.system.namespaces"));
+ }
+ };
+
class CS_10 : public Base {
public:
CS_10() : Base( "CS_10" ) {}
@@ -187,6 +227,7 @@ namespace ClientTests {
add<DropIndex>();
add<ReIndex>();
add<ReIndex2>();
+ add<BuildIndex>();
add<CS_10>();
add<PushBack>();
add<Create>();
Please sign in to comment.
Something went wrong with that request. Please try again.