StatusWith<UserHandle> AuthorizationManagerImpl::_acquireUserSlowPath(CacheGuard& guard,
                                                                      OperationContext* opCtx,
                                                                      const UserName& userName) {
    int authzVersion = _version;

    // 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();
    std::unique_ptr<User> user;
    for (int i = 0; i < maxAcquireRetries; ++i) {
        if (authzVersion == schemaVersionInvalid) {
            Status status = _externalState->getStoredAuthorizationVersion(opCtx, &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(opCtx, 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;

    // All this does is re-acquire the _cacheWriteMutex if we don't hold it already - a caller
    // may also call endFetchPhase() after this returns.
    guard.endFetchPhase();

    if (guard.isSameCacheGeneration()) {
        if (_version == schemaVersionInvalid)
            _version = authzVersion;
        return _userCache.insertOrAssignAndGet(userName, std::move(user));
    } 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();
        return UserHandle(std::move(user));
    }
}
    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::auto_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 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(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();
    }