void forceDatabaseRefresh(OperationContext* opCtx, const StringData dbName) {
    invariant(!opCtx->lockState()->isLocked());
    invariant(!opCtx->getClient()->isInDirectClient());

    auto const shardingState = ShardingState::get(opCtx);
    invariant(shardingState->canAcceptShardedCommands());

    const auto refreshedDbVersion =
        uassertStatusOK(Grid::get(opCtx)->catalogCache()->getDatabaseWithRefresh(opCtx, dbName))
            .databaseVersion();

    // First, check under a shared lock if another thread already updated the cached version.
    // This is a best-effort optimization to make as few threads as possible to convoy on the
    // exclusive lock below.
    auto databaseHolder = DatabaseHolder::get(opCtx);
    {
        // Take the DBLock directly rather than using AutoGetDb, to prevent a recursive call
        // into checkDbVersion().
        Lock::DBLock dbLock(opCtx, dbName, MODE_IS);
        auto db = databaseHolder->getDb(opCtx, dbName);
        if (!db) {
            log() << "Database " << dbName
                  << " has been dropped; not caching the refreshed databaseVersion";
            return;
        }

        auto& dss = DatabaseShardingState::get(db);
        auto dssLock = DatabaseShardingState::DSSLock::lock(opCtx, &dss);

        const auto cachedDbVersion = dss.getDbVersion(opCtx, dssLock);
        if (cachedDbVersion && cachedDbVersion->getUuid() == refreshedDbVersion.getUuid() &&
            cachedDbVersion->getLastMod() >= refreshedDbVersion.getLastMod()) {
            LOG(2) << "Skipping setting cached databaseVersion for " << dbName
                   << " to refreshed version " << refreshedDbVersion.toBSON()
                   << " because current cached databaseVersion is already "
                   << cachedDbVersion->toBSON();
            return;
        }
    }

    // The cached version is older than the refreshed version; update the cached version.
    Lock::DBLock dbLock(opCtx, dbName, MODE_X);
    auto db = databaseHolder->getDb(opCtx, dbName);
    if (!db) {
        log() << "Database " << dbName
              << " has been dropped; not caching the refreshed databaseVersion";
        return;
    }

    auto& dss = DatabaseShardingState::get(db);
    auto dssLock = DatabaseShardingState::DSSLock::lockExclusive(opCtx, &dss);

    dss.setDbVersion(opCtx, std::move(refreshedDbVersion), dssLock);
}
Ejemplo n.º 2
0
    bool run(OperationContext* opCtx,
             const std::string& dbNameUnused,
             const BSONObj& cmdObj,
             BSONObjBuilder& result) final {
        Lock::GlobalLock global(opCtx, MODE_X);

        // This command will fail without modifying the catalog if there are any databases that are
        // marked drop-pending. (Otherwise, the Database object will be reconstructed when
        // re-opening the catalog, but with the drop pending flag cleared.)
        auto databaseHolder = DatabaseHolder::get(opCtx);
        std::vector<std::string> allDbs;
        getGlobalServiceContext()->getStorageEngine()->listDatabases(&allDbs);
        for (auto&& dbName : allDbs) {
            const auto db = databaseHolder->getDb(opCtx, dbName);
            if (db->isDropPending(opCtx)) {
                uasserted(ErrorCodes::DatabaseDropPending,
                          str::stream() << "cannot restart the catalog because database " << dbName
                                        << " is pending removal");
            }
        }

        log() << "Closing database catalog";
        auto state = catalog::closeCatalog(opCtx);

        log() << "Reopening database catalog";
        catalog::openCatalog(opCtx, state);

        return true;
    }
Ejemplo n.º 3
0
void DurableViewCatalog::onExternalChange(OperationContext* opCtx, const NamespaceString& name) {
    dassert(opCtx->lockState()->isDbLockedForMode(name.db(), MODE_IX));
    auto databaseHolder = DatabaseHolder::get(opCtx);
    auto db = databaseHolder->getDb(opCtx, name.db());
    if (db) {
        opCtx->recoveryUnit()->onCommit(
            [db](boost::optional<Timestamp>) { ViewCatalog::get(db)->invalidate(); });
    }
}
Ejemplo n.º 4
0
void MojDbSandwichDatabase::postUpdate(MojDbStorageTxn* txn, MojSize size)
{
    if (txn) {
        // TODO: implement quotas
        // XXX: static_cast<MojDbSandwichTxn*>(txn)->didUpdate(size);
    } else {
        if (engine()->lazySync())
            engine()->getUpdater()->sendEvent( getDb() );
    }
}
Ejemplo n.º 5
0
MojErr MojDbSandwichDatabase::close()
{
    LOG_TRACE("Entering function %s", __FUNCTION__);

    MojErr err = MojErrNone;
    if (m_db.Valid()) {
        err = closeImpl();
        if (engine()->lazySync())
            engine()->getUpdater()->close( getDb() );
    }
    return err;
}
Ejemplo n.º 6
0
MinVisibleTimestampMap closeCatalog(OperationContext* opCtx) {
    invariant(opCtx->lockState()->isW());

    MinVisibleTimestampMap minVisibleTimestampMap;
    std::vector<std::string> allDbs;
    opCtx->getServiceContext()->getStorageEngine()->listDatabases(&allDbs);

    auto databaseHolder = DatabaseHolder::get(opCtx);
    for (auto&& dbName : allDbs) {
        const auto db = databaseHolder->getDb(opCtx, dbName);
        for (auto collIt = db->begin(opCtx); collIt != db->end(opCtx); ++collIt) {
            auto coll = *collIt;
            if (!coll) {
                break;
            }

            OptionalCollectionUUID uuid = coll->uuid();
            boost::optional<Timestamp> minVisible = coll->getMinimumVisibleSnapshot();

            // If there's a minimum visible, invariant there's also a UUID.
            invariant(!minVisible || uuid);
            if (uuid && minVisible) {
                LOG(1) << "closeCatalog: preserving min visible timestamp. Collection: "
                       << coll->ns() << " UUID: " << uuid << " TS: " << minVisible;
                minVisibleTimestampMap[*uuid] = *minVisible;
            }
        }
    }

    // Need to mark the UUIDCatalog as open if we our closeAll fails, dismissed if successful.
    auto reopenOnFailure = makeGuard([opCtx] { UUIDCatalog::get(opCtx).onOpenCatalog(opCtx); });
    // Closing UUID Catalog: only lookupNSSByUUID will fall back to using pre-closing state to
    // allow authorization for currently unknown UUIDs. This is needed because authorization needs
    // to work before acquiring locks, and might otherwise spuriously regard a UUID as unknown
    // while reloading the catalog.
    UUIDCatalog::get(opCtx).onCloseCatalog(opCtx);
    LOG(1) << "closeCatalog: closing UUID catalog";

    // Close all databases.
    log() << "closeCatalog: closing all databases";
    databaseHolder->closeAll(opCtx);

    // Close the storage engine's catalog.
    log() << "closeCatalog: closing storage engine catalog";
    opCtx->getServiceContext()->getStorageEngine()->closeCatalog(opCtx);

    reopenOnFailure.dismiss();
    return minVisibleTimestampMap;
}
Ejemplo n.º 7
0
MojErr MojDbSandwichDatabase::open(const MojChar* dbName, MojDbSandwichEngine* eng)
{
    LOG_TRACE("Entering function %s", __FUNCTION__);
    MojAssert(dbName && eng);

    // save eng and name
    m_engine = eng;
    MojErr err = m_name.assign(dbName);
    MojErrCheck(err);

    if (engine()->lazySync())
        engine()->getUpdater()->open( getDb() );

    return MojErrNone;
}
Ejemplo n.º 8
0
    Cursor::const_iterator& Cursor::const_iterator::operator++()
    {
      // fetch next row
      log_debug("mysql_stmt_fetch");
      int ret = ::mysql_stmt_fetch(getStmt());

      if (ret == 1)
        throw Error("mysql_stmt_fetch", getDb());
      else if (ret == MYSQL_NO_DATA)
        cursor = Cursor();
#ifdef MYSQL_DATA_TRUNCATED
      else if (ret == MYSQL_DATA_TRUNCATED)
      {
        log_error("mysql_stmt_fetch returned MYSQL_DATA_TRUNCATED");
        throw std::runtime_error("mysql_stmt_fetch returned MYSQL_DATA_TRUNCATED");
      }
#endif
    }
Ejemplo n.º 9
0
ConnectionProperties::ConnectionProperties(Database *database, MainWin *mainwin)
{
    QPushButton *pBOK;
    QPushButton *pBCancel;
    QWidget *layoutWidget;
    QVBoxLayout *verticalLayout;

    setWindowFlags(Qt::FramelessWindowHint);
    this->setAttribute(Qt::WA_DeleteOnClose);
    resize(256, 288);
    setModal(true);
    setParent(mainwin);
    setWindowFlags(Qt::Window);
    setWindowModality(Qt::WindowModal);
    pBOK = new QPushButton(this);
    pBOK->setObjectName(QString::fromUtf8("pBOK"));
    pBOK->setAutoDefault(false);
    pBOK->setGeometry(QRect(30, 250, 75, 23));
    QFont font;
    font.setFamily(QString::fromUtf8("Verdana"));
    pBOK->setFont(font);
    pBCancel = new QPushButton(this);
    pBCancel->setObjectName(QString::fromUtf8("pBCancel"));
    pBCancel->setGeometry(QRect(140, 250, 75, 23));
    pBCancel->setFont(font);
    pBCancel->setAutoDefault(false);
    setDb(new QLineEdit(this));
    getDb()->setObjectName(QString::fromUtf8("lEDb"));
    getDb()->setGeometry(QRect(100, 90, 133, 20));
    getDb()->setFont(font);
    getDb()->setAutoFillBackground(true);
    getDb()->setInputMethodHints(Qt::ImhNone);
    setSrv(new QLineEdit(this));
    getSrv()->setObjectName(QString::fromUtf8("lESrv"));
    getSrv()->setGeometry(QRect(100, 47, 133, 20));
    getSrv()->setFont(font);
    setPort(new QLineEdit(this));
    getPort()->setObjectName(QString::fromUtf8("lEPort"));
    getPort()->setGeometry(QRect(100, 130, 133, 20));
    getPort()->setFont(font);
    getPort()->setInputMethodHints(Qt::ImhFormattedNumbersOnly);
    getPort()->setInputMask("00000");
    setUser(new QLineEdit(this));
    getUser()->setObjectName(QString::fromUtf8("lEUser"));
    getUser()->setGeometry(QRect(100, 171, 133, 20));
    getUser()->setFont(font);
    setPass(new QLineEdit(this));
    getPass()->setObjectName(QString::fromUtf8("lEPass"));
    getPass()->setGeometry(QRect(100, 213, 133, 20));
    getPass()->setEchoMode(QLineEdit::Password);
    getPass()->setFont(font);
    layoutWidget = new QWidget(this);
    layoutWidget->setObjectName(QString::fromUtf8("layoutWidget"));
    layoutWidget->setGeometry(QRect(20, 40, 71, 201));
    verticalLayout = new QVBoxLayout(layoutWidget);
    verticalLayout->setObjectName(QString::fromUtf8("verticalLayout"));
    verticalLayout->setContentsMargins(0, 0, 0, 0);
    lSrv = new QLabel(layoutWidget);
    lSrv->setObjectName(QString::fromUtf8("lSrv"));
    lSrv->setFont(font);
    verticalLayout->addWidget(lSrv);
    lDb = new QLabel(layoutWidget);
    lDb->setObjectName(QString::fromUtf8("lDb"));
    lDb->setFont(font);
    verticalLayout->addWidget(lDb);
    lPort = new QLabel(layoutWidget);
    lPort->setObjectName(QString::fromUtf8("lPort"));
    lPort->setFont(font);
    verticalLayout->addWidget(lPort);
    lUser = new QLabel(layoutWidget);
    lUser->setObjectName(QString::fromUtf8("lUser"));
    lUser->setFont(font);
    verticalLayout->addWidget(lUser);
    lPass = new QLabel(layoutWidget);
    lPass->setObjectName(QString::fromUtf8("lPass"));
    lPass->setFont(font);
    verticalLayout->addWidget(lPass);
    lTitle = new QLabel(this);
    lTitle->setObjectName(QString::fromUtf8("lTitle"));
    lTitle->setGeometry(QRect(20, 10, 211, 21));
    lTitle->setAlignment(Qt::AlignHCenter);
    QFont font1;
    font1.setFamily(QString::fromUtf8("Verdana"));
    font1.setPointSize(14);
    lTitle->setFont(font1);
    QWidget::setTabOrder(getSrv(), getDb());
    QWidget::setTabOrder(getDb(), getPort());
    QWidget::setTabOrder(getPort(), getUser());
    QWidget::setTabOrder(getUser(), getPass());
    QWidget::setTabOrder(getPass(), pBOK);
    QWidget::setTabOrder(pBOK, pBCancel);
    QObject::connect(pBCancel, SIGNAL(clicked()), this, SLOT(close()));
    QObject::connect(pBOK, SIGNAL(clicked()), this, SLOT(okslot()));
    QObject::connect(this, SIGNAL(oksignal(QString,qint32,QString,QString,QString)),
                     mainwin, SLOT(newDatabase(QString,qint32,QString,QString,QString)));
    QObject::connect(getSrv(), SIGNAL(returnPressed()), getDb(), SLOT(setFocus()));
    QObject::connect(getDb(), SIGNAL(returnPressed()), getPort(), SLOT(setFocus()));
    QObject::connect(getPort(), SIGNAL(returnPressed()), getUser(), SLOT(setFocus()));
    QObject::connect(getUser(), SIGNAL(returnPressed()), getPass(), SLOT(setFocus()));
    QObject::connect(getPass(), SIGNAL(returnPressed()), pBOK, SLOT(setFocus()));
    setWindowTitle(QApplication::translate("Connection", "Connection", 0, QApplication::UnicodeUTF8));
    pBOK->setText(QApplication::translate("Connection", "OK", 0, QApplication::UnicodeUTF8));
    pBCancel->setText(QApplication::translate("Connection", "Cancel", 0, QApplication::UnicodeUTF8));
    if(!database->getDatabaseStatus()) {
        getSrv()->setText(database->getHost());
        getDb()->setText(database->getName());
        getPort()->setText(database->getPort());
        getUser()->setText(database->getUser());
        getPass()->setText(database->getPassword());
    }
    else {
        QSqlDatabase database_connection = QSqlDatabase::database(QString("base").append(QString::number(database->getId())));
        getSrv()->setText(database_connection.hostName());
        getDb()->setText(database->getName());
        getPort()->setText(QString::number(database_connection.port()));
        getUser()->setText(database_connection.userName());
        getPass()->setText(database_connection.password());
    }
    lTitle->setText(QApplication::translate("Connection", "Connection", 0, QApplication::UnicodeUTF8));
    lSrv->setText(QApplication::translate("Connection", "Server", 0, QApplication::UnicodeUTF8));
    lDb->setText(QApplication::translate("Connection", "Database", 0, QApplication::UnicodeUTF8));
    lPort->setText(QApplication::translate("Connection", "Port", 0, QApplication::UnicodeUTF8));
    lUser->setText(QApplication::translate("Connection", "Username", 0, QApplication::UnicodeUTF8));
    lPass->setText(QApplication::translate("Connection", "Password", 0, QApplication::UnicodeUTF8));
}
Ejemplo n.º 10
0
Status createCollectionForApplyOps(OperationContext* opCtx,
                                   const std::string& dbName,
                                   const BSONElement& ui,
                                   const BSONObj& cmdObj,
                                   const BSONObj& idIndex) {
    invariant(opCtx->lockState()->isDbLockedForMode(dbName, MODE_X));

    const NamespaceString newCollName(CommandHelpers::parseNsCollectionRequired(dbName, cmdObj));
    auto newCmd = cmdObj;

    auto databaseHolder = DatabaseHolder::get(opCtx);
    auto* const db = databaseHolder->getDb(opCtx, dbName);

    // If a UUID is given, see if we need to rename a collection out of the way, and whether the
    // collection already exists under a different name. If so, rename it into place. As this is
    // done during replay of the oplog, the operations do not need to be atomic, just idempotent.
    // We need to do the renaming part in a separate transaction, as we cannot transactionally
    // create a database on MMAPv1, which could result in createCollection failing if the database
    // does not yet exist.
    if (ui.ok()) {
        // Return an optional, indicating whether we need to early return (if the collection already
        // exists, or in case of an error).
        using Result = boost::optional<Status>;
        auto result =
            writeConflictRetry(opCtx, "createCollectionForApplyOps", newCollName.ns(), [&] {
                WriteUnitOfWork wunit(opCtx);
                // Options need the field to be named "uuid", so parse/recreate.
                auto uuid = uassertStatusOK(UUID::parse(ui));
                uassert(ErrorCodes::InvalidUUID,
                        "Invalid UUID in applyOps create command: " + uuid.toString(),
                        uuid.isRFC4122v4());

                auto& catalog = UUIDCatalog::get(opCtx);
                const auto currentName = catalog.lookupNSSByUUID(uuid);
                auto serviceContext = opCtx->getServiceContext();
                auto opObserver = serviceContext->getOpObserver();
                if (currentName == newCollName)
                    return Result(Status::OK());

                if (currentName.isDropPendingNamespace()) {
                    log() << "CMD: create " << newCollName
                          << " - existing collection with conflicting UUID " << uuid
                          << " is in a drop-pending state: " << currentName;
                    return Result(Status(ErrorCodes::NamespaceExists,
                                         str::stream() << "existing collection "
                                                       << currentName.toString()
                                                       << " with conflicting UUID "
                                                       << uuid.toString()
                                                       << " is in a drop-pending state."));
                }

                // In the case of oplog replay, a future command may have created or renamed a
                // collection with that same name. In that case, renaming this future collection to
                // a random temporary name is correct: once all entries are replayed no temporary
                // names will remain.  On MMAPv1 the rename can result in index names that are too
                // long. However this should only happen for initial sync and "resync collection"
                // for rollback, so we can let the error propagate resulting in an abort and restart
                // of the initial sync or result in rollback to fassert, requiring a resync of that
                // node.
                const bool stayTemp = true;
                if (auto futureColl = db ? db->getCollection(opCtx, newCollName) : nullptr) {
                    auto tmpNameResult =
                        db->makeUniqueCollectionNamespace(opCtx, "tmp%%%%%.create");
                    if (!tmpNameResult.isOK()) {
                        return Result(tmpNameResult.getStatus().withContext(
                            str::stream() << "Cannot generate temporary "
                                             "collection namespace for applyOps "
                                             "create command: collection: "
                                          << newCollName));
                    }
                    const auto& tmpName = tmpNameResult.getValue();
                    // It is ok to log this because this doesn't happen very frequently.
                    log() << "CMD: create " << newCollName
                          << " - renaming existing collection with conflicting UUID " << uuid
                          << " to temporary collection " << tmpName;
                    Status status = db->renameCollection(opCtx, newCollName, tmpName, stayTemp);
                    if (!status.isOK())
                        return Result(status);
                    opObserver->onRenameCollection(opCtx,
                                                   newCollName,
                                                   tmpName,
                                                   futureColl->uuid(),
                                                   /*dropTargetUUID*/ {},
                                                   /*numRecords*/ 0U,
                                                   stayTemp);
                }

                // If the collection with the requested UUID already exists, but with a different
                // name, just rename it to 'newCollName'.
                if (catalog.lookupCollectionByUUID(uuid)) {
                    uassert(40655,
                            str::stream() << "Invalid name " << newCollName << " for UUID " << uuid,
                            currentName.db() == newCollName.db());
                    Status status = db->renameCollection(opCtx, currentName, newCollName, stayTemp);
                    if (!status.isOK())
                        return Result(status);
                    opObserver->onRenameCollection(opCtx,
                                                   currentName,
                                                   newCollName,
                                                   uuid,
                                                   /*dropTargetUUID*/ {},
                                                   /*numRecords*/ 0U,
                                                   stayTemp);

                    wunit.commit();
                    return Result(Status::OK());
                }

                // A new collection with the specific UUID must be created, so add the UUID to the
                // creation options. Regular user collection creation commands cannot do this.
                auto uuidObj = uuid.toBSON();
                newCmd = cmdObj.addField(uuidObj.firstElement());
                wunit.commit();

                return Result(boost::none);
            });

        if (result) {
            return *result;
        }
    }

    return createCollection(
        opCtx, newCollName, newCmd, idIndex, CollectionOptions::parseForStorage);
}
/**
 * Return whether there are non-local databases. If there was an error becauses the wrong mongod
 * version was used for these datafiles, a DBException with status ErrorCodes::MustDowngrade is
 * thrown.
 */
bool repairDatabasesAndCheckVersion(OperationContext* opCtx) {
    auto const storageEngine = opCtx->getServiceContext()->getStorageEngine();
    Lock::GlobalWrite lk(opCtx);

    std::vector<std::string> dbNames = storageEngine->listDatabases();

    // Rebuilding indexes must be done before a database can be opened, except when using repair,
    // which rebuilds all indexes when it is done.
    if (!storageGlobalParams.readOnly && !storageGlobalParams.repair) {
        // Determine whether this is a replica set node running in standalone mode. If we're in
        // repair mode, we cannot set the flag yet as it needs to open a database and look through a
        // collection. Rebuild the necessary indexes after setting the flag.
        setReplSetMemberInStandaloneMode(opCtx);
        rebuildIndexes(opCtx, storageEngine);
    }

    bool ensuredCollectionProperties = false;

    // Repair all databases first, so that we do not try to open them if they are in bad shape
    auto databaseHolder = DatabaseHolder::get(opCtx);
    if (storageGlobalParams.repair) {
        invariant(!storageGlobalParams.readOnly);

        if (MONGO_FAIL_POINT(exitBeforeDataRepair)) {
            log() << "Exiting because 'exitBeforeDataRepair' fail point was set.";
            quickExit(EXIT_ABRUPT);
        }

        // Ensure that the local database is repaired first, if it exists, so that we can open it
        // before any other database to be able to determine if this is a replica set node running
        // in standalone mode before rebuilding any indexes.
        auto dbNamesIt = std::find(dbNames.begin(), dbNames.end(), NamespaceString::kLocalDb);
        if (dbNamesIt != dbNames.end()) {
            std::swap(dbNames.front(), *dbNamesIt);
            invariant(dbNames.front() == NamespaceString::kLocalDb);
        }

        stdx::function<void(const std::string& dbName)> onRecordStoreRepair =
            [opCtx](const std::string& dbName) {
                if (dbName == NamespaceString::kLocalDb) {
                    setReplSetMemberInStandaloneMode(opCtx);
                }
            };

        for (const auto& dbName : dbNames) {
            LOG(1) << "    Repairing database: " << dbName;
            fassertNoTrace(18506,
                           repairDatabase(opCtx, storageEngine, dbName, onRecordStoreRepair));
        }

        // All collections must have UUIDs before restoring the FCV document to a version that
        // requires UUIDs.
        uassertStatusOK(ensureCollectionProperties(opCtx, dbNames));
        ensuredCollectionProperties = true;

        // Attempt to restore the featureCompatibilityVersion document if it is missing.
        NamespaceString fcvNSS(NamespaceString::kServerConfigurationNamespace);

        auto db = databaseHolder->getDb(opCtx, fcvNSS.db());
        Collection* versionColl;
        BSONObj featureCompatibilityVersion;
        if (!db || !(versionColl = db->getCollection(opCtx, fcvNSS)) ||
            !Helpers::findOne(opCtx,
                              versionColl,
                              BSON("_id" << FeatureCompatibilityVersionParser::kParameterName),
                              featureCompatibilityVersion)) {
            uassertStatusOK(restoreMissingFeatureCompatibilityVersionDocument(opCtx, dbNames));
        }
    }

    if (!ensuredCollectionProperties) {
        uassertStatusOK(ensureCollectionProperties(opCtx, dbNames));
    }

    if (!storageGlobalParams.readOnly) {
        // We open the "local" database before calling hasReplSetConfigDoc() to ensure the in-memory
        // catalog entries for the 'kSystemReplSetNamespace' collection have been populated if the
        // collection exists. If the "local" database didn't exist at this point yet, then it will
        // be created. If the mongod is running in a read-only mode, then it is fine to not open the
        // "local" database and populate the catalog entries because we won't attempt to drop the
        // temporary collections anyway.
        Lock::DBLock dbLock(opCtx, NamespaceString::kSystemReplSetNamespace.db(), MODE_X);
        databaseHolder->openDb(opCtx, NamespaceString::kSystemReplSetNamespace.db());
    }

    if (storageGlobalParams.repair) {
        if (MONGO_FAIL_POINT(exitBeforeRepairInvalidatesConfig)) {
            log() << "Exiting because 'exitBeforeRepairInvalidatesConfig' fail point was set.";
            quickExit(EXIT_ABRUPT);
        }
        // This must be done after opening the "local" database as it modifies the replica set
        // config.
        auto repairObserver = StorageRepairObserver::get(opCtx->getServiceContext());
        repairObserver->onRepairDone(opCtx);
        if (repairObserver->isDataModified()) {
            warning() << "Modifications made by repair:";
            const auto& mods = repairObserver->getModifications();
            for (const auto& mod : mods) {
                warning() << "  " << mod;
            }
            if (hasReplSetConfigDoc(opCtx)) {
                warning() << "WARNING: Repair may have modified replicated data. This node will no "
                             "longer be able to join a replica set without a full re-sync";
            }
        }
    }

    const repl::ReplSettings& replSettings =
        repl::ReplicationCoordinator::get(opCtx)->getSettings();

    // On replica set members we only clear temp collections on DBs other than "local" during
    // promotion to primary. On pure slaves, they are only cleared when the oplog tells them
    // to. The local DB is special because it is not replicated.  See SERVER-10927 for more
    // details.
    const bool shouldClearNonLocalTmpCollections =
        !(hasReplSetConfigDoc(opCtx) || replSettings.usingReplSets());

    // To check whether a featureCompatibilityVersion document exists.
    bool fcvDocumentExists = false;

    // To check whether we have databases other than local.
    bool nonLocalDatabases = false;

    // Refresh list of database names to include newly-created admin, if it exists.
    dbNames = storageEngine->listDatabases();
    for (const auto& dbName : dbNames) {
        if (dbName != "local") {
            nonLocalDatabases = true;
        }
        LOG(1) << "    Recovering database: " << dbName;

        auto db = databaseHolder->openDb(opCtx, dbName);
        invariant(db);

        // First thing after opening the database is to check for file compatibility,
        // otherwise we might crash if this is a deprecated format.
        auto status = storageEngine->currentFilesCompatible(opCtx);
        if (!status.isOK()) {
            if (status.code() == ErrorCodes::CanRepairToDowngrade) {
                // Convert CanRepairToDowngrade statuses to MustUpgrade statuses to avoid logging a
                // potentially confusing and inaccurate message.
                //
                // TODO SERVER-24097: Log a message informing the user that they can start the
                // current version of mongod with --repair and then proceed with normal startup.
                status = {ErrorCodes::MustUpgrade, status.reason()};
            }
            severe() << "Unable to start mongod due to an incompatibility with the data files and"
                        " this version of mongod: "
                     << redact(status);
            severe() << "Please consult our documentation when trying to downgrade to a previous"
                        " major release";
            quickExit(EXIT_NEED_UPGRADE);
            MONGO_UNREACHABLE;
        }


        // If the server configuration collection already contains a valid
        // featureCompatibilityVersion document, cache it in-memory as a server parameter.
        if (dbName == "admin") {
            if (Collection* versionColl =
                    db->getCollection(opCtx, NamespaceString::kServerConfigurationNamespace)) {
                BSONObj featureCompatibilityVersion;
                if (Helpers::findOne(
                        opCtx,
                        versionColl,
                        BSON("_id" << FeatureCompatibilityVersionParser::kParameterName),
                        featureCompatibilityVersion)) {
                    auto swVersion =
                        FeatureCompatibilityVersionParser::parse(featureCompatibilityVersion);
                    // Note this error path captures all cases of an FCV document existing,
                    // but with any value other than "4.0" or "4.2". This includes unexpected
                    // cases with no path forward such as the FCV value not being a string.
                    uassert(ErrorCodes::MustDowngrade,
                            str::stream()
                                << "UPGRADE PROBLEM: Found an invalid "
                                   "featureCompatibilityVersion document (ERROR: "
                                << swVersion.getStatus()
                                << "). If the current featureCompatibilityVersion is below "
                                   "4.0, see the documentation on upgrading at "
                                << feature_compatibility_version_documentation::kUpgradeLink
                                << ".",
                            swVersion.isOK());

                    fcvDocumentExists = true;
                    auto version = swVersion.getValue();
                    serverGlobalParams.featureCompatibility.setVersion(version);
                    FeatureCompatibilityVersion::updateMinWireVersion();

                    // On startup, if the version is in an upgrading or downrading state, print a
                    // warning.
                    if (version ==
                        ServerGlobalParams::FeatureCompatibility::Version::kUpgradingTo42) {
                        log() << "** WARNING: A featureCompatibilityVersion upgrade did not "
                              << "complete. " << startupWarningsLog;
                        log() << "**          The current featureCompatibilityVersion is "
                              << FeatureCompatibilityVersionParser::toString(version) << "."
                              << startupWarningsLog;
                        log() << "**          To fix this, use the setFeatureCompatibilityVersion "
                              << "command to resume upgrade to 4.2." << startupWarningsLog;
                    } else if (version == ServerGlobalParams::FeatureCompatibility::Version::
                                              kDowngradingTo40) {
                        log() << "** WARNING: A featureCompatibilityVersion downgrade did not "
                              << "complete. " << startupWarningsLog;
                        log() << "**          The current featureCompatibilityVersion is "
                              << FeatureCompatibilityVersionParser::toString(version) << "."
                              << startupWarningsLog;
                        log() << "**          To fix this, use the setFeatureCompatibilityVersion "
                              << "command to resume downgrade to 4.0." << startupWarningsLog;
                    }
                }
            }
        }

        if (replSettings.usingReplSets()) {
            // We only care about _id indexes and drop-pending collections if we are in a replset.
            db->checkForIdIndexesAndDropPendingCollections(opCtx);
            // Ensure oplog is capped (mongodb does not guarantee order of inserts on noncapped
            // collections)
            if (db->name() == "local") {
                checkForCappedOplog(opCtx, db);
            }
        }

        if (!storageGlobalParams.readOnly &&
            (shouldClearNonLocalTmpCollections || dbName == "local")) {
            db->clearTmpCollections(opCtx);
        }
    }

    // Fail to start up if there is no featureCompatibilityVersion document and there are non-local
    // databases present.
    if (!fcvDocumentExists && nonLocalDatabases) {
        severe()
            << "Unable to start up mongod due to missing featureCompatibilityVersion document.";
        severe() << "Please run with --repair to restore the document.";
        fassertFailedNoTrace(40652);
    }

    LOG(1) << "done repairDatabases";
    return nonLocalDatabases;
}