Status AuthorizationManager::acquireUser(OperationContext* txn,
                                         const UserName& userName,
                                         User** acquiredUser) {
    if (userName == internalSecurity.user->getName()) {
        *acquiredUser = internalSecurity.user;
        return Status::OK();
    }

    unordered_map<UserName, User*>::iterator it;

    CacheGuard guard(this, CacheGuard::fetchSynchronizationManual);
    while ((_userCache.end() == (it = _userCache.find(userName))) &&
           guard.otherUpdateInFetchPhase()) {
        guard.wait();
    }

    if (it != _userCache.end()) {
        fassert(16914, it->second);
        fassert(17003, it->second->isValid());
        fassert(17008, it->second->getRefCount() > 0);
        it->second->incrementRefCount();
        *acquiredUser = it->second;
        return Status::OK();
    }

    std::unique_ptr<User> user;

    int authzVersion = _version;
    guard.beginFetchPhase();

    // Number of times to retry a user document that fetches due to transient
    // AuthSchemaIncompatible errors.  These errors should only ever occur during and shortly
    // after schema upgrades.
    static const int maxAcquireRetries = 2;
    Status status = Status::OK();
    for (int i = 0; i < maxAcquireRetries; ++i) {
        if (authzVersion == schemaVersionInvalid) {
            Status status = _externalState->getStoredAuthorizationVersion(txn, &authzVersion);
            if (!status.isOK())
                return status;
        }

        switch (authzVersion) {
            default:
                status = Status(ErrorCodes::BadValue,
                                mongoutils::str::stream()
                                    << "Illegal value for authorization data schema version, "
                                    << authzVersion);
                break;
            case schemaVersion28SCRAM:
            case schemaVersion26Final:
            case schemaVersion26Upgrade:
                status = _fetchUserV2(txn, userName, &user);
                break;
            case schemaVersion24:
                status = Status(ErrorCodes::AuthSchemaIncompatible,
                                mongoutils::str::stream()
                                    << "Authorization data schema version "
                                    << schemaVersion24
                                    << " not supported after MongoDB version 2.6.");
                break;
        }
        if (status.isOK())
            break;
        if (status != ErrorCodes::AuthSchemaIncompatible)
            return status;

        authzVersion = schemaVersionInvalid;
    }
    if (!status.isOK())
        return status;

    guard.endFetchPhase();

    user->incrementRefCount();
    // NOTE: It is not safe to throw an exception from here to the end of the method.
    if (guard.isSameCacheGeneration()) {
        _userCache.insert(std::make_pair(userName, user.get()));
        if (_version == schemaVersionInvalid)
            _version = authzVersion;
    } else {
        // If the cache generation changed while this thread was in fetch mode, the data
        // associated with the user may now be invalid, so we must mark it as such.  The caller
        // may still opt to use the information for a short while, but not indefinitely.
        user->invalidate();
    }
    *acquiredUser = user.release();

    return Status::OK();
}