diff options
author | Gregory Noma <gregory.noma@gmail.com> | 2022-09-14 18:34:02 -0400 |
---|---|---|
committer | Evergreen Agent <no-reply@evergreen.mongodb.com> | 2022-09-16 20:30:14 +0000 |
commit | bfd35c950c9863ef08b8f7adbdf760820ef4da96 (patch) | |
tree | 81c4c63191d7f4f0f04bff28533b3fd591f7abbe /src/mongo/db/concurrency | |
parent | ec54f761d043e56cf484de864a89c3645223f878 (diff) | |
download | mongo-bfd35c950c9863ef08b8f7adbdf760820ef4da96.tar.gz |
SERVER-67383 Track resource names using `ResourceCatalog`
Diffstat (limited to 'src/mongo/db/concurrency')
-rw-r--r-- | src/mongo/db/concurrency/SConscript | 3 | ||||
-rw-r--r-- | src/mongo/db/concurrency/lock_manager.cpp | 6 | ||||
-rw-r--r-- | src/mongo/db/concurrency/resource_catalog.cpp | 97 | ||||
-rw-r--r-- | src/mongo/db/concurrency/resource_catalog.h | 61 | ||||
-rw-r--r-- | src/mongo/db/concurrency/resource_catalog_test.cpp | 153 |
5 files changed, 315 insertions, 5 deletions
diff --git a/src/mongo/db/concurrency/SConscript b/src/mongo/db/concurrency/SConscript index b902468ada9..37880a0c061 100644 --- a/src/mongo/db/concurrency/SConscript +++ b/src/mongo/db/concurrency/SConscript @@ -53,6 +53,7 @@ env.Library( 'lock_state.cpp', 'lock_stats.cpp', 'replication_state_transition_lock_guard.cpp', + 'resource_catalog.cpp', ], LIBDEPS=[ '$BUILD_DIR/mongo/db/service_context', @@ -64,7 +65,6 @@ env.Library( 'lock_manager_defs', ], LIBDEPS_PRIVATE=[ - '$BUILD_DIR/mongo/db/catalog/collection_catalog', '$BUILD_DIR/mongo/db/concurrency/flow_control_ticketholder', '$BUILD_DIR/mongo/db/server_base', ], @@ -98,6 +98,7 @@ env.CppUnitTest( 'lock_manager_test.cpp', 'lock_state_test.cpp', 'lock_stats_test.cpp', + 'resource_catalog_test.cpp', ], LIBDEPS=[ '$BUILD_DIR/mongo/db/auth/authmocks', diff --git a/src/mongo/db/concurrency/lock_manager.cpp b/src/mongo/db/concurrency/lock_manager.cpp index 4bd35cb207e..fe3acad56b5 100644 --- a/src/mongo/db/concurrency/lock_manager.cpp +++ b/src/mongo/db/concurrency/lock_manager.cpp @@ -40,9 +40,9 @@ #include "mongo/base/static_assert.h" #include "mongo/bson/bsonobjbuilder.h" #include "mongo/config.h" -#include "mongo/db/catalog/collection_catalog.h" #include "mongo/db/concurrency/d_concurrency.h" #include "mongo/db/concurrency/locker.h" +#include "mongo/db/concurrency/resource_catalog.h" #include "mongo/db/service_context.h" #include "mongo/logv2/log.h" #include "mongo/util/assert_util.h" @@ -975,9 +975,7 @@ std::string ResourceId::toString() const { } if (getType() == RESOURCE_DATABASE || getType() == RESOURCE_COLLECTION) { - auto catalog = CollectionCatalog::get(getGlobalServiceContext()); - boost::optional<std::string> resourceName = catalog->lookupResourceName(*this); - if (resourceName) { + if (auto resourceName = ResourceCatalog::get(getGlobalServiceContext()).name(*this)) { ss << ", " << *resourceName; } } diff --git a/src/mongo/db/concurrency/resource_catalog.cpp b/src/mongo/db/concurrency/resource_catalog.cpp new file mode 100644 index 00000000000..62183c3a4f8 --- /dev/null +++ b/src/mongo/db/concurrency/resource_catalog.cpp @@ -0,0 +1,97 @@ +/** + * Copyright (C) 2022-present MongoDB, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the Server Side Public License, version 1, + * as published by MongoDB, Inc. + * + * 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 + * Server Side Public License for more details. + * + * You should have received a copy of the Server Side Public License + * along with this program. If not, see + * <http://www.mongodb.com/licensing/server-side-public-license>. + * + * 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 Server Side 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. + */ + +#include "mongo/db/concurrency/resource_catalog.h" + +#include "mongo/db/service_context.h" + +namespace mongo { +namespace { +const auto getResourceCatalog = ServiceContext::declareDecoration<ResourceCatalog>(); +} // namespace + +ResourceCatalog& ResourceCatalog::get(ServiceContext* svcCtx) { + return getResourceCatalog(svcCtx); +} + +void ResourceCatalog::add(ResourceId id, const NamespaceString& ns) { + invariant(id.getType() == RESOURCE_COLLECTION); + _add(id, ns.toStringWithTenantId()); +} + +void ResourceCatalog::add(ResourceId id, const DatabaseName& dbName) { + invariant(id.getType() == RESOURCE_DATABASE); + _add(id, dbName.toStringWithTenantId()); +} + +void ResourceCatalog::_add(ResourceId id, std::string name) { + stdx::lock_guard<Latch> lk{_mutex}; + _resources[id].insert(std::move(name)); +} + +void ResourceCatalog::remove(ResourceId id, const NamespaceString& ns) { + invariant(id.getType() == RESOURCE_COLLECTION); + _remove(id, ns.toStringWithTenantId()); +} + +void ResourceCatalog::remove(ResourceId id, const DatabaseName& dbName) { + invariant(id.getType() == RESOURCE_DATABASE); + _remove(id, dbName.toStringWithTenantId()); +} + +void ResourceCatalog::_remove(ResourceId id, const std::string& name) { + stdx::lock_guard<Latch> lk{_mutex}; + + auto it = _resources.find(id); + if (it == _resources.end()) { + return; + } + + it->second.erase(name); + + if (it->second.empty()) { + _resources.erase(it); + } +} + +void ResourceCatalog::clear() { + stdx::lock_guard<Latch> lk{_mutex}; + _resources.clear(); +} + +boost::optional<std::string> ResourceCatalog::name(ResourceId id) const { + invariant(id.getType() == RESOURCE_DATABASE || id.getType() == RESOURCE_COLLECTION); + stdx::lock_guard<Latch> lk{_mutex}; + + auto it = _resources.find(id); + return it == _resources.end() || it->second.size() > 1 + ? boost::none + : boost::make_optional(*it->second.begin()); +} +} // namespace mongo diff --git a/src/mongo/db/concurrency/resource_catalog.h b/src/mongo/db/concurrency/resource_catalog.h new file mode 100644 index 00000000000..44f684053b4 --- /dev/null +++ b/src/mongo/db/concurrency/resource_catalog.h @@ -0,0 +1,61 @@ +/** + * Copyright (C) 2022-present MongoDB, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the Server Side Public License, version 1, + * as published by MongoDB, Inc. + * + * 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 + * Server Side Public License for more details. + * + * You should have received a copy of the Server Side Public License + * along with this program. If not, see + * <http://www.mongodb.com/licensing/server-side-public-license>. + * + * 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 Server Side 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/concurrency/lock_manager_defs.h" + +namespace mongo { +class ResourceCatalog { +public: + static ResourceCatalog& get(ServiceContext* scvCtx); + + void add(ResourceId id, const NamespaceString& ns); + void add(ResourceId id, const DatabaseName& dbName); + + void remove(ResourceId id, const NamespaceString& ns); + void remove(ResourceId id, const DatabaseName& dbName); + + void clear(); + + /** + * Returns the name of a resource by its id. If the id is not found or it maps to multiple + * resources, returns boost::none. + */ + boost::optional<std::string> name(ResourceId id) const; + +private: + void _add(ResourceId id, std::string name); + + void _remove(ResourceId id, const std::string& name); + + mutable Mutex _mutex = MONGO_MAKE_LATCH("ResourceCatalog"); + stdx::unordered_map<ResourceId, StringSet> _resources; +}; +} // namespace mongo diff --git a/src/mongo/db/concurrency/resource_catalog_test.cpp b/src/mongo/db/concurrency/resource_catalog_test.cpp new file mode 100644 index 00000000000..99752d3acd4 --- /dev/null +++ b/src/mongo/db/concurrency/resource_catalog_test.cpp @@ -0,0 +1,153 @@ +/** + * Copyright (C) 2022-present MongoDB, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the Server Side Public License, version 1, + * as published by MongoDB, Inc. + * + * 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 + * Server Side Public License for more details. + * + * You should have received a copy of the Server Side Public License + * along with this program. If not, see + * <http://www.mongodb.com/licensing/server-side-public-license>. + * + * 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 Server Side 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. + */ + +#include "mongo/db/concurrency/resource_catalog.h" +#include "mongo/unittest/death_test.h" +#include "mongo/unittest/unittest.h" + +namespace mongo { +namespace { +class ResourceCatalogTest : public unittest::Test { +public: + void setUp() { + ASSERT_EQ(firstResourceId, secondResourceId); + ASSERT_NE(firstResourceId, thirdResourceId); + } + +protected: + NamespaceString firstCollection{boost::none, "1661880728"}; + ResourceId firstResourceId{RESOURCE_COLLECTION, firstCollection}; + + NamespaceString secondCollection{boost::none, "1626936312"}; + ResourceId secondResourceId{RESOURCE_COLLECTION, secondCollection}; + + NamespaceString thirdCollection{boost::none, "2930102946"}; + ResourceId thirdResourceId{RESOURCE_COLLECTION, thirdCollection}; + + ResourceCatalog catalog; +}; + +TEST_F(ResourceCatalogTest, EmptyTest) { + auto resource = catalog.name(firstResourceId); + ASSERT_EQ(boost::none, resource); + + catalog.remove(secondResourceId, secondCollection); + resource = catalog.name(secondResourceId); + ASSERT_EQ(boost::none, resource); +} + +TEST_F(ResourceCatalogTest, InsertTest) { + catalog.add(firstResourceId, firstCollection); + auto resource = catalog.name(thirdResourceId); + ASSERT_EQ(boost::none, resource); + + catalog.add(thirdResourceId, thirdCollection); + + resource = catalog.name(firstResourceId); + ASSERT_EQ(firstCollection.toStringWithTenantId(), *resource); + + resource = catalog.name(thirdResourceId); + ASSERT_EQ(thirdCollection.toStringWithTenantId(), resource); +} + +TEST_F(ResourceCatalogTest, RemoveTest) { + catalog.add(firstResourceId, firstCollection); + catalog.add(thirdResourceId, thirdCollection); + + // This fails to remove the resource because of an invalid namespace. + catalog.remove(firstResourceId, NamespaceString(boost::none, "BadNamespace")); + auto resource = catalog.name(firstResourceId); + ASSERT_EQ(firstCollection.toStringWithTenantId(), *resource); + + catalog.remove(firstResourceId, firstCollection); + catalog.remove(firstResourceId, firstCollection); + catalog.remove(thirdResourceId, thirdCollection); + + resource = catalog.name(firstResourceId); + ASSERT_EQ(boost::none, resource); + + resource = catalog.name(thirdResourceId); + ASSERT_EQ(boost::none, resource); +} + +TEST_F(ResourceCatalogTest, CollisionTest) { + // firstCollection and secondCollection map to the same ResourceId. + catalog.add(firstResourceId, firstCollection); + catalog.add(secondResourceId, secondCollection); + + // Looking up the namespace on a ResourceId while it has a collision should + // return the empty string. + auto resource = catalog.name(firstResourceId); + ASSERT_EQ(boost::none, resource); + + resource = catalog.name(secondResourceId); + ASSERT_EQ(boost::none, resource); + + // We remove a namespace, resolving the collision. + catalog.remove(firstResourceId, firstCollection); + resource = catalog.name(secondResourceId); + ASSERT_EQ(secondCollection.toStringWithTenantId(), *resource); + + // Adding the same namespace twice does not create a collision. + catalog.add(secondResourceId, secondCollection); + resource = catalog.name(secondResourceId); + ASSERT_EQ(secondCollection.toStringWithTenantId(), *resource); + + // The map should function normally for entries without collisions. + catalog.add(firstResourceId, firstCollection); + resource = catalog.name(secondResourceId); + ASSERT_EQ(boost::none, resource); + + catalog.add(thirdResourceId, thirdCollection); + resource = catalog.name(thirdResourceId); + ASSERT_EQ(thirdCollection.toStringWithTenantId(), *resource); + + catalog.remove(thirdResourceId, thirdCollection); + resource = catalog.name(thirdResourceId); + ASSERT_EQ(boost::none, resource); + + catalog.remove(firstResourceId, firstCollection); + catalog.remove(secondResourceId, secondCollection); + + resource = catalog.name(firstResourceId); + ASSERT_EQ(boost::none, resource); + + resource = catalog.name(secondResourceId); + ASSERT_EQ(boost::none, resource); +} + +DEATH_TEST_F(ResourceCatalogTest, AddDatabaseInvalidResourceType, "invariant") { + catalog.add({RESOURCE_GLOBAL, 0}, DatabaseName{"db"}); +} + +DEATH_TEST_F(ResourceCatalogTest, AddCollectionInvalidResourceType, "invariant") { + catalog.add({RESOURCE_GLOBAL, 0}, NamespaceString{"db.coll"}); +} +} // namespace +} // namespace mongo |