summaryrefslogtreecommitdiff
path: root/src/mongo
diff options
context:
space:
mode:
authorMathias Stearn <mathias@10gen.com>2014-07-30 17:34:46 -0400
committerMathias Stearn <mathias@10gen.com>2014-08-13 17:30:25 -0400
commit00913e47de5aced5267e44e82ac9e976bbaac089 (patch)
tree26002b9f1eb4e7b3f295bd2a4cf24a68aa13cad3 /src/mongo
parentc610cfe5c58d1f4301f5535d3e166d5d4332bc87 (diff)
downloadmongo-00913e47de5aced5267e44e82ac9e976bbaac089.tar.gz
SERVER-13951 Split index building in to UnitOfWork-sized stages
All index builds now go through the MultiIndexBuilder as its API was already close to ideal. The following tickets have also been addressed by this commit: SERVER-14710 Remove dropDups SERVER-12309 Cloner build indexes in parallel SERVER-14737 Initial sync uses bg index building SERVER-9135 fast index build for initial sync SERVER-2747 can't kill index in phase 2 SERVER-8917 check error code rather than assuming all errors are dups SERVER-14820 compact enforces unique while claiming not to SERVER-14746 IndexRebuilder should be foreground and fail fatally
Diffstat (limited to 'src/mongo')
-rw-r--r--src/mongo/db/background.h3
-rw-r--r--src/mongo/db/catalog/collection.cpp13
-rw-r--r--src/mongo/db/catalog/collection.h5
-rw-r--r--src/mongo/db/catalog/collection_compact.cpp23
-rw-r--r--src/mongo/db/catalog/database.cpp4
-rw-r--r--src/mongo/db/catalog/index_catalog.cpp178
-rw-r--r--src/mongo/db/catalog/index_catalog.h41
-rw-r--r--src/mongo/db/catalog/index_create.cpp443
-rw-r--r--src/mongo/db/catalog/index_create.h173
-rw-r--r--src/mongo/db/cloner.cpp232
-rw-r--r--src/mongo/db/commands/create_indexes.cpp92
-rw-r--r--src/mongo/db/commands/drop_indexes.cpp51
-rw-r--r--src/mongo/db/commands/mr.cpp52
-rw-r--r--src/mongo/db/commands/rename_collection.cpp274
-rw-r--r--src/mongo/db/commands/write_commands/batch_executor.cpp29
-rw-r--r--src/mongo/db/db.cpp3
-rw-r--r--src/mongo/db/dbhelpers.cpp11
-rw-r--r--src/mongo/db/index/btree_access_method.cpp1
-rw-r--r--src/mongo/db/index/btree_based_access_method.cpp4
-rw-r--r--src/mongo/db/index/btree_based_access_method.h1
-rw-r--r--src/mongo/db/index/btree_based_bulk_access_method.cpp51
-rw-r--r--src/mongo/db/index/btree_based_bulk_access_method.h3
-rw-r--r--src/mongo/db/index/index_access_method.h2
-rw-r--r--src/mongo/db/index_builder.cpp49
-rw-r--r--src/mongo/db/index_builder.h4
-rw-r--r--src/mongo/db/index_rebuilder.cpp148
-rw-r--r--src/mongo/db/index_rebuilder.h30
-rw-r--r--src/mongo/db/instance.cpp26
-rw-r--r--src/mongo/db/pdfile_private.h35
-rw-r--r--src/mongo/db/repl/oplog.cpp24
-rw-r--r--src/mongo/db/repl/repl_set_impl.cpp1
-rw-r--r--src/mongo/db/storage/heap1/heap1_btree_impl.cpp16
-rw-r--r--src/mongo/db/storage/mmap_v1/btree/btree_interface.cpp4
-rw-r--r--src/mongo/db/storage/mmap_v1/btree/btree_logic.cpp35
-rw-r--r--src/mongo/db/storage/mmap_v1/btree/btree_logic.h7
-rw-r--r--src/mongo/db/storage/mmap_v1/repair_database.cpp42
-rw-r--r--src/mongo/db/storage/rocks/rocks_sorted_data_impl.h1
-rw-r--r--src/mongo/db/storage/sorted_data_interface.h11
-rw-r--r--src/mongo/dbtests/counttests.cpp13
-rw-r--r--src/mongo/dbtests/indexcatalogtests.cpp14
-rw-r--r--src/mongo/dbtests/indexupdatetests.cpp142
-rw-r--r--src/mongo/dbtests/oplogstarttests.cpp2
-rw-r--r--src/mongo/dbtests/querytests.cpp8
-rw-r--r--src/mongo/dbtests/repltests.cpp3
-rw-r--r--src/mongo/s/d_migrate.cpp51
45 files changed, 1201 insertions, 1154 deletions
diff --git a/src/mongo/db/background.h b/src/mongo/db/background.h
index 3db4767f95a..4072bd0cdd3 100644
--- a/src/mongo/db/background.h
+++ b/src/mongo/db/background.h
@@ -44,8 +44,7 @@
namespace mongo {
/* these are administrative operations / jobs
- for a namespace running in the background, and that only one
- at a time per namespace is permitted, and that if in progress,
+ for a namespace running in the background, and that if in progress,
you aren't allowed to do other NamespaceDetails major manipulations
(such as dropping ns or db) even in the foreground and must
instead uassert.
diff --git a/src/mongo/db/catalog/collection.cpp b/src/mongo/db/catalog/collection.cpp
index c4c6787c133..3966ccae972 100644
--- a/src/mongo/db/catalog/collection.cpp
+++ b/src/mongo/db/catalog/collection.cpp
@@ -198,20 +198,17 @@ namespace mongo {
StatusWith<DiskLoc> Collection::insertDocument( OperationContext* txn,
const BSONObj& doc,
- MultiIndexBlock& indexBlock ) {
+ MultiIndexBlock* indexBlock,
+ bool enforceQuota ) {
StatusWith<DiskLoc> loc = _recordStore->insertRecord( txn,
doc.objdata(),
doc.objsize(),
- 0 );
+ _enforceQuota(enforceQuota) );
if ( !loc.isOK() )
return loc;
- InsertDeleteOptions indexOptions;
- indexOptions.logIfError = false;
- indexOptions.dupsAllowed = true; // in repair we should be doing no checking
-
- Status status = indexBlock.insert( doc, loc.getValue(), indexOptions );
+ Status status = indexBlock->insert( doc, loc.getValue() );
if ( !status.isOK() )
return StatusWith<DiskLoc>( status );
@@ -483,7 +480,7 @@ namespace mongo {
// 4) re-create indexes
for ( size_t i = 0; i < indexSpecs.size(); i++ ) {
- status = _indexCatalog.createIndex(txn, indexSpecs[i], false);
+ status = _indexCatalog.createIndexOnEmptyCollection(txn, indexSpecs[i]);
if ( !status.isOK() )
return status;
}
diff --git a/src/mongo/db/catalog/collection.h b/src/mongo/db/catalog/collection.h
index d789d4bb977..9ca4c76e97b 100644
--- a/src/mongo/db/catalog/collection.h
+++ b/src/mongo/db/catalog/collection.h
@@ -164,6 +164,8 @@ namespace mongo {
/**
* this does NOT modify the doc before inserting
* i.e. will not add an _id field for documents that are missing it
+ *
+ * If enforceQuota is false, quotas will be ignored.
*/
StatusWith<DiskLoc> insertDocument( OperationContext* txn,
const BSONObj& doc,
@@ -175,7 +177,8 @@ namespace mongo {
StatusWith<DiskLoc> insertDocument( OperationContext* txn,
const BSONObj& doc,
- MultiIndexBlock& indexBlock );
+ MultiIndexBlock* indexBlock,
+ bool enforceQuota );
/**
* updates the document @ oldLocation with newDoc
diff --git a/src/mongo/db/catalog/collection_compact.cpp b/src/mongo/db/catalog/collection_compact.cpp
index 1e4d7d15d97..bd8527b5e62 100644
--- a/src/mongo/db/catalog/collection_compact.cpp
+++ b/src/mongo/db/catalog/collection_compact.cpp
@@ -84,11 +84,7 @@ namespace mongo {
}
virtual void inserted( const RecordData& recData, const DiskLoc& newLocation ) {
- InsertDeleteOptions options;
- options.logIfError = false;
- options.dupsAllowed = true; // in compact we should be doing no checking
-
- _multiIndexBlock->insert( recData.toBson(), newLocation, options );
+ _multiIndexBlock->insert( recData.toBson(), newLocation );
}
private:
@@ -148,20 +144,29 @@ namespace mongo {
CompactStats stats;
- MultiIndexBlock multiIndexBlock(txn, this);
- status = multiIndexBlock.init( indexSpecs );
+ MultiIndexBlock indexer(txn, this);
+ indexer.allowInterruption();
+ indexer.ignoreUniqueConstraint(); // in compact we should be doing no checking
+
+ status = indexer.init( indexSpecs );
if ( !status.isOK() )
return StatusWith<CompactStats>( status );
- MyCompactAdaptor adaptor(this, &multiIndexBlock);
+ MyCompactAdaptor adaptor(this, &indexer);
_recordStore->compact( txn, &adaptor, compactOptions, &stats );
log() << "starting index commits";
- status = multiIndexBlock.commit();
+ status = indexer.doneInserting();
if ( !status.isOK() )
return StatusWith<CompactStats>( status );
+ {
+ WriteUnitOfWork wunit(txn->recoveryUnit());
+ indexer.commit();
+ wunit.commit();
+ }
+
return StatusWith<CompactStats>( stats );
}
diff --git a/src/mongo/db/catalog/database.cpp b/src/mongo/db/catalog/database.cpp
index f4fb419f30f..3418a5d3592 100644
--- a/src/mongo/db/catalog/database.cpp
+++ b/src/mongo/db/catalog/database.cpp
@@ -510,7 +510,9 @@ namespace mongo {
if ( collection->requiresIdIndex() ) {
if ( options.autoIndexId == CollectionOptions::YES ||
options.autoIndexId == CollectionOptions::DEFAULT ) {
- uassertStatusOK( collection->getIndexCatalog()->ensureHaveIdIndex(txn) );
+ IndexCatalog* ic = collection->getIndexCatalog();
+ uassertStatusOK(
+ ic->createIndexOnEmptyCollection(txn, ic->getDefaultIdIndexSpec()));
}
}
diff --git a/src/mongo/db/catalog/index_catalog.cpp b/src/mongo/db/catalog/index_catalog.cpp
index dd4bd0814f7..b8567599410 100644
--- a/src/mongo/db/catalog/index_catalog.cpp
+++ b/src/mongo/db/catalog/index_catalog.cpp
@@ -153,7 +153,7 @@ namespace mongo {
fassertFailed(17198);
}
- Status IndexCatalog::_checkUnfinished() const {
+ Status IndexCatalog::checkUnfinished() const {
if ( _unfinishedIndexes.size() == 0 )
return Status::OK();
@@ -272,14 +272,40 @@ namespace mongo {
return StatusWith<BSONObj>( fixed );
}
- Status IndexCatalog::createIndex( OperationContext* txn,
- BSONObj spec,
- bool mayInterrupt,
- ShutdownBehavior shutdownBehavior ) {
+namespace {
+ class IndexCleanupOnRollback : public RecoveryUnit::Change {
+ public:
+ /**
+ * None of these pointers are owned by this class.
+ */
+ IndexCleanupOnRollback(Collection* collection,
+ IndexCatalogEntryContainer* entries,
+ const IndexDescriptor* desc)
+ : _collection(collection),
+ _entries(entries),
+ _desc(desc) {
+ }
+
+ virtual void commit() {}
+
+ virtual void rollback() {
+ _entries->remove(_desc);
+ _collection->infoCache()->reset();
+ }
+
+ private:
+ Collection* _collection;
+ IndexCatalogEntryContainer* _entries;
+ const IndexDescriptor* _desc;
+ };
+} // namespace
+
+ Status IndexCatalog::createIndexOnEmptyCollection(OperationContext* txn, BSONObj spec) {
txn->lockState()->assertWriteLocked( _collection->_database->name() );
+ invariant(_collection->numRecords() == 0);
_checkMagic();
- Status status = _checkUnfinished();
+ Status status = checkUnfinished();
if ( !status.isOK() )
return status;
@@ -307,55 +333,21 @@ namespace mongo {
invariant( entry );
IndexDescriptor* descriptor = entry->descriptor();
invariant( descriptor );
-
- string idxName = descriptor->indexName(); // out copy for yields, etc...
-
invariant( entry == _entries.find( descriptor ) );
- try {
- Client& client = cc();
-
- _inProgressIndexes[descriptor] = &client;
-
- // buildAnIndex can yield. During a yield, the Collection that owns this
- // IndexCatalog can be dropped, which means both the Collection and IndexCatalog
- // can be destructed out from under us. The runner used by the index build will
- // throw a particular exception when it detects that this occurred.
- buildAnIndex( txn, _collection, entry, mayInterrupt );
- indexBuildBlock.success();
-
- InProgressIndexesMap::iterator it = _inProgressIndexes.find(descriptor);
- _inProgressIndexes.erase(it);
-
- // sanity check
- invariant( _collection->getCatalogEntry()->isIndexReady( idxName ) );
-
- return Status::OK();
- }
- catch ( const AssertionException& exc ) {
- // At this point, *this may have been destructed, if we dropped the collection
- // while we were yielding. indexBuildBlock will not touch an invalid _collection
- // pointer if you call abort() on it.
-
- log() << "index build failed." << " spec: " << spec << " error: " << exc;
+ status = entry->accessMethod()->initializeAsEmpty(txn);
+ if (!status.isOK())
+ return status;
- if ( shutdownBehavior == SHUTDOWN_LEAVE_DIRTY &&
- exc.getCode() == ErrorCodes::InterruptedAtShutdown ) {
- indexBuildBlock.abort();
- }
- else if ( exc.getCode() == ErrorCodes::CursorNotFound ) {
- // The cursor was killed because the collection was dropped. No need to clean up.
- indexBuildBlock.abort();
- }
- else {
- indexBuildBlock.fail();
+ txn->recoveryUnit()->registerChange(new IndexCleanupOnRollback(_collection,
+ &_entries,
+ entry->descriptor()));
+ indexBuildBlock.success();
- InProgressIndexesMap::iterator it = _inProgressIndexes.find(descriptor);
- _inProgressIndexes.erase(it);
- }
+ // sanity check
+ invariant(_collection->getCatalogEntry()->isIndexReady(descriptor->indexName()));
- return exc.toStatus();
- }
+ return Status::OK();
}
IndexCatalog::IndexBuildBlock::IndexBuildBlock(OperationContext* txn,
@@ -420,24 +412,14 @@ namespace mongo {
}
void IndexCatalog::IndexBuildBlock::fail() {
- if ( !_inProgress ) {
- // taken care of already when success() is called
- return;
- }
-
- Client::Context context( _txn,
- _collection->ns().ns(),
- _collection->_database );
-
- // if we're here, the index build failed or was interrupted
+ try {
+ fassert( 17204, _catalog->_collection->ok() ); // defensive
- _inProgress = false; // defensive
- fassert( 17204, _catalog->_collection->ok() ); // defensive
+ _inProgress = false;
- IndexCatalogEntry* entry = _catalog->_entries.find( _indexName );
- invariant( entry == _entry );
+ IndexCatalogEntry* entry = _catalog->_entries.find( _indexName );
+ invariant( entry == _entry );
- try {
if ( entry ) {
_catalog->_dropIndex(_txn, entry);
}
@@ -451,15 +433,13 @@ namespace mongo {
error() << "exception while cleaning up in-progress index build: " << exc.what();
fassertFailedWithStatus(17493, exc.toStatus());
}
-
}
- void IndexCatalog::IndexBuildBlock::abort() {
+ void IndexCatalog::IndexBuildBlock::abortWithoutCleanup() {
_inProgress = false;
}
void IndexCatalog::IndexBuildBlock::success() {
-
fassert( 17206, _inProgress );
_inProgress = false;
@@ -475,7 +455,6 @@ namespace mongo {
fassert( 17331, entry && entry == _entry );
entry->setIsReady( true );
-
}
@@ -624,24 +603,14 @@ namespace mongo {
return Status::OK();
}
- Status IndexCatalog::ensureHaveIdIndex(OperationContext* txn) {
- if ( haveIdIndex() )
- return Status::OK();
-
+ BSONObj IndexCatalog::getDefaultIdIndexSpec() const {
dassert( _idObj["_id"].type() == NumberInt );
BSONObjBuilder b;
b.append( "name", "_id_" );
b.append( "ns", _collection->ns().ns() );
b.append( "key", _idObj );
- BSONObj o = b.done();
-
- Status s = createIndex(txn, o, false);
- if ( s.isOK() || s.code() == ErrorCodes::IndexAlreadyExists ) {
- return Status::OK();
- }
-
- return s;
+ return b.obj();
}
Status IndexCatalog::dropAllIndexes(OperationContext* txn,
@@ -747,7 +716,7 @@ namespace mongo {
return Status( ErrorCodes::BadValue, "IndexCatalog::_dropIndex passed NULL" );
_checkMagic();
- Status status = _checkUnfinished();
+ Status status = checkUnfinished();
if ( !status.isOK() )
return status;
@@ -1147,13 +1116,13 @@ namespace mongo {
if ( s == "_id" ) {
// skip
}
+ else if ( s == "dropDup" ) {
+ // dropDups is silently ignored and removed from the spec as of SERVER-14710.
+ }
else if ( s == "v" || s == "unique" ||
s == "key" || s == "name" ) {
// covered above
}
- else if ( s == "key" ) {
- b.append( "key", fixIndexKey( e.Obj() ) );
- }
else {
b.append(e);
}
@@ -1163,39 +1132,10 @@ namespace mongo {
return b.obj();
}
- std::vector<BSONObj>
- IndexCatalog::killMatchingIndexBuilds(const IndexCatalog::IndexKillCriteria& criteria) {
- std::vector<BSONObj> indexes;
- for (InProgressIndexesMap::iterator it = _inProgressIndexes.begin();
- it != _inProgressIndexes.end();
- it++) {
- // check criteria
- IndexDescriptor* desc = it->first;
- Client* client = it->second;
- if (!criteria.ns.empty() && (desc->parentNS() != criteria.ns)) {
- continue;
- }
- if (!criteria.name.empty() && (desc->indexName() != criteria.name)) {
- continue;
- }
- if (!criteria.key.isEmpty() && (desc->keyPattern() != criteria.key)) {
- continue;
- }
- indexes.push_back(desc->keyPattern());
- CurOp* op = client->curop();
- log() << "halting index build: " << desc->keyPattern();
- // Note that we can only be here if the background index build in question is
- // yielding. The bg index code is set up specially to check for interrupt
- // immediately after it recovers from yield, such that no further work is done
- // on the index build. Thus this thread does not have to synchronize with the
- // bg index operation; we can just assume that it is safe to proceed.
- getGlobalEnvironment()->killOperation(op->opNum());
- }
-
- if (indexes.size() > 0) {
- log() << "halted " << indexes.size() << " index build(s)" << endl;
- }
-
- return indexes;
+ std::vector<BSONObj> IndexCatalog::killMatchingIndexBuilds(
+ const IndexCatalog::IndexKillCriteria& criteria) {
+ // This is just a no-op stub. When SERVER-14860 is resolved, this will either be filled out
+ // or removed entirely.
+ return std::vector<BSONObj>();
}
}
diff --git a/src/mongo/db/catalog/index_catalog.h b/src/mongo/db/catalog/index_catalog.h
index 9d509b9ebbe..a541a8bae0a 100644
--- a/src/mongo/db/catalog/index_catalog.h
+++ b/src/mongo/db/catalog/index_catalog.h
@@ -73,6 +73,11 @@ namespace mongo {
bool haveIdIndex() const;
+ /**
+ * Returns the spec for the id index to create by default for this collection.
+ */
+ BSONObj getDefaultIdIndexSpec() const;
+
IndexDescriptor* findIdIndex() const;
/**
@@ -104,6 +109,12 @@ namespace mongo {
IndexAccessMethod* getIndex( const IndexDescriptor* desc );
const IndexAccessMethod* getIndex( const IndexDescriptor* desc ) const;
+ /**
+ * Returns a not-ok Status if there are any unfinished index builds. No new indexes should
+ * be built when in this state.
+ */
+ Status checkUnfinished() const;
+
class IndexIterator {
public:
bool more();
@@ -134,17 +145,11 @@ namespace mongo {
// ---- index set modifiers ------
- Status ensureHaveIdIndex(OperationContext* txn);
-
- enum ShutdownBehavior {
- SHUTDOWN_CLEANUP, // fully clean up this build
- SHUTDOWN_LEAVE_DIRTY // leave as if kill -9 happened, so have to deal with on restart
- };
-
- Status createIndex( OperationContext* txn,
- BSONObj spec,
- bool mayInterrupt,
- ShutdownBehavior shutdownBehavior = SHUTDOWN_CLEANUP );
+ /**
+ * Call this only on an empty collection from inside a WriteUnitOfWork. Index creation on an
+ * empty collection can be rolled back as part of a larger WUOW.
+ */
+ Status createIndexOnEmptyCollection(OperationContext* txn, BSONObj spec);
StatusWith<BSONObj> prepareSpecForCreate( OperationContext* txn,
const BSONObj& original ) const;
@@ -192,6 +197,7 @@ namespace mongo {
* 4) system.namespaces entry for index ns
*/
class IndexBuildBlock {
+ MONGO_DISALLOW_COPYING(IndexBuildBlock);
public:
IndexBuildBlock(OperationContext* txn,
Collection* collection,
@@ -212,12 +218,11 @@ namespace mongo {
* we're stopping the build
* do NOT cleanup, leave meta data as is
*/
- void abort();
+ void abortWithoutCleanup();
IndexCatalogEntry* getEntry() { return _entry; }
private:
-
Collection* _collection;
IndexCatalog* _catalog;
std::string _ns;
@@ -263,8 +268,6 @@ namespace mongo {
static BSONObj fixIndexKey( const BSONObj& key );
private:
- typedef unordered_map<IndexDescriptor*, Client*> InProgressIndexesMap;
-
bool _shouldOverridePlugin( OperationContext* txn, const BSONObj& keyPattern ) const;
/**
@@ -276,11 +279,6 @@ namespace mongo {
void _checkMagic() const;
-
- // checks if there is anything in _leftOverIndexes
- // meaning we shouldn't modify catalog
- Status _checkUnfinished() const;
-
Status _indexRecord(OperationContext* txn,
IndexCatalogEntry* index,
const BSONObj& obj,
@@ -326,9 +324,6 @@ namespace mongo {
std::vector<BSONObj> _unfinishedIndexes;
static const BSONObj _idObj; // { _id : 1 }
-
- // Track in-progress index builds, in order to find and stop them when necessary.
- InProgressIndexesMap _inProgressIndexes;
};
}
diff --git a/src/mongo/db/catalog/index_create.cpp b/src/mongo/db/catalog/index_create.cpp
index a483be3981b..de75707458f 100644
--- a/src/mongo/db/catalog/index_create.cpp
+++ b/src/mongo/db/catalog/index_create.cpp
@@ -41,7 +41,6 @@
#include "mongo/db/catalog/collection.h"
#include "mongo/db/clientcursor.h"
#include "mongo/db/curop.h"
-#include "mongo/db/pdfile_private.h"
#include "mongo/db/query/internal_plans.h"
#include "mongo/db/repl/oplog.h"
#include "mongo/db/repl/repl_coordinator_global.h"
@@ -54,262 +53,71 @@
namespace mongo {
/**
- * Add the provided (obj, dl) pair to the provided index.
+ * On rollback sets MultiIndexBlock::_needToCleanup to true.
*/
- static void addKeysToIndex(OperationContext* txn,
- Collection* collection,
- const IndexDescriptor* descriptor,
- IndexAccessMethod* accessMethod,
- const BSONObj& obj, const DiskLoc &recordLoc ) {
-
- InsertDeleteOptions options;
- options.logIfError = false;
- options.dupsAllowed = true;
-
- if ( descriptor->isIdIndex() || descriptor->unique() ) {
- if (!repl::getGlobalReplicationCoordinator()->shouldIgnoreUniqueIndex(descriptor)) {
- options.dupsAllowed = false;
- }
- }
-
- int64_t inserted;
- Status ret = accessMethod->insert(txn, obj, recordLoc, options, &inserted);
- uassertStatusOK( ret );
- }
-
- unsigned long long addExistingToIndex( OperationContext* txn,
- Collection* collection,
- const IndexDescriptor* descriptor,
- IndexAccessMethod* accessMethod,
- bool canBeKilled ) {
-
- string ns = collection->ns().ns(); // our copy for sanity
-
- bool dupsAllowed = !descriptor->unique();
- bool dropDups = descriptor->dropDups();
-
- string curopMessage;
- {
- stringstream ss;
- ss << "Index Build";
- if ( canBeKilled )
- ss << "(background)";
- curopMessage = ss.str();
- }
-
- ProgressMeter* progress = txn->setMessage(curopMessage.c_str(),
- curopMessage,
- collection->numRecords());
-
- unsigned long long n = 0;
- unsigned long long numDropped = 0;
-
- auto_ptr<PlanExecutor> exec(InternalPlanner::collectionScan(txn,ns,collection));
+ class MultiIndexBlock::SetNeedToCleanupOnRollback : public RecoveryUnit::Change {
+ public:
+ explicit SetNeedToCleanupOnRollback(MultiIndexBlock* indexer) : _indexer(indexer) {}
- std::string idxName = descriptor->indexName();
+ virtual void commit() {}
+ virtual void rollback() { _indexer->_needToCleanup = true; }
- // After this yields in the loop, idx may point at a different index (if indexes get
- // flipped, see insert_makeIndex) or even an empty IndexDetails, so nothing below should
- // depend on idx. idxNo should be recalculated after each yield.
+ private:
+ MultiIndexBlock* const _indexer;
+ };
- BSONObj js;
- DiskLoc loc;
- while (PlanExecutor::ADVANCED == exec->getNext(&js, &loc)) {
- try {
- if ( !dupsAllowed && dropDups ) {
- LastError::Disabled led( lastError.get() );
- addKeysToIndex(txn, collection, descriptor, accessMethod, js, loc);
- }
- else {
- addKeysToIndex(txn, collection, descriptor, accessMethod, js, loc);
- }
- }
- catch( AssertionException& e ) {
- if (ErrorCodes::isInterruption(DBException::convertExceptionCode(e.getCode()))) {
- txn->checkForInterrupt();
- }
-
- // TODO: Does exception really imply dropDups exception?
- if (dropDups) {
- bool execEOF = exec->isEOF();
- exec->saveState();
- BSONObj toDelete;
- collection->deleteDocument( txn, loc, false, true, &toDelete );
- repl::logOp(txn, "d", ns.c_str(), toDelete);
-
- if (!exec->restoreState(txn)) {
- // PlanExecutor got killed somehow. This probably shouldn't happen.
- if (execEOF) {
- // Quote: "We were already at the end. Normal.
- // TODO: Why is this normal?
- }
- else {
- uasserted(ErrorCodes::CursorNotFound,
- "cursor gone during bg index; dropDups");
- }
- break;
- }
- // We deleted a record, but we didn't actually yield the dblock.
- // TODO: Why did the old code assume we yielded the lock?
- numDropped++;
- }
- else {
- log() << "background addExistingToIndex exception " << e.what() << endl;
- throw;
- }
- }
-
- n++;
- progress->hit();
-
- txn->recoveryUnit()->commitIfNeeded();
-
- if (canBeKilled) {
- // Checking for interrupt here is necessary because the bg index
- // interruptors can only interrupt this index build while they hold
- // a write lock, and yieldAndCheckIfOK only checks for
- // interrupt prior to yielding our write lock. We need to check the kill flag
- // here before another iteration of the loop.
- txn->checkForInterrupt();
- }
-
- progress->setTotalWhileRunning( collection->numRecords() );
- }
-
- progress->finished();
- if ( dropDups && numDropped )
- log() << "\t index build dropped: " << numDropped << " dups";
- return n;
+ MultiIndexBlock::MultiIndexBlock(OperationContext* txn, Collection* collection)
+ : _collection(collection),
+ _txn(txn),
+ _buildInBackground(false),
+ _allowInterruption(false),
+ _ignoreUnique(false),
+ _needToCleanup(true) {
}
- // ---------------------------
-
- // throws DBException
- void buildAnIndex( OperationContext* txn,
- Collection* collection,
- IndexCatalogEntry* btreeState,
- bool mayInterrupt ) {
-
- const string ns = collection->ns().ns(); // our copy
- verify(txn->lockState()->isWriteLocked(ns));
-
- const IndexDescriptor* idx = btreeState->descriptor();
- const BSONObj& idxInfo = idx->infoObj();
-
- LOG(0) << "build index on: " << ns
- << " properties: " << idx->toString() << endl;
- audit::logCreateIndex( currentClient.get(), &idxInfo, idx->indexName(), ns );
-
- Timer t;
-
- // this is so that people know there are more keys to look at when doing
- // things like in place updates, etc...
- collection->infoCache()->addedIndex();
+ MultiIndexBlock::~MultiIndexBlock() {
+ if (!_needToCleanup || _indexes.empty())
+ return;
- if ( collection->numRecords() == 0 ) {
- Status status = btreeState->accessMethod()->initializeAsEmpty(txn);
- massert( 17343,
- str::stream() << "IndexAccessMethod::initializeAsEmpty failed" << status.toString(),
- status.isOK() );
- LOG(0) << "\t added index to empty collection";
+ try {
+ WriteUnitOfWork wunit(_txn->recoveryUnit());
+ // This cleans up all index builds. Because that may need to write, it is done inside
+ // of a WUOW. Nothing inside this block can fail, and it is made fatal if it does.
+ for (size_t i = 0; i < _indexes.size(); i++) {
+ _indexes[i].block->fail();
+ }
+ wunit.commit();
return;
}
-
- scoped_ptr<BackgroundOperation> backgroundOperation;
- bool doInBackground = false;
-
- if ( idxInfo["background"].trueValue() && !inDBRepair ) {
- doInBackground = true;
- backgroundOperation.reset( new BackgroundOperation(ns) );
- uassert( 13130,
- "can't start bg index b/c in recursive lock (db.eval?)",
- !txn->lockState()->isRecursive() );
- log() << "\t building index in background";
+ catch (const std::exception& e) {
+ error() << "Caught exception while cleaning up partially built indexes: " << e.what();
}
+ catch (...) {
+ error() << "Caught unknown exception while cleaning up partially built indexes.";
+ }
+ fassertFailed(18644);
+ }
- Status status = btreeState->accessMethod()->initializeAsEmpty(txn);
- massert( 17342,
- str::stream()
- << "IndexAccessMethod::initializeAsEmpty failed"
- << status.toString(),
- status.isOK() );
-
- IndexAccessMethod* bulk = doInBackground ?
- NULL : btreeState->accessMethod()->initiateBulk(txn);
- scoped_ptr<IndexAccessMethod> bulkHolder(bulk);
- IndexAccessMethod* iam = bulk ? bulk : btreeState->accessMethod();
-
- if ( bulk )
- log() << "\t building index using bulk method";
-
- unsigned long long n = addExistingToIndex( txn,
- collection,
- btreeState->descriptor(),
- iam,
- doInBackground );
-
- if ( bulk ) {
- LOG(1) << "\t bulk commit starting";
- std::set<DiskLoc> dupsToDrop;
-
- Status status = btreeState->accessMethod()->commitBulk( bulk,
- mayInterrupt,
- &dupsToDrop );
-
- // Code above us expects a uassert in case of dupkey errors.
- if (ErrorCodes::DuplicateKey == status.code()) {
- uassertStatusOK(status);
- }
-
- // Any other errors are probably bad and deserve a massert.
- massert( 17398,
- str::stream() << "commitBulk failed: " << status.toString(),
- status.isOK() );
-
- if ( dupsToDrop.size() )
- log() << "\t bulk dropping " << dupsToDrop.size() << " dups";
-
- for( set<DiskLoc>::const_iterator i = dupsToDrop.begin(); i != dupsToDrop.end(); ++i ) {
- BSONObj toDelete;
- collection->deleteDocument( txn,
- *i,
- false /* cappedOk */,
- true /* noWarn */,
- &toDelete );
- if (repl::getGlobalReplicationCoordinator()->canAcceptWritesForDatabase(
- collection->ns().db())) {
- repl::logOp(txn, "d", ns.c_str(), toDelete);
- }
-
- txn->recoveryUnit()->commitIfNeeded();
-
- RARELY if ( mayInterrupt ) {
- txn->checkForInterrupt();
- }
+ void MultiIndexBlock::removeExistingIndexes(std::vector<BSONObj>* specs) const {
+ for (size_t i = 0; i < specs->size(); i++) {
+ Status status =
+ _collection->getIndexCatalog()->prepareSpecForCreate(_txn, (*specs)[i]).getStatus();
+ if (status.code() == ErrorCodes::IndexAlreadyExists) {
+ specs->erase(specs->begin() + i);
+ i--;
}
+ // intentionally ignoring other error codes
}
-
- LOG(0) << "build index done. scanned " << n << " total records. "
- << t.millis() / 1000.0 << " secs" << endl;
-
- // this one is so people know that the index is finished
- collection->infoCache()->addedIndex();
}
- // ----------------------------
+ Status MultiIndexBlock::init(const std::vector<BSONObj>& indexSpecs) {
+ WriteUnitOfWork wunit(_txn->recoveryUnit());
+ const string& ns = _collection->ns().ns();
- MultiIndexBlock::MultiIndexBlock(OperationContext* txn, Collection* collection)
- : _collection(collection), _txn(txn) {
- }
+ Status status = _collection->getIndexCatalog()->checkUnfinished();
+ if ( !status.isOK() )
+ return status;
- MultiIndexBlock::~MultiIndexBlock() {
- for ( size_t i = 0; i < _states.size(); i++ ) {
- delete _states[i].bulk;
- delete _states[i].block;
- }
- }
-
- Status MultiIndexBlock::init(std::vector<BSONObj>& indexSpecs) {
for ( size_t i = 0; i < indexSpecs.size(); i++ ) {
BSONObj info = indexSpecs[i];
@@ -321,6 +129,8 @@ namespace mongo {
return s;
}
+ // Any foreground indexes make all indexes be build in the foreground.
+ _buildInBackground = (_buildInBackground && info["background"].trueValue());
}
for ( size_t i = 0; i < indexSpecs.size(); i++ ) {
@@ -332,57 +142,162 @@ namespace mongo {
return status;
info = statusWithInfo.getValue();
- IndexState state;
- state.block = new IndexCatalog::IndexBuildBlock(_txn, _collection, info);
- status = state.block->init();
+ IndexToBuild index;
+ index.block = boost::make_shared<IndexCatalog::IndexBuildBlock>(_txn,
+ _collection,
+ info);
+ status = index.block->init();
if ( !status.isOK() )
return status;
- state.real = state.block->getEntry()->accessMethod();
- status = state.real->initializeAsEmpty(_txn);
+ index.real = index.block->getEntry()->accessMethod();
+ status = index.real->initializeAsEmpty(_txn);
if ( !status.isOK() )
return status;
- state.bulk = state.real->initiateBulk(_txn);
+ if (!_buildInBackground) {
+ // Bulk build process requires foreground building as it assumes nothing is changing
+ // under it.
+ // TODO SERVER-14860 make background not just be a slower foreground.
+ index.bulk.reset(index.real->initiateBulk(_txn));
+ }
+
+ const IndexDescriptor* descriptor = index.block->getEntry()->descriptor();
+
+ index.options.logIfError = false; // logging happens elsewhere if needed.
+ index.options.dupsAllowed = !descriptor->unique()
+ || _ignoreUnique
+ || repl::getGlobalReplicationCoordinator()
+ ->shouldIgnoreUniqueIndex(descriptor);
- _states.push_back( state );
+ log() << "build index on: " << ns << " properties: " << descriptor->toString();
+ if (index.bulk)
+ log() << "\t building index using bulk method";
+
+ // TODO SERVER-14888 Suppress this in cases we don't want to audit.
+ audit::logCreateIndex(_txn->getClient(), &info, descriptor->indexName(), ns);
+
+ _indexes.push_back( index );
}
+ // this is so that operations examining the list of indexes know there are more keys to look
+ // at when doing things like in place updates, etc...
+ _collection->infoCache()->addedIndex();
+
+ if (_buildInBackground)
+ _backgroundOperation.reset(new BackgroundOperation(ns));
+
+ wunit.commit();
return Status::OK();
}
- Status MultiIndexBlock::insert( const BSONObj& doc,
- const DiskLoc& loc,
- const InsertDeleteOptions& options ) {
+ Status MultiIndexBlock::insertAllDocumentsInCollection(std::set<DiskLoc>* dupsOut) {
+ const char* curopMessage = _buildInBackground ? "Index Build (background)" : "Index Build";
+ ProgressMeter* progress = _txn->setMessage(curopMessage,
+ curopMessage,
+ _collection->numRecords());
+
+ Timer t;
+
+ unsigned long long n = 0;
+ scoped_ptr<PlanExecutor> exec(InternalPlanner::collectionScan(_txn,
+ _collection->ns().ns(),
+ _collection));
+
+ BSONObj objToIndex;
+ DiskLoc loc;
+ while (PlanExecutor::ADVANCED == exec->getNext(&objToIndex, &loc)) {
+ {
+ bool shouldCommitWUnit = true;
+ WriteUnitOfWork wunit(_txn->recoveryUnit());
+ Status ret = insert(objToIndex, loc);
+ if (!ret.isOK()) {
+ if (dupsOut && ret.code() == ErrorCodes::DuplicateKey) {
+ // If dupsOut is non-null, we should only fail the specific insert that
+ // led to a DuplicateKey rather than the whole index build.
+ dupsOut->insert(loc);
+ shouldCommitWUnit = false;
+ }
+ else {
+ return ret;
+ }
+ }
- for ( size_t i = 0; i < _states.size(); i++ ) {
- Status idxStatus = _states[i].forInsert()->insert( _txn,
+ if (shouldCommitWUnit)
+ wunit.commit();
+ }
+
+ n++;
+ progress->hit();
+
+ if (_allowInterruption)
+ _txn->checkForInterrupt();
+
+ progress->setTotalWhileRunning( _collection->numRecords() );
+ }
+
+ progress->finished();
+
+ Status ret = doneInserting(dupsOut);
+ if (!ret.isOK())
+ return ret;
+
+ log() << "build index done. scanned " << n << " total records. "
+ << t.seconds() << " secs" << endl;
+
+ return Status::OK();
+ }
+
+ Status MultiIndexBlock::insert(const BSONObj& doc, const DiskLoc& loc) {
+ for ( size_t i = 0; i < _indexes.size(); i++ ) {
+ int64_t unused;
+ Status idxStatus = _indexes[i].forInsert()->insert( _txn,
doc,
loc,
- options,
- NULL );
+ _indexes[i].options,
+ &unused );
if ( !idxStatus.isOK() )
return idxStatus;
}
return Status::OK();
}
- Status MultiIndexBlock::commit() {
- for ( size_t i = 0; i < _states.size(); i++ ) {
- if ( _states[i].bulk == NULL )
+ Status MultiIndexBlock::doneInserting(std::set<DiskLoc>* dupsOut) {
+ for ( size_t i = 0; i < _indexes.size(); i++ ) {
+ if ( _indexes[i].bulk == NULL )
continue;
- Status status = _states[i].real->commitBulk( _states[i].bulk,
- false,
- NULL );
- if ( !status.isOK() )
+ LOG(1) << "\t bulk commit starting for index: "
+ << _indexes[i].block->getEntry()->descriptor()->indexName();
+ Status status = _indexes[i].real->commitBulk( _indexes[i].bulk.get(),
+ _allowInterruption,
+ _indexes[i].options.dupsAllowed,
+ dupsOut );
+ if ( !status.isOK() ) {
return status;
+ }
+ }
+
+ return Status::OK();
+ }
+
+ void MultiIndexBlock::abortWithoutCleanup() {
+ for ( size_t i = 0; i < _indexes.size(); i++ ) {
+ _indexes[i].block->abortWithoutCleanup();
}
+ _indexes.clear();
+ _needToCleanup = false;
+ }
- for ( size_t i = 0; i < _states.size(); i++ ) {
- _states[i].block->success();
+ void MultiIndexBlock::commit() {
+ for ( size_t i = 0; i < _indexes.size(); i++ ) {
+ _indexes[i].block->success();
}
- return Status::OK();
+ // this one is so operations examining the list of indexes know that the index is finished
+ _collection->infoCache()->addedIndex();
+
+ _txn->recoveryUnit()->registerChange(new SetNeedToCleanupOnRollback(this));
+ _needToCleanup = false;
}
} // namespace mongo
diff --git a/src/mongo/db/catalog/index_create.h b/src/mongo/db/catalog/index_create.h
index 0bfd413c935..bd252d885a9 100644
--- a/src/mongo/db/catalog/index_create.h
+++ b/src/mongo/db/catalog/index_create.h
@@ -30,6 +30,7 @@
#pragma once
+#include <set>
#include <string>
#include <vector>
@@ -40,53 +41,167 @@
namespace mongo {
+ class BackgroundOperation;
class BSONObj;
class Collection;
- class IndexCatalogEntry;
class OperationContext;
- // Build an index in the foreground
- // If background is false, uses fast index builder
- // If background is true, uses background index builder; blocks until done.
- void buildAnIndex( OperationContext* txn,
- Collection* collection,
- IndexCatalogEntry* btreeState,
- bool mayInterrupt );
-
+ /**
+ * Builds one or more indexes.
+ *
+ * If any method other than insert() returns a not-ok Status, this MultiIndexBlock should be
+ * considered failed and must be destroyed.
+ *
+ * If a MultiIndexBlock is destroyed before commit() or if commit() is rolled back, it will
+ * clean up all traces of the indexes being constructed. MultiIndexBlocks should not be
+ * destructed from inside of a WriteUnitOfWork as any cleanup needed should never be rolled back
+ * (as it is itself essentially a form of rollback, you don't want to "rollback the rollback").
+ */
class MultiIndexBlock {
MONGO_DISALLOW_COPYING( MultiIndexBlock );
public:
- MultiIndexBlock(OperationContext* txn,
- Collection* collection );
- ~MultiIndexBlock();
+ /**
+ * Neither pointer is owned.
+ */
+ MultiIndexBlock(OperationContext* txn, Collection* collection);
+
+ /**
+ * By default we ignore the 'background' flag in specs when building an index. If this is
+ * called before init(), we will build the indexes in the background as long as *all* specs
+ * call for background indexing. If any spec calls for foreground indexing all indexes will
+ * be built in the foreground, as there is no concurrency benefit to building a subset of
+ * indexes in the background, but there is a performance benefit to building all in the
+ * foreground.
+ */
+ void allowBackgroundBuilding() { _buildInBackground = true; }
+
+ /**
+ * Call this before init() to allow the index build to be interrupted.
+ * This only affects builds using the insertAllDocumentsInCollection helper.
+ */
+ void allowInterruption() { _allowInterruption = true; }
+
+ /**
+ * By default we enforce the 'unique' flag in specs when building an index by failing.
+ * If this is called before init(), we will ignore unique violations. This has no effect if
+ * no specs are unique.
+ *
+ * If this is called, any dupsOut sets passed in will never be filled.
+ */
+ void ignoreUniqueConstraint() { _ignoreUnique = true; }
+
+ /**
+ * Removes pre-existing indexes from 'specs'. If this isn't done, init() may fail with
+ * IndexAlreadyExists.
+ */
+ void removeExistingIndexes(std::vector<BSONObj>* specs) const;
+
+ /**
+ * Prepares the index(es) for building.
+ *
+ * Does not need to be called inside of a WriteUnitOfWork (but can be due to nesting).
+ */
+ Status init(const std::vector<BSONObj>& specs);
+ Status init(const BSONObj& spec) {
+ return init(std::vector<BSONObj>(1, spec));
+ }
+
+ /**
+ * Inserts all documents in the Collection into the indexes and logs with timing info.
+ *
+ * This is a simplified replacement for insert and doneInserting. Do not call this if you
+ * are calling either of them.
+ *
+ * If dupsOut is passed as non-NULL, violators of uniqueness constraints will be added to
+ * the set rather than failing the build. Documents added to this set are not indexed, so
+ * callers MUST either fail this index build or delete the documents from the collection.
+ *
+ * Can throw an exception if interrupted.
+ *
+ * Should not be called inside of a WriteUnitOfWork.
+ */
+ Status insertAllDocumentsInCollection(std::set<DiskLoc>* dupsOut = NULL);
+
+ /**
+ * Call this after init() for each document in the collection.
+ *
+ * Do not call if you called insertAllDocumentsInCollection();
+ *
+ * Should be called inside of a WriteUnitOfWork.
+ */
+ Status insert(const BSONObj& wholeDocument, const DiskLoc& loc);
+
+ /**
+ * Call this after the last insert(). This gives the index builder a chance to do any
+ * long-running operations in separate units of work from commit().
+ *
+ * Do not call if you called insertAllDocumentsInCollection();
+ *
+ * If dupsOut is passed as non-NULL, violators of uniqueness constraints will be added to
+ * the set. Documents added to this set are not indexed, so callers MUST either fail this
+ * index build or delete the documents from the collection.
+ *
+ * Should not be called inside of a WriteUnitOfWork.
+ */
+ Status doneInserting(std::set<DiskLoc>* dupsOut = NULL);
+
+ /**
+ * Marks the index ready for use. Should only be called as the last method after
+ * doneInserting() or insertAllDocumentsInCollection() return success.
+ *
+ * Should be called inside of a WriteUnitOfWork. If the index building is to be logOp'd,
+ * logOp() should be called from the same unit of work as commit().
+ */
+ void commit();
+
+ /**
+ * May be called at any time after construction but before a successful commit(). Suppresses
+ * the default behavior on destruction of removing all traces of uncommitted index builds.
+ *
+ * The most common use of this is if the indexes were already dropped via some other
+ * mechanism such as the whole collection being dropped. In that case, it would be invalid
+ * to try to remove the indexes again. Also, replication uses this to ensure that indexes
+ * that are being built on shutdown are resumed on startup.
+ *
+ * Do not use this unless you are really sure you need to.
+ *
+ * Does not matter whether it is called inside of a WriteUnitOfWork. Will not be rolled
+ * back.
+ */
+ void abortWithoutCleanup();
- Status init( std::vector<BSONObj>& specs );
+ ~MultiIndexBlock();
- Status insert( const BSONObj& doc,
- const DiskLoc& loc,
- const InsertDeleteOptions& options );
+ private:
+ class SetNeedToCleanupOnRollback;
- Status commit();
+ struct IndexToBuild {
+ IndexToBuild() : real(NULL) {}
- private:
- Collection* _collection;
+ IndexAccessMethod* forInsert() { return bulk ? bulk.get() : real; }
- struct IndexState {
- IndexState()
- : block( NULL ), real( NULL ), bulk( NULL ) {
- }
+ boost::shared_ptr<IndexCatalog::IndexBuildBlock> block;
- IndexAccessMethod* forInsert() { return bulk ? bulk : real; }
+ IndexAccessMethod* real; // owned elsewhere
+ boost::shared_ptr<IndexAccessMethod> bulk;
- IndexCatalog::IndexBuildBlock* block;
- IndexAccessMethod* real;
- IndexAccessMethod* bulk;
+ InsertDeleteOptions options;
};
- std::vector<IndexState> _states;
+ std::vector<IndexToBuild> _indexes;
- // Not owned here, must outlive 'this'
+ boost::scoped_ptr<BackgroundOperation> _backgroundOperation;
+
+
+ // Pointers not owned here and must outlive 'this'
+ Collection* _collection;
OperationContext* _txn;
+
+ bool _buildInBackground;
+ bool _allowInterruption;
+ bool _ignoreUnique;
+
+ bool _needToCleanup;
};
} // namespace mongo
diff --git a/src/mongo/db/cloner.cpp b/src/mongo/db/cloner.cpp
index 7bf725ca60b..d4cd38a2c8e 100644
--- a/src/mongo/db/cloner.cpp
+++ b/src/mongo/db/cloner.cpp
@@ -40,6 +40,7 @@
#include "mongo/db/auth/resource_pattern.h"
#include "mongo/db/auth/authorization_session.h"
#include "mongo/db/catalog/collection.h"
+#include "mongo/db/catalog/index_create.h"
#include "mongo/db/cloner.h"
#include "mongo/db/commands.h"
#include "mongo/db/commands/copydb.h"
@@ -183,14 +184,12 @@ namespace mongo {
NamespaceString from_collection;
NamespaceString to_collection;
time_t saveLast;
- list<BSONObj> *indexesToBuild; // deferred query results (e.g. index insert/build)
bool logForRepl;
bool _mayYield;
bool _mayBeInterrupted;
};
/* copy the specified collection
- isindex - if true, this is system.indexes collection, in which we do some transformation when copying.
*/
void Cloner::copy(OperationContext* txn,
const string& toDBName,
@@ -202,8 +201,6 @@ namespace mongo {
bool mayYield,
bool mayBeInterrupted,
Query query) {
-
- list<BSONObj> indexesToBuild;
LOG(2) << "\t\tcloning collection " << from_collection << " to " << to_collection << " on " << _conn->getServerAddress() << " with filter " << query.toString() << endl;
Fun f(txn, toDBName);
@@ -211,7 +208,6 @@ namespace mongo {
f.from_collection = from_collection;
f.to_collection = to_collection;
f.saveLast = time( 0 );
- f.indexesToBuild = &indexesToBuild;
f.logForRepl = logForRepl;
f._mayYield = mayYield;
f._mayBeInterrupted = mayBeInterrupted;
@@ -222,42 +218,6 @@ namespace mongo {
_conn->query(stdx::function<void(DBClientCursorBatchIterator &)>(f), from_collection,
query, 0, options);
}
-
- // We are under lock here again, so reload the database in case it may have disappeared
- // during the temp release
- bool unused;
- Database* db = dbHolder().getOrCreate(txn, toDBName, unused);
-
- if ( indexesToBuild.size() ) {
- for (list<BSONObj>::const_iterator i = indexesToBuild.begin();
- i != indexesToBuild.end();
- ++i) {
-
- BSONObj spec = *i;
- string ns = spec["ns"].String(); // this was fixed when pulled off network
- Collection* collection = db->getCollection( txn, ns );
- if ( !collection ) {
- collection = db->createCollection( txn, ns );
- verify( collection );
- }
-
- Status status = collection->getIndexCatalog()->createIndex(txn, spec, mayBeInterrupted);
- if ( status.code() == ErrorCodes::IndexAlreadyExists ) {
- // no-op
- }
- else if ( !status.isOK() ) {
- error() << "error creating index when cloning spec: " << spec
- << " error: " << status.toString();
- uassertStatusOK( status );
- }
-
- if (logForRepl)
- repl::logOp(txn, "i", to_collection.ns().c_str(), spec);
-
- txn->recoveryUnit()->commitIfNeeded();
-
- }
- }
}
void Cloner::copyIndexes(OperationContext* txn,
@@ -273,76 +233,59 @@ namespace mongo {
LOG(2) << "\t\t copyIndexes " << from_collection << " to " << to_collection
<< " on " << _conn->getServerAddress();
- list<BSONObj> indexesToBuild;
+ vector<BSONObj> indexesToBuild;
{
Lock::TempRelease tempRelease(txn->lockState());
- indexesToBuild = _conn->getIndexSpecs( from_collection,
- slaveOk ? QueryOption_SlaveOk : 0 );
+ list<BSONObj> sourceIndexes = _conn->getIndexSpecs( from_collection,
+ slaveOk ? QueryOption_SlaveOk : 0 );
+ for (list<BSONObj>::const_iterator it = sourceIndexes.begin();
+ it != sourceIndexes.end(); ++it) {
+ indexesToBuild.push_back(fixindex(to_collection.db().toString(), *it));
+ }
}
+ if (indexesToBuild.empty())
+ return;
+
// We are under lock here again, so reload the database in case it may have disappeared
// during the temp release
bool unused;
Database* db = dbHolder().getOrCreate(txn, toDBName, unused);
- if ( indexesToBuild.size() ) {
- for (list<BSONObj>::const_iterator i = indexesToBuild.begin();
- i != indexesToBuild.end();
- ++i) {
-
- BSONObj spec = fixindex( to_collection.db().toString(), *i );
- string ns = spec["ns"].String();
- Collection* collection = db->getCollection( txn, ns );
- if ( !collection ) {
- collection = db->createCollection( txn, ns );
- verify( collection );
- }
-
- Status status = collection->getIndexCatalog()->createIndex(txn,
- spec,
- mayBeInterrupted);
- if ( status.code() == ErrorCodes::IndexAlreadyExists ) {
- // no-op
- }
- else if ( !status.isOK() ) {
- error() << "error creating index when cloning spec: " << spec
- << " error: " << status.toString();
- uassertStatusOK( status );
- }
+ Collection* collection = db->getCollection( txn, to_collection );
+ if ( !collection ) {
+ WriteUnitOfWork wunit(txn->recoveryUnit());
+ collection = db->createCollection( txn, to_collection.ns() );
+ invariant(collection);
+ wunit.commit();
+ }
- if (logForRepl)
- repl::logOp(txn, "i", to_collection.ns().c_str(), spec);
+ // TODO pass the MultiIndexBlock when inserting into the collection rather than building the
+ // indexes after the fact. This depends on holding a lock on the collection the whole time
+ // from creation to completion without yielding to ensure the index and the collection
+ // matches. It also wouldn't work on non-empty collections so we would need both
+ // implementations anyway as long as that is supported.
+ MultiIndexBlock indexer(txn, collection);
+ if (mayBeInterrupted)
+ indexer.allowInterruption();
- txn->recoveryUnit()->commitIfNeeded();
+ indexer.removeExistingIndexes(&indexesToBuild);
+ if (indexesToBuild.empty())
+ return;
- }
- }
- }
+ uassertStatusOK(indexer.init(indexesToBuild));
+ uassertStatusOK(indexer.insertAllDocumentsInCollection());
- /**
- * validate the cloner query was successful
- * @param cur Cursor the query was executed on
- * @param errCode out Error code encountered during the query
- * @param errmsg out Error message encountered during the query
- */
- bool validateQueryResults(const auto_ptr<DBClientCursor>& cur,
- int32_t* errCode,
- string& errmsg) {
- if ( cur.get() == 0 )
- return false;
- if ( cur->more() ) {
- BSONObj first = cur->next();
- BSONElement errField = getErrField(first);
- if(!errField.eoo()) {
- errmsg = errField.str();
- if (errCode)
- *errCode = first.getIntField("code");
- return false;
+ WriteUnitOfWork wunit(txn->recoveryUnit());
+ indexer.commit();
+ if (logForRepl) {
+ for (vector<BSONObj>::const_iterator it = indexesToBuild.begin();
+ it != indexesToBuild.end(); ++it) {
+ repl::logOp(txn, "i", to_collection.ns().c_str(), *it);
}
- cur->putBack(first);
}
- return true;
+ wunit.commit();
}
bool Cloner::copyCollection(OperationContext* txn,
@@ -356,7 +299,6 @@ namespace mongo {
const NamespaceString nss(ns);
Lock::DBWrite dbWrite(txn->lockState(), nss.db());
- WriteUnitOfWork wunit(txn->recoveryUnit());
const string dbName = nss.db().toString();
@@ -367,11 +309,13 @@ namespace mongo {
string temp = dbName + ".system.namespaces";
BSONObj config = _conn->findOne(temp , BSON("name" << ns));
if (config["options"].isABSONObj()) {
+ WriteUnitOfWork wunit(txn->recoveryUnit());
Status status = userCreateNS(txn, db, ns, config["options"].Obj(), logForRepl, 0);
if ( !status.isOK() ) {
errmsg = status.toString();
return false;
}
+ wunit.commit();
}
// main data
@@ -391,13 +335,9 @@ namespace mongo {
logForRepl, false, true, mayYield,
mayBeInterrupted);
- wunit.commit();
- txn->recoveryUnit()->commitIfNeeded();
return true;
}
- extern bool inDBRepair;
-
bool Cloner::go(OperationContext* txn,
const std::string& toDBName,
const string& masterHost,
@@ -457,7 +397,7 @@ namespace mongo {
return false;
if (!repl::replAuthenticate(con.get()))
return false;
-
+
_conn = con;
}
else {
@@ -535,20 +475,26 @@ namespace mongo {
NamespaceString from_name( opts.fromDB, collectionName );
NamespaceString to_name( toDBName, collectionName );
- // Copy releases the lock, so we need to re-load the database. This should probably
- // throw if the database has changed in between, but for now preserve the existing
- // behaviour.
- bool unused;
- Database* db = dbHolder().getOrCreate(txn, toDBName, unused);
-
- /* we defer building id index for performance - building it in batch is much faster */
- Status createStatus = userCreateNS( txn, db, to_name.ns(), options,
- opts.logForRepl, false );
- if ( !createStatus.isOK() ) {
- errmsg = str::stream() << "failed to create collection \""
- << to_name.ns() << "\": "
- << createStatus.reason();
- return false;
+ Database* db;
+ {
+ WriteUnitOfWork wunit(txn->recoveryUnit());
+ // Copy releases the lock, so we need to re-load the database. This should
+ // probably throw if the database has changed in between, but for now preserve
+ // the existing behaviour.
+ bool unused;
+ db = dbHolder().getOrCreate(txn, toDBName, unused);
+
+ // we defer building id index for performance - building it in batch is much
+ // faster
+ Status createStatus = userCreateNS( txn, db, to_name.ns(), options,
+ opts.logForRepl, false );
+ if ( !createStatus.isOK() ) {
+ errmsg = str::stream() << "failed to create collection \""
+ << to_name.ns() << "\": "
+ << createStatus.reason();
+ return false;
+ }
+ wunit.commit();
}
LOG(1) << "\t\t cloning " << from_name << " -> " << to_name << endl;
@@ -567,27 +513,53 @@ namespace mongo {
opts.mayBeInterrupted,
q);
- {
- /* we need dropDups to be true as we didn't do a true snapshot and this is before applying oplog operations
- that occur during the initial sync. inDBRepair makes dropDups be true.
- */
- bool old = inDBRepair;
- try {
- inDBRepair = true;
- Collection* c = db->getCollection( txn, to_name );
- if ( c )
- c->getIndexCatalog()->ensureHaveIdIndex(txn);
- inDBRepair = old;
+ db = dbHolder().get(txn, toDBName);
+ uassert(18645,
+ str::stream() << "database " << toDBName << " dropped during clone",
+ db);
+
+ Collection* c = db->getCollection( txn, to_name );
+ if ( c && !c->getIndexCatalog()->haveIdIndex() ) {
+ // We need to drop objects with duplicate _ids because we didn't do a true
+ // snapshot and this is before applying oplog operations that occur during the
+ // initial sync.
+ set<DiskLoc> dups;
+
+ MultiIndexBlock indexer(txn, c);
+ if (opts.mayBeInterrupted)
+ indexer.allowInterruption();
+
+ uassertStatusOK(indexer.init(c->getIndexCatalog()->getDefaultIdIndexSpec()));
+ uassertStatusOK(indexer.insertAllDocumentsInCollection(&dups));
+
+ for (set<DiskLoc>::const_iterator it = dups.begin(); it != dups.end(); ++it) {
+ WriteUnitOfWork wunit(txn->recoveryUnit());
+ BSONObj id;
+
+ c->deleteDocument(txn, *it, true, true, opts.logForRepl ? &id : NULL);
+ if (opts.logForRepl)
+ repl::logOp(txn, "d", c->ns().ns().c_str(), id);
+ wunit.commit();
}
- catch(...) {
- inDBRepair = old;
- throw;
+
+ if (!dups.empty()) {
+ log() << "index build dropped: " << dups.size() << " dups";
+ }
+
+ WriteUnitOfWork wunit(txn->recoveryUnit());
+ indexer.commit();
+ if (opts.logForRepl) {
+ repl::logOp(txn,
+ "i",
+ c->ns().getSystemIndexesCollection().c_str(),
+ c->getIndexCatalog()->getDefaultIdIndexSpec());
}
+ wunit.commit();
}
}
}
- // now build the indexes
+ // now build the secondary indexes
if ( opts.syncIndexes ) {
for ( list<BSONObj>::iterator i=toClone.begin(); i != toClone.end(); i++ ) {
BSONObj collection = *i;
diff --git a/src/mongo/db/commands/create_indexes.cpp b/src/mongo/db/commands/create_indexes.cpp
index 53db0748a77..2b4e5c56c06 100644
--- a/src/mongo/db/commands/create_indexes.cpp
+++ b/src/mongo/db/commands/create_indexes.cpp
@@ -28,9 +28,15 @@
* it in the license file.
*/
+#include "mongo/platform/basic.h"
+
+#include <string>
+#include <vector>
+
#include "mongo/db/auth/authorization_session.h"
#include "mongo/db/catalog/collection.h"
#include "mongo/db/catalog/database.h"
+#include "mongo/db/catalog/index_create.h"
#include "mongo/db/client.h"
#include "mongo/db/commands.h"
#include "mongo/db/ops/insert.h"
@@ -126,57 +132,41 @@ namespace mongo {
}
}
-
- {
- // We first take a read lock to see if we need to do anything
- // as many calls are ensureIndex (and hence no-ops), this is good so its a shared
- // lock for common calls. We only take write lock if needed.
- // Note: createIndexes command does not currently respect shard versioning.
- Client::ReadContext readContext(txn, ns, false /* doVersion */);
- const Collection* collection = readContext.ctx().db()->getCollection(txn, ns.ns());
- if ( collection ) {
- for ( size_t i = 0; i < specs.size(); i++ ) {
- BSONObj spec = specs[i];
- StatusWith<BSONObj> statusWithSpec =
- collection->getIndexCatalog()->prepareSpecForCreate( txn, spec );
- status = statusWithSpec.getStatus();
- if ( status.code() == ErrorCodes::IndexAlreadyExists ) {
- specs.erase( specs.begin() + i );
- i--;
- continue;
- }
- if ( !status.isOK() )
- return appendCommandStatus( result, status );
- }
-
- if ( specs.size() == 0 ) {
- result.append( "numIndexesBefore",
- collection->getIndexCatalog()->numIndexesTotal() );
- result.append( "note", "all indexes already exist" );
- return true;
- }
-
- // need to create index
- }
- }
-
// now we know we have to create index(es)
// Note: createIndexes command does not currently respect shard versioning.
- Client::WriteContext writeContext(txn, ns.ns(), false /* doVersion */ );
- Database* db = writeContext.ctx().db();
+ Lock::DBWrite lk(txn->lockState(), ns.ns());
+ Client::Context ctx(txn, ns.ns(), false /* doVersion */ );
+ Database* db = ctx.db();
Collection* collection = db->getCollection( txn, ns.ns() );
result.appendBool( "createdCollectionAutomatically", collection == NULL );
if ( !collection ) {
+ WriteUnitOfWork wunit(txn->recoveryUnit());
collection = db->createCollection( txn, ns.ns() );
invariant( collection );
+ wunit.commit();
}
result.append( "numIndexesBefore", collection->getIndexCatalog()->numIndexesTotal() );
- for ( size_t i = 0; i < specs.size(); i++ ) {
- BSONObj spec = specs[i];
+ MultiIndexBlock indexer(txn, collection);
+ indexer.allowBackgroundBuilding();
+ indexer.allowInterruption();
+
+ const size_t origSpecsSize = specs.size();
+ indexer.removeExistingIndexes(&specs);
+ if (specs.size() == 0) {
+ result.append( "note", "all indexes already exist" );
+ return true;
+ }
+
+ if (specs.size() != origSpecsSize) {
+ result.append( "note", "index already exists" );
+ }
+
+ for ( size_t i = 0; i < specs.size(); i++ ) {
+ const BSONObj& spec = specs[i];
if ( spec["unique"].trueValue() ) {
status = checkUniqueIndexConstraints(txn, ns.ns(), spec["key"].Obj());
@@ -185,28 +175,28 @@ namespace mongo {
return false;
}
}
+ }
- status = collection->getIndexCatalog()->createIndex(txn, spec, true);
- if ( status.code() == ErrorCodes::IndexAlreadyExists ) {
- if ( !result.hasField( "note" ) )
- result.append( "note", "index already exists" );
- continue;
- }
+ uassertStatusOK(indexer.init(specs));
+ uassertStatusOK(indexer.insertAllDocumentsInCollection());
- if ( !status.isOK() ) {
- appendCommandStatus( result, status );
- return false;
- }
+ {
+ WriteUnitOfWork wunit(txn->recoveryUnit());
+
+ indexer.commit();
if ( !fromRepl ) {
- std::string systemIndexes = ns.getSystemIndexesCollection();
- repl::logOp(txn, "i", systemIndexes.c_str(), spec);
+ for ( size_t i = 0; i < specs.size(); i++ ) {
+ std::string systemIndexes = ns.getSystemIndexesCollection();
+ repl::logOp(txn, "i", systemIndexes.c_str(), specs[i]);
+ }
}
+
+ wunit.commit();
}
result.append( "numIndexesAfter", collection->getIndexCatalog()->numIndexesTotal() );
- writeContext.commit();
return true;
}
diff --git a/src/mongo/db/commands/drop_indexes.cpp b/src/mongo/db/commands/drop_indexes.cpp
index 834bfee0d92..57d13401372 100644
--- a/src/mongo/db/commands/drop_indexes.cpp
+++ b/src/mongo/db/commands/drop_indexes.cpp
@@ -32,6 +32,9 @@
#include "mongo/platform/basic.h"
+#include <string>
+#include <vector>
+
#include "mongo/db/background.h"
#include "mongo/db/commands.h"
#include "mongo/db/index_builder.h"
@@ -41,6 +44,7 @@
#include "mongo/db/catalog/collection_catalog_entry.h"
#include "mongo/db/catalog/database.h"
#include "mongo/db/catalog/index_catalog.h"
+#include "mongo/db/catalog/index_create.h"
#include "mongo/db/catalog/index_key_validate.h"
#include "mongo/db/repl/oplog.h"
#include "mongo/db/operation_context_impl.h"
@@ -234,7 +238,6 @@ namespace mongo {
LOG(0) << "CMD: reIndex " << toDeleteNs << endl;
Lock::DBWrite dbXLock(txn->lockState(), dbname);
- WriteUnitOfWork wunit(txn->recoveryUnit());
Client::Context ctx(txn, toDeleteNs);
Collection* collection = ctx.db()->getCollection( txn, toDeleteNs );
@@ -255,31 +258,53 @@ namespace mongo {
for ( size_t i = 0; i < indexNames.size(); i++ ) {
const string& name = indexNames[i];
BSONObj spec = collection->getCatalogEntry()->getIndexSpec( name );
- all.push_back( spec.getOwned() );
+ all.push_back(spec.removeField("v").getOwned());
+
+ const BSONObj key = spec.getObjectField("key");
+ const Status keyStatus = validateKeyPattern(key);
+ if (!keyStatus.isOK()) {
+ errmsg = str::stream()
+ << "Cannot rebuild index " << spec << ": " << keyStatus.reason()
+ << " For more info see http://dochub.mongodb.org/core/index-validation";
+ return false;
+ }
}
}
result.appendNumber( "nIndexesWas", all.size() );
- Status s = collection->getIndexCatalog()->dropAllIndexes(txn, true);
- if ( !s.isOK() ) {
- errmsg = "dropIndexes failed";
- return appendCommandStatus( result, s );
+ {
+ WriteUnitOfWork wunit(txn->recoveryUnit());
+ Status s = collection->getIndexCatalog()->dropAllIndexes(txn, true);
+ if ( !s.isOK() ) {
+ errmsg = "dropIndexes failed";
+ return appendCommandStatus( result, s );
+ }
+ wunit.commit();
}
- for ( size_t i = 0; i < all.size(); i++ ) {
- BSONObj o = all[i];
- LOG(1) << "reIndex ns: " << toDeleteNs << " index: " << o << endl;
- Status s = collection->getIndexCatalog()->createIndex(txn, o, false);
- if ( !s.isOK() )
- return appendCommandStatus( result, s );
+ MultiIndexBlock indexer(txn, collection);
+ indexer.allowBackgroundBuilding();
+ // do not want interruption as that will leave us without indexes.
+
+ Status status = indexer.init(all);
+ if (!status.isOK())
+ return appendCommandStatus( result, status );
+
+ status = indexer.insertAllDocumentsInCollection();
+ if (!status.isOK())
+ return appendCommandStatus( result, status );
+
+ {
+ WriteUnitOfWork wunit(txn->recoveryUnit());
+ indexer.commit();
+ wunit.commit();
}
result.append( "nIndexes", (int)all.size() );
result.append( "indexes", all );
IndexBuilder::restoreIndexes(indexesInProg);
- wunit.commit();
return true;
}
} cmdReIndex;
diff --git a/src/mongo/db/commands/mr.cpp b/src/mongo/db/commands/mr.cpp
index 2d7bd76e485..acc5ab3f94c 100644
--- a/src/mongo/db/commands/mr.cpp
+++ b/src/mongo/db/commands/mr.cpp
@@ -348,20 +348,23 @@ namespace mongo {
// Intentionally not replicating the inc collection to secondaries.
Client::WriteContext incCtx(_txn, _config.incLong);
Collection* incColl = incCtx.ctx().db()->getCollection( _txn, _config.incLong );
- if ( !incColl ) {
- CollectionOptions options;
- options.setNoIdIndex();
- options.temp = true;
- incColl = incCtx.ctx().db()->createCollection( _txn, _config.incLong, options );
- }
+ invariant(!incColl);
+
+ CollectionOptions options;
+ options.setNoIdIndex();
+ options.temp = true;
+ incColl = incCtx.ctx().db()->createCollection( _txn, _config.incLong, options );
+ invariant(incColl);
BSONObj indexSpec = BSON( "key" << BSON( "0" << 1 ) << "ns" << _config.incLong
<< "name" << "_temp_0" );
- Status status = incColl->getIndexCatalog()->createIndex(_txn, indexSpec, false);
+ Status status = incColl->getIndexCatalog()->createIndexOnEmptyCollection(_txn,
+ indexSpec);
if ( !status.isOK() ) {
uasserted( 17305 , str::stream() << "createIndex failed for mr incLong ns: " <<
_config.incLong << " err: " << status.code() );
}
+ incCtx.commit();
}
vector<BSONObj> indexesToInsert;
@@ -402,22 +405,31 @@ namespace mongo {
repl::getGlobalReplicationCoordinator()->
canAcceptWritesForDatabase(nsToDatabase(_config.tempNamespace.c_str())));
Collection* tempColl = tempCtx.ctx().db()->getCollection( _txn, _config.tempNamespace );
- if ( !tempColl ) {
- CollectionOptions options;
- options.temp = true;
- tempColl = tempCtx.ctx().db()->createCollection( _txn, _config.tempNamespace, options );
-
- // Log the createCollection operation.
- BSONObjBuilder b;
- b.append( "create", nsToCollectionSubstring( _config.tempNamespace ));
- b.appendElements( options.toBSON() );
- string logNs = nsToDatabase( _config.tempNamespace ) + ".$cmd";
- repl::logOp(_txn, "c", logNs.c_str(), b.obj());
- }
+ invariant(!tempColl);
+
+ CollectionOptions options;
+ options.temp = true;
+ tempColl = tempCtx.ctx().db()->createCollection(_txn,
+ _config.tempNamespace,
+ options);
+
+ // Log the createCollection operation.
+ BSONObjBuilder b;
+ b.append( "create", nsToCollectionSubstring( _config.tempNamespace ));
+ b.appendElements( options.toBSON() );
+ string logNs = nsToDatabase( _config.tempNamespace ) + ".$cmd";
+ repl::logOp(_txn, "c", logNs.c_str(), b.obj());
for ( vector<BSONObj>::iterator it = indexesToInsert.begin();
it != indexesToInsert.end(); ++it ) {
- tempColl->getIndexCatalog()->createIndex(_txn, *it, false );
+ Status status =
+ tempColl->getIndexCatalog()->createIndexOnEmptyCollection(_txn, *it);
+ if (!status.isOK()) {
+ if (status.code() == ErrorCodes::IndexAlreadyExists) {
+ continue;
+ }
+ uassertStatusOK(status);
+ }
// Log the createIndex operation.
string logNs = nsToDatabase( _config.tempNamespace ) + ".system.indexes";
repl::logOp(_txn, "i", logNs.c_str(), *it);
diff --git a/src/mongo/db/commands/rename_collection.cpp b/src/mongo/db/commands/rename_collection.cpp
index be1a74ad521..fb981a83dff 100644
--- a/src/mongo/db/commands/rename_collection.cpp
+++ b/src/mongo/db/commands/rename_collection.cpp
@@ -31,6 +31,7 @@
#include "mongo/client/dbclientcursor.h"
#include "mongo/db/catalog/collection.h"
#include "mongo/db/catalog/index_catalog.h"
+#include "mongo/db/catalog/index_create.h"
#include "mongo/db/commands.h"
#include "mongo/db/commands/rename_collection.h"
#include "mongo/db/dbhelpers.h"
@@ -38,9 +39,10 @@
#include "mongo/db/index/index_descriptor.h"
#include "mongo/db/instance.h"
#include "mongo/db/namespace_string.h"
+#include "mongo/db/operation_context_impl.h"
#include "mongo/db/ops/insert.h"
#include "mongo/db/repl/oplog.h"
-#include "mongo/db/operation_context_impl.h"
+#include "mongo/util/scopeguard.h"
namespace mongo {
@@ -92,27 +94,28 @@ namespace mongo {
IndexBuilder::restoreIndexes( indexesInProg );
}
- virtual bool run(OperationContext* txn, const string& dbname, BSONObj& cmdObj, int, string& errmsg, BSONObjBuilder& result, bool fromRepl) {
- Lock::GlobalWrite globalWriteLock(txn->lockState());
+ static void dropCollection(OperationContext* txn, Database* db, StringData collName) {
WriteUnitOfWork wunit(txn->recoveryUnit());
- if (!wrappedRun(txn, dbname, cmdObj, errmsg, result, fromRepl)) {
- return false;
- }
- if (!fromRepl) {
- repl::logOp(txn, "c",(dbname + ".$cmd").c_str(), cmdObj);
+ if (db->dropCollection(txn, collName).isOK()) {
+ // ignoring failure case
+ wunit.commit();
}
- wunit.commit();
- return true;
}
- virtual bool wrappedRun(OperationContext* txn,
- const string& dbname,
- BSONObj& cmdObj,
- string& errmsg,
- BSONObjBuilder& result,
- bool fromRepl) {
+
+ virtual bool run(OperationContext* txn,
+ const string& dbname,
+ BSONObj& cmdObj,
+ int,
+ string& errmsg,
+ BSONObjBuilder& result,
+ bool fromRepl) {
+ Lock::GlobalWrite globalWriteLock(txn->lockState());
string source = cmdObj.getStringField( name.c_str() );
string target = cmdObj.getStringField( "to" );
+ // We stay in source context the whole time. This is mostly to set the CurOp namespace.
+ Client::Context ctx(txn, source);
+
if ( !NamespaceString::validCollectionComponent(target.c_str()) ) {
errmsg = "invalid collection name: " + target;
return false;
@@ -136,22 +139,15 @@ namespace mongo {
}
}
- string sourceDB = nsToDatabase(source);
- string targetDB = nsToDatabase(target);
-
- bool capped = false;
- long long size = 0;
- std::vector<BSONObj> indexesInProg;
+ Database* const sourceDB = dbHolder().get(txn, nsToDatabase(source));
+ Collection* const sourceColl = sourceDB ? sourceDB->getCollection(txn, source)
+ : NULL;
+ if (!sourceColl) {
+ errmsg = "source namespace does not exist";
+ return false;
+ }
{
- Client::Context srcCtx(txn, source);
- Collection* sourceColl = srcCtx.db()->getCollection( txn, source );
-
- if ( !sourceColl ) {
- errmsg = "source namespace does not exist";
- return false;
- }
-
// Ensure that collection name does not exceed maximum length.
// Ensure that index names do not push the length over the max.
// Iterator includes unfinished indexes.
@@ -175,184 +171,144 @@ namespace mongo {
errmsg = sb.str();
return false;
}
+ }
- {
+ const std::vector<BSONObj> indexesInProg = stopIndexBuilds(txn, sourceDB, cmdObj);
+ // Dismissed on success
+ ScopeGuard indexBuildRestorer = MakeGuard(IndexBuilder::restoreIndexes, indexesInProg);
- indexesInProg = stopIndexBuilds( txn, srcCtx.db(), cmdObj );
- capped = sourceColl->isCapped();
- if ( capped ) {
- size = sourceColl->getRecordStore()->storageSize( txn );
- }
- }
- }
+ bool unused;
+ Database* const targetDB = dbHolder().getOrCreate(txn, nsToDatabase(target), unused);
{
- Client::Context ctx(txn, target );
+ WriteUnitOfWork wunit(txn->recoveryUnit());
// Check if the target namespace exists and if dropTarget is true.
// If target exists and dropTarget is not true, return false.
- if ( ctx.db()->getCollection( txn, target ) ) {
- if ( !cmdObj["dropTarget"].trueValue() ) {
+ if (targetDB->getCollection(txn, target)) {
+ if (!cmdObj["dropTarget"].trueValue()) {
errmsg = "target namespace exists";
return false;
}
- Status s = ctx.db()->dropCollection( txn, target );
+ Status s = targetDB->dropCollection(txn, target);
if ( !s.isOK() ) {
errmsg = s.toString();
- restoreIndexBuildsOnSource( indexesInProg, source );
return false;
}
}
// If we are renaming in the same database, just
// rename the namespace and we're done.
- if ( sourceDB == targetDB ) {
- Status s = ctx.db()->renameCollection( txn, source, target,
- cmdObj["stayTemp"].trueValue() );
- if ( !s.isOK() ) {
- errmsg = s.toString();
- restoreIndexBuildsOnSource( indexesInProg, source );
- return false;
+ if (sourceDB == targetDB) {
+ Status s = targetDB->renameCollection(txn,
+ source,
+ target,
+ cmdObj["stayTemp"].trueValue() );
+ if (!s.isOK()) {
+ return appendCommandStatus(result, s);
+ }
+
+ if (!fromRepl) {
+ repl::logOp(txn, "c", (dbname + ".$cmd").c_str(), cmdObj);
}
+
+ wunit.commit();
+ indexBuildRestorer.Dismiss();
return true;
}
+ }
- // Otherwise, we are enaming across databases, so we must copy all
- // the data and then remove the source collection.
+ // If we get here, we are renaming across databases, so we must copy all the data and
+ // indexes, then remove the source collection.
- // Create the target collection.
- Collection* targetColl = NULL;
- if ( capped ) {
- CollectionOptions options;
- options.capped = true;
- options.cappedSize = size;
- options.setNoIdIndex();
+ // Create the target collection. It will be removed if we fail to copy the collection.
+ // TODO use a temp collection and unset the temp flag on success.
+ Collection* targetColl = NULL;
+ {
+ CollectionOptions options;
+ options.setNoIdIndex();
- targetColl = ctx.db()->createCollection( txn, target, options );
- }
- else {
- CollectionOptions options;
- options.setNoIdIndex();
- // No logOp necessary because the entire renameCollection command is one logOp.
- targetColl = ctx.db()->createCollection( txn, target, options );
+ if (sourceColl->isCapped()) {
+ // TODO stop assuming storageSize == cappedSize
+ options.capped = true;
+ options.cappedSize = sourceColl->getRecordStore()->storageSize(txn);
}
- if ( !targetColl ) {
+
+ WriteUnitOfWork wunit(txn->recoveryUnit());
+
+ // No logOp necessary because the entire renameCollection command is one logOp.
+ targetColl = targetDB->createCollection(txn, target, options);
+ if (!targetColl) {
errmsg = "Failed to create target collection.";
- restoreIndexBuildsOnSource( indexesInProg, source );
return false;
}
- }
- // Copy over all the data from source collection to target collection.
- bool insertSuccessful = true;
- boost::scoped_ptr<RecordIterator> sourceIt;
- Collection* sourceColl = NULL;
-
- {
- Client::Context srcCtx(txn, source);
- sourceColl = srcCtx.db()->getCollection( txn, source );
- sourceIt.reset( sourceColl->getIterator( txn, DiskLoc(), false, CollectionScanParams::FORWARD ) );
+ wunit.commit();
}
- Collection* targetColl = NULL;
- while ( !sourceIt->isEOF() ) {
- BSONObj o;
- {
- Client::Context srcCtx(txn, source);
- o = sourceColl->docFor(sourceIt->getNext());
- }
- // Insert and check return status of insert.
- {
- Client::Context ctx(txn, target );
- if ( !targetColl )
- targetColl = ctx.db()->getCollection( txn, target );
- // No logOp necessary because the entire renameCollection command is one logOp.
- Status s = targetColl->insertDocument( txn, o, true ).getStatus();
- if ( !s.isOK() ) {
- insertSuccessful = false;
- errmsg = s.toString();
- break;
- }
- txn->recoveryUnit()->commitIfNeeded();
- }
- }
+ // Dismissed on success
+ ScopeGuard targetCollectionDropper = MakeGuard(dropCollection, txn, targetDB, target);
- // If inserts were unsuccessful, drop the target collection and return false.
- if ( !insertSuccessful ) {
- Client::Context ctx(txn, target );
- Status s = ctx.db()->dropCollection( txn, target );
- if ( !s.isOK() )
- errmsg = s.toString();
- restoreIndexBuildsOnSource( indexesInProg, source );
- return false;
- }
+ MultiIndexBlock indexer(txn, targetColl);
+ indexer.allowInterruption();
- // Copy over the indexes to temp storage and then to the target..
- vector<BSONObj> copiedIndexes;
- bool indexSuccessful = true;
+ // Copy the index descriptions from the source collection, adjusting the ns field.
{
- Client::Context srcCtx(txn, source);
+ std::vector<BSONObj> indexesToCopy;
IndexCatalog::IndexIterator sourceIndIt =
sourceColl->getIndexCatalog()->getIndexIterator( true );
-
- while ( sourceIndIt.more() ) {
- BSONObj currIndex = sourceIndIt.next()->infoObj();
+ while (sourceIndIt.more()) {
+ const BSONObj currIndex = sourceIndIt.next()->infoObj();
// Process the source index.
- BSONObjBuilder b;
- BSONObjIterator i( currIndex );
- while( i.moreWithEOO() ) {
- BSONElement e = i.next();
- if ( e.eoo() )
- break;
- else if ( strcmp( e.fieldName(), "ns" ) == 0 )
- b.append( "ns", target );
- else
- b.append( e );
- }
-
- BSONObj newIndex = b.obj();
- copiedIndexes.push_back( newIndex );
+ BSONObjBuilder newIndex;
+ newIndex.append("ns", target);
+ newIndex.appendElementsUnique(currIndex);
+ indexesToCopy.push_back(newIndex.obj());
}
+ indexer.init(indexesToCopy);
+ }
+
+ // Copy over all the data from source collection to target collection.
+ boost::scoped_ptr<RecordIterator> sourceIt(sourceColl->getIterator(txn));
+ while (!sourceIt->isEOF()) {
+ txn->checkForInterrupt(false);
+
+ const BSONObj obj = sourceColl->docFor(sourceIt->getNext());
+
+ WriteUnitOfWork wunit(txn->recoveryUnit());
+ // No logOp necessary because the entire renameCollection command is one logOp.
+ Status status = targetColl->insertDocument(txn, obj, &indexer, true).getStatus();
+ if (!status.isOK())
+ return appendCommandStatus(result, status);
+ wunit.commit();
}
+ Status status = indexer.doneInserting();
+ if (!status.isOK())
+ return appendCommandStatus(result, status);
+
{
- Client::Context ctx(txn, target );
- if ( !targetColl )
- targetColl = ctx.db()->getCollection( txn, target );
+ // Getting here means we successfully built the target copy. We now remove the
+ // source collection and finalize the rename.
+ WriteUnitOfWork wunit(txn->recoveryUnit());
- for ( vector<BSONObj>::iterator it = copiedIndexes.begin();
- it != copiedIndexes.end(); ++it ) {
- Status s = targetColl->getIndexCatalog()->createIndex(txn, *it, true );
- if ( !s.isOK() ) {
- indexSuccessful = false;
- errmsg = s.toString();
- break;
- }
- }
+ Status status = sourceDB->dropCollection(txn, source);
+ if (!status.isOK())
+ return appendCommandStatus(result, status);
- // If indexes were unsuccessful, drop the target collection and return false.
- if ( !indexSuccessful ) {
- Status s = ctx.db()->dropCollection( txn, target );
- if ( !s.isOK() )
- errmsg = s.toString();
- restoreIndexBuildsOnSource( indexesInProg, source );
- return false;
- }
- }
+ indexer.commit();
- // Drop the source collection.
- {
- Client::Context srcCtx(txn, source);
- Status s = srcCtx.db()->dropCollection( txn, source );
- if ( !s.isOK() ) {
- errmsg = s.toString();
- restoreIndexBuildsOnSource( indexesInProg, source );
- return false;
+ if (!fromRepl) {
+ repl::logOp(txn, "c", (dbname + ".$cmd").c_str(), cmdObj);
}
+
+ wunit.commit();
}
+ indexBuildRestorer.Dismiss();
+ targetCollectionDropper.Dismiss();
return true;
}
} cmdrenamecollection;
diff --git a/src/mongo/db/commands/write_commands/batch_executor.cpp b/src/mongo/db/commands/write_commands/batch_executor.cpp
index 211aeafd00e..ce0eea74e78 100644
--- a/src/mongo/db/commands/write_commands/batch_executor.cpp
+++ b/src/mongo/db/commands/write_commands/batch_executor.cpp
@@ -42,6 +42,7 @@
#include "mongo/db/introspect.h"
#include "mongo/db/lasterror.h"
#include "mongo/db/namespace_string.h"
+#include "mongo/db/catalog/index_create.h"
#include "mongo/db/ops/delete_executor.h"
#include "mongo/db/ops/delete_request.h"
#include "mongo/db/ops/insert.h"
@@ -974,14 +975,12 @@ namespace mongo {
try {
if (state->lockAndCheck(result)) {
- WriteUnitOfWork wunit (state->txn->recoveryUnit());
if (!state->request->isInsertIndexRequest()) {
singleInsert(state->txn, insertDoc, state->getCollection(), result);
}
else {
singleCreateIndex(state->txn, insertDoc, state->getCollection(), result);
}
- wunit.commit();
}
}
catch (const DBException& ex) {
@@ -1040,6 +1039,7 @@ namespace mongo {
txn->lockState()->assertWriteLocked( insertNS );
+ WriteUnitOfWork wunit(txn->recoveryUnit());
StatusWith<DiskLoc> status = collection->insertDocument( txn, docToInsert, true );
if ( !status.isOK() ) {
@@ -1047,8 +1047,8 @@ namespace mongo {
}
else {
repl::logOp( txn, "i", insertNS.c_str(), docToInsert );
- txn->recoveryUnit()->commitIfNeeded();
result->getStats().n = 1;
+ wunit.commit();
}
}
@@ -1067,18 +1067,31 @@ namespace mongo {
txn->lockState()->assertWriteLocked( indexNS );
- Status status = collection->getIndexCatalog()->createIndex(txn, indexDesc, true);
+ MultiIndexBlock indexer(txn, collection);
+ indexer.allowBackgroundBuilding();
+ indexer.allowInterruption();
+ Status status = indexer.init(indexDesc);
if ( status.code() == ErrorCodes::IndexAlreadyExists ) {
result->getStats().n = 0;
+ return; // inserting an existing index is a no-op.
}
- else if ( !status.isOK() ) {
+ if (!status.isOK()) {
result->setError(toWriteError(status));
+ return;
}
- else {
- repl::logOp( txn, "i", indexNS.c_str(), indexDesc );
- result->getStats().n = 1;
+
+ status = indexer.insertAllDocumentsInCollection();
+ if (!status.isOK()) {
+ result->setError(toWriteError(status));
+ return;
}
+
+ WriteUnitOfWork wunit(txn->recoveryUnit());
+ indexer.commit();
+ repl::logOp( txn, "i", indexNS.c_str(), indexDesc );
+ result->getStats().n = 1;
+ wunit.commit();
}
static void multiUpdate( OperationContext* txn,
diff --git a/src/mongo/db/db.cpp b/src/mongo/db/db.cpp
index 9fc531a5e7c..0bfd4b06997 100644
--- a/src/mongo/db/db.cpp
+++ b/src/mongo/db/db.cpp
@@ -669,8 +669,7 @@ namespace mongo {
getDeleter()->startWorkers();
- // Starts a background thread that rebuilds all incomplete indices.
- indexRebuilder.go();
+ restartInProgressIndexesFromLastShutdown();
listen(listenPort);
diff --git a/src/mongo/db/dbhelpers.cpp b/src/mongo/db/dbhelpers.cpp
index 63d7a8065e2..aa9fbb07b31 100644
--- a/src/mongo/db/dbhelpers.cpp
+++ b/src/mongo/db/dbhelpers.cpp
@@ -37,6 +37,7 @@
#include <fstream>
#include "mongo/client/dbclientinterface.h"
+#include "mongo/db/catalog/index_create.h"
#include "mongo/db/db.h"
#include "mongo/db/exec/working_set_common.h"
#include "mongo/db/json.h"
@@ -77,10 +78,18 @@ namespace mongo {
b.appendBool("unique", unique);
BSONObj o = b.done();
- Status status = collection->getIndexCatalog()->createIndex(txn, o, false);
+ MultiIndexBlock indexer(txn, collection);
+
+ Status status = indexer.init(o);
if ( status.code() == ErrorCodes::IndexAlreadyExists )
return;
uassertStatusOK( status );
+
+ uassertStatusOK(indexer.insertAllDocumentsInCollection());
+
+ WriteUnitOfWork wunit(txn->recoveryUnit());
+ indexer.commit();
+ wunit.commit();
}
/* fetch a single object from collection ns that matches query
diff --git a/src/mongo/db/index/btree_access_method.cpp b/src/mongo/db/index/btree_access_method.cpp
index eb296b0f739..1002903b921 100644
--- a/src/mongo/db/index/btree_access_method.cpp
+++ b/src/mongo/db/index/btree_access_method.cpp
@@ -34,7 +34,6 @@
#include "mongo/db/index/btree_index_cursor.h"
#include "mongo/db/jsobj.h"
#include "mongo/db/keypattern.h"
-#include "mongo/db/pdfile_private.h"
namespace mongo {
diff --git a/src/mongo/db/index/btree_based_access_method.cpp b/src/mongo/db/index/btree_based_access_method.cpp
index afed5a92661..88264581e53 100644
--- a/src/mongo/db/index/btree_based_access_method.cpp
+++ b/src/mongo/db/index/btree_based_access_method.cpp
@@ -37,7 +37,6 @@
#include "mongo/db/index/btree_index_cursor.h"
#include "mongo/db/jsobj.h"
#include "mongo/db/keypattern.h"
-#include "mongo/db/pdfile_private.h"
#include "mongo/db/server_parameters.h"
#include "mongo/db/operation_context.h"
#include "mongo/util/log.h"
@@ -328,13 +327,14 @@ namespace mongo {
Status BtreeBasedAccessMethod::commitBulk(IndexAccessMethod* bulkRaw,
bool mayInterrupt,
+ bool dupsAllowed,
set<DiskLoc>* dupsToDrop) {
if (!_newInterface->isEmpty()) {
return Status(ErrorCodes::InternalError, "trying to commit but has data already");
}
BtreeBasedBulkAccessMethod* bulk = static_cast<BtreeBasedBulkAccessMethod*>(bulkRaw);
- return bulk->commit(dupsToDrop, mayInterrupt);
+ return bulk->commit(dupsToDrop, mayInterrupt, dupsAllowed);
}
} // namespace mongo
diff --git a/src/mongo/db/index/btree_based_access_method.h b/src/mongo/db/index/btree_based_access_method.h
index 3566e80cadf..386d36e2e23 100644
--- a/src/mongo/db/index/btree_based_access_method.h
+++ b/src/mongo/db/index/btree_based_access_method.h
@@ -95,6 +95,7 @@ namespace mongo {
virtual Status commitBulk( IndexAccessMethod* bulk,
bool mayInterrupt,
+ bool dupsAllowed,
std::set<DiskLoc>* dups );
virtual Status touch(OperationContext* txn, const BSONObj& obj);
diff --git a/src/mongo/db/index/btree_based_bulk_access_method.cpp b/src/mongo/db/index/btree_based_bulk_access_method.cpp
index 6139853c86e..03372363011 100644
--- a/src/mongo/db/index/btree_based_bulk_access_method.cpp
+++ b/src/mongo/db/index/btree_based_bulk_access_method.cpp
@@ -33,8 +33,6 @@
#include "mongo/db/index/btree_based_bulk_access_method.h"
#include "mongo/db/curop.h"
-#include "mongo/db/pdfile_private.h" // This is for inDBRepair.
-#include "mongo/db/repl/repl_coordinator_global.h"
#include "mongo/db/operation_context.h"
#include "mongo/db/storage_options.h"
#include "mongo/util/log.h"
@@ -117,18 +115,9 @@ namespace mongo {
}
Status BtreeBasedBulkAccessMethod::commit(set<DiskLoc>* dupsToDrop,
- bool mayInterrupt) {
- if (_isMultiKey) {
- _real->_btreeState->setMultikey( _txn );
- }
-
+ bool mayInterrupt,
+ bool dupsAllowed) {
Timer timer;
- IndexCatalogEntry* entry = _real->_btreeState;
-
- bool dupsAllowed = !entry->descriptor()->unique() ||
- repl::getGlobalReplicationCoordinator()->shouldIgnoreUniqueIndex(entry->descriptor());
-
- bool dropDups = entry->descriptor()->dropDups() || inDBRepair;
scoped_ptr<BSONObjExternalSorter::Iterator> i(_sorter->done());
@@ -140,12 +129,23 @@ namespace mongo {
scoped_ptr<SortedDataBuilderInterface> builder;
- builder.reset(_interface->getBulkBuilder(_txn, dupsAllowed));
+ {
+ WriteUnitOfWork wunit(_txn->recoveryUnit());
+
+ if (_isMultiKey) {
+ _real->_btreeState->setMultikey( _txn );
+ }
+
+ builder.reset(_interface->getBulkBuilder(_txn, dupsAllowed));
+ wunit.commit();
+ }
while (i->more()) {
if (mayInterrupt)
_txn->checkForInterrupt(/*heedMutex*/ false);
+ WriteUnitOfWork wunit(_txn->recoveryUnit());
+
// Get the next datum and add it to the builder.
BSONObjExternalSorter::Data d = i->next();
Status status = builder->addKey(d.first, d.second);
@@ -155,23 +155,21 @@ namespace mongo {
return status;
}
+ invariant(!dupsAllowed); // shouldn't be getting DupKey errors if dupsAllowed.
+
// If we're here it's a duplicate key.
- if (dropDups) {
- static const size_t kMaxDupsToStore = 1000000;
+ if (dupsToDrop) {
dupsToDrop->insert(d.second);
- if (dupsToDrop->size() > kMaxDupsToStore) {
- return Status(ErrorCodes::InternalError,
- "Too many dups on index build with dropDups = true");
- }
- }
- else if (!dupsAllowed) {
- return status;
+ continue;
}
+
+ return status;
}
// If we're here either it's a dup and we're cool with it or the addKey went just
// fine.
pm.hit();
+ wunit.commit();
}
pm.finished();
@@ -181,12 +179,7 @@ namespace mongo {
LOG(timer.seconds() > 10 ? 0 : 1 ) << "\t done building bottom layer, going to commit";
- unsigned long long keysCommit = builder->commit(mayInterrupt);
-
- if (!dropDups && (keysCommit != _keysInserted)) {
- warning() << "not all entries were added to the index, probably some "
- << "keys were too large" << endl;
- }
+ builder->commit(mayInterrupt);
return Status::OK();
}
diff --git a/src/mongo/db/index/btree_based_bulk_access_method.h b/src/mongo/db/index/btree_based_bulk_access_method.h
index 06d900fee0a..49fc1f53028 100644
--- a/src/mongo/db/index/btree_based_bulk_access_method.h
+++ b/src/mongo/db/index/btree_based_bulk_access_method.h
@@ -59,7 +59,7 @@ namespace mongo {
const InsertDeleteOptions& options,
int64_t* numInserted);
- Status commit(std::set<DiskLoc>* dupsToDrop, bool mayInterrupt);
+ Status commit(std::set<DiskLoc>* dupsToDrop, bool mayInterrupt, bool dupsAllowed);
// Exposed for testing.
static ExternalSortComparison* getComparison(int version, const BSONObj& keyPattern);
@@ -70,6 +70,7 @@ namespace mongo {
virtual Status commitBulk(IndexAccessMethod* bulk,
bool mayInterrupt,
+ bool dupsAllowed,
std::set<DiskLoc>* dups) {
invariant(this == bulk);
return Status::OK();
diff --git a/src/mongo/db/index/index_access_method.h b/src/mongo/db/index/index_access_method.h
index 3786406a7fd..c35cfbbe2ae 100644
--- a/src/mongo/db/index/index_access_method.h
+++ b/src/mongo/db/index/index_access_method.h
@@ -184,11 +184,13 @@ namespace mongo {
* and should not be used.
* @param bulk - something created from initiateBulk
* @param mayInterrupt - is this commit interruptable (will cancel)
+ * @param dupsAllowed - if false, error or fill 'dups' if any duplicate values are found
* @param dups - if NULL, error out on dups if not allowed
* if not NULL, put the bad DiskLocs there
*/
virtual Status commitBulk( IndexAccessMethod* bulk,
bool mayInterrupt,
+ bool dupsAllowed,
std::set<DiskLoc>* dups ) = 0;
};
diff --git a/src/mongo/db/index_builder.cpp b/src/mongo/db/index_builder.cpp
index 5398c5f3b5c..d2ba234e5ee 100644
--- a/src/mongo/db/index_builder.cpp
+++ b/src/mongo/db/index_builder.cpp
@@ -36,6 +36,7 @@
#include "mongo/db/curop.h"
#include "mongo/db/catalog/database.h"
#include "mongo/db/catalog/database_holder.h"
+#include "mongo/db/catalog/index_create.h"
#include "mongo/db/concurrency/d_concurrency.h"
#include "mongo/db/repl/rs.h"
#include "mongo/db/operation_context_impl.h"
@@ -73,7 +74,7 @@ namespace mongo {
Database* db = dbHolder().get(&txn, ns.db().toString());
- Status status = build(&txn, db);
+ Status status = build(&txn, db, true);
if ( !status.isOK() ) {
log() << "IndexBuilder could not build index: " << status.toString();
}
@@ -82,24 +83,56 @@ namespace mongo {
cc().shutdown();
}
- Status IndexBuilder::build(OperationContext* txn, Database* db) const {
+ Status IndexBuilder::buildInForeground(OperationContext* txn, Database* db) const {
+ return build(txn, db, false);
+ }
+
+ Status IndexBuilder::build(OperationContext* txn,
+ Database* db,
+ bool allowBackgroundBuilding) const {
const string ns = _index["ns"].String();
Collection* c = db->getCollection( txn, ns );
if ( !c ) {
+ WriteUnitOfWork wunit(txn->recoveryUnit());
c = db->getOrCreateCollection( txn, ns );
verify(c);
+ wunit.commit();
}
// Show which index we're building in the curop display.
txn->getCurOp()->setQuery(_index);
- Status status = c->getIndexCatalog()->createIndex( txn,
- _index,
- true,
- IndexCatalog::SHUTDOWN_LEAVE_DIRTY );
- if ( status.code() == ErrorCodes::IndexAlreadyExists )
- return Status::OK();
+
+ MultiIndexBlock indexer(txn, c);
+ indexer.allowInterruption();
+ if (allowBackgroundBuilding)
+ indexer.allowBackgroundBuilding();
+
+ Status status = Status::OK();
+ try {
+ status = indexer.init(_index);
+ if ( status.code() == ErrorCodes::IndexAlreadyExists )
+ return Status::OK();
+
+ if (status.isOK())
+ status = indexer.insertAllDocumentsInCollection();
+
+ if (status.isOK()) {
+ WriteUnitOfWork wunit(txn->recoveryUnit());
+ indexer.commit();
+ wunit.commit();
+ }
+ }
+ catch (const DBException& e) {
+ status = e.toStatus();
+ }
+
+ if (status.code() == ErrorCodes::InterruptedAtShutdown) {
+ // leave it as-if kill -9 happened. This will be handled on restart.
+ indexer.abortWithoutCleanup();
+ }
+
return status;
}
diff --git a/src/mongo/db/index_builder.h b/src/mongo/db/index_builder.h
index 1d3699e6671..9c71ec7c85d 100644
--- a/src/mongo/db/index_builder.h
+++ b/src/mongo/db/index_builder.h
@@ -56,7 +56,7 @@ namespace mongo {
*/
virtual std::string name() const;
- Status build(OperationContext* txn, Database* db) const;
+ Status buildInForeground(OperationContext* txn, Database* db) const;
/**
* Kill all in-progress indexes matching criteria, if non-empty:
@@ -75,6 +75,8 @@ namespace mongo {
static void restoreIndexes(const std::vector<BSONObj>& indexes);
private:
+ Status build(OperationContext* txn, Database* db, bool allowBackgroundBuilding) const;
+
const BSONObj _index;
std::string _name; // name of this builder, not related to the index
static AtomicUInt32 _indexBuildCount;
diff --git a/src/mongo/db/index_rebuilder.cpp b/src/mongo/db/index_rebuilder.cpp
index 328a5b62078..0445bdc50c6 100644
--- a/src/mongo/db/index_rebuilder.cpp
+++ b/src/mongo/db/index_rebuilder.cpp
@@ -32,11 +32,15 @@
#include "mongo/db/index_rebuilder.h"
+#include <list>
+#include <string>
+
#include "mongo/db/auth/authorization_session.h"
#include "mongo/db/auth/user_name.h"
#include "mongo/db/catalog/collection.h"
#include "mongo/db/catalog/database.h"
#include "mongo/db/catalog/database_catalog_entry.h"
+#include "mongo/db/catalog/index_create.h"
#include "mongo/db/client.h"
#include "mongo/db/global_environment_experiment.h"
#include "mongo/db/instance.h"
@@ -47,46 +51,8 @@
namespace mongo {
- IndexRebuilder indexRebuilder;
-
- IndexRebuilder::IndexRebuilder() {}
-
- std::string IndexRebuilder::name() const {
- return "IndexRebuilder";
- }
-
- void IndexRebuilder::run() {
- Client::initThread(name().c_str());
- ON_BLOCK_EXIT_OBJ(cc(), &Client::shutdown);
-
- OperationContextImpl txn;
-
- cc().getAuthorizationSession()->grantInternalAuthorization();
-
- std::vector<std::string> dbNames;
-
- StorageEngine* storageEngine = getGlobalEnvironment()->getGlobalStorageEngine();
- storageEngine->listDatabases( &dbNames );
-
- try {
- std::list<std::string> collNames;
- for (std::vector<std::string>::const_iterator dbName = dbNames.begin();
- dbName < dbNames.end();
- dbName++) {
- Client::ReadContext ctx(&txn, *dbName);
-
- Database* db = ctx.ctx().db();
- db->getDatabaseCatalogEntry()->getCollectionNamespaces(&collNames);
- }
- checkNS(&txn, collNames);
- }
- catch (const DBException& e) {
- warning() << "Index rebuilding did not complete: " << e.what() << endl;
- }
- LOG(1) << "checking complete" << endl;
- }
-
- void IndexRebuilder::checkNS(OperationContext* txn, const std::list<std::string>& nsToCheck) {
+namespace {
+ void checkNS(OperationContext* txn, const std::list<std::string>& nsToCheck) {
bool firstTime = true;
for (std::list<std::string>::const_iterator it = nsToCheck.begin();
it != nsToCheck.end();
@@ -98,9 +64,10 @@ namespace mongo {
// This write lock is held throughout the index building process
// for this namespace.
- Client::WriteContext ctx(txn, ns);
+ Lock::DBWrite lk(txn->lockState(), ns);
+ Client::Context ctx(txn, ns);
- Collection* collection = ctx.ctx().db()->getCollection(txn, ns);
+ Collection* collection = ctx.db()->getCollection(txn, ns);
if ( collection == NULL )
continue;
@@ -112,45 +79,88 @@ namespace mongo {
continue;
}
- vector<BSONObj> indexesToBuild = indexCatalog->getAndClearUnfinishedIndexes(txn);
- // The indexes have now been removed from system.indexes, so the only record 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.
+ MultiIndexBlock indexer(txn, collection);
- if ( indexesToBuild.size() == 0 ) {
- continue;
- }
+ {
+ WriteUnitOfWork wunit(txn->recoveryUnit());
+ vector<BSONObj> indexesToBuild = indexCatalog->getAndClearUnfinishedIndexes(txn);
- log() << "found " << indexesToBuild.size()
- << " interrupted index build(s) on " << ns;
+ // The indexes have now been removed from system.indexes, so the only record 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 must stay in the same WriteUnitOfWork
+ // to ensure that no journaling will happen between now and the entry being
+ // re-written in MultiIndexBlock::init(). The actual index building is done outside
+ // of this WUOW.
- if (firstTime) {
- log() << "note: restart the server with --noIndexBuildRetry to skip index rebuilds";
- firstTime = false;
+ if (indexesToBuild.empty()) {
+ continue;
+ }
+
+ log() << "found " << indexesToBuild.size()
+ << " interrupted index build(s) on " << ns;
+
+ if (firstTime) {
+ log() << "note: restart the server with --noIndexBuildRetry "
+ << "to skip index rebuilds";
+ firstTime = false;
+ }
+
+ if (!serverGlobalParams.indexBuildRetry) {
+ log() << " not rebuilding interrupted indexes";
+ continue;
+ }
+
+ uassertStatusOK(indexer.init(indexesToBuild));
+
+ wunit.commit();
}
- if (!serverGlobalParams.indexBuildRetry) {
- log() << " not rebuilding interrupted indexes";
- continue;
+ try {
+ uassertStatusOK(indexer.insertAllDocumentsInCollection());
+
+ WriteUnitOfWork wunit(txn->recoveryUnit());
+ indexer.commit();
+ wunit.commit();
+ }
+ catch (...) {
+ // If anything went wrong, leave the indexes partially built so that we pick them up
+ // again on restart.
+ indexer.abortWithoutCleanup();
+ throw;
}
+ }
+ }
+} // namespace
- // TODO: these can/should/must be done in parallel
- for ( size_t i = 0; i < indexesToBuild.size(); i++ ) {
- BSONObj indexObj = indexesToBuild[i];
+ void restartInProgressIndexesFromLastShutdown() {
+ OperationContextImpl txn;
- log() << "going to rebuild: " << indexObj;
+ cc().getAuthorizationSession()->grantInternalAuthorization();
- Status status = indexCatalog->createIndex(txn, indexObj, false);
- if ( !status.isOK() ) {
- log() << "building index failed: " << status.toString() << " index: " << indexObj;
- }
+ std::vector<std::string> dbNames;
+ StorageEngine* storageEngine = getGlobalEnvironment()->getGlobalStorageEngine();
+ storageEngine->listDatabases( &dbNames );
+
+ try {
+ std::list<std::string> collNames;
+ for (std::vector<std::string>::const_iterator dbName = dbNames.begin();
+ dbName < dbNames.end();
+ ++dbName) {
+ Client::ReadContext ctx(&txn, *dbName);
+
+ Database* db = ctx.ctx().db();
+ db->getDatabaseCatalogEntry()->getCollectionNamespaces(&collNames);
}
- ctx.commit();
+ checkNS(&txn, collNames);
}
+ catch (const DBException& e) {
+ error() << "Index rebuilding did not complete: " << e.toString();
+ log() << "note: restart the server with --noIndexBuildRetry to skip index rebuilds";
+ fassertFailedNoTrace(18643);
+ }
+ LOG(1) << "checking complete" << endl;
}
-
}
diff --git a/src/mongo/db/index_rebuilder.h b/src/mongo/db/index_rebuilder.h
index 71e8bd55952..4fd59b14966 100644
--- a/src/mongo/db/index_rebuilder.h
+++ b/src/mongo/db/index_rebuilder.h
@@ -28,31 +28,11 @@
#pragma once
-#include <list>
-#include <string>
-
-#include "mongo/util/background.h"
-
namespace mongo {
- class OperationContext;
-
- // 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();
-
- std::string name() const;
- void run();
-
- private:
- /**
- * Check each collection in the passed in list to see if it has any in-progress index
- * builds that need to be retried. If so, calls retryIndexBuild.
- */
- void checkNS(OperationContext* txn, const std::list<std::string>& nsToCheck);
- };
-
- extern IndexRebuilder indexRebuilder;
+ /**
+ * Restarts building indexes that were in progress during shutdown.
+ * Only call this at startup before taking requests.
+ */
+ void restartInProgressIndexesFromLastShutdown();
}
diff --git a/src/mongo/db/instance.cpp b/src/mongo/db/instance.cpp
index a7073da73e7..bb77e2af469 100644
--- a/src/mongo/db/instance.cpp
+++ b/src/mongo/db/instance.cpp
@@ -58,6 +58,7 @@
#include "mongo/db/matcher/matcher.h"
#include "mongo/db/mongod_options.h"
#include "mongo/db/namespace_string.h"
+#include "mongo/db/catalog/index_create.h"
#include "mongo/db/commands/count.h"
#include "mongo/db/ops/delete_executor.h"
#include "mongo/db/ops/delete_request.h"
@@ -794,24 +795,36 @@ namespace mongo {
Collection* collection = ctx.db()->getCollection( txn, targetNS );
if ( !collection ) {
// implicitly create
+ WriteUnitOfWork wunit(txn->recoveryUnit());
collection = ctx.db()->createCollection( txn, targetNS );
verify( collection );
+ wunit.commit();
}
// Only permit interrupting an (index build) insert if the
// insert comes from a socket client request rather than a
// parent operation using the client interface. The parent
// operation might not support interrupts.
- bool mayInterrupt = txn->getCurOp()->parent() == NULL;
+ const bool mayInterrupt = txn->getCurOp()->parent() == NULL;
txn->getCurOp()->setQuery(js);
- Status status = collection->getIndexCatalog()->createIndex(txn, js, mayInterrupt);
+ MultiIndexBlock indexer(txn, collection);
+ indexer.allowBackgroundBuilding();
+ if (mayInterrupt)
+ indexer.allowInterruption();
+
+ Status status = indexer.init(js);
if ( status.code() == ErrorCodes::IndexAlreadyExists )
- return;
+ return; // inserting an existing index is a no-op.
+ uassertStatusOK(status);
+ uassertStatusOK(indexer.insertAllDocumentsInCollection());
- uassertStatusOK( status );
+ WriteUnitOfWork wunit(txn->recoveryUnit());
+ indexer.commit();
repl::logOp(txn, "i", ns, js);
+ wunit.commit();
+
return;
}
@@ -820,6 +833,7 @@ namespace mongo {
if ( !fixed.getValue().isEmpty() )
js = fixed.getValue();
+ WriteUnitOfWork wunit(txn->recoveryUnit());
Collection* collection = ctx.db()->getCollection( txn, ns );
if ( !collection ) {
collection = ctx.db()->createCollection( txn, ns );
@@ -829,6 +843,7 @@ namespace mongo {
StatusWith<DiskLoc> status = collection->insertDocument( txn, js, true );
uassertStatusOK( status.getStatus() );
repl::logOp(txn, "i", ns, js);
+ wunit.commit();
}
NOINLINE_DECL void insertMulti(OperationContext* txn,
@@ -841,7 +856,6 @@ namespace mongo {
for (i=0; i<objs.size(); i++){
try {
checkAndInsert(txn, ctx, ns, objs[i]);
- txn->recoveryUnit()->commitIfNeeded();
} catch (const UserException& ex) {
if (!keepGoing || i == objs.size()-1){
globalOpCounters.incInsertInWriteLock(i);
@@ -891,7 +905,6 @@ namespace mongo {
if ( handlePossibleShardedMessage( m , 0 ) )
return;
- WriteUnitOfWork wunit(txn->recoveryUnit());
Client::Context ctx(txn, ns);
if (multi.size() > 1) {
@@ -902,7 +915,6 @@ namespace mongo {
globalOpCounters.incInsertInWriteLock(1);
op.debug().ninserted = 1;
}
- wunit.commit();
}
DBDirectClient::DBDirectClient()
diff --git a/src/mongo/db/pdfile_private.h b/src/mongo/db/pdfile_private.h
deleted file mode 100644
index 9ce405e29aa..00000000000
--- a/src/mongo/db/pdfile_private.h
+++ /dev/null
@@ -1,35 +0,0 @@
-/**
-* 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/>.
-*
-* 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.
-*/
-
-// This file contains declarations that should not be considered part of pdfile's
-// public interface, but are currently accessed by other modules within mongod.
-
-#pragma once
-namespace mongo {
- extern bool inDBRepair;
-}
diff --git a/src/mongo/db/repl/oplog.cpp b/src/mongo/db/repl/oplog.cpp
index dca80acef6f..5a288d6da18 100644
--- a/src/mongo/db/repl/oplog.cpp
+++ b/src/mongo/db/repl/oplog.cpp
@@ -588,7 +588,7 @@ namespace repl {
}
else {
IndexBuilder builder(o);
- Status status = builder.build(txn, db);
+ Status status = builder.buildInForeground(txn, db);
if ( status.isOK() ) {
// yay
}
@@ -633,8 +633,16 @@ namespace repl {
else {
// probably don't need this since all replicated colls have _id indexes now
// but keep it just in case
- RARELY if ( indexCatalog && !collection->isCapped() ) {
- indexCatalog->ensureHaveIdIndex(txn);
+ RARELY if ( indexCatalog
+ && !collection->isCapped()
+ && !indexCatalog->haveIdIndex() ) {
+ try {
+ Helpers::ensureIndex(txn, collection, BSON("_id" << 1), true, "_id_");
+ }
+ catch (const DBException& e) {
+ warning() << "Ignoring error building id index on " << collection->ns()
+ << ": " << e.toString();
+ }
}
/* todo : it may be better to do an insert here, and then catch the dup key exception and do update
@@ -662,8 +670,14 @@ namespace repl {
// probably don't need this since all replicated colls have _id indexes now
// but keep it just in case
- RARELY if ( indexCatalog && !collection->isCapped() ) {
- indexCatalog->ensureHaveIdIndex(txn);
+ RARELY if ( indexCatalog && !collection->isCapped() && !indexCatalog->haveIdIndex() ) {
+ try {
+ Helpers::ensureIndex(txn, collection, BSON("_id" << 1), true, "_id_");
+ }
+ catch (const DBException& e) {
+ warning() << "Ignoring error building id index on " << collection->ns()
+ << ": " << e.toString();
+ }
}
OpDebug debug;
diff --git a/src/mongo/db/repl/repl_set_impl.cpp b/src/mongo/db/repl/repl_set_impl.cpp
index 2d1eb935fac..afdbf06b622 100644
--- a/src/mongo/db/repl/repl_set_impl.cpp
+++ b/src/mongo/db/repl/repl_set_impl.cpp
@@ -470,7 +470,6 @@ namespace {
void ReplSetImpl::_go() {
OperationContextImpl txn;
- indexRebuilder.wait();
try {
loadLastOpTimeWritten(&txn);
}
diff --git a/src/mongo/db/storage/heap1/heap1_btree_impl.cpp b/src/mongo/db/storage/heap1/heap1_btree_impl.cpp
index 4864cdaca26..b5f8f6f3c75 100644
--- a/src/mongo/db/storage/heap1/heap1_btree_impl.cpp
+++ b/src/mongo/db/storage/heap1/heap1_btree_impl.cpp
@@ -85,18 +85,10 @@ namespace {
Heap1BtreeBuilderImpl(IndexSet* data, long long* currentKeySize, bool dupsAllowed)
: _data(data),
_currentKeySize( currentKeySize ),
- _dupsAllowed(dupsAllowed),
- _committed(false) {
+ _dupsAllowed(dupsAllowed) {
invariant(_data->empty());
}
- ~Heap1BtreeBuilderImpl() {
- if (!_committed) {
- _data->clear();
- *_currentKeySize = 0;
- }
- }
-
Status addKey(const BSONObj& key, const DiskLoc& loc) {
// inserts should be in ascending order.
@@ -120,16 +112,10 @@ namespace {
return Status::OK();
}
- unsigned long long commit(bool mayInterrupt) {
- _committed = true;
- return _data->size();
- }
-
private:
IndexSet* const _data;
long long* _currentKeySize;
const bool _dupsAllowed;
- bool _committed;
};
class Heap1BtreeImpl : public SortedDataInterface {
diff --git a/src/mongo/db/storage/mmap_v1/btree/btree_interface.cpp b/src/mongo/db/storage/mmap_v1/btree/btree_interface.cpp
index 2e98509ee7a..f1ca5d55427 100644
--- a/src/mongo/db/storage/mmap_v1/btree/btree_interface.cpp
+++ b/src/mongo/db/storage/mmap_v1/btree/btree_interface.cpp
@@ -47,10 +47,6 @@ namespace mongo {
return _builder->addKey(key, loc);
}
- unsigned long long commit(bool mayInterrupt) {
- return _builder->commit(mayInterrupt);
- }
-
private:
typename BtreeLogic<OnDiskFormat>::Builder* _builder;
diff --git a/src/mongo/db/storage/mmap_v1/btree/btree_logic.cpp b/src/mongo/db/storage/mmap_v1/btree/btree_logic.cpp
index 12551b4193b..b177cb42343 100644
--- a/src/mongo/db/storage/mmap_v1/btree/btree_logic.cpp
+++ b/src/mongo/db/storage/mmap_v1/btree/btree_logic.cpp
@@ -54,9 +54,6 @@ namespace mongo {
// we get to the root and it is full, a new root is created above the current root. When
// creating a new right sibling, it is set as its parent's nextChild as all keys in the right
// sibling will be higher than all keys currently in the parent.
- //
- // Phase 3 (the commit phase):
- // Does nothing. This may go away soon.
//
// Public Builder logic
@@ -74,7 +71,6 @@ namespace mongo {
bool dupsAllowed)
: _logic(logic),
_dupsAllowed(dupsAllowed),
- _numAdded(0),
_txn(txn) {
// The normal bulk building path calls initAsEmpty, so we already have an empty root bucket.
@@ -86,10 +82,8 @@ namespace mongo {
_logic->_headManager->setHead(_txn, _rightLeafLoc);
}
- _rightLeaf = _getModifiableBucket(_rightLeafLoc);
-
// must be empty when starting
- invariant(_rightLeaf->n == 0);
+ invariant(_getBucket(_rightLeafLoc)->n == 0);
}
template <class BtreeLayout>
@@ -105,7 +99,7 @@ namespace mongo {
}
// If we have a previous key to compare to...
- if (_numAdded > 0) {
+ if (_keyLast.get()) {
int cmp = _keyLast->woCompare(*key, _logic->_ordering);
// This shouldn't happen ever. We expect keys in sorted order.
@@ -118,25 +112,19 @@ namespace mongo {
return Status(ErrorCodes::DuplicateKey, _logic->dupKeyError(*_keyLast));
}
}
-
- if (!_logic->pushBack(_rightLeaf, loc, *key, DiskLoc())) {
+
+ BucketType* rightLeaf = _getModifiableBucket(_rightLeafLoc);
+ if (!_logic->pushBack(rightLeaf, loc, *key, DiskLoc())) {
// bucket was full, so split and try with the new node.
- _rightLeafLoc = newBucket(_rightLeaf, _rightLeafLoc);
- _rightLeaf = _getModifiableBucket(_rightLeafLoc);
- invariant(_logic->pushBack(_rightLeaf, loc, *key, DiskLoc()));
+ _rightLeafLoc = newBucket(rightLeaf, _rightLeafLoc);
+ rightLeaf = _getModifiableBucket(_rightLeafLoc);
+ invariant(_logic->pushBack(rightLeaf, loc, *key, DiskLoc()));
}
_keyLast = key;
- _numAdded++;
- mayCommitProgressDurably();
return Status::OK();
}
- template <class BtreeLayout>
- unsigned long long BtreeLogic<BtreeLayout>::Builder::commit(bool mayInterrupt) {
- return _numAdded;
- }
-
//
// Private Builder logic
//
@@ -187,13 +175,6 @@ namespace mongo {
}
template <class BtreeLayout>
- void BtreeLogic<BtreeLayout>::Builder::mayCommitProgressDurably() {
- if (_txn->recoveryUnit()->commitIfNeeded()) {
- _rightLeaf = _getModifiableBucket(_rightLeafLoc);
- }
- }
-
- template <class BtreeLayout>
typename BtreeLogic<BtreeLayout>::BucketType*
BtreeLogic<BtreeLayout>::Builder::_getModifiableBucket(DiskLoc loc) {
return _logic->btreemod(_txn, _logic->getBucket(loc));
diff --git a/src/mongo/db/storage/mmap_v1/btree/btree_logic.h b/src/mongo/db/storage/mmap_v1/btree/btree_logic.h
index 8a11a57ec83..6b086567af2 100644
--- a/src/mongo/db/storage/mmap_v1/btree/btree_logic.h
+++ b/src/mongo/db/storage/mmap_v1/btree/btree_logic.h
@@ -99,8 +99,6 @@ namespace mongo {
Status addKey(const BSONObj& key, const DiskLoc& loc);
- unsigned long long commit(bool mayInterrupt);
-
private:
friend class BtreeLogic;
@@ -113,17 +111,14 @@ namespace mongo {
*/
DiskLoc newBucket(BucketType* leftSib, DiskLoc leftSibLoc);
- void mayCommitProgressDurably();
BucketType* _getModifiableBucket(DiskLoc loc);
BucketType* _getBucket(DiskLoc loc);
// Not owned.
BtreeLogic* _logic;
- DiskLoc _rightLeafLoc; // DiskLoc of _rightLeaf
- BucketType* _rightLeaf; // This is always the right-most (highest) leaf bucket.
+ DiskLoc _rightLeafLoc; // DiskLoc of right-most (highest) leaf bucket.
bool _dupsAllowed;
- long long _numAdded;
auto_ptr<KeyDataOwnedType> _keyLast;
// Not owned.
diff --git a/src/mongo/db/storage/mmap_v1/repair_database.cpp b/src/mongo/db/storage/mmap_v1/repair_database.cpp
index 7b1acdd4a83..d1e9364876f 100644
--- a/src/mongo/db/storage/mmap_v1/repair_database.cpp
+++ b/src/mongo/db/storage/mmap_v1/repair_database.cpp
@@ -53,18 +53,6 @@ namespace mongo {
typedef boost::filesystem::path Path;
- // TODO SERVER-4328
- bool inDBRepair = false;
- struct doingRepair {
- doingRepair() {
- verify( ! inDBRepair );
- inDBRepair = true;
- }
- ~doingRepair() {
- inDBRepair = false;
- }
- };
-
// inheritable class to implement an operation that may be applied to all
// files in a database using _applyOpToDataFiles()
class FileOp {
@@ -285,7 +273,6 @@ namespace mongo {
invariant( dbName.find( '.' ) == string::npos );
scoped_ptr<RepairFileDeleter> repairFileDeleter;
- doingRepair dr;
log() << "repairDatabase " << dbName << endl;
@@ -386,7 +373,9 @@ namespace mongo {
Collection* tempCollection = NULL;
{
Client::Context tempContext(txn, ns, tempDatabase );
- tempCollection = tempDatabase->createCollection( txn, ns, options, true, false );
+ WriteUnitOfWork wunit(txn->recoveryUnit());
+ tempCollection = tempDatabase->createCollection(txn, ns, options, true, false);
+ wunit.commit();
}
Client::Context readContext(txn, ns, originalDatabase);
@@ -395,7 +384,8 @@ namespace mongo {
// data
- MultiIndexBlock indexBlock(txn, tempCollection );
+ // TODO SERVER-14812 add a mode that drops duplicates rather than failing
+ MultiIndexBlock indexer(txn, tempCollection );
{
vector<BSONObj> indexes;
IndexCatalog::IndexIterator ii =
@@ -406,10 +396,9 @@ namespace mongo {
}
Client::Context tempContext(txn, ns, tempDatabase);
- Status status = indexBlock.init( indexes );
+ Status status = indexer.init( indexes );
if ( !status.isOK() )
return status;
-
}
scoped_ptr<RecordIterator> iterator(
@@ -422,19 +411,28 @@ namespace mongo {
BSONObj doc = originalCollection->docFor( loc );
Client::Context tempContext(txn, ns, tempDatabase);
- StatusWith<DiskLoc> result = tempCollection->insertDocument( txn, doc, indexBlock );
+
+ WriteUnitOfWork wunit(txn->recoveryUnit());
+ StatusWith<DiskLoc> result = tempCollection->insertDocument(txn,
+ doc,
+ &indexer,
+ false);
if ( !result.isOK() )
return result.getStatus();
- txn->recoveryUnit()->commitIfNeeded();
+ wunit.commit();
txn->checkForInterrupt(false);
}
+
+ Status status = indexer.doneInserting();
+ if (!status.isOK())
+ return status;
{
Client::Context tempContext(txn, ns, tempDatabase);
- Status status = indexBlock.commit();
- if ( !status.isOK() )
- return status;
+ WriteUnitOfWork wunit(txn->recoveryUnit());
+ indexer.commit();
+ wunit.commit();
}
}
diff --git a/src/mongo/db/storage/rocks/rocks_sorted_data_impl.h b/src/mongo/db/storage/rocks/rocks_sorted_data_impl.h
index 87eefa16ae4..5004a042504 100644
--- a/src/mongo/db/storage/rocks/rocks_sorted_data_impl.h
+++ b/src/mongo/db/storage/rocks/rocks_sorted_data_impl.h
@@ -48,7 +48,6 @@ namespace mongo {
class RocksSortedDataBuilderImpl : public SortedDataBuilderInterface {
public:
virtual Status addKey(const BSONObj& key, const DiskLoc& loc) = 0;
- virtual unsigned long long commit(bool mayInterrupt) = 0;
};
/**
diff --git a/src/mongo/db/storage/sorted_data_interface.h b/src/mongo/db/storage/sorted_data_interface.h
index bb87c33e876..486440f1252 100644
--- a/src/mongo/db/storage/sorted_data_interface.h
+++ b/src/mongo/db/storage/sorted_data_interface.h
@@ -194,12 +194,15 @@ namespace mongo {
virtual Status addKey(const BSONObj& key, const DiskLoc& loc) = 0;
/**
- * commit work. if not called, destructor will clean up partially completed work
- * (in case exception has happened).
+ * Do any necessary work to finish building the tree.
*
- * Returns number of keys added.
+ * The default implementation may be used if no commit phase is necessary because addKey
+ * always leaves the tree in a valid state.
+ *
+ * This is called outside of any WriteUnitOfWork to allow implementations to split this up
+ * into multiple units.
*/
- virtual unsigned long long commit(bool mayInterrupt) = 0;
+ virtual void commit(bool mayInterrupt) {}
};
} // namespace mongo
diff --git a/src/mongo/dbtests/counttests.cpp b/src/mongo/dbtests/counttests.cpp
index 879d4353a0e..b18fb31af79 100644
--- a/src/mongo/dbtests/counttests.cpp
+++ b/src/mongo/dbtests/counttests.cpp
@@ -31,6 +31,7 @@
#include <boost/thread/thread.hpp>
#include "mongo/db/db.h"
+#include "mongo/db/dbhelpers.h"
#include "mongo/db/json.h"
#include "mongo/db/commands/count.h"
#include "mongo/db/catalog/collection.h"
@@ -66,13 +67,11 @@ namespace CountTests {
return "unittests.counttests";
}
void addIndex( const BSONObj &key ) {
- BSONObjBuilder b;
- b.append( "name", key.firstElementFieldName() );
- b.append( "ns", ns() );
- b.append( "key", key );
- BSONObj o = b.done();
- Status s = _collection->getIndexCatalog()->createIndex(&_txn, o, false);
- uassertStatusOK( s );
+ Helpers::ensureIndex(&_txn,
+ _collection,
+ key,
+ /*unique=*/ false,
+ /*name=*/ key.firstElementFieldName());
}
void insert( const char *s ) {
insert( fromjson( s ) );
diff --git a/src/mongo/dbtests/indexcatalogtests.cpp b/src/mongo/dbtests/indexcatalogtests.cpp
index 21fb94ca24b..7236f6f0b5a 100644
--- a/src/mongo/dbtests/indexcatalogtests.cpp
+++ b/src/mongo/dbtests/indexcatalogtests.cpp
@@ -18,6 +18,7 @@
#include "mongo/db/catalog/index_catalog.h"
#include "mongo/db/db.h"
+#include "mongo/db/dbhelpers.h"
#include "mongo/db/index/index_descriptor.h"
#include "mongo/db/catalog/collection.h"
#include "mongo/db/operation_context_impl.h"
@@ -54,17 +55,8 @@ namespace IndexCatalogTests {
int numFinishedIndexesStart = _catalog->numIndexesReady();
- BSONObjBuilder b1;
- b1.append("key", BSON("x" << 1));
- b1.append("ns", _ns);
- b1.append("name", "_x_0");
- _catalog->createIndex(&txn, b1.obj(), true);
-
- BSONObjBuilder b2;
- b2.append("key", BSON("y" << 1));
- b2.append("ns", _ns);
- b2.append("name", "_y_0");
- _catalog->createIndex(&txn, b2.obj(), true);
+ Helpers::ensureIndex(&txn, _coll, BSON("x" << 1), false, "_x_0");
+ Helpers::ensureIndex(&txn, _coll, BSON("y" << 1), false, "_y_0");
ASSERT_TRUE(_catalog->numIndexesReady() == numFinishedIndexesStart+2);
diff --git a/src/mongo/dbtests/indexupdatetests.cpp b/src/mongo/dbtests/indexupdatetests.cpp
index 06013e481e1..3e4d4566b25 100644
--- a/src/mongo/dbtests/indexupdatetests.cpp
+++ b/src/mongo/dbtests/indexupdatetests.cpp
@@ -35,6 +35,7 @@
#include "mongo/db/index/btree_based_bulk_access_method.h"
#include "mongo/db/index/index_descriptor.h"
#include "mongo/db/catalog/collection.h"
+#include "mongo/db/catalog/index_create.h"
#include "mongo/db/operation_context_impl.h"
#include "mongo/platform/cstdint.h"
@@ -92,6 +93,27 @@ namespace IndexUpdateTests {
}
#endif
+ bool buildIndexInterrupted(const BSONObj& key, bool allowInterruption) {
+ try {
+ MultiIndexBlock indexer(&_txn, collection());
+ if (allowInterruption)
+ indexer.allowInterruption();
+
+ uassertStatusOK(indexer.init(key));
+ uassertStatusOK(indexer.insertAllDocumentsInCollection());
+ WriteUnitOfWork wunit(_txn.recoveryUnit());
+ indexer.commit();
+ wunit.commit();
+ }
+ catch (const DBException& e) {
+ if (ErrorCodes::isInterruption(ErrorCodes::Error(e.getCode())))
+ return true;
+
+ throw;
+ }
+ return false;
+ }
+
OperationContextImpl _txn;
Client::WriteContext _ctx;
DBDirectClient _client;
@@ -312,6 +334,108 @@ namespace IndexUpdateTests {
bool _mayInterrupt;
};
#endif
+ /** Index creation ignores unique constraints when told to. */
+ template <bool background>
+ class InsertBuildIgnoreUnique : public IndexBuildBase {
+ public:
+ void run() {
+ // Create a new collection.
+ Database* db = _ctx.ctx().db();
+ db->dropCollection( &_txn, _ns );
+ Collection* coll = db->createCollection( &_txn, _ns );
+
+ coll->insertDocument( &_txn, BSON( "_id" << 1 << "a" << "dup" ), true );
+ coll->insertDocument( &_txn, BSON( "_id" << 2 << "a" << "dup" ), true );
+
+ MultiIndexBlock indexer(&_txn, coll);
+ indexer.allowBackgroundBuilding();
+ indexer.ignoreUniqueConstraint();
+
+ const BSONObj spec = BSON("name" << "a"
+ << "ns" << coll->ns().ns()
+ << "key" << BSON("a" << 1)
+ << "unique" << true
+ << "background" << background);
+
+ ASSERT_OK(indexer.init(spec));
+ ASSERT_OK(indexer.insertAllDocumentsInCollection());
+
+ WriteUnitOfWork wunit(_txn.recoveryUnit());
+ indexer.commit();
+ wunit.commit();
+ }
+ };
+
+ /** Index creation enforces unique constraints unless told not to. */
+ template <bool background>
+ class InsertBuildEnforceUnique : public IndexBuildBase {
+ public:
+ void run() {
+ // Create a new collection.
+ Database* db = _ctx.ctx().db();
+ db->dropCollection( &_txn, _ns );
+ Collection* coll = db->createCollection( &_txn, _ns );
+
+ coll->insertDocument( &_txn, BSON( "_id" << 1 << "a" << "dup" ), true );
+ coll->insertDocument( &_txn, BSON( "_id" << 2 << "a" << "dup" ), true );
+
+ MultiIndexBlock indexer(&_txn, coll);
+ indexer.allowBackgroundBuilding();
+ // indexer.ignoreUniqueConstraint(); // not calling this
+
+ const BSONObj spec = BSON("name" << "a"
+ << "ns" << coll->ns().ns()
+ << "key" << BSON("a" << 1)
+ << "unique" << true
+ << "background" << background);
+
+ ASSERT_OK(indexer.init(spec));
+ const Status status = indexer.insertAllDocumentsInCollection();
+ ASSERT_EQUALS(status.code(), ErrorCodes::DuplicateKey);
+ }
+ };
+
+ /** Index creation fills a passed-in set of dups rather than failing. */
+ template <bool background>
+ class InsertBuildFillDups : public IndexBuildBase {
+ public:
+ void run() {
+ // Create a new collection.
+ Database* db = _ctx.ctx().db();
+ db->dropCollection( &_txn, _ns );
+ Collection* coll = db->createCollection( &_txn, _ns );
+
+ StatusWith<DiskLoc> loc1 = coll->insertDocument(&_txn,
+ BSON("_id" << 1 << "a" << "dup"),
+ true);
+ StatusWith<DiskLoc> loc2 = coll->insertDocument(&_txn,
+ BSON("_id" << 2 << "a" << "dup"),
+ true);
+
+ ASSERT_OK(loc1.getStatus());
+ ASSERT_OK(loc2.getStatus());
+
+
+ MultiIndexBlock indexer(&_txn, coll);
+ indexer.allowBackgroundBuilding();
+ // indexer.ignoreUniqueConstraint(); // not calling this
+
+ const BSONObj spec = BSON("name" << "a"
+ << "ns" << coll->ns().ns()
+ << "key" << BSON("a" << 1)
+ << "unique" << true
+ << "background" << background);
+
+ ASSERT_OK(indexer.init(spec));
+
+ std::set<DiskLoc> dups;
+ ASSERT_OK(indexer.insertAllDocumentsInCollection(&dups));
+
+ // either loc1 or loc2 should be in dups but not both.
+ ASSERT_EQUALS(dups.size(), 1U);
+ ASSERT(dups.count(loc1.getValue()) || dups.count(loc2.getValue()));
+ }
+ };
/** Index creation is killed if mayInterrupt is true. */
class InsertBuildIndexInterrupt : public IndexBuildBase {
@@ -334,8 +458,7 @@ namespace IndexUpdateTests {
getGlobalEnvironment()->setKillAllOperations();
BSONObj indexInfo = BSON( "key" << BSON( "a" << 1 ) << "ns" << _ns << "name" << "a_1" );
// The call is interrupted because mayInterrupt == true.
- Status status = coll->getIndexCatalog()->createIndex(&_txn, indexInfo, true );
- ASSERT_NOT_OK( status.code() );
+ ASSERT_TRUE(buildIndexInterrupted(indexInfo, true));
// only want to interrupt the index build
getGlobalEnvironment()->unsetKillAllOperations();
// The new index is not listed in the index catalog because the index build failed.
@@ -363,8 +486,7 @@ namespace IndexUpdateTests {
getGlobalEnvironment()->setKillAllOperations();
BSONObj indexInfo = BSON( "key" << BSON( "a" << 1 ) << "ns" << _ns << "name" << "a_1" );
// The call is not interrupted because mayInterrupt == false.
- Status status = coll->getIndexCatalog()->createIndex(&_txn, indexInfo, false );
- ASSERT_OK( status.code() );
+ ASSERT_FALSE(buildIndexInterrupted(indexInfo, false));
// only want to interrupt the index build
getGlobalEnvironment()->unsetKillAllOperations();
// The new index is listed in the index catalog because the index build completed.
@@ -397,8 +519,7 @@ namespace IndexUpdateTests {
"ns" << _ns <<
"name" << "_id_" );
// The call is interrupted because mayInterrupt == true.
- Status status = coll->getIndexCatalog()->createIndex(&_txn, indexInfo, true );
- ASSERT_NOT_OK( status.code() );
+ ASSERT_TRUE(buildIndexInterrupted(indexInfo, true));
// only want to interrupt the index build
getGlobalEnvironment()->unsetKillAllOperations();
// The new index is not listed in the index catalog because the index build failed.
@@ -431,8 +552,7 @@ namespace IndexUpdateTests {
"ns" << _ns <<
"name" << "_id_" );
// The call is not interrupted because mayInterrupt == false.
- Status status = coll->getIndexCatalog()->createIndex(&_txn, indexInfo, false );
- ASSERT_OK( status.code() );
+ ASSERT_FALSE(buildIndexInterrupted(indexInfo, false));
// only want to interrupt the index build
getGlobalEnvironment()->unsetKillAllOperations();
// The new index is listed in the index catalog because the index build succeeded.
@@ -709,6 +829,12 @@ namespace IndexUpdateTests {
//add<BuildBottomUp>();
//add<InterruptBuildBottomUp>( false );
//add<InterruptBuildBottomUp>( true );
+ add<InsertBuildIgnoreUnique<true> >();
+ add<InsertBuildIgnoreUnique<false> >();
+ add<InsertBuildEnforceUnique<true> >();
+ add<InsertBuildEnforceUnique<false> >();
+ add<InsertBuildFillDups<true> >();
+ add<InsertBuildFillDups<false> >();
add<InsertBuildIndexInterrupt>();
add<InsertBuildIndexInterruptDisallowed>();
add<InsertBuildIdIndexInterrupt>();
diff --git a/src/mongo/dbtests/oplogstarttests.cpp b/src/mongo/dbtests/oplogstarttests.cpp
index 7a8995aba0c..d23a85456a1 100644
--- a/src/mongo/dbtests/oplogstarttests.cpp
+++ b/src/mongo/dbtests/oplogstarttests.cpp
@@ -44,7 +44,7 @@ namespace OplogStartTests {
if (!c) {
c = _context.db()->createCollection(&_txn, ns());
}
- c->getIndexCatalog()->ensureHaveIdIndex(&_txn);
+ ASSERT(c->getIndexCatalog()->haveIdIndex());
}
~Base() {
diff --git a/src/mongo/dbtests/querytests.cpp b/src/mongo/dbtests/querytests.cpp
index d68f473cb49..e23f524f7ae 100644
--- a/src/mongo/dbtests/querytests.cpp
+++ b/src/mongo/dbtests/querytests.cpp
@@ -89,13 +89,7 @@ namespace QueryTests {
return "unittests.querytests";
}
void addIndex( const BSONObj &key ) {
- BSONObjBuilder b;
- b.append( "name", key.firstElementFieldName() );
- b.append( "ns", ns() );
- b.append( "key", key );
- BSONObj o = b.done();
- Status s = _collection->getIndexCatalog()->createIndex(&_txn, o, false);
- uassertStatusOK( s );
+ Helpers::ensureIndex(&_txn, _collection, key, false, key.firstElementFieldName());
}
void insert( const char *s ) {
insert( fromjson( s ) );
diff --git a/src/mongo/dbtests/repltests.cpp b/src/mongo/dbtests/repltests.cpp
index dc00e4f6e7e..0c385d51db4 100644
--- a/src/mongo/dbtests/repltests.cpp
+++ b/src/mongo/dbtests/repltests.cpp
@@ -83,7 +83,8 @@ namespace ReplTests {
c = ctx.ctx().db()->createCollection(&_txn, ns());
}
- c->getIndexCatalog()->ensureHaveIdIndex(&_txn);
+ ASSERT(c->getIndexCatalog()->haveIdIndex());
+ ctx.commit();
}
~Base() {
try {
diff --git a/src/mongo/s/d_migrate.cpp b/src/mongo/s/d_migrate.cpp
index f5520b63dde..99ee08c6e65 100644
--- a/src/mongo/s/d_migrate.cpp
+++ b/src/mongo/s/d_migrate.cpp
@@ -52,6 +52,7 @@
#include "mongo/db/auth/authorization_manager_global.h"
#include "mongo/db/auth/authorization_session.h"
#include "mongo/db/auth/privilege.h"
+#include "mongo/db/catalog/index_create.h"
#include "mongo/db/clientcursor.h"
#include "mongo/db/commands.h"
#include "mongo/db/dbhelpers.h"
@@ -1743,41 +1744,59 @@ namespace mongo {
{
// 1. copy indexes
- vector<BSONObj> all;
+ vector<BSONObj> indexSpecs;
{
auto_ptr<DBClientCursor> indexes = conn->getIndexes( ns );
while ( indexes->more() ) {
- all.push_back( indexes->nextSafe().getOwned() );
+ indexSpecs.push_back( indexes->nextSafe().getOwned() );
}
}
- for ( unsigned i=0; i<all.size(); i++ ) {
- BSONObj idx = all[i];
- Client::WriteContext ctx(txn, ns );
- Database* db = ctx.ctx().db();
- Collection* collection = db->getCollection( txn, ns );
- if ( !collection ) {
- errmsg = str::stream() << "collection dropped during migration: " << ns;
+ Lock::DBWrite lk(txn->lockState(), ns);
+ Client::Context ctx(txn, ns);
+ Database* db = ctx.db();
+ Collection* collection = db->getCollection( txn, ns );
+ if ( !collection ) {
+ errmsg = str::stream() << "collection dropped during migration: " << ns;
+ warning() << errmsg;
+ setState(FAIL);
+ return;
+ }
+
+ MultiIndexBlock indexer(txn, collection);
+
+ indexer.removeExistingIndexes(&indexSpecs);
+
+ if (!indexSpecs.empty()) {
+ Status status = indexer.init(indexSpecs);
+ if ( !status.isOK() ) {
+ errmsg = str::stream() << "failed to create index before migrating data. "
+ << " error: " << status.toString();
warning() << errmsg;
setState(FAIL);
return;
}
- Status status = collection->getIndexCatalog()->createIndex(txn, idx, false);
- if ( !status.isOK() && status.code() != ErrorCodes::IndexAlreadyExists ) {
+ status = indexer.insertAllDocumentsInCollection();
+ if ( !status.isOK() ) {
errmsg = str::stream() << "failed to create index before migrating data. "
- << " idx: " << idx
<< " error: " << status.toString();
warning() << errmsg;
setState(FAIL);
return;
}
- // make sure to create index on secondaries as well
- repl::logOp(txn, "i", db->getSystemIndexesName().c_str(), idx,
- NULL, NULL, true /* fromMigrate */);
- ctx.commit();
+ WriteUnitOfWork wunit(txn->recoveryUnit());
+ indexer.commit();
+
+ for (size_t i = 0; i < indexSpecs.size(); i++) {
+ // make sure to create index on secondaries as well
+ repl::logOp(txn, "i", db->getSystemIndexesName().c_str(), indexSpecs[i],
+ NULL, NULL, true /* fromMigrate */);
+ }
+
+ wunit.commit();
}
timing.done(1);