summaryrefslogtreecommitdiff
path: root/src/mongo/db/concurrency
diff options
context:
space:
mode:
authorGregory Noma <gregory.noma@gmail.com>2022-09-14 18:34:02 -0400
committerEvergreen Agent <no-reply@evergreen.mongodb.com>2022-09-16 20:30:14 +0000
commitbfd35c950c9863ef08b8f7adbdf760820ef4da96 (patch)
tree81c4c63191d7f4f0f04bff28533b3fd591f7abbe /src/mongo/db/concurrency
parentec54f761d043e56cf484de864a89c3645223f878 (diff)
downloadmongo-bfd35c950c9863ef08b8f7adbdf760820ef4da96.tar.gz
SERVER-67383 Track resource names using `ResourceCatalog`
Diffstat (limited to 'src/mongo/db/concurrency')
-rw-r--r--src/mongo/db/concurrency/SConscript3
-rw-r--r--src/mongo/db/concurrency/lock_manager.cpp6
-rw-r--r--src/mongo/db/concurrency/resource_catalog.cpp97
-rw-r--r--src/mongo/db/concurrency/resource_catalog.h61
-rw-r--r--src/mongo/db/concurrency/resource_catalog_test.cpp153
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