void WiredTigerSession::releaseCursor(uint64_t id, WT_CURSOR* cursor) { invariant(_session); invariant(cursor); _cursorsOut--; invariantWTOK(cursor->reset(cursor)); // Cursors are pushed to the front of the list and removed from the back _cursors.push_front(WiredTigerCachedCursor(id, _cursorGen++, cursor)); // A negative value for wiredTigercursorCacheSize means to use hybrid caching. std::uint32_t cacheSize = abs(kWiredTigerCursorCacheSize.load()); while (!_cursors.empty() && _cursorGen - _cursors.back()._gen > cacheSize) { cursor = _cursors.back()._cursor; _cursors.pop_back(); invariantWTOK(cursor->close(cursor)); } }
StatusWith<std::vector<BSONObj>> MultiIndexBlockImpl::init(const std::vector<BSONObj>& indexSpecs) { WriteUnitOfWork wunit(_opCtx); invariant(_indexes.empty()); _opCtx->recoveryUnit()->registerChange(new CleanupIndexesVectorOnRollback(this)); const string& ns = _collection->ns().ns(); const auto idxCat = _collection->getIndexCatalog(); invariant(idxCat); invariant(idxCat->ok()); Status status = idxCat->checkUnfinished(); if (!status.isOK()) return status; for (size_t i = 0; i < indexSpecs.size(); i++) { BSONObj info = indexSpecs[i]; string pluginName = IndexNames::findPluginName(info["key"].Obj()); if (pluginName.size()) { Status s = _collection->getIndexCatalog()->_upgradeDatabaseMinorVersionIfNeeded( _opCtx, pluginName); if (!s.isOK()) return s; } // Any foreground indexes make all indexes be built in the foreground. _buildInBackground = (_buildInBackground && info["background"].trueValue()); } std::vector<BSONObj> indexInfoObjs; indexInfoObjs.reserve(indexSpecs.size()); std::size_t eachIndexBuildMaxMemoryUsageBytes = 0; if (!indexSpecs.empty()) { eachIndexBuildMaxMemoryUsageBytes = static_cast<std::size_t>(maxIndexBuildMemoryUsageMegabytes.load()) * 1024 * 1024 / indexSpecs.size(); } for (size_t i = 0; i < indexSpecs.size(); i++) { BSONObj info = indexSpecs[i]; StatusWith<BSONObj> statusWithInfo = _collection->getIndexCatalog()->prepareSpecForCreate(_opCtx, info); Status status = statusWithInfo.getStatus(); if (!status.isOK()) return status; info = statusWithInfo.getValue(); indexInfoObjs.push_back(info); IndexToBuild index; index.block.reset(new IndexCatalogImpl::IndexBuildBlock(_opCtx, _collection, info)); status = index.block->init(); if (!status.isOK()) return status; index.real = index.block->getEntry()->accessMethod(); status = index.real->initializeAsEmpty(_opCtx); if (!status.isOK()) return status; if (!_buildInBackground) { // Bulk build process requires foreground building as it assumes nothing is changing // under it. index.bulk = index.real->initiateBulk(eachIndexBuildMaxMemoryUsageBytes); } const IndexDescriptor* descriptor = index.block->getEntry()->descriptor(); IndexCatalog::prepareInsertDeleteOptions(_opCtx, descriptor, &index.options); index.options.dupsAllowed = index.options.dupsAllowed || _ignoreUnique; if (_ignoreUnique) { index.options.getKeysMode = IndexAccessMethod::GetKeysMode::kRelaxConstraints; } log() << "build index on: " << ns << " properties: " << descriptor->toString(); if (index.bulk) log() << "\t building index using bulk method; build may temporarily use up to " << eachIndexBuildMaxMemoryUsageBytes / 1024 / 1024 << " megabytes of RAM"; index.filterExpression = index.block->getEntry()->getFilterExpression(); // TODO SERVER-14888 Suppress this in cases we don't want to audit. audit::logCreateIndex(_opCtx->getClient(), &info, descriptor->indexName(), ns); _indexes.push_back(std::move(index)); } if (_buildInBackground) _backgroundOperation.reset(new BackgroundOperation(ns)); wunit.commit(); if (MONGO_FAIL_POINT(crashAfterStartingIndexBuild)) { log() << "Index build interrupted due to 'crashAfterStartingIndexBuild' failpoint. Exiting " "after waiting for changes to become durable."; Locker::LockSnapshot lockInfo; _opCtx->lockState()->saveLockStateAndUnlock(&lockInfo); if (_opCtx->recoveryUnit()->waitUntilDurable()) { quickExit(EXIT_TEST); } } return indexInfoObjs; }
bool WiredTigerSessionCache::isEngineCachingCursors() { return kWiredTigerCursorCacheSize.load() <= 0; }
void WiredTigerSessionCache::releaseSession(WiredTigerSession* session) { invariant(session); invariant(session->cursorsOut() == 0); const int shuttingDown = _shuttingDown.fetchAndAdd(1); ON_BLOCK_EXIT([this] { _shuttingDown.fetchAndSubtract(1); }); if (shuttingDown & kShuttingDownMask) { // There is a race condition with clean shutdown, where the storage engine is ripped from // underneath OperationContexts, which are not "active" (i.e., do not have any locks), but // are just about to delete the recovery unit. See SERVER-16031 for more information. Since // shutting down the WT_CONNECTION will close all WT_SESSIONS, we shouldn't also try to // directly close this session. session->_session = nullptr; // Prevents calling _session->close() in destructor. delete session; return; } { WT_SESSION* ss = session->getSession(); uint64_t range; // This checks that we are only caching idle sessions and not something which might hold // locks or otherwise prevent truncation. invariantWTOK(ss->transaction_pinned_range(ss, &range)); invariant(range == 0); // Release resources in the session we're about to cache. // If we are using hybrid caching, then close cursors now and let them // be cached at the WiredTiger level. if (kWiredTigerCursorCacheSize.load() < 0) { session->closeAllCursors(""); } invariantWTOK(ss->reset(ss)); } // If the cursor epoch has moved on, close all cursors in the session. uint64_t cursorEpoch = _cursorEpoch.load(); if (session->_getCursorEpoch() != cursorEpoch) session->closeCursorsForQueuedDrops(_engine); bool returnedToCache = false; uint64_t currentEpoch = _epoch.load(); bool dropQueuedIdentsAtSessionEnd = session->isDropQueuedIdentsAtSessionEndAllowed(); // Reset this session's flag for dropping queued idents to default, before returning it to // session cache. session->dropQueuedIdentsAtSessionEndAllowed(true); if (session->_getEpoch() == currentEpoch) { // check outside of lock to reduce contention stdx::lock_guard<stdx::mutex> lock(_cacheLock); if (session->_getEpoch() == _epoch.load()) { // recheck inside the lock for correctness returnedToCache = true; _sessions.push_back(session); } } else invariant(session->_getEpoch() < currentEpoch); if (!returnedToCache) delete session; if (dropQueuedIdentsAtSessionEnd && _engine && _engine->haveDropsQueued()) _engine->dropSomeQueuedIdents(); }