From 611979eb5cafba7bb5369a19948f82453f117c65 Mon Sep 17 00:00:00 2001 From: Kaloian Manassiev Date: Mon, 20 Jan 2020 10:03:08 -0500 Subject: SERVER-44978 Thread-through ServiceContext and ThreadPool to ReadThroughCache There are no functional changes to this CR, it just instantiates every usage of ReadThroughCache with a ServiceContext and ThreadPool, which will be used for making the acquire method asynchronous. --- src/mongo/db/SConscript | 1 + src/mongo/db/auth/authorization_manager.cpp | 4 +- src/mongo/db/auth/authorization_manager.h | 2 +- src/mongo/db/auth/authorization_manager_global.cpp | 2 +- src/mongo/db/auth/authorization_manager_impl.cpp | 53 ++++++++++++++++------ src/mongo/db/auth/authorization_manager_impl.h | 45 +++++++++--------- src/mongo/db/auth/authorization_manager_test.cpp | 21 +-------- src/mongo/db/auth/authorization_session_test.cpp | 3 +- .../db/auth/sasl_authentication_session_test.cpp | 7 ++- src/mongo/db/auth/sasl_mechanism_registry_test.cpp | 4 +- src/mongo/db/auth/sasl_scram_test.cpp | 3 +- src/mongo/db/logical_session_cache_test.cpp | 3 +- src/mongo/db/logical_session_id_test.cpp | 3 +- src/mongo/db/read_write_concern_defaults.cpp | 21 +++++++-- src/mongo/db/read_write_concern_defaults.h | 6 ++- src/mongo/embedded/embedded_auth_manager.cpp | 2 +- src/mongo/util/SConscript | 4 +- src/mongo/util/read_through_cache.cpp | 15 +++++- src/mongo/util/read_through_cache.h | 38 ++++++++++++---- src/mongo/util/read_through_cache_test.cpp | 23 ++++++++-- 20 files changed, 167 insertions(+), 93 deletions(-) diff --git a/src/mongo/db/SConscript b/src/mongo/db/SConscript index c1099bfa5bf..f1810e06368 100644 --- a/src/mongo/db/SConscript +++ b/src/mongo/db/SConscript @@ -382,6 +382,7 @@ env.Library( ], LIBDEPS_PRIVATE=[ '$BUILD_DIR/mongo/util/caching', + '$BUILD_DIR/mongo/util/concurrency/thread_pool', 'logical_clock', ], ) diff --git a/src/mongo/db/auth/authorization_manager.cpp b/src/mongo/db/auth/authorization_manager.cpp index 0a2eaf493f0..5ad982559dc 100644 --- a/src/mongo/db/auth/authorization_manager.cpp +++ b/src/mongo/db/auth/authorization_manager.cpp @@ -71,9 +71,9 @@ const int AuthorizationManager::schemaVersion26Upgrade; const int AuthorizationManager::schemaVersion26Final; const int AuthorizationManager::schemaVersion28SCRAM; -std::unique_ptr AuthorizationManager::create() { +std::unique_ptr AuthorizationManager::create(ServiceContext* serviceContext) { static auto w = MONGO_WEAK_FUNCTION_DEFINITION(AuthorizationManager::create); - return w(); + return w(serviceContext); } } // namespace mongo diff --git a/src/mongo/db/auth/authorization_manager.h b/src/mongo/db/auth/authorization_manager.h index 3ac6c7cc7ce..a14122cad30 100644 --- a/src/mongo/db/auth/authorization_manager.h +++ b/src/mongo/db/auth/authorization_manager.h @@ -80,7 +80,7 @@ public: static AuthorizationManager* get(ServiceContext& service); static void set(ServiceContext* service, std::unique_ptr authzManager); - static std::unique_ptr create(); + static std::unique_ptr create(ServiceContext* serviceContext); AuthorizationManager() = default; diff --git a/src/mongo/db/auth/authorization_manager_global.cpp b/src/mongo/db/auth/authorization_manager_global.cpp index af3342c1c5d..9a7651bb91f 100644 --- a/src/mongo/db/auth/authorization_manager_global.cpp +++ b/src/mongo/db/auth/authorization_manager_global.cpp @@ -44,7 +44,7 @@ ServiceContext::ConstructorActionRegisterer createAuthorizationManager( "CreateAuthorizationManager", {"OIDGeneration", "EndStartupOptionStorage"}, [](ServiceContext* service) { - auto authzManager = AuthorizationManager::create(); + auto authzManager = AuthorizationManager::create(service); authzManager->setAuthEnabled(serverGlobalParams.authState == ServerGlobalParams::AuthState::kEnabled); authzManager->setShouldValidateAuthSchemaOnStartup(gStartupAuthSchemaValidation); diff --git a/src/mongo/db/auth/authorization_manager_impl.cpp b/src/mongo/db/auth/authorization_manager_impl.cpp index 603cf4c924c..a1314fddee0 100644 --- a/src/mongo/db/auth/authorization_manager_impl.cpp +++ b/src/mongo/db/auth/authorization_manager_impl.cpp @@ -285,8 +285,10 @@ Status initializeUserFromPrivilegeDocument(User* user, const BSONObj& privDoc) { return Status::OK(); } -std::unique_ptr authorizationManagerCreateImpl() { - return std::make_unique(); +std::unique_ptr authorizationManagerCreateImpl( + ServiceContext* serviceContext) { + return std::make_unique(serviceContext, + AuthzManagerExternalState::create()); } auto authorizationManagerCreateRegistration = @@ -310,17 +312,35 @@ Status AuthorizationManagerPinnedUsersServerParameter::setFromString(const std:: return authorizationManagerPinnedUsers.setFromString(str); } -AuthorizationManagerImpl::AuthorizationManagerImpl() - : AuthorizationManagerImpl(AuthzManagerExternalState::create(), - InstallMockForTestingOrAuthImpl{}) {} - AuthorizationManagerImpl::AuthorizationManagerImpl( - std::unique_ptr externalState, InstallMockForTestingOrAuthImpl) + ServiceContext* service, std::unique_ptr externalState) : _externalState(std::move(externalState)), - _authSchemaVersionCache(_externalState.get()), - _userCache(&_authSchemaVersionCache, _externalState.get(), authorizationManagerCacheSize) {} + _threadPool([] { + ThreadPool::Options options; + options.poolName = "AuthorizationManager"; + options.minThreads = 0; + options.maxThreads = ThreadPool::Options::kUnlimited; + + // Ensure all threads have a client + options.onCreateThread = [](const std::string& threadName) { + Client::initThread(threadName.c_str()); + }; -AuthorizationManagerImpl::~AuthorizationManagerImpl() {} + return options; + }()), + _authSchemaVersionCache(service, _threadPool, _externalState.get()), + _userCache(service, + _threadPool, + authorizationManagerCacheSize, + &_authSchemaVersionCache, + _externalState.get()) { + _threadPool.startup(); +} + +AuthorizationManagerImpl::~AuthorizationManagerImpl() { + _threadPool.shutdown(); + _threadPool.join(); +} std::unique_ptr AuthorizationManagerImpl::makeAuthorizationSession() { return std::make_unique( @@ -593,8 +613,11 @@ std::vector AuthorizationManagerImpl::getU } AuthorizationManagerImpl::AuthSchemaVersionCache::AuthSchemaVersionCache( + ServiceContext* service, + ThreadPoolInterface& threadPool, AuthzManagerExternalState* externalState) - : ReadThroughCache(1, _mutex), _externalState(externalState) {} + : ReadThroughCache(_mutex, service, threadPool, 1 /* cacheSize */), + _externalState(externalState) {} boost::optional AuthorizationManagerImpl::AuthSchemaVersionCache::lookup( OperationContext* opCtx, const int& unusedKey) { @@ -607,10 +630,12 @@ boost::optional AuthorizationManagerImpl::AuthSchemaVersionCache::lookup( } AuthorizationManagerImpl::UserCacheImpl::UserCacheImpl( + ServiceContext* service, + ThreadPoolInterface& threadPool, + int cacheSize, AuthSchemaVersionCache* authSchemaVersionCache, - AuthzManagerExternalState* externalState, - int cacheSize) - : UserCache(cacheSize, _mutex), + AuthzManagerExternalState* externalState) + : UserCache(_mutex, service, threadPool, cacheSize), _authSchemaVersionCache(authSchemaVersionCache), _externalState(externalState) {} diff --git a/src/mongo/db/auth/authorization_manager_impl.h b/src/mongo/db/auth/authorization_manager_impl.h index 81951ab3680..72f7e8f6969 100644 --- a/src/mongo/db/auth/authorization_manager_impl.h +++ b/src/mongo/db/auth/authorization_manager_impl.h @@ -34,6 +34,7 @@ #include "mongo/platform/mutex.h" #include "mongo/stdx/condition_variable.h" #include "mongo/stdx/unordered_map.h" +#include "mongo/util/concurrency/thread_pool.h" namespace mongo { @@ -42,16 +43,14 @@ namespace mongo { */ class AuthorizationManagerImpl : public AuthorizationManager { public: - ~AuthorizationManagerImpl() override; - - AuthorizationManagerImpl(); - struct InstallMockForTestingOrAuthImpl { explicit InstallMockForTestingOrAuthImpl() = default; }; - AuthorizationManagerImpl(std::unique_ptr externalState, - InstallMockForTestingOrAuthImpl); + AuthorizationManagerImpl(ServiceContext* service, + std::unique_ptr externalState); + ~AuthorizationManagerImpl(); + std::unique_ptr makeAuthorizationSession() override; @@ -137,32 +136,30 @@ private: std::unique_ptr _externalState; - /** - * True if AuthSchema startup checks should be applied in this AuthorizationManager. - * - * Changes to its value are not synchronized, so it should only be set at initalization-time. - */ + // True if AuthSchema startup checks should be applied in this AuthorizationManager. Changes to + // its value are not synchronized, so it should only be set once, at initalization time. bool _startupAuthSchemaValidation{true}; - /** - * True if access control enforcement is enabled in this AuthorizationManager. - * - * Changes to its value are not synchronized, so it should only be set at initalization-time. - */ + // True if access control enforcement is enabled in this AuthorizationManager. Changes to its + // value are not synchronized, so it should only be set once, at initalization time. bool _authEnabled{false}; - /** - * A cache of whether there are any users set up for the cluster. - */ + // A cache of whether there are any users set up for the cluster. AtomicWord _privilegeDocsExist{false}; + // Thread pool on which to perform the blocking activities that load the user credentials from + // storage + ThreadPool _threadPool; + /** * Cache which contains at most a single entry (which has key 0), whose value is the version of * the auth schema. */ class AuthSchemaVersionCache : public ReadThroughCache { public: - AuthSchemaVersionCache(AuthzManagerExternalState* externalState); + AuthSchemaVersionCache(ServiceContext* service, + ThreadPoolInterface& threadPool, + AuthzManagerExternalState* externalState); // Even though the dist cache permits for lookup to return boost::none for non-existent // values, the contract of the authorization manager is that it should throw an exception if @@ -181,9 +178,11 @@ private: */ class UserCacheImpl : public UserCache { public: - UserCacheImpl(AuthSchemaVersionCache* authSchemaVersionCache, - AuthzManagerExternalState* externalState, - int cacheSize); + UserCacheImpl(ServiceContext* service, + ThreadPoolInterface& threadPool, + int cacheSize, + AuthSchemaVersionCache* authSchemaVersionCache, + AuthzManagerExternalState* externalState); // Even though the dist cache permits for lookup to return boost::none for non-existent // values, the contract of the authorization manager is that it should throw an exception if diff --git a/src/mongo/db/auth/authorization_manager_test.cpp b/src/mongo/db/auth/authorization_manager_test.cpp index 30a9c19a766..f381d76ae47 100644 --- a/src/mongo/db/auth/authorization_manager_test.cpp +++ b/src/mongo/db/auth/authorization_manager_test.cpp @@ -85,8 +85,7 @@ public: auto localExternalState = std::make_unique(); externalState = localExternalState.get(); auto localAuthzManager = std::make_unique( - std::move(localExternalState), - AuthorizationManagerImpl::InstallMockForTestingOrAuthImpl{}); + getServiceContext(), std::move(localExternalState)); authzManager = localAuthzManager.get(); externalState->setAuthorizationManager(authzManager); authzManager->setAuthEnabled(true); @@ -258,24 +257,6 @@ private: } }; -class AuthorizationManagerWithExplicitUserPrivilegesTest : public ::mongo::unittest::Test { -public: - virtual void setUp() { - auto localExternalState = - std::make_unique(); - externalState = localExternalState.get(); - externalState->setAuthzVersion(AuthorizationManager::schemaVersion26Final); - authzManager = std::make_unique( - std::move(localExternalState), - AuthorizationManagerImpl::InstallMockForTestingOrAuthImpl{}); - externalState->setAuthorizationManager(authzManager.get()); - authzManager->setAuthEnabled(true); - } - - std::unique_ptr authzManager; - AuthzManagerExternalStateMockWithExplicitUserPrivileges* externalState; -}; - // Tests SERVER-21535, unrecognized actions should be ignored rather than causing errors. TEST_F(AuthorizationManagerTest, testAcquireV2UserWithUnrecognizedActions) { diff --git a/src/mongo/db/auth/authorization_session_test.cpp b/src/mongo/db/auth/authorization_session_test.cpp index f7df2444e5e..febccc348d2 100644 --- a/src/mongo/db/auth/authorization_session_test.cpp +++ b/src/mongo/db/auth/authorization_session_test.cpp @@ -103,8 +103,7 @@ public: managerState = localManagerState.get(); managerState->setAuthzVersion(AuthorizationManager::schemaVersion26Final); auto uniqueAuthzManager = std::make_unique( - std::move(localManagerState), - AuthorizationManagerImpl::InstallMockForTestingOrAuthImpl{}); + serviceContext.get(), std::move(localManagerState)); authzManager = uniqueAuthzManager.get(); AuthorizationManager::set(serviceContext.get(), std::move(uniqueAuthzManager)); auto localSessionState = std::make_unique(authzManager); diff --git a/src/mongo/db/auth/sasl_authentication_session_test.cpp b/src/mongo/db/auth/sasl_authentication_session_test.cpp index bdf8edc175d..d83f8455a23 100644 --- a/src/mongo/db/auth/sasl_authentication_session_test.cpp +++ b/src/mongo/db/auth/sasl_authentication_session_test.cpp @@ -55,7 +55,6 @@ #include "mongo/util/password_digest.h" namespace mongo { - namespace { class SaslConversation : public ServiceContextTest { @@ -94,10 +93,10 @@ SaslConversation::SaslConversation(std::string mech) : opCtx(makeOperationContext()), authManagerExternalState(new AuthzManagerExternalStateMock), authManager(new AuthorizationManagerImpl( - std::unique_ptr(authManagerExternalState), - AuthorizationManagerImpl::InstallMockForTestingOrAuthImpl{})), + getServiceContext(), + std::unique_ptr(authManagerExternalState))), authSession(authManager->makeAuthorizationSession()), - registry(opCtx->getServiceContext(), {"SCRAM-SHA-1", "SCRAM-SHA-256", "PLAIN"}), + registry(getServiceContext(), {"SCRAM-SHA-1", "SCRAM-SHA-256", "PLAIN"}), mechanism(mech) { AuthorizationManager::set(getServiceContext(), diff --git a/src/mongo/db/auth/sasl_mechanism_registry_test.cpp b/src/mongo/db/auth/sasl_mechanism_registry_test.cpp index a969c00b47a..e26efea8140 100644 --- a/src/mongo/db/auth/sasl_mechanism_registry_test.cpp +++ b/src/mongo/db/auth/sasl_mechanism_registry_test.cpp @@ -181,8 +181,8 @@ public: : opCtx(makeOperationContext()), authManagerExternalState(new AuthzManagerExternalStateMock()), authManager(new AuthorizationManagerImpl( - std::unique_ptr(authManagerExternalState), - AuthorizationManagerImpl::InstallMockForTestingOrAuthImpl{})), + getServiceContext(), + std::unique_ptr(authManagerExternalState))), // By default the registry is initialized with all mechanisms enabled. registry(opCtx->getServiceContext(), {"FOO", "BAR", "InternalAuth"}) { AuthorizationManager::set(getServiceContext(), diff --git a/src/mongo/db/auth/sasl_scram_test.cpp b/src/mongo/db/auth/sasl_scram_test.cpp index 4b2ec0b84d5..81b89590101 100644 --- a/src/mongo/db/auth/sasl_scram_test.cpp +++ b/src/mongo/db/auth/sasl_scram_test.cpp @@ -191,8 +191,7 @@ protected: std::make_unique(); authzManagerExternalState = uniqueAuthzManagerExternalStateMock.get(); auto newManager = std::make_unique( - std::move(uniqueAuthzManagerExternalStateMock), - AuthorizationManagerImpl::InstallMockForTestingOrAuthImpl{}); + serviceContext.get(), std::move(uniqueAuthzManagerExternalStateMock)); authzSession = std::make_unique( std::make_unique(newManager.get()), AuthorizationSessionImpl::InstallMockForTestingOrAuthImpl{}); diff --git a/src/mongo/db/logical_session_cache_test.cpp b/src/mongo/db/logical_session_cache_test.cpp index b4a301e6b92..d1a6be816e1 100644 --- a/src/mongo/db/logical_session_cache_test.cpp +++ b/src/mongo/db/logical_session_cache_test.cpp @@ -70,7 +70,8 @@ public: : _service(std::make_shared()), _sessions(std::make_shared()) { - AuthorizationManager::set(getServiceContext(), AuthorizationManager::create()); + AuthorizationManager::set(getServiceContext(), + AuthorizationManager::create(getServiceContext())); // Re-initialize the client after setting the AuthorizationManager to get an // AuthorizationSession. diff --git a/src/mongo/db/logical_session_id_test.cpp b/src/mongo/db/logical_session_id_test.cpp index 30a2529fec6..91a9efdbf8e 100644 --- a/src/mongo/db/logical_session_id_test.cpp +++ b/src/mongo/db/logical_session_id_test.cpp @@ -77,8 +77,7 @@ public: managerState = localManagerState.get(); managerState->setAuthzVersion(AuthorizationManager::schemaVersion26Final); auto authzManager = std::make_unique( - std::move(localManagerState), - AuthorizationManagerImpl::InstallMockForTestingOrAuthImpl{}); + getServiceContext(), std::move(localManagerState)); authzManager->setAuthEnabled(true); AuthorizationManager::set(getServiceContext(), std::move(authzManager)); Client::releaseCurrent(); diff --git a/src/mongo/db/read_write_concern_defaults.cpp b/src/mongo/db/read_write_concern_defaults.cpp index 020d094cd72..a09653a1a31 100644 --- a/src/mongo/db/read_write_concern_defaults.cpp +++ b/src/mongo/db/read_write_concern_defaults.cpp @@ -208,13 +208,28 @@ void ReadWriteConcernDefaults::create(ServiceContext* service, FetchDefaultsFn f } ReadWriteConcernDefaults::ReadWriteConcernDefaults(FetchDefaultsFn fetchDefaultsFn) - : _defaults([fetchDefaultsFn = std::move(fetchDefaultsFn)]( + : _threadPool([] { + ThreadPool::Options options; + options.poolName = "ReadWriteConcernDefaults"; + options.minThreads = 0; + options.maxThreads = 1; + + // Ensure all threads have a client + options.onCreateThread = [](const std::string& threadName) { + Client::initThread(threadName.c_str()); + }; + + return options; + }()), + _defaults(_threadPool, + [fetchDefaultsFn = std::move(fetchDefaultsFn)]( OperationContext* opCtx, const Type&) { return fetchDefaultsFn(opCtx); }) {} ReadWriteConcernDefaults::~ReadWriteConcernDefaults() = default; -ReadWriteConcernDefaults::Cache::Cache(LookupFn lookupFn) - : ReadThroughCache(1, _mutex), _lookupFn(lookupFn) {} +ReadWriteConcernDefaults::Cache::Cache(ThreadPoolInterface& threadPool, LookupFn lookupFn) + : ReadThroughCache(_mutex, getGlobalServiceContext(), threadPool, 1 /* cacheSize */), + _lookupFn(std::move(lookupFn)) {} boost::optional ReadWriteConcernDefaults::Cache::lookup( OperationContext* opCtx, const ReadWriteConcernDefaults::Type& key) { diff --git a/src/mongo/db/read_write_concern_defaults.h b/src/mongo/db/read_write_concern_defaults.h index d493fdf2cc3..259a3eafd24 100644 --- a/src/mongo/db/read_write_concern_defaults.h +++ b/src/mongo/db/read_write_concern_defaults.h @@ -37,6 +37,7 @@ #include "mongo/db/service_context.h" #include "mongo/db/write_concern_options.h" #include "mongo/platform/mutex.h" +#include "mongo/util/concurrency/thread_pool.h" #include "mongo/util/concurrency/with_lock.h" #include "mongo/util/read_through_cache.h" @@ -161,7 +162,7 @@ private: Cache& operator=(const Cache&) = delete; public: - Cache(LookupFn lookupFn); + Cache(ThreadPoolInterface& threadPool, LookupFn lookupFn); virtual ~Cache() = default; boost::optional lookup(OperationContext* opCtx, const Type& key) override; @@ -172,6 +173,9 @@ private: LookupFn _lookupFn; }; + // Thread pool on which to perform loading of the cached RWC defaults + ThreadPool _threadPool; + Cache _defaults; }; diff --git a/src/mongo/embedded/embedded_auth_manager.cpp b/src/mongo/embedded/embedded_auth_manager.cpp index bb8c81c5907..671fab3c41d 100644 --- a/src/mongo/embedded/embedded_auth_manager.cpp +++ b/src/mongo/embedded/embedded_auth_manager.cpp @@ -149,7 +149,7 @@ private: namespace { -std::unique_ptr authorizationManagerCreateImpl() { +std::unique_ptr authorizationManagerCreateImpl(ServiceContext*) { return std::make_unique(); } diff --git a/src/mongo/util/SConscript b/src/mongo/util/SConscript index 0cf1b69be67..b2793e906c9 100644 --- a/src/mongo/util/SConscript +++ b/src/mongo/util/SConscript @@ -207,8 +207,9 @@ env.Library( source=[ 'read_through_cache.cpp', ], - LIBDEPS=[ + LIBDEPS_PRIVATE=[ '$BUILD_DIR/mongo/base', + '$BUILD_DIR/mongo/db/service_context', ] ) @@ -609,6 +610,7 @@ icuEnv.CppUnitTest( 'caching', 'clock_source_mock', 'clock_sources', + 'concurrency/thread_pool', 'diagnostic_info', 'dns_query', 'fail_point', diff --git a/src/mongo/util/read_through_cache.cpp b/src/mongo/util/read_through_cache.cpp index a46f93c326f..877421fac9e 100644 --- a/src/mongo/util/read_through_cache.cpp +++ b/src/mongo/util/read_through_cache.cpp @@ -31,9 +31,14 @@ #include "mongo/util/read_through_cache.h" +#include "mongo/db/client.h" + namespace mongo { -ReadThroughCacheBase::ReadThroughCacheBase(Mutex& mutex) : _cacheWriteMutex(mutex) {} +ReadThroughCacheBase::ReadThroughCacheBase(Mutex& mutex, + ServiceContext* service, + ThreadPoolInterface& threadPool) + : _serviceContext(service), _threadPool(threadPool), _cacheWriteMutex(mutex) {} ReadThroughCacheBase::~ReadThroughCacheBase() = default; @@ -42,6 +47,14 @@ OID ReadThroughCacheBase::getCacheGeneration() const { return _fetchGeneration; } +void ReadThroughCacheBase::_asyncWork(WorkWithOpContext work) { + _threadPool.schedule([this, work = std::move(work)](Status status) { + ThreadClient tc(_serviceContext); + auto opCtxHolder = tc->makeOperationContext(); + work(opCtxHolder.get()); + }); +} + void ReadThroughCacheBase::_updateCacheGeneration(const CacheGuard&) { _fetchGeneration = OID::gen(); } diff --git a/src/mongo/util/read_through_cache.h b/src/mongo/util/read_through_cache.h index f13cd486e13..54e5d4bfc45 100644 --- a/src/mongo/util/read_through_cache.h +++ b/src/mongo/util/read_through_cache.h @@ -35,6 +35,8 @@ #include "mongo/db/operation_context.h" #include "mongo/platform/mutex.h" #include "mongo/stdx/condition_variable.h" +#include "mongo/util/concurrency/thread_pool_interface.h" +#include "mongo/util/functional.h" #include "mongo/util/invalidating_lru_cache.h" namespace mongo { @@ -53,7 +55,7 @@ public: OID getCacheGeneration() const; protected: - ReadThroughCacheBase(Mutex& mutex); + ReadThroughCacheBase(Mutex& mutex, ServiceContext* service, ThreadPoolInterface& threadPool); virtual ~ReadThroughCacheBase(); @@ -177,11 +179,29 @@ protected: friend class ReadThroughCacheBase::CacheGuard; + /** + * Creates a client and an operation context and executes the specified 'work' under that + * environment. + */ + using WorkWithOpContext = unique_function; + void _asyncWork(WorkWithOpContext work); + /** * Updates _fetchGeneration to a new OID */ void _updateCacheGeneration(const CacheGuard&); + /** + * Service context under which this cache has been instantiated (used for access to service-wide + * functionality, such as client/operation context creation) + */ + ServiceContext* const _serviceContext; + + /** + * Thread pool, to be used for invoking the blocking loader work. + */ + ThreadPoolInterface& _threadPool; + /** * Protects _fetchGeneration and _isFetchPhaseBusy. Manipulated via CacheGuard. */ @@ -329,8 +349,7 @@ public: if (guard.isSameCacheGeneration()) return ValueHandle(_cache.insertOrAssignAndGet( - key, - {std::move(*value), opCtx->getServiceContext()->getFastClockSource()->now()})); + key, {std::move(*value), _serviceContext->getFastClockSource()->now()})); // If the cache generation changed while this thread was in fetch mode, the data // associated with the value may now be invalid, so we will throw out the fetched value @@ -379,14 +398,17 @@ public: protected: /** - * ReadThroughCache constructor, to be called by sub-classes. Accepts the initial size of the - * cache, and a reference to a Mutex. The Mutex is for the exclusive use of the + * ReadThroughCache constructor, to be called by sub-classes. The 'cacheSize' parameter + * represents the maximum size of the cache and 'mutex' is for the exclusive use of the * ReadThroughCache, the sub-class should never actually use it (apart from passing it to this - * constructor). Having the Mutex stored by the sub-class allows latch diagnostics to be + * constructor). Having the Mutex stored by the sub-class allows latch diagnostics to be * correctly associated with the sub-class (not the generic ReadThroughCache class). */ - ReadThroughCache(int cacheSize, Mutex& mutex) - : ReadThroughCacheBase(mutex), _cache(cacheSize) {} + ReadThroughCache(Mutex& mutex, + ServiceContext* service, + ThreadPoolInterface& threadPool, + int cacheSize) + : ReadThroughCacheBase(mutex, service, threadPool), _cache(cacheSize) {} private: /** diff --git a/src/mongo/util/read_through_cache_test.cpp b/src/mongo/util/read_through_cache_test.cpp index f7420a235be..fe7372e411d 100644 --- a/src/mongo/util/read_through_cache_test.cpp +++ b/src/mongo/util/read_through_cache_test.cpp @@ -33,6 +33,7 @@ #include "mongo/db/service_context_test_fixture.h" #include "mongo/unittest/unittest.h" +#include "mongo/util/concurrency/thread_pool.h" #include "mongo/util/read_through_cache.h" namespace mongo { @@ -44,14 +45,28 @@ struct CachedValue { class Cache : public ReadThroughCache { public: - Cache(size_t size, LookupFn lookupFn) - : ReadThroughCache(size, _mutex), _lookupFn(std::move(lookupFn)) {} + Cache(ServiceContext* service, size_t size, LookupFn lookupFn) + : ReadThroughCache(_mutex, service, _threadPool, size), _lookupFn(std::move(lookupFn)) {} private: boost::optional lookup(OperationContext* opCtx, const std::string& key) override { return _lookupFn(opCtx, key); } + ThreadPool _threadPool{[] { + ThreadPool::Options options; + options.poolName = "ReadThroughCacheTest"; + options.minThreads = 0; + options.maxThreads = 1; + + // Ensure all threads have a client + options.onCreateThread = [](const std::string& threadName) { + Client::initThread(threadName.c_str()); + }; + + return options; + }()}; + Mutex _mutex = MONGO_MAKE_LATCH("ReadThroughCacheTest::Cache"); LookupFn _lookupFn; @@ -65,7 +80,7 @@ protected: TEST_F(ReadThroughCacheTest, FetchInvalidateAndRefetch) { int countLookups = 0; - Cache cache(1, [&](OperationContext*, const std::string& key) { + Cache cache(getServiceContext(), 1, [&](OperationContext*, const std::string& key) { ASSERT_EQ("TestKey", key); countLookups++; @@ -87,7 +102,7 @@ TEST_F(ReadThroughCacheTest, FetchInvalidateAndRefetch) { TEST_F(ReadThroughCacheTest, CacheSizeZero) { int countLookups = 0; - Cache cache(0, [&](OperationContext*, const std::string& key) { + Cache cache(getServiceContext(), 0, [&](OperationContext*, const std::string& key) { ASSERT_EQ("TestKey", key); countLookups++; -- cgit v1.2.1