summaryrefslogtreecommitdiff
path: root/src/mongo
diff options
context:
space:
mode:
authorADAM David Alan Martin <adam.martin@10gen.com>2017-04-24 17:48:06 -0400
committerADAM David Alan Martin <adam.martin@10gen.com>2017-04-24 17:48:06 -0400
commitcaec188e15b6e6015c2360b8e5bafc160d0d1f48 (patch)
tree3cc332db0cfe23cdfd6aeb32c8a079aa47357614 /src/mongo
parent918ac217df662fcb0865dba278df9151c3665fbc (diff)
downloadmongo-caec188e15b6e6015c2360b8e5bafc160d0d1f48.tar.gz
SERVER-28842 Slice `DatabaseHolder` using a vtable
The `DatabaseHolder` class and the `dbHolder` free function are used in many libraries which are circularly dependent with `catalog`. By adding a vtable to facilitate dynamically injectable implementations, we can break those dependencies.
Diffstat (limited to 'src/mongo')
-rw-r--r--src/mongo/db/catalog/SConscript13
-rw-r--r--src/mongo/db/catalog/collection.h48
-rw-r--r--src/mongo/db/catalog/database_holder.cpp207
-rw-r--r--src/mongo/db/catalog/database_holder.h95
-rw-r--r--src/mongo/db/catalog/database_holder_impl.cpp249
-rw-r--r--src/mongo/db/catalog/database_holder_impl.h98
6 files changed, 480 insertions, 230 deletions
diff --git a/src/mongo/db/catalog/SConscript b/src/mongo/db/catalog/SConscript
index dd2e2ddebd7..0776c567bc3 100644
--- a/src/mongo/db/catalog/SConscript
+++ b/src/mongo/db/catalog/SConscript
@@ -92,6 +92,16 @@ env.Library(
LIBDEPS=[
],
)
+
+env.Library(
+ target='database_holder',
+ source=[
+ "database_holder.cpp",
+ ],
+ LIBDEPS=[
+ ],
+)
+
env.Library(
target='collection_info_cache',
source=[
@@ -113,7 +123,7 @@ env.Library(
"create_collection.cpp",
"cursor_manager.cpp",
"database_impl.cpp",
- "database_holder.cpp",
+ "database_holder_impl.cpp",
"drop_collection.cpp",
"drop_database.cpp",
"drop_indexes.cpp",
@@ -127,6 +137,7 @@ env.Library(
'collection_info_cache',
'collection_options',
'database',
+ 'database_holder',
'index_catalog',
'index_catalog_entry',
'index_key_validate',
diff --git a/src/mongo/db/catalog/collection.h b/src/mongo/db/catalog/collection.h
index 5be5529499e..1f4bd4bf6bb 100644
--- a/src/mongo/db/catalog/collection.h
+++ b/src/mongo/db/catalog/collection.h
@@ -319,30 +319,6 @@ public:
};
private:
- std::unique_ptr<Impl> _pimpl;
-
- // This structure exists to give us a customization point to decide how to force users of this
- // class to depend upon the corresponding `index_catalog_entry.cpp` Translation Unit (TU). All
- // public forwarding functions call `_impl(), and `_impl` creates an instance of this structure.
- struct TUHook {
- static void hook() noexcept;
-
- explicit inline TUHook() noexcept {
- if (kDebugBuild)
- this->hook();
- }
- };
-
- inline const Impl& _impl() const {
- TUHook{};
- return *this->_pimpl;
- }
-
- inline Impl& _impl() {
- TUHook{};
- return *this->_pimpl;
- }
-
static std::unique_ptr<Impl> makeImpl(Collection* _this,
OperationContext* opCtx,
StringData fullNS,
@@ -739,6 +715,30 @@ private:
return this->_impl().recordStoreGoingToUpdateInPlace(opCtx, loc);
}
+ // This structure exists to give us a customization point to decide how to force users of this
+ // class to depend upon the corresponding `collection.cpp` Translation Unit (TU). All public
+ // forwarding functions call `_impl(), and `_impl` creates an instance of this structure.
+ struct TUHook {
+ static void hook() noexcept;
+
+ explicit inline TUHook() noexcept {
+ if (kDebugBuild)
+ this->hook();
+ }
+ };
+
+ inline const Impl& _impl() const {
+ TUHook{};
+ return *this->_pimpl;
+ }
+
+ inline Impl& _impl() {
+ TUHook{};
+ return *this->_pimpl;
+ }
+
+ std::unique_ptr<Impl> _pimpl;
+
friend class DatabaseImpl;
friend class IndexCatalogImpl;
};
diff --git a/src/mongo/db/catalog/database_holder.cpp b/src/mongo/db/catalog/database_holder.cpp
index bf13aea1860..0b0c4336662 100644
--- a/src/mongo/db/catalog/database_holder.cpp
+++ b/src/mongo/db/catalog/database_holder.cpp
@@ -1,5 +1,5 @@
/**
- * Copyright (C) 2012-2014 MongoDB Inc.
+ * Copyright (C) 2017 MongoDB Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
@@ -32,205 +32,36 @@
#include "mongo/db/catalog/database_holder.h"
-#include "mongo/db/audit.h"
-#include "mongo/db/auth/auth_index_d.h"
-#include "mongo/db/background.h"
-#include "mongo/db/catalog/database.h"
-#include "mongo/db/catalog/database_catalog_entry.h"
-#include "mongo/db/client.h"
-#include "mongo/db/clientcursor.h"
-#include "mongo/db/operation_context.h"
-#include "mongo/db/service_context.h"
-#include "mongo/db/storage/storage_engine.h"
-#include "mongo/util/log.h"
-#include "mongo/util/scopeguard.h"
-
namespace mongo {
-using std::set;
-using std::string;
-using std::stringstream;
+DatabaseHolder::Impl::~Impl() = default;
namespace {
-
-StringData _todb(StringData ns) {
- size_t i = ns.find('.');
- if (i == std::string::npos) {
- uassert(13074, "db name can't be empty", ns.size());
- return ns;
- }
-
- uassert(13075, "db name can't be empty", i > 0);
-
- const StringData d = ns.substr(0, i);
- uassert(13280,
- "invalid db name: " + ns.toString(),
- NamespaceString::validDBName(d, NamespaceString::DollarInDbNameBehavior::Allow));
-
- return d;
-}
-
-
-DatabaseHolder _dbHolder;
-
+stdx::function<DatabaseHolder::factory_function_type> factory;
} // namespace
-
-DatabaseHolder& dbHolder() {
- return _dbHolder;
-}
-
-
-Database* DatabaseHolder::get(OperationContext* opCtx, StringData ns) const {
- const StringData db = _todb(ns);
- invariant(opCtx->lockState()->isDbLockedForMode(db, MODE_IS));
-
- stdx::lock_guard<SimpleMutex> lk(_m);
- DBs::const_iterator it = _dbs.find(db);
- if (it != _dbs.end()) {
- return it->second;
- }
-
- return NULL;
-}
-
-std::set<std::string> DatabaseHolder::_getNamesWithConflictingCasing_inlock(StringData name) {
- std::set<std::string> duplicates;
-
- for (const auto& nameAndPointer : _dbs) {
- // A name that's equal with case-insensitive match must be identical, or it's a duplicate.
- if (name.equalCaseInsensitive(nameAndPointer.first) && name != nameAndPointer.first)
- duplicates.insert(nameAndPointer.first);
- }
- return duplicates;
+void DatabaseHolder::registerFactory(decltype(factory) newFactory) {
+ factory = std::move(newFactory);
}
-std::set<std::string> DatabaseHolder::getNamesWithConflictingCasing(StringData name) {
- stdx::lock_guard<SimpleMutex> lk(_m);
- return _getNamesWithConflictingCasing_inlock(name);
-}
-
-Database* DatabaseHolder::openDb(OperationContext* opCtx, StringData ns, bool* justCreated) {
- const StringData dbname = _todb(ns);
- invariant(opCtx->lockState()->isDbLockedForMode(dbname, MODE_X));
-
- if (justCreated)
- *justCreated = false; // Until proven otherwise.
-
- stdx::unique_lock<SimpleMutex> lk(_m);
-
- // The following will insert a nullptr for dbname, which will treated the same as a non-
- // existant database by the get method, yet still counts in getNamesWithConflictingCasing.
- if (auto db = _dbs[dbname])
- return db;
-
- // We've inserted a nullptr entry for dbname: make sure to remove it on unsuccessful exit.
- auto removeDbGuard = MakeGuard([this, &lk, dbname] {
- if (!lk.owns_lock())
- lk.lock();
- _dbs.erase(dbname);
- });
-
- // Check casing in lock to avoid transient duplicates.
- auto duplicates = _getNamesWithConflictingCasing_inlock(dbname);
- uassert(ErrorCodes::DatabaseDifferCase,
- str::stream() << "db already exists with different case already have: ["
- << *duplicates.cbegin()
- << "] trying to create ["
- << dbname.toString()
- << "]",
- duplicates.empty());
-
-
- // Do the catalog lookup and database creation outside of the scoped lock, because these may
- // block. Only one thread can be inside this method for the same DB name, because of the
- // requirement for X-lock on the database when we enter. So there is no way we can insert two
- // different databases for the same name.
- lk.unlock();
- StorageEngine* storageEngine = getGlobalServiceContext()->getGlobalStorageEngine();
- DatabaseCatalogEntry* entry = storageEngine->getDatabaseCatalogEntry(opCtx, dbname);
-
- if (!entry->exists()) {
- audit::logCreateDatabase(&cc(), dbname);
- if (justCreated)
- *justCreated = true;
- }
-
- auto newDb = stdx::make_unique<Database>(opCtx, dbname, entry);
-
- // Finally replace our nullptr entry with the new Database pointer.
- removeDbGuard.Dismiss();
- lk.lock();
- auto it = _dbs.find(dbname);
- invariant(it != _dbs.end() && it->second == nullptr);
- it->second = newDb.release();
- invariant(_getNamesWithConflictingCasing_inlock(dbname.toString()).empty());
-
- return it->second;
+auto DatabaseHolder::makeImpl() -> std::unique_ptr<Impl> {
+ return factory();
}
-void DatabaseHolder::close(OperationContext* opCtx, StringData ns, const std::string& reason) {
- invariant(opCtx->lockState()->isW());
-
- const StringData dbName = _todb(ns);
-
- stdx::lock_guard<SimpleMutex> lk(_m);
+void DatabaseHolder::TUHook::hook() noexcept {}
- DBs::const_iterator it = _dbs.find(dbName);
- if (it == _dbs.end()) {
- return;
- }
-
- it->second->close(opCtx, reason);
- delete it->second;
- _dbs.erase(it);
+namespace {
+stdx::function<decltype(dbHolder)> dbHolderImpl;
+} // namespace
+} // namespace mongo
- getGlobalServiceContext()->getGlobalStorageEngine()->closeDatabase(opCtx, dbName.toString());
+// The `mongo::` prefix is necessary to placate MSVC -- it is unable to properly identify anonymous
+// nested namespace members in `decltype` expressions when defining functions using scope-resolution
+// syntax.
+void mongo::registerDbHolderImpl(decltype(mongo::dbHolderImpl) impl) {
+ dbHolderImpl = std::move(impl);
}
-bool DatabaseHolder::closeAll(OperationContext* opCtx,
- BSONObjBuilder& result,
- bool force,
- const std::string& reason) {
- invariant(opCtx->lockState()->isW());
-
- stdx::lock_guard<SimpleMutex> lk(_m);
-
- set<string> dbs;
- for (DBs::const_iterator i = _dbs.begin(); i != _dbs.end(); ++i) {
- dbs.insert(i->first);
- }
-
- BSONArrayBuilder bb(result.subarrayStart("dbs"));
- int nNotClosed = 0;
- for (set<string>::iterator i = dbs.begin(); i != dbs.end(); ++i) {
- string name = *i;
-
- LOG(2) << "DatabaseHolder::closeAll name:" << name;
-
- if (!force && BackgroundOperation::inProgForDb(name)) {
- log() << "WARNING: can't close database " << name
- << " because a bg job is in progress - try killOp command";
- nNotClosed++;
- continue;
- }
-
- Database* db = _dbs[name];
- db->close(opCtx, reason);
- delete db;
-
- _dbs.erase(name);
-
- getGlobalServiceContext()->getGlobalStorageEngine()->closeDatabase(opCtx, name);
-
- bb.append(name);
- }
-
- bb.done();
- if (nNotClosed) {
- result.append("nNotClosed", nNotClosed);
- }
-
- return true;
-}
+auto mongo::dbHolder() -> DatabaseHolder& {
+ return dbHolderImpl();
}
diff --git a/src/mongo/db/catalog/database_holder.h b/src/mongo/db/catalog/database_holder.h
index ecde0496b76..58af1747218 100644
--- a/src/mongo/db/catalog/database_holder.h
+++ b/src/mongo/db/catalog/database_holder.h
@@ -1,5 +1,5 @@
/**
- * Copyright (C) 2012-2014 MongoDB Inc.
+ * Copyright (C) 2017 MongoDB Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
@@ -33,12 +33,12 @@
#include "mongo/base/string_data.h"
#include "mongo/db/namespace_string.h"
+#include "mongo/stdx/functional.h"
#include "mongo/stdx/mutex.h"
#include "mongo/util/concurrency/mutex.h"
#include "mongo/util/string_map.h"
namespace mongo {
-
class Database;
class OperationContext;
@@ -47,13 +47,43 @@ class OperationContext;
*/
class DatabaseHolder {
public:
- DatabaseHolder() = default;
+ class Impl {
+ public:
+ virtual ~Impl() = 0;
+
+ virtual Database* get(OperationContext* opCtx, StringData ns) const = 0;
+
+ virtual Database* openDb(OperationContext* opCtx, StringData ns, bool* justCreated) = 0;
+
+ virtual void close(OperationContext* opCtx, StringData ns, const std::string& reason) = 0;
+
+ virtual bool closeAll(OperationContext* opCtx,
+ BSONObjBuilder& result,
+ bool force,
+ const std::string& reason) = 0;
+
+ virtual std::set<std::string> getNamesWithConflictingCasing(StringData name) = 0;
+ };
+
+private:
+ static std::unique_ptr<Impl> makeImpl();
+
+public:
+ using factory_function_type = decltype(makeImpl);
+
+ static void registerFactory(stdx::function<factory_function_type> factory);
+
+ inline ~DatabaseHolder() = default;
+
+ inline explicit DatabaseHolder() : _pimpl(makeImpl()) {}
/**
* Retrieves an already opened database or returns NULL. Must be called with the database
* locked in at least IS-mode.
*/
- Database* get(OperationContext* opCtx, StringData ns) const;
+ inline Database* get(OperationContext* const opCtx, const StringData ns) const {
+ return this->_impl().get(opCtx, ns);
+ }
/**
* Retrieves a database reference if it is already opened, or opens it if it hasn't been
@@ -62,12 +92,20 @@ public:
* @param justCreated Returns whether the database was newly created (true) or it already
* existed (false). Can be NULL if this information is not necessary.
*/
- Database* openDb(OperationContext* opCtx, StringData ns, bool* justCreated = NULL);
+ inline Database* openDb(OperationContext* const opCtx,
+ const StringData ns,
+ bool* const justCreated = nullptr) {
+ return this->_impl().openDb(opCtx, ns, justCreated);
+ }
/**
* Closes the specified database. Must be called with the database locked in X-mode.
*/
- void close(OperationContext* opCtx, StringData ns, const std::string& reason);
+ inline void close(OperationContext* const opCtx,
+ const StringData ns,
+ const std::string& reason) {
+ return this->_impl().close(opCtx, ns, reason);
+ }
/**
* Closes all opened databases. Must be called with the global lock acquired in X-mode.
@@ -76,23 +114,46 @@ public:
* @param force Force close even if something underway - use at shutdown
* @param reason The reason for close.
*/
- bool closeAll(OperationContext* opCtx,
- BSONObjBuilder& result,
- bool force,
- const std::string& reason);
+ inline bool closeAll(OperationContext* const opCtx,
+ BSONObjBuilder& result,
+ const bool force,
+ const std::string& reason) {
+ return this->_impl().closeAll(opCtx, result, force, reason);
+ }
/**
* Returns the set of existing database names that differ only in casing.
*/
- std::set<std::string> getNamesWithConflictingCasing(StringData name);
+ inline std::set<std::string> getNamesWithConflictingCasing(const StringData name) {
+ return this->_impl().getNamesWithConflictingCasing(name);
+ }
private:
- std::set<std::string> _getNamesWithConflictingCasing_inlock(StringData name);
+ // This structure exists to give us a customization point to decide how to force users of this
+ // class to depend upon the corresponding `database_holder.cpp` Translation Unit (TU). All
+ // public forwarding functions call `_impl(), and `_impl` creates an instance of this structure.
+ struct TUHook {
+ static void hook() noexcept;
+
+ explicit inline TUHook() noexcept {
+ if (kDebugBuild)
+ this->hook();
+ }
+ };
+
+ inline const Impl& _impl() const {
+ TUHook{};
+ return *this->_pimpl;
+ }
+
+ inline Impl& _impl() {
+ TUHook{};
+ return *this->_pimpl;
+ }
- typedef StringMap<Database*> DBs;
- mutable SimpleMutex _m;
- DBs _dbs;
+ std::unique_ptr<Impl> _pimpl;
};
-DatabaseHolder& dbHolder();
-}
+extern DatabaseHolder& dbHolder();
+extern void registerDbHolderImpl(stdx::function<decltype(dbHolder)> impl);
+} // namespace mongo
diff --git a/src/mongo/db/catalog/database_holder_impl.cpp b/src/mongo/db/catalog/database_holder_impl.cpp
new file mode 100644
index 00000000000..a9a7573eccc
--- /dev/null
+++ b/src/mongo/db/catalog/database_holder_impl.cpp
@@ -0,0 +1,249 @@
+/**
+ * Copyright (C) 2017 MongoDB Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the GNU Affero General Public License in all respects for
+ * all of the code used other than as permitted herein. If you modify file(s)
+ * with this exception, you may extend this exception to your version of the
+ * file(s), but you are not obligated to do so. If you do not wish to do so,
+ * delete this exception statement from your version. If you delete this
+ * exception statement from all source files in the program, then also delete
+ * it in the license file.
+ */
+
+#define MONGO_LOG_DEFAULT_COMPONENT ::mongo::logger::LogComponent::kStorage
+
+#include "mongo/platform/basic.h"
+
+#include "mongo/db/catalog/database_holder_impl.h"
+
+#include "mongo/base/init.h"
+#include "mongo/db/audit.h"
+#include "mongo/db/auth/auth_index_d.h"
+#include "mongo/db/background.h"
+#include "mongo/db/catalog/database.h"
+#include "mongo/db/catalog/database_catalog_entry.h"
+#include "mongo/db/client.h"
+#include "mongo/db/clientcursor.h"
+#include "mongo/db/operation_context.h"
+#include "mongo/db/service_context.h"
+#include "mongo/db/storage/storage_engine.h"
+#include "mongo/util/log.h"
+#include "mongo/util/scopeguard.h"
+
+namespace mongo {
+namespace {
+
+DatabaseHolder& dbHolderImpl() {
+ static DatabaseHolder _dbHolder;
+ return _dbHolder;
+}
+
+MONGO_INITIALIZER_WITH_PREREQUISITES(InitializeDbHolderimpl, ("InitializeDatabaseHolderFactory"))
+(InitializerContext* const) {
+ registerDbHolderImpl(dbHolderImpl);
+ return Status::OK();
+}
+
+MONGO_INITIALIZER(InitializeDatabaseHolderFactory)(InitializerContext* const) {
+ DatabaseHolder::registerFactory([] { return stdx::make_unique<DatabaseHolderImpl>(); });
+ return Status::OK();
+}
+
+} // namespace
+
+using std::set;
+using std::size_t;
+using std::string;
+using std::stringstream;
+
+namespace {
+
+StringData _todb(StringData ns) {
+ size_t i = ns.find('.');
+ if (i == std::string::npos) {
+ uassert(13074, "db name can't be empty", ns.size());
+ return ns;
+ }
+
+ uassert(13075, "db name can't be empty", i > 0);
+
+ const StringData d = ns.substr(0, i);
+ uassert(13280,
+ "invalid db name: " + ns.toString(),
+ NamespaceString::validDBName(d, NamespaceString::DollarInDbNameBehavior::Allow));
+
+ return d;
+}
+
+} // namespace
+
+
+Database* DatabaseHolderImpl::get(OperationContext* opCtx, StringData ns) const {
+ const StringData db = _todb(ns);
+ invariant(opCtx->lockState()->isDbLockedForMode(db, MODE_IS));
+
+ stdx::lock_guard<SimpleMutex> lk(_m);
+ DBs::const_iterator it = _dbs.find(db);
+ if (it != _dbs.end()) {
+ return it->second;
+ }
+
+ return NULL;
+}
+
+std::set<std::string> DatabaseHolderImpl::_getNamesWithConflictingCasing_inlock(StringData name) {
+ std::set<std::string> duplicates;
+
+ for (const auto& nameAndPointer : _dbs) {
+ // A name that's equal with case-insensitive match must be identical, or it's a duplicate.
+ if (name.equalCaseInsensitive(nameAndPointer.first) && name != nameAndPointer.first)
+ duplicates.insert(nameAndPointer.first);
+ }
+ return duplicates;
+}
+
+std::set<std::string> DatabaseHolderImpl::getNamesWithConflictingCasing(StringData name) {
+ stdx::lock_guard<SimpleMutex> lk(_m);
+ return _getNamesWithConflictingCasing_inlock(name);
+}
+
+Database* DatabaseHolderImpl::openDb(OperationContext* opCtx, StringData ns, bool* justCreated) {
+ const StringData dbname = _todb(ns);
+ invariant(opCtx->lockState()->isDbLockedForMode(dbname, MODE_X));
+
+ if (justCreated)
+ *justCreated = false; // Until proven otherwise.
+
+ stdx::unique_lock<SimpleMutex> lk(_m);
+
+ // The following will insert a nullptr for dbname, which will treated the same as a non-
+ // existant database by the get method, yet still counts in getNamesWithConflictingCasing.
+ if (auto db = _dbs[dbname])
+ return db;
+
+ // We've inserted a nullptr entry for dbname: make sure to remove it on unsuccessful exit.
+ auto removeDbGuard = MakeGuard([this, &lk, dbname] {
+ if (!lk.owns_lock())
+ lk.lock();
+ _dbs.erase(dbname);
+ });
+
+ // Check casing in lock to avoid transient duplicates.
+ auto duplicates = _getNamesWithConflictingCasing_inlock(dbname);
+ uassert(ErrorCodes::DatabaseDifferCase,
+ str::stream() << "db already exists with different case already have: ["
+ << *duplicates.cbegin()
+ << "] trying to create ["
+ << dbname.toString()
+ << "]",
+ duplicates.empty());
+
+
+ // Do the catalog lookup and database creation outside of the scoped lock, because these may
+ // block. Only one thread can be inside this method for the same DB name, because of the
+ // requirement for X-lock on the database when we enter. So there is no way we can insert two
+ // different databases for the same name.
+ lk.unlock();
+ StorageEngine* storageEngine = getGlobalServiceContext()->getGlobalStorageEngine();
+ DatabaseCatalogEntry* entry = storageEngine->getDatabaseCatalogEntry(opCtx, dbname);
+
+ if (!entry->exists()) {
+ audit::logCreateDatabase(&cc(), dbname);
+ if (justCreated)
+ *justCreated = true;
+ }
+
+ auto newDb = stdx::make_unique<Database>(opCtx, dbname, entry);
+
+ // Finally replace our nullptr entry with the new Database pointer.
+ removeDbGuard.Dismiss();
+ lk.lock();
+ auto it = _dbs.find(dbname);
+ invariant(it != _dbs.end() && it->second == nullptr);
+ it->second = newDb.release();
+ invariant(_getNamesWithConflictingCasing_inlock(dbname.toString()).empty());
+
+ return it->second;
+}
+
+void DatabaseHolderImpl::close(OperationContext* opCtx, StringData ns, const std::string& reason) {
+ invariant(opCtx->lockState()->isW());
+
+ const StringData dbName = _todb(ns);
+
+ stdx::lock_guard<SimpleMutex> lk(_m);
+
+ DBs::const_iterator it = _dbs.find(dbName);
+ if (it == _dbs.end()) {
+ return;
+ }
+
+ it->second->close(opCtx, reason);
+ delete it->second;
+ _dbs.erase(it);
+
+ getGlobalServiceContext()->getGlobalStorageEngine()->closeDatabase(opCtx, dbName.toString());
+}
+
+bool DatabaseHolderImpl::closeAll(OperationContext* opCtx,
+ BSONObjBuilder& result,
+ bool force,
+ const std::string& reason) {
+ invariant(opCtx->lockState()->isW());
+
+ stdx::lock_guard<SimpleMutex> lk(_m);
+
+ set<string> dbs;
+ for (DBs::const_iterator i = _dbs.begin(); i != _dbs.end(); ++i) {
+ dbs.insert(i->first);
+ }
+
+ BSONArrayBuilder bb(result.subarrayStart("dbs"));
+ int nNotClosed = 0;
+ for (set<string>::iterator i = dbs.begin(); i != dbs.end(); ++i) {
+ string name = *i;
+
+ LOG(2) << "DatabaseHolder::closeAll name:" << name;
+
+ if (!force && BackgroundOperation::inProgForDb(name)) {
+ log() << "WARNING: can't close database " << name
+ << " because a bg job is in progress - try killOp command";
+ nNotClosed++;
+ continue;
+ }
+
+ Database* db = _dbs[name];
+ db->close(opCtx, reason);
+ delete db;
+
+ _dbs.erase(name);
+
+ getGlobalServiceContext()->getGlobalStorageEngine()->closeDatabase(opCtx, name);
+
+ bb.append(name);
+ }
+
+ bb.done();
+ if (nNotClosed) {
+ result.append("nNotClosed", nNotClosed);
+ }
+
+ return true;
+}
+} // namespace mongo
diff --git a/src/mongo/db/catalog/database_holder_impl.h b/src/mongo/db/catalog/database_holder_impl.h
new file mode 100644
index 00000000000..c60d0043123
--- /dev/null
+++ b/src/mongo/db/catalog/database_holder_impl.h
@@ -0,0 +1,98 @@
+/**
+ * Copyright (C) 2017 MongoDB Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the GNU Affero General Public License in all respects for
+ * all of the code used other than as permitted herein. If you modify file(s)
+ * with this exception, you may extend this exception to your version of the
+ * file(s), but you are not obligated to do so. If you do not wish to do so,
+ * delete this exception statement from your version. If you delete this
+ * exception statement from all source files in the program, then also delete
+ * it in the license file.
+ */
+
+#pragma once
+
+#include "mongo/db/catalog/database_holder.h"
+
+#include <set>
+#include <string>
+
+#include "mongo/base/string_data.h"
+#include "mongo/db/namespace_string.h"
+#include "mongo/stdx/mutex.h"
+#include "mongo/util/concurrency/mutex.h"
+#include "mongo/util/string_map.h"
+
+namespace mongo {
+
+class Database;
+class OperationContext;
+
+/**
+ * Registry of opened databases.
+ */
+class DatabaseHolderImpl : public DatabaseHolder::Impl {
+public:
+ DatabaseHolderImpl() = default;
+
+ /**
+ * Retrieves an already opened database or returns NULL. Must be called with the database
+ * locked in at least IS-mode.
+ */
+ Database* get(OperationContext* opCtx, StringData ns) const override;
+
+ /**
+ * Retrieves a database reference if it is already opened, or opens it if it hasn't been
+ * opened/created yet. Must be called with the database locked in X-mode.
+ *
+ * @param justCreated Returns whether the database was newly created (true) or it already
+ * existed (false). Can be NULL if this information is not necessary.
+ */
+ Database* openDb(OperationContext* opCtx, StringData ns, bool* justCreated = nullptr) override;
+
+ /**
+ * Closes the specified database. Must be called with the database locked in X-mode.
+ */
+ void close(OperationContext* opCtx, StringData ns, const std::string& reason) override;
+
+ /**
+ * Closes all opened databases. Must be called with the global lock acquired in X-mode.
+ *
+ * @param result Populated with the names of the databases, which were closed.
+ * @param force Force close even if something underway - use at shutdown
+ * @param reason The reason for close.
+ */
+ bool closeAll(OperationContext* opCtx,
+ BSONObjBuilder& result,
+ bool force,
+ const std::string& reason) override;
+
+ /**
+ * Returns the set of existing database names that differ only in casing.
+ */
+ std::set<std::string> getNamesWithConflictingCasing(StringData name) override;
+
+private:
+ std::set<std::string> _getNamesWithConflictingCasing_inlock(StringData name);
+
+ typedef StringMap<Database*> DBs;
+ mutable SimpleMutex _m;
+ DBs _dbs;
+};
+} // namespace mongo