/** * Copyright (C) 2013 10gen Inc. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . * * As a special exception, the copyright holders give permission to link the * code of portions of this program with the OpenSSL library under certain * conditions as described in each individual source file and distribute * linked combinations including the program with the OpenSSL library. You * must comply with the GNU Affero General Public License in all respects for * all of the code used other than as permitted herein. If you modify file(s) * with this exception, you may extend this exception to your version of the * file(s), but you are not obligated to do so. If you do not wish to do so, * delete this exception statement from your version. If you delete this * exception statement from all source files in the program, then also delete * it in the license file. */ #include "mongo/db/index/btree_based_builder.h" #include "mongo/db/btreebuilder.h" #include "mongo/db/index/catalog_hack.h" #include "mongo/db/index/index_descriptor.h" #include "mongo/db/index/index_access_method.h" #include "mongo/db/kill_current_op.h" #include "mongo/db/pdfile_private.h" #include "mongo/db/query/internal_plans.h" #include "mongo/db/repl/is_master.h" #include "mongo/db/repl/rs.h" #include "mongo/db/sort_phase_one.h" #include "mongo/util/processinfo.h" namespace mongo { int oldCompare(const BSONObj& l,const BSONObj& r, const Ordering &o); // key.cpp class ExternalSortComparisonV0 : public ExternalSortComparison { public: ExternalSortComparisonV0(const BSONObj& ordering) : _ordering(Ordering::make(ordering)) { } virtual ~ExternalSortComparisonV0() { } virtual int compare(const ExternalSortDatum& l, const ExternalSortDatum& r) const { int x = oldCompare(l.first, r.first, _ordering); if (x) { return x; } return l.second.compare(r.second); } private: const Ordering _ordering; }; class ExternalSortComparisonV1 : public ExternalSortComparison { public: ExternalSortComparisonV1(const BSONObj& ordering) : _ordering(Ordering::make(ordering)) { } virtual ~ExternalSortComparisonV1() { } virtual int compare(const ExternalSortDatum& l, const ExternalSortDatum& r) const { int x = l.first.woCompare(r.first, _ordering, /*considerfieldname*/false); if (x) { return x; } return l.second.compare(r.second); } private: const Ordering _ordering; }; template< class V > void buildBottomUpPhases2And3( bool dupsAllowed, IndexDetails& idx, BSONObjExternalSorter& sorter, bool dropDups, set& dupsToDrop, CurOp* op, SortPhaseOne* phase1, ProgressMeterHolder& pm, Timer& t, bool mayInterrupt ) { BtreeBuilder btBuilder(dupsAllowed, idx); BSONObj keyLast; auto_ptr i = sorter.iterator(); // verifies that pm and op refer to the same ProgressMeter verify(pm == op->setMessage("index: (2/3) btree bottom up", "Index: (2/3) BTree Bottom Up Progress", phase1->nkeys, 10)); while( i->more() ) { RARELY killCurrentOp.checkForInterrupt( !mayInterrupt ); ExternalSortDatum d = i->next(); try { if ( !dupsAllowed && dropDups ) { LastError::Disabled led( lastError.get() ); btBuilder.addKey(d.first, d.second); } else { btBuilder.addKey(d.first, d.second); } } catch( AssertionException& e ) { if ( dupsAllowed ) { // unknown exception?? throw; } if( e.interrupted() ) { killCurrentOp.checkForInterrupt(); } if ( ! dropDups ) throw; /* we could queue these on disk, but normally there are very few dups, so instead we keep in ram and have a limit. */ dupsToDrop.insert(d.second); uassert( 10092 , "too may dups on index build with dropDups=true", dupsToDrop.size() < 1000000 ); } pm.hit(); } pm.finished(); op->setMessage("index: (3/3) btree-middle", "Index: (3/3) BTree Middle Progress"); LOG(t.seconds() > 10 ? 0 : 1 ) << "\t done building bottom layer, going to commit" << endl; btBuilder.commit( mayInterrupt ); if ( btBuilder.getn() != phase1->nkeys && ! dropDups ) { warning() << "not all entries were added to the index, probably some " "keys were too large" << endl; } } DiskLoc BtreeBasedBuilder::makeEmptyIndex(const IndexDetails& idx) { if (0 == idx.version()) { return BtreeBucket::addBucket(idx); } else { return BtreeBucket::addBucket(idx); } } ExternalSortComparison* BtreeBasedBuilder::getComparison(int version, const BSONObj& keyPattern) { if (0 == version) { return new ExternalSortComparisonV0(keyPattern); } else { verify(1 == version); return new ExternalSortComparisonV1(keyPattern); } } void BtreeBasedBuilder::addKeysToPhaseOne(NamespaceDetails* d, const char* ns, const IndexDetails& idx, const BSONObj& order, SortPhaseOne* phaseOne, int64_t nrecords, ProgressMeter* progressMeter, bool mayInterrupt, int idxNo) { auto_ptr runner(InternalPlanner::collectionScan(ns)); phaseOne->sortCmp.reset(getComparison(idx.version(), idx.keyPattern())); phaseOne->sorter.reset(new BSONObjExternalSorter(phaseOne->sortCmp.get())); phaseOne->sorter->hintNumObjects( nrecords ); auto_ptr desc(CatalogHack::getDescriptor(d, idxNo)); auto_ptr iam(CatalogHack::getBtreeBasedIndex(desc.get())); BSONObj o; DiskLoc loc; Runner::RunnerState state; while (Runner::RUNNER_ADVANCED == (state = runner->getNext(&o, &loc))) { RARELY killCurrentOp.checkForInterrupt( !mayInterrupt ); BSONObjSet keys; iam->getKeys(o, &keys); phaseOne->addKeys(keys, loc, mayInterrupt); progressMeter->hit(); if (logger::globalLogDomain()->shouldLog(logger::LogSeverity::Debug(2)) && phaseOne->n % 10000 == 0 ) { printMemInfo( "\t iterating objects" ); } } uassert(17050, "Internal error reading docs from collection", Runner::RUNNER_EOF == state); } uint64_t BtreeBasedBuilder::fastBuildIndex(const char* ns, NamespaceDetails* d, IndexDetails& idx, bool mayInterrupt, int idxNo) { CurOp * op = cc().curop(); Timer t; MONGO_TLOG(1) << "fastBuildIndex " << ns << ' ' << idx.info.obj().toString() << endl; bool dupsAllowed = !idx.unique() || ignoreUniqueIndex(idx); bool dropDups = idx.dropDups() || inDBRepair; BSONObj order = idx.keyPattern(); getDur().writingDiskLoc(idx.head).Null(); if ( logger::globalLogDomain()->shouldLog(logger::LogSeverity::Debug(2) ) ) printMemInfo( "before index start" ); /* get and sort all the keys ----- */ ProgressMeterHolder pm(op->setMessage("index: (1/3) external sort", "Index: (1/3) External Sort Progress", d->numRecords(), 10)); SortPhaseOne phase1; addKeysToPhaseOne(d, ns, idx, order, &phase1, d->numRecords(), pm.get(), mayInterrupt, idxNo ); pm.finished(); BSONObjExternalSorter& sorter = *(phase1.sorter); if( phase1.multi ) { d->setIndexIsMultikey(ns, idxNo); } if ( logger::globalLogDomain()->shouldLog(logger::LogSeverity::Debug(2) ) ) printMemInfo( "before final sort" ); phase1.sorter->sort( mayInterrupt ); if ( logger::globalLogDomain()->shouldLog(logger::LogSeverity::Debug(2) ) ) printMemInfo( "after final sort" ); LOG(t.seconds() > 5 ? 0 : 1) << "\t external sort used : " << sorter.numFiles() << " files " << " in " << t.seconds() << " secs" << endl; set dupsToDrop; /* build index --- */ if( idx.version() == 0 ) buildBottomUpPhases2And3(dupsAllowed, idx, sorter, dropDups, dupsToDrop, op, &phase1, pm, t, mayInterrupt); else if( idx.version() == 1 ) buildBottomUpPhases2And3(dupsAllowed, idx, sorter, dropDups, dupsToDrop, op, &phase1, pm, t, mayInterrupt); else verify(false); if( dropDups ) log() << "\t fastBuildIndex dupsToDrop:" << dupsToDrop.size() << endl; BtreeBasedBuilder::doDropDups(ns, d, dupsToDrop, mayInterrupt); return phase1.n; } void BtreeBasedBuilder::doDropDups(const char* ns, NamespaceDetails* d, const set& dupsToDrop, bool mayInterrupt) { for( set::const_iterator i = dupsToDrop.begin(); i != dupsToDrop.end(); ++i ) { RARELY killCurrentOp.checkForInterrupt( !mayInterrupt ); theDataFileMgr.deleteRecord( d, ns, i->rec(), *i, false /* cappedOk */, true /* noWarn */, isMaster( ns ) /* logOp */ ); getDur().commitIfNeeded(); } } } // namespace mongo