void FeatureCompatibilityVersion::set(OperationContext* txn, StringData version) { uassert(40284, "featureCompatibilityVersion must be '3.4' or '3.2'. See " "http://dochub.mongodb.org/core/3.4-feature-compatibility.", version == FeatureCompatibilityVersionCommandParser::kVersion34 || version == FeatureCompatibilityVersionCommandParser::kVersion32); DBDirectClient client(txn); NamespaceString nss(FeatureCompatibilityVersion::kCollection); if (version == FeatureCompatibilityVersionCommandParser::kVersion34) { // We build a v=2 index on the "admin.system.version" collection as part of setting the // featureCompatibilityVersion to 3.4. This is a new index version that isn't supported by // versions of MongoDB earlier than 3.4 that will cause 3.2 secondaries to crash when it is // replicated. std::vector<BSONObj> indexSpecs{k32IncompatibleIndexSpec}; { ScopedTransaction transaction(txn, MODE_IX); AutoGetOrCreateDb autoDB(txn, nss.db(), MODE_X); uassert(ErrorCodes::NotMaster, str::stream() << "Cannot set featureCompatibilityVersion to '" << version << "'. Not primary while attempting to create index on: " << nss.ns(), repl::ReplicationCoordinator::get(txn->getServiceContext()) ->canAcceptWritesFor(nss)); IndexBuilder builder(k32IncompatibleIndexSpec, false); auto status = builder.buildInForeground(txn, autoDB.getDb()); uassertStatusOK(status); MONGO_WRITE_CONFLICT_RETRY_LOOP_BEGIN { WriteUnitOfWork wuow(txn); getGlobalServiceContext()->getOpObserver()->onCreateIndex( txn, autoDB.getDb()->getSystemIndexesName(), k32IncompatibleIndexSpec, false); wuow.commit(); } MONGO_WRITE_CONFLICT_RETRY_LOOP_END(txn, "FeatureCompatibilityVersion::set", nss.ns()); } // We then update the featureCompatibilityVersion document stored in the // "admin.system.version" collection. We do this after creating the v=2 index in order to // maintain the invariant that if the featureCompatibilityVersion is 3.4, then // 'k32IncompatibleIndexSpec' index exists on the "admin.system.version" collection. BSONObj updateResult; client.runCommand(nss.db().toString(), makeUpdateCommand(version, WriteConcernOptions::Majority), updateResult); uassertStatusOK(getStatusFromCommandResult(updateResult)); uassertStatusOK(getWriteConcernStatusFromCommandResult(updateResult)); // We then update the value of the featureCompatibilityVersion server parameter. serverGlobalParams.featureCompatibility.version.store( ServerGlobalParams::FeatureCompatibility::Version::k34); } else if (version == FeatureCompatibilityVersionCommandParser::kVersion32) {
Status dropDatabase(OperationContext* opCtx, const std::string& dbName) { uassert(ErrorCodes::IllegalOperation, "Cannot drop a database in read-only mode", !storageGlobalParams.readOnly); // TODO (Kal): OldClientContext legacy, needs to be removed { CurOp::get(opCtx)->ensureStarted(); stdx::lock_guard<Client> lk(*opCtx->getClient()); CurOp::get(opCtx)->setNS_inlock(dbName); } auto replCoord = repl::ReplicationCoordinator::get(opCtx); std::size_t numCollectionsToDrop = 0; // We have to wait for the last drop-pending collection to be removed if there are no // collections to drop. repl::OpTime latestDropPendingOpTime; using Result = boost::optional<Status>; // Get an optional result--if it's there, early return; otherwise, wait for collections to drop. auto result = writeConflictRetry(opCtx, "dropDatabase_collection", dbName, [&] { Lock::GlobalWrite lk(opCtx); AutoGetDb autoDB(opCtx, dbName, MODE_X); Database* const db = autoDB.getDb(); if (!db) { return Result(Status(ErrorCodes::NamespaceNotFound, str::stream() << "Could not drop database " << dbName << " because it does not exist")); } bool userInitiatedWritesAndNotPrimary = opCtx->writesAreReplicated() && !replCoord->canAcceptWritesForDatabase(opCtx, dbName); if (userInitiatedWritesAndNotPrimary) { return Result( Status(ErrorCodes::NotMaster, str::stream() << "Not primary while dropping database " << dbName)); } log() << "dropDatabase " << dbName << " - starting"; db->setDropPending(opCtx, true); // If Database::dropCollectionEventIfSystem() fails, we should reset the drop-pending state // on Database. auto dropPendingGuard = MakeGuard([&db, opCtx] { db->setDropPending(opCtx, false); }); for (auto collection : *db) { const auto& nss = collection->ns(); if (nss.isDropPendingNamespace() && replCoord->isReplEnabled() && opCtx->writesAreReplicated()) { log() << "dropDatabase " << dbName << " - found drop-pending collection: " << nss; latestDropPendingOpTime = std::max( latestDropPendingOpTime, uassertStatusOK(nss.getDropPendingNamespaceOpTime())); continue; } if (replCoord->isOplogDisabledFor(opCtx, nss) || nss.isSystemDotIndexes()) { continue; } log() << "dropDatabase " << dbName << " - dropping collection: " << nss; WriteUnitOfWork wunit(opCtx); fassertStatusOK(40476, db->dropCollectionEvenIfSystem(opCtx, nss)); wunit.commit(); numCollectionsToDrop++; } dropPendingGuard.Dismiss(); // If there are no collection drops to wait for, we complete the drop database operation. if (numCollectionsToDrop == 0U && latestDropPendingOpTime.isNull()) { return Result(_finishDropDatabase(opCtx, dbName, db)); } return Result(boost::none); }); if (result) { return *result; } // If waitForWriteConcern() returns an error or throws an exception, we should reset the // drop-pending state on Database. auto dropPendingGuardWhileAwaitingReplication = MakeGuard([dbName, opCtx] { Lock::GlobalWrite lk(opCtx); AutoGetDb autoDB(opCtx, dbName, MODE_X); if (auto db = autoDB.getDb()) { db->setDropPending(opCtx, false); } }); { // Holding of any locks is disallowed while awaiting replication because this can // potentially block for long time while doing network activity. // // Even though dropDatabase() does not explicitly acquire any locks before awaiting // replication, it is possible that the caller of this function may already have acquired // a lock. The applyOps command is an example of a dropDatabase() caller that does this. // Therefore, we have to release any locks using a TempRelease RAII object. // // TODO: Remove the use of this TempRelease object when SERVER-29802 is completed. // The work in SERVER-29802 will adjust the locking rules around applyOps operations and // dropDatabase is expected to be one of the operations where we expect to no longer acquire // the global lock. Lock::TempRelease release(opCtx->lockState()); if (numCollectionsToDrop > 0U) { auto status = replCoord->awaitReplicationOfLastOpForClient(opCtx, kDropDatabaseWriteConcern) .status; if (!status.isOK()) { return Status(status.code(), str::stream() << "dropDatabase " << dbName << " failed waiting for " << numCollectionsToDrop << " collection drops to replicate: " << status.reason()); } log() << "dropDatabase " << dbName << " - successfully dropped " << numCollectionsToDrop << " collections. dropping database"; } else { invariant(!latestDropPendingOpTime.isNull()); auto status = replCoord ->awaitReplication(opCtx, latestDropPendingOpTime, kDropDatabaseWriteConcern) .status; if (!status.isOK()) { return Status( status.code(), str::stream() << "dropDatabase " << dbName << " failed waiting for pending collection drops (most recent drop optime: " << latestDropPendingOpTime.toString() << ") to replicate: " << status.reason()); } log() << "dropDatabase " << dbName << " - pending collection drops completed. dropping database"; } } dropPendingGuardWhileAwaitingReplication.Dismiss(); return writeConflictRetry(opCtx, "dropDatabase_database", dbName, [&] { Lock::GlobalWrite lk(opCtx); AutoGetDb autoDB(opCtx, dbName, MODE_X); if (auto db = autoDB.getDb()) { return _finishDropDatabase(opCtx, dbName, db); } return Status(ErrorCodes::NamespaceNotFound, str::stream() << "Could not drop database " << dbName << " because it does not exist after dropping " << numCollectionsToDrop << " collection(s)."); }); }