Status MultiIndexBlock::insertAllDocumentsInCollection(std::set<DiskLoc>* dupsOut) { const char* curopMessage = _buildInBackground ? "Index Build (background)" : "Index Build"; ProgressMeter* progress = _txn->setMessage(curopMessage, curopMessage, _collection->numRecords(_txn)); 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); 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; } } if (shouldCommitWUnit) wunit.commit(); } n++; progress->hit(); if (_allowInterruption) _txn->checkForInterrupt(); progress->setTotalWhileRunning( _collection->numRecords(_txn) ); } 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(); }
virtual void subthread(int tnumber) { Client::initThread("mongomutextest"); const ServiceContext::UniqueOperationContext txnPtr = cc().makeOperationContext(); OperationContext& txn = *txnPtr; sleepmillis(0); for (int i = 0; i < N; i++) { int x = std::rand(); bool sometimes = (x % 15 == 0); if (i % 7 == 0) { Lock::GlobalRead r(txn.lockState()); // nested test Lock::GlobalRead r2(txn.lockState()); } else if (i % 7 == 1) { Lock::GlobalRead r(txn.lockState()); ASSERT(txn.lockState()->isReadLocked()); } else if (i % 7 == 4 && tnumber == 1 /*only one upgrader legal*/) { Lock::GlobalWrite w(txn.lockState()); ASSERT(txn.lockState()->isW()); if (i % 7 == 2) { Lock::TempRelease t(txn.lockState()); } } else if (i % 7 == 2) { Lock::GlobalWrite w(txn.lockState()); ASSERT(txn.lockState()->isW()); if (sometimes) { Lock::TempRelease t(txn.lockState()); } } else if (i % 7 == 3) { Lock::GlobalWrite w(txn.lockState()); { Lock::TempRelease t(txn.lockState()); } Lock::GlobalRead r(txn.lockState()); ASSERT(txn.lockState()->isW()); if (sometimes) { Lock::TempRelease t(txn.lockState()); } } else if (i % 7 == 5) { { ScopedTransaction scopedXact(&txn, MODE_IS); Lock::DBLock r(txn.lockState(), "foo", MODE_S); } { ScopedTransaction scopedXact(&txn, MODE_IS); Lock::DBLock r(txn.lockState(), "bar", MODE_S); } } else if (i % 7 == 6) { if (i > N / 2) { int q = i % 11; if (q == 0) { ScopedTransaction scopedXact(&txn, MODE_IS); Lock::DBLock r(txn.lockState(), "foo", MODE_S); ASSERT(txn.lockState()->isDbLockedForMode("foo", MODE_S)); Lock::DBLock r2(txn.lockState(), "foo", MODE_S); ASSERT(txn.lockState()->isDbLockedForMode("foo", MODE_S)); Lock::DBLock r3(txn.lockState(), "local", MODE_S); ASSERT(txn.lockState()->isDbLockedForMode("foo", MODE_S)); ASSERT(txn.lockState()->isDbLockedForMode("local", MODE_S)); } else if (q == 1) { // test locking local only -- with no preceding lock { ScopedTransaction scopedXact(&txn, MODE_IS); Lock::DBLock x(txn.lockState(), "local", MODE_S); } { ScopedTransaction scopedXact(&txn, MODE_IX); Lock::DBLock x(txn.lockState(), "local", MODE_X); // No actual writing here, so no WriteUnitOfWork if (sometimes) { Lock::TempRelease t(txn.lockState()); } } } else if (q == 1) { { ScopedTransaction scopedXact(&txn, MODE_IS); Lock::DBLock x(txn.lockState(), "admin", MODE_S); } { ScopedTransaction scopedXact(&txn, MODE_IX); Lock::DBLock x(txn.lockState(), "admin", MODE_X); } } else if (q == 3) { ScopedTransaction scopedXact(&txn, MODE_IX); Lock::DBLock x(txn.lockState(), "foo", MODE_X); Lock::DBLock y(txn.lockState(), "admin", MODE_S); } else if (q == 4) { ScopedTransaction scopedXact(&txn, MODE_IS); Lock::DBLock x(txn.lockState(), "foo2", MODE_S); Lock::DBLock y(txn.lockState(), "admin", MODE_S); } else { ScopedTransaction scopedXact(&txn, MODE_IX); Lock::DBLock w(txn.lockState(), "foo", MODE_X); { Lock::TempRelease t(txn.lockState()); } Lock::DBLock r2(txn.lockState(), "foo", MODE_S); Lock::DBLock r3(txn.lockState(), "local", MODE_S); } } else { ScopedTransaction scopedXact(&txn, MODE_IS); Lock::DBLock r(txn.lockState(), "foo", MODE_S); Lock::DBLock r2(txn.lockState(), "foo", MODE_S); Lock::DBLock r3(txn.lockState(), "local", MODE_S); } } pm.hit(); } }
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<Runner> runner(InternalPlanner::collectionScan(ns,collection)); std::string idxName = descriptor->indexName(); // 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. BSONObj js; DiskLoc loc; while (Runner::RUNNER_ADVANCED == runner->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 runnerEOF = runner->isEOF(); runner->saveState(); BSONObj toDelete; collection->deleteDocument( txn, loc, false, true, &toDelete ); repl::logOp(txn, "d", ns.c_str(), toDelete); if (!runner->restoreState(txn)) { // Runner got killed somehow. This probably shouldn't happen. if (runnerEOF) { // 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; }