summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGregory Wlodarek <gregory.wlodarek@mongodb.com>2019-04-02 14:06:20 -0400
committerGregory Wlodarek <gregory.wlodarek@mongodb.com>2019-04-08 14:35:47 -0400
commit28a361799cf6a0d7ea1c3196b19aa9a75795b56d (patch)
tree0e9426b0faa3e6fa80e88d726336632c16e696f5
parent02a87ee5b1942d24e1d7a20502c79d36218929fe (diff)
downloadmongo-28a361799cf6a0d7ea1c3196b19aa9a75795b56d.tar.gz
SERVER-39902 lockInfo should use UUIDCatalog to map resourceIds to collection names
-rw-r--r--src/mongo/db/catalog/SConscript1
-rw-r--r--src/mongo/db/catalog/uuid_catalog.cpp85
-rw-r--r--src/mongo/db/catalog/uuid_catalog.h24
-rw-r--r--src/mongo/db/catalog/uuid_catalog_test.cpp229
-rw-r--r--src/mongo/db/concurrency/SConscript1
-rw-r--r--src/mongo/db/concurrency/lock_manager.cpp56
-rw-r--r--src/mongo/db/concurrency/lock_manager_defs.h41
-rw-r--r--src/mongo/db/concurrency/lock_state_test.cpp63
-rw-r--r--src/mongo/db/concurrency/lock_stats_test.cpp15
9 files changed, 420 insertions, 95 deletions
diff --git a/src/mongo/db/catalog/SConscript b/src/mongo/db/catalog/SConscript
index 89817e1fb7a..7b1e314e286 100644
--- a/src/mongo/db/catalog/SConscript
+++ b/src/mongo/db/catalog/SConscript
@@ -342,7 +342,6 @@ env.CppUnitTest(
],
LIBDEPS=[
'uuid_catalog',
- '$BUILD_DIR/mongo/db/concurrency/lock_manager',
'$BUILD_DIR/mongo/db/service_context',
'$BUILD_DIR/mongo/db/storage/kv/kv_prefix',
],
diff --git a/src/mongo/db/catalog/uuid_catalog.cpp b/src/mongo/db/catalog/uuid_catalog.cpp
index 40f5863c32f..51bda980b72 100644
--- a/src/mongo/db/catalog/uuid_catalog.cpp
+++ b/src/mongo/db/catalog/uuid_catalog.cpp
@@ -34,6 +34,7 @@
#include "mongo/db/catalog/database.h"
#include "mongo/db/catalog/database_holder.h"
+#include "mongo/db/concurrency/lock_manager_defs.h"
#include "mongo/db/storage/recovery_unit.h"
#include "mongo/util/assert_util.h"
#include "mongo/util/log.h"
@@ -277,6 +278,12 @@ void UUIDCatalog::setCollectionNamespace(OperationContext* opCtx,
_collections[toCollection] = _collections[fromCollection];
_collections.erase(fromCollection);
+ ResourceId oldRid = ResourceId(RESOURCE_COLLECTION, fromCollection.ns());
+ ResourceId newRid = ResourceId(RESOURCE_COLLECTION, toCollection.ns());
+
+ removeResource(oldRid, fromCollection.ns());
+ addResource(newRid, toCollection.ns());
+
opCtx->recoveryUnit()->onRollback([this, coll, fromCollection, toCollection, catalogEntry] {
stdx::lock_guard<stdx::mutex> lock(_catalogLock);
coll->setNs(std::move(fromCollection));
@@ -284,6 +291,12 @@ void UUIDCatalog::setCollectionNamespace(OperationContext* opCtx,
_collections[fromCollection] = _collections[toCollection];
_collections.erase(toCollection);
+
+ ResourceId oldRid = ResourceId(RESOURCE_COLLECTION, fromCollection.ns());
+ ResourceId newRid = ResourceId(RESOURCE_COLLECTION, toCollection.ns());
+
+ removeResource(newRid, toCollection.ns());
+ addResource(oldRid, fromCollection.ns());
});
}
@@ -296,6 +309,9 @@ void UUIDCatalog::onCloseDatabase(Database* db) {
deregisterCollectionObject(coll->uuid().get());
}
}
+
+ auto rid = ResourceId(RESOURCE_DATABASE, db->name());
+ removeResource(rid, db->name());
}
void UUIDCatalog::onCloseCatalog(OperationContext* opCtx) {
@@ -492,6 +508,12 @@ void UUIDCatalog::registerCollectionObject(CollectionUUID uuid, std::unique_ptr<
_catalog[uuid].collection = std::move(coll);
_catalog[uuid].collectionPtr = _catalog[uuid].collection.get();
+
+ auto dbRid = ResourceId(RESOURCE_DATABASE, dbName);
+ addResource(dbRid, dbName);
+
+ auto collRid = ResourceId(RESOURCE_COLLECTION, ns.ns());
+ addResource(collRid, ns.ns());
}
std::unique_ptr<Collection> UUIDCatalog::deregisterCollectionObject(CollectionUUID uuid) {
@@ -517,6 +539,9 @@ std::unique_ptr<Collection> UUIDCatalog::deregisterCollectionObject(CollectionUU
// Make sure collection catalog entry still exists.
invariant(_catalog[uuid].collectionCatalogEntry);
+ auto collRid = ResourceId(RESOURCE_COLLECTION, ns.ns());
+ removeResource(collRid, ns.ns());
+
// Removal from an ordered map will invalidate iterators and potentially references to the
// references to the erased element.
_generationNumber++;
@@ -576,6 +601,9 @@ void UUIDCatalog::deregisterAllCatalogEntriesAndCollectionObjects() {
_orderedCollections.clear();
_catalog.clear();
+ stdx::lock_guard<stdx::mutex> resourceLock(_resourceLock);
+ _resourceInformation.clear();
+
_generationNumber++;
}
@@ -587,4 +615,61 @@ UUIDCatalog::iterator UUIDCatalog::end() const {
return iterator(_orderedCollections.end());
}
+boost::optional<std::string> UUIDCatalog::lookupResourceName(const ResourceId& rid) {
+ invariant(rid.getType() == RESOURCE_DATABASE || rid.getType() == RESOURCE_COLLECTION);
+ stdx::lock_guard<stdx::mutex> lock(_resourceLock);
+
+ auto search = _resourceInformation.find(rid);
+ if (search == _resourceInformation.end()) {
+ return boost::none;
+ }
+
+ std::set<std::string>& namespaces = search->second;
+
+ // When there are multiple namespaces mapped to the same ResourceId, return boost::none as the
+ // ResourceId does not identify a single namespace.
+ if (namespaces.size() > 1) {
+ return boost::none;
+ }
+
+ return *namespaces.begin();
+}
+
+void UUIDCatalog::removeResource(const ResourceId& rid, const std::string& entry) {
+ invariant(rid.getType() == RESOURCE_DATABASE || rid.getType() == RESOURCE_COLLECTION);
+ stdx::lock_guard<stdx::mutex> lock(_resourceLock);
+
+ auto search = _resourceInformation.find(rid);
+ if (search == _resourceInformation.end()) {
+ return;
+ }
+
+ std::set<std::string>& namespaces = search->second;
+ namespaces.erase(entry);
+
+ // Remove the map entry if this is the last namespace in the set for the ResourceId.
+ if (namespaces.size() == 0) {
+ _resourceInformation.erase(search);
+ }
+}
+
+void UUIDCatalog::addResource(const ResourceId& rid, const std::string& entry) {
+ invariant(rid.getType() == RESOURCE_DATABASE || rid.getType() == RESOURCE_COLLECTION);
+ stdx::lock_guard<stdx::mutex> lock(_resourceLock);
+
+ auto search = _resourceInformation.find(rid);
+ if (search == _resourceInformation.end()) {
+ std::set<std::string> newSet = {entry};
+ _resourceInformation.insert(std::make_pair(rid, newSet));
+ return;
+ }
+
+ std::set<std::string>& namespaces = search->second;
+ if (namespaces.count(entry) > 0) {
+ return;
+ }
+
+ namespaces.insert(entry);
+}
+
} // namespace mongo
diff --git a/src/mongo/db/catalog/uuid_catalog.h b/src/mongo/db/catalog/uuid_catalog.h
index 928910fe4ee..0e39d22e0c8 100644
--- a/src/mongo/db/catalog/uuid_catalog.h
+++ b/src/mongo/db/catalog/uuid_catalog.h
@@ -29,6 +29,7 @@
#pragma once
+#include <set>
#include <unordered_map>
#include "mongo/db/catalog/collection.h"
@@ -392,6 +393,23 @@ public:
iterator begin(StringData db) const;
iterator end() const;
+ /**
+ * Lookup the name of a resource by its ResourceId. If there are multiple namespaces mapped to
+ * the same ResourceId entry, we return the boost::none for those namespaces until there is
+ * only one namespace in the set. If the ResourceId is not found, boost::none is returned.
+ */
+ boost::optional<std::string> lookupResourceName(const ResourceId& rid);
+
+ /**
+ * Removes an existing ResourceId 'rid' with namespace 'entry' from the map.
+ */
+ void removeResource(const ResourceId& rid, const std::string& entry);
+
+ /**
+ * Inserts a new ResourceId 'rid' into the map with namespace 'entry'.
+ */
+ void addResource(const ResourceId& rid, const std::string& entry);
+
private:
class FinishDropChange;
friend class UUIDCatalog::iterator;
@@ -429,5 +447,11 @@ private:
* Generation number to track changes to the catalog that could invalidate iterators.
*/
uint64_t _generationNumber;
+
+ // Protects _resourceInformation.
+ mutable stdx::mutex _resourceLock;
+
+ // Mapping from ResourceId to a set of strings that contains collection and database namespaces.
+ std::map<ResourceId, std::set<std::string>> _resourceInformation;
};
} // namespace mongo
diff --git a/src/mongo/db/catalog/uuid_catalog_test.cpp b/src/mongo/db/catalog/uuid_catalog_test.cpp
index d7dfe0575f6..b262c4a0c5f 100644
--- a/src/mongo/db/catalog/uuid_catalog_test.cpp
+++ b/src/mongo/db/catalog/uuid_catalog_test.cpp
@@ -29,10 +29,13 @@
#include "mongo/db/catalog/uuid_catalog.h"
#include <algorithm>
+#include <boost/optional/optional_io.hpp>
#include "mongo/db/catalog/collection_catalog_entry_mock.h"
#include "mongo/db/catalog/collection_mock.h"
+#include "mongo/db/concurrency/lock_manager_defs.h"
#include "mongo/db/operation_context_noop.h"
+#include "mongo/unittest/death_test.h"
#include "mongo/unittest/unittest.h"
using namespace mongo;
@@ -146,8 +149,228 @@ protected:
std::map<std::string, std::map<CollectionUUID, CollectionMock*>> dbMap;
};
+class UUIDCatalogResourceMapTest : public unittest::Test {
+public:
+ void setUp() {
+ // The first and second collection namespaces map to the same ResourceId.
+ firstCollection = "1661880728";
+ secondCollection = "1626936312";
+
+ firstResourceId = ResourceId(RESOURCE_COLLECTION, firstCollection);
+ secondResourceId = ResourceId(RESOURCE_COLLECTION, secondCollection);
+ ASSERT_EQ(firstResourceId, secondResourceId);
+
+ thirdCollection = "2930102946";
+ thirdResourceId = ResourceId(RESOURCE_COLLECTION, thirdCollection);
+ ASSERT_NE(firstResourceId, thirdResourceId);
+ }
+
+protected:
+ std::string firstCollection;
+ ResourceId firstResourceId;
+
+ std::string secondCollection;
+ ResourceId secondResourceId;
+
+ std::string thirdCollection;
+ ResourceId thirdResourceId;
+
+ UUIDCatalog catalog;
+};
+
+TEST_F(UUIDCatalogResourceMapTest, EmptyTest) {
+ boost::optional<std::string> resource = catalog.lookupResourceName(firstResourceId);
+ ASSERT_EQ(boost::none, resource);
+
+ catalog.removeResource(secondResourceId, secondCollection);
+ resource = catalog.lookupResourceName(secondResourceId);
+ ASSERT_EQ(boost::none, resource);
+}
+
+TEST_F(UUIDCatalogResourceMapTest, InsertTest) {
+ catalog.addResource(firstResourceId, firstCollection);
+ boost::optional<std::string> resource = catalog.lookupResourceName(thirdResourceId);
+ ASSERT_EQ(boost::none, resource);
+
+ catalog.addResource(thirdResourceId, thirdCollection);
+
+ resource = catalog.lookupResourceName(firstResourceId);
+ ASSERT_EQ(firstCollection, *resource);
+
+ resource = catalog.lookupResourceName(thirdResourceId);
+ ASSERT_EQ(thirdCollection, resource);
+}
+
+TEST_F(UUIDCatalogResourceMapTest, RemoveTest) {
+ catalog.addResource(firstResourceId, firstCollection);
+ catalog.addResource(thirdResourceId, thirdCollection);
+
+ // This fails to remove the resource because of an invalid namespace.
+ catalog.removeResource(firstResourceId, "BadNamespace");
+ boost::optional<std::string> resource = catalog.lookupResourceName(firstResourceId);
+ ASSERT_EQ(firstCollection, *resource);
+
+ catalog.removeResource(firstResourceId, firstCollection);
+ catalog.removeResource(firstResourceId, firstCollection);
+ catalog.removeResource(thirdResourceId, thirdCollection);
+
+ resource = catalog.lookupResourceName(firstResourceId);
+ ASSERT_EQ(boost::none, resource);
+
+ resource = catalog.lookupResourceName(thirdResourceId);
+ ASSERT_EQ(boost::none, resource);
+}
+
+TEST_F(UUIDCatalogResourceMapTest, CollisionTest) {
+ // firstCollection and secondCollection map to the same ResourceId.
+ catalog.addResource(firstResourceId, firstCollection);
+ catalog.addResource(secondResourceId, secondCollection);
+
+ // Looking up the namespace on a ResourceId while it has a collision should
+ // return the empty string.
+ boost::optional<std::string> resource = catalog.lookupResourceName(firstResourceId);
+ ASSERT_EQ(boost::none, resource);
+
+ resource = catalog.lookupResourceName(secondResourceId);
+ ASSERT_EQ(boost::none, resource);
+
+ // We remove a namespace, resolving the collision.
+ catalog.removeResource(firstResourceId, firstCollection);
+ resource = catalog.lookupResourceName(secondResourceId);
+ ASSERT_EQ(secondCollection, *resource);
+
+ // Adding the same namespace twice does not create a collision.
+ catalog.addResource(secondResourceId, secondCollection);
+ resource = catalog.lookupResourceName(secondResourceId);
+ ASSERT_EQ(secondCollection, *resource);
+
+ // The map should function normally for entries without collisions.
+ catalog.addResource(firstResourceId, firstCollection);
+ resource = catalog.lookupResourceName(secondResourceId);
+ ASSERT_EQ(boost::none, resource);
+
+ catalog.addResource(thirdResourceId, thirdCollection);
+ resource = catalog.lookupResourceName(thirdResourceId);
+ ASSERT_EQ(thirdCollection, *resource);
+
+ catalog.removeResource(thirdResourceId, thirdCollection);
+ resource = catalog.lookupResourceName(thirdResourceId);
+ ASSERT_EQ(boost::none, resource);
+
+ catalog.removeResource(firstResourceId, firstCollection);
+ catalog.removeResource(secondResourceId, secondCollection);
+
+ resource = catalog.lookupResourceName(firstResourceId);
+ ASSERT_EQ(boost::none, resource);
+
+ resource = catalog.lookupResourceName(secondResourceId);
+ ASSERT_EQ(boost::none, resource);
+}
+
+class UUIDCatalogResourceTest : public unittest::Test {
+public:
+ void setUp() {
+ for (int i = 0; i < 5; i++) {
+ NamespaceString nss("resourceDb", "coll" + std::to_string(i));
+ auto coll = std::make_unique<CollectionMock>(nss);
+ auto newCatalogEntry = std::make_unique<CollectionCatalogEntryMock>(nss.ns());
+ auto uuid = coll->uuid();
+
+ catalog.registerCatalogEntry(uuid.get(), std::move(newCatalogEntry));
+ catalog.onCreateCollection(&opCtx, std::move(coll), uuid.get());
+ }
+
+ int numEntries = 0;
+ for (auto it = catalog.begin("resourceDb"); it != catalog.end(); it++) {
+ auto coll = *it;
+ std::string collName = coll->ns().ns();
+ ResourceId rid(RESOURCE_COLLECTION, collName);
+
+ ASSERT_NE(catalog.lookupResourceName(rid), boost::none);
+ numEntries++;
+ }
+ ASSERT_EQ(5, numEntries);
+ }
+
+ void tearDown() {
+ for (auto it = catalog.begin("resourceDb"); it != catalog.end(); ++it) {
+ auto coll = *it;
+ auto uuid = coll->uuid().get();
+ if (!coll) {
+ break;
+ }
+
+ catalog.deregisterCollectionObject(uuid);
+ catalog.deregisterCatalogEntry(uuid);
+ }
+
+ int numEntries = 0;
+ for (auto it = catalog.begin("resourceDb"); it != catalog.end(); it++) {
+ numEntries++;
+ }
+ ASSERT_EQ(0, numEntries);
+ }
+
+protected:
+ OperationContextNoop opCtx;
+ UUIDCatalog catalog;
+};
+
namespace {
+TEST_F(UUIDCatalogResourceTest, RemoveAllResources) {
+ catalog.deregisterAllCatalogEntriesAndCollectionObjects();
+
+ const std::string dbName = "resourceDb";
+ auto rid = ResourceId(RESOURCE_DATABASE, dbName);
+ ASSERT_EQ(boost::none, catalog.lookupResourceName(rid));
+
+ for (int i = 0; i < 5; i++) {
+ NamespaceString nss("resourceDb", "coll" + std::to_string(i));
+ rid = ResourceId(RESOURCE_COLLECTION, nss.ns());
+ ASSERT_EQ(boost::none, catalog.lookupResourceName((rid)));
+ }
+}
+
+TEST_F(UUIDCatalogResourceTest, LookupDatabaseResource) {
+ const std::string dbName = "resourceDb";
+ auto rid = ResourceId(RESOURCE_DATABASE, dbName);
+ boost::optional<std::string> ridStr = catalog.lookupResourceName(rid);
+
+ ASSERT(ridStr);
+ ASSERT(ridStr->find(dbName) != std::string::npos);
+}
+
+TEST_F(UUIDCatalogResourceTest, LookupMissingDatabaseResource) {
+ const std::string dbName = "missingDb";
+ auto rid = ResourceId(RESOURCE_DATABASE, dbName);
+ ASSERT(!catalog.lookupResourceName(rid));
+}
+
+TEST_F(UUIDCatalogResourceTest, LookupCollectionResource) {
+ const std::string collNs = "resourceDb.coll1";
+ auto rid = ResourceId(RESOURCE_COLLECTION, collNs);
+ boost::optional<std::string> ridStr = catalog.lookupResourceName(rid);
+
+ ASSERT(ridStr);
+ ASSERT(ridStr->find(collNs) != std::string::npos);
+}
+
+TEST_F(UUIDCatalogResourceTest, LookupMissingCollectionResource) {
+ const std::string dbName = "resourceDb.coll5";
+ auto rid = ResourceId(RESOURCE_COLLECTION, dbName);
+ ASSERT(!catalog.lookupResourceName(rid));
+}
+
+TEST_F(UUIDCatalogResourceTest, RemoveCollection) {
+ const std::string collNs = "resourceDb.coll1";
+ auto coll = catalog.lookupCollectionByNamespace(NamespaceString(collNs));
+ auto uniqueColl = catalog.deregisterCollectionObject(coll->uuid().get());
+ catalog.deregisterCatalogEntry(uniqueColl->uuid().get());
+ auto rid = ResourceId(RESOURCE_COLLECTION, collNs);
+ ASSERT(!catalog.lookupResourceName(rid));
+}
+
// Create an iterator over the UUIDCatalog and assert that all collections are present.
// Iteration ends when the end of the catalog is reached.
TEST_F(UUIDCatalogIterationTest, EndAtEndOfCatalog) {
@@ -448,4 +671,10 @@ TEST_F(UUIDCatalogTest, LookupNSSByUUIDForClosedCatalogReturnsFreshestNSS) {
ASSERT_EQUALS(catalog.lookupCollectionByUUID(colUUID), newCol);
ASSERT_EQUALS(catalog.lookupNSSByUUID(colUUID), newNss);
}
+
+DEATH_TEST_F(UUIDCatalogResourceTest, AddInvalidResourceType, "invariant") {
+ auto rid = ResourceId(RESOURCE_GLOBAL, 0);
+ catalog.addResource(rid, "");
+}
+
} // namespace
diff --git a/src/mongo/db/concurrency/SConscript b/src/mongo/db/concurrency/SConscript
index 986bb83c616..b2364a65afe 100644
--- a/src/mongo/db/concurrency/SConscript
+++ b/src/mongo/db/concurrency/SConscript
@@ -55,6 +55,7 @@ env.Library(
'$BUILD_DIR/third_party/shim_boost',
],
LIBDEPS_PRIVATE=[
+ '$BUILD_DIR/mongo/db/catalog/uuid_catalog',
'$BUILD_DIR/mongo/db/concurrency/flow_control_ticketholder',
],
)
diff --git a/src/mongo/db/concurrency/lock_manager.cpp b/src/mongo/db/concurrency/lock_manager.cpp
index 0862d70e129..237e182d8eb 100644
--- a/src/mongo/db/concurrency/lock_manager.cpp
+++ b/src/mongo/db/concurrency/lock_manager.cpp
@@ -33,13 +33,12 @@
#include "mongo/db/concurrency/lock_manager.h"
-#include <third_party/murmurhash3/MurmurHash3.h>
-
#include "mongo/base/data_type_endian.h"
#include "mongo/base/data_view.h"
#include "mongo/base/static_assert.h"
#include "mongo/bson/bsonobjbuilder.h"
#include "mongo/config.h"
+#include "mongo/db/catalog/uuid_catalog.h"
#include "mongo/db/concurrency/d_concurrency.h"
#include "mongo/db/concurrency/locker.h"
#include "mongo/util/assert_util.h"
@@ -99,23 +98,6 @@ uint32_t modeMask(LockMode mode) {
return 1 << mode;
}
-uint64_t hashStringData(StringData str) {
- char hash[16];
- MurmurHash3_x64_128(str.rawData(), str.size(), 0, hash);
- return static_cast<size_t>(ConstDataView(hash).read<LittleEndian<std::uint64_t>>());
-}
-
-/**
- * Maps the resource id to a human-readable string.
- */
-static const char* ResourceTypeNames[] = {
- "Invalid", "Global", "Database", "Collection", "Metadata", "Mutex"};
-
-// Ensure we do not add new types without updating the names array
-MONGO_STATIC_ASSERT((sizeof(ResourceTypeNames) / sizeof(ResourceTypeNames[0])) ==
- ResourceTypesCount);
-
-
/**
* Maps the LockRequest status to a human-readable string.
*/
@@ -975,28 +957,6 @@ LockHead* LockManager::LockBucket::findOrInsert(ResourceId resId) {
//
// ResourceId
//
-
-uint64_t ResourceId::fullHash(ResourceType type, uint64_t hashId) {
- return (static_cast<uint64_t>(type) << (64 - resourceTypeBits)) +
- (hashId & (std::numeric_limits<uint64_t>::max() >> resourceTypeBits));
-}
-
-ResourceId::ResourceId(ResourceType type, StringData ns)
- : _fullHash(fullHash(type, hashStringData(ns))) {
-#ifdef MONGO_CONFIG_DEBUG_BUILD
- _nsCopy = ns.toString();
-#endif
-}
-
-ResourceId::ResourceId(ResourceType type, const std::string& ns)
- : _fullHash(fullHash(type, hashStringData(ns))) {
-#ifdef MONGO_CONFIG_DEBUG_BUILD
- _nsCopy = ns;
-#endif
-}
-
-ResourceId::ResourceId(ResourceType type, uint64_t hashId) : _fullHash(fullHash(type, hashId)) {}
-
std::string ResourceId::toString() const {
StringBuilder ss;
ss << "{" << _fullHash << ": " << resourceTypeName(getType()) << ", " << getHashId();
@@ -1004,9 +964,13 @@ std::string ResourceId::toString() const {
ss << ", " << Lock::ResourceMutex::getName(*this);
}
-#ifdef MONGO_CONFIG_DEBUG_BUILD
- ss << ", " << _nsCopy;
-#endif
+ if (getType() == RESOURCE_DATABASE || getType() == RESOURCE_COLLECTION) {
+ UUIDCatalog& uuidCatalog = UUIDCatalog::get(getGlobalServiceContext());
+ boost::optional<std::string> resourceName = uuidCatalog.lookupResourceName(*this);
+ if (resourceName) {
+ ss << ", " << *resourceName;
+ }
+ }
ss << "}";
@@ -1055,10 +1019,6 @@ bool isModeCovered(LockMode mode, LockMode coveringMode) {
LockConflictsTable[coveringMode];
}
-const char* resourceTypeName(ResourceType resourceType) {
- return ResourceTypeNames[resourceType];
-}
-
const char* lockRequestStatusName(LockRequest::Status status) {
return LockRequestStatusNames[status];
}
diff --git a/src/mongo/db/concurrency/lock_manager_defs.h b/src/mongo/db/concurrency/lock_manager_defs.h
index 2146f44f83f..141b3ee4899 100644
--- a/src/mongo/db/concurrency/lock_manager_defs.h
+++ b/src/mongo/db/concurrency/lock_manager_defs.h
@@ -31,8 +31,13 @@
#include <cstdint>
#include <limits>
+#include <map>
#include <string>
+#include <third_party/murmurhash3/MurmurHash3.h>
+
+#include "mongo/base/data_type_endian.h"
+#include "mongo/base/data_view.h"
#include "mongo/base/static_assert.h"
#include "mongo/base/string_data.h"
#include "mongo/config.h"
@@ -167,9 +172,21 @@ enum ResourceType {
};
/**
+ * Maps the resource id to a human-readable string.
+ */
+static const char* ResourceTypeNames[] = {
+ "Invalid", "Global", "Database", "Collection", "Metadata", "Mutex"};
+
+// Ensure we do not add new types without updating the names array.
+MONGO_STATIC_ASSERT((sizeof(ResourceTypeNames) / sizeof(ResourceTypeNames[0])) ==
+ ResourceTypesCount);
+
+/**
* Returns a human-readable name for the specified resource type.
*/
-const char* resourceTypeName(ResourceType resourceType);
+static const char* resourceTypeName(ResourceType resourceType) {
+ return ResourceTypeNames[resourceType];
+}
/**
* Uniquely identifies a lockable resource.
@@ -192,9 +209,10 @@ public:
};
ResourceId() : _fullHash(0) {}
- ResourceId(ResourceType type, StringData ns);
- ResourceId(ResourceType type, const std::string& ns);
- ResourceId(ResourceType type, uint64_t hashId);
+ ResourceId(ResourceType type, StringData ns) : _fullHash(fullHash(type, hashStringData(ns))) {}
+ ResourceId(ResourceType type, const std::string& ns)
+ : _fullHash(fullHash(type, hashStringData(ns))) {}
+ ResourceId(ResourceType type, uint64_t hashId) : _fullHash(fullHash(type, hashId)) {}
bool isValid() const {
return getType() != RESOURCE_INVALID;
@@ -232,13 +250,16 @@ private:
*/
uint64_t _fullHash;
- static uint64_t fullHash(ResourceType type, uint64_t hashId);
+ static uint64_t fullHash(ResourceType type, uint64_t hashId) {
+ return (static_cast<uint64_t>(type) << (64 - resourceTypeBits)) +
+ (hashId & (std::numeric_limits<uint64_t>::max() >> resourceTypeBits));
+ }
-#ifdef MONGO_CONFIG_DEBUG_BUILD
- // Keep the complete namespace name for debugging purposes (TODO: this will be
- // removed once we are confident in the robustness of the lock manager).
- std::string _nsCopy;
-#endif
+ static uint64_t hashStringData(StringData str) {
+ char hash[16];
+ MurmurHash3_x64_128(str.rawData(), str.size(), 0, hash);
+ return static_cast<size_t>(ConstDataView(hash).read<LittleEndian<std::uint64_t>>());
+ }
};
#ifndef MONGO_CONFIG_DEBUG_BUILD
diff --git a/src/mongo/db/concurrency/lock_state_test.cpp b/src/mongo/db/concurrency/lock_state_test.cpp
index 63eb6e1a676..3b44e23a444 100644
--- a/src/mongo/db/concurrency/lock_state_test.cpp
+++ b/src/mongo/db/concurrency/lock_state_test.cpp
@@ -38,6 +38,7 @@
#include "mongo/config.h"
#include "mongo/db/concurrency/lock_manager_test_help.h"
#include "mongo/db/concurrency/locker.h"
+#include "mongo/db/service_context_test_fixture.h"
#include "mongo/unittest/unittest.h"
#include "mongo/util/fail_point_service.h"
#include "mongo/util/log.h"
@@ -45,7 +46,9 @@
namespace mongo {
-TEST(LockerImpl, LockNoConflict) {
+class LockerImplTest : public unittest::Test, public ScopedGlobalServiceContextForTest {};
+
+TEST_F(LockerImplTest, LockNoConflict) {
const ResourceId resId(RESOURCE_COLLECTION, "TestDB.collection"_sd);
LockerImpl locker;
@@ -63,7 +66,7 @@ TEST(LockerImpl, LockNoConflict) {
locker.unlockGlobal();
}
-TEST(LockerImpl, ReLockNoConflict) {
+TEST_F(LockerImplTest, ReLockNoConflict) {
const ResourceId resId(RESOURCE_COLLECTION, "TestDB.collection"_sd);
LockerImpl locker;
@@ -81,7 +84,7 @@ TEST(LockerImpl, ReLockNoConflict) {
ASSERT(locker.unlockGlobal());
}
-TEST(LockerImpl, ConflictWithTimeout) {
+TEST_F(LockerImplTest, ConflictWithTimeout) {
const ResourceId resId(RESOURCE_COLLECTION, "TestDB.collection"_sd);
LockerImpl locker1;
@@ -101,7 +104,7 @@ TEST(LockerImpl, ConflictWithTimeout) {
ASSERT(locker2.unlockGlobal());
}
-TEST(LockerImpl, ConflictUpgradeWithTimeout) {
+TEST_F(LockerImplTest, ConflictUpgradeWithTimeout) {
const ResourceId resId(RESOURCE_COLLECTION, "TestDB.collection"_sd);
LockerImpl locker1;
@@ -121,7 +124,7 @@ TEST(LockerImpl, ConflictUpgradeWithTimeout) {
locker2.unlockGlobal();
}
-TEST(LockerImpl, FailPointInLockFailsGlobalNonIntentLocksIfTheyCannotBeImmediatelyGranted) {
+TEST_F(LockerImplTest, FailPointInLockFailsGlobalNonIntentLocksIfTheyCannotBeImmediatelyGranted) {
LockerImpl locker1;
locker1.lockGlobal(MODE_IX);
@@ -140,7 +143,7 @@ TEST(LockerImpl, FailPointInLockFailsGlobalNonIntentLocksIfTheyCannotBeImmediate
locker1.unlockGlobal();
}
-TEST(LockerImpl, FailPointInLockFailsNonIntentLocksIfTheyCannotBeImmediatelyGranted) {
+TEST_F(LockerImplTest, FailPointInLockFailsNonIntentLocksIfTheyCannotBeImmediatelyGranted) {
// Granted MODE_X lock, fail incoming MODE_S and MODE_X.
const ResourceId resId(RESOURCE_COLLECTION, "TestDB.collection"_sd);
@@ -177,7 +180,7 @@ TEST(LockerImpl, FailPointInLockFailsNonIntentLocksIfTheyCannotBeImmediatelyGran
locker1.unlockGlobal();
}
-TEST(LockerImpl, ReadTransaction) {
+TEST_F(LockerImplTest, ReadTransaction) {
LockerImpl locker;
locker.lockGlobal(MODE_IS);
@@ -195,7 +198,7 @@ TEST(LockerImpl, ReadTransaction) {
/**
* Test that saveLockerImpl works by examining the output.
*/
-TEST(LockerImpl, saveAndRestoreGlobal) {
+TEST_F(LockerImplTest, saveAndRestoreGlobal) {
Locker::LockSnapshot lockInfo;
LockerImpl locker;
@@ -222,7 +225,7 @@ TEST(LockerImpl, saveAndRestoreGlobal) {
/**
* Test that saveLockerImpl can save and restore the RSTL.
*/
-TEST(LockerImpl, saveAndRestoreRSTL) {
+TEST_F(LockerImplTest, saveAndRestoreRSTL) {
Locker::LockSnapshot lockInfo;
LockerImpl locker;
@@ -259,7 +262,7 @@ TEST(LockerImpl, saveAndRestoreRSTL) {
/**
* Test that we don't unlock when we have the global lock more than once.
*/
-TEST(LockerImpl, saveAndRestoreGlobalAcquiredTwice) {
+TEST_F(LockerImplTest, saveAndRestoreGlobalAcquiredTwice) {
Locker::LockSnapshot lockInfo;
LockerImpl locker;
@@ -285,7 +288,7 @@ TEST(LockerImpl, saveAndRestoreGlobalAcquiredTwice) {
/**
* Tests that restoreLockerImpl works by locking a db and collection and saving + restoring.
*/
-TEST(LockerImpl, saveAndRestoreDBAndCollection) {
+TEST_F(LockerImplTest, saveAndRestoreDBAndCollection) {
Locker::LockSnapshot lockInfo;
LockerImpl locker;
@@ -313,7 +316,7 @@ TEST(LockerImpl, saveAndRestoreDBAndCollection) {
ASSERT(locker.unlockGlobal());
}
-TEST(LockerImpl, releaseWriteUnitOfWork) {
+TEST_F(LockerImplTest, releaseWriteUnitOfWork) {
Locker::LockSnapshot lockInfo;
LockerImpl locker;
@@ -341,7 +344,7 @@ TEST(LockerImpl, releaseWriteUnitOfWork) {
// Destructor should succeed since the locker's state should be empty.
}
-TEST(LockerImpl, restoreWriteUnitOfWork) {
+TEST_F(LockerImplTest, restoreWriteUnitOfWork) {
Locker::LockSnapshot lockInfo;
LockerImpl locker;
@@ -381,7 +384,7 @@ TEST(LockerImpl, restoreWriteUnitOfWork) {
ASSERT_FALSE(locker.isLocked());
}
-TEST(LockerImpl, releaseAndRestoreReadOnlyWriteUnitOfWork) {
+TEST_F(LockerImplTest, releaseAndRestoreReadOnlyWriteUnitOfWork) {
Locker::LockSnapshot lockInfo;
LockerImpl locker;
@@ -424,7 +427,7 @@ TEST(LockerImpl, releaseAndRestoreReadOnlyWriteUnitOfWork) {
ASSERT_FALSE(locker.isLocked());
}
-TEST(LockerImpl, releaseAndRestoreEmptyWriteUnitOfWork) {
+TEST_F(LockerImplTest, releaseAndRestoreEmptyWriteUnitOfWork) {
Locker::LockSnapshot lockInfo;
LockerImpl locker;
@@ -445,7 +448,7 @@ TEST(LockerImpl, releaseAndRestoreEmptyWriteUnitOfWork) {
ASSERT_FALSE(locker.isLocked());
}
-TEST(LockerImpl, DefaultLocker) {
+TEST_F(LockerImplTest, DefaultLocker) {
const ResourceId resId(RESOURCE_DATABASE, "TestDB"_sd);
LockerImpl locker;
@@ -463,7 +466,7 @@ TEST(LockerImpl, DefaultLocker) {
ASSERT(locker.unlockGlobal());
}
-TEST(LockerImpl, SharedLocksShouldTwoPhaseLockIsTrue) {
+TEST_F(LockerImplTest, SharedLocksShouldTwoPhaseLockIsTrue) {
// Test that when setSharedLocksShouldTwoPhaseLock is true and we are in a WUOW, unlock on IS
// and S locks are postponed until endWriteUnitOfWork() is called. Mode IX and X locks always
// participate in two-phased locking, regardless of the setting.
@@ -519,7 +522,7 @@ TEST(LockerImpl, SharedLocksShouldTwoPhaseLockIsTrue) {
ASSERT_EQ(locker.getLockMode(globalResId), MODE_NONE);
}
-TEST(LockerImpl, ModeIXAndXLockParticipatesInTwoPhaseLocking) {
+TEST_F(LockerImplTest, ModeIXAndXLockParticipatesInTwoPhaseLocking) {
// Unlock on mode IX and X locks during a WUOW should always be postponed until
// endWriteUnitOfWork() is called. Mode IS and S locks should unlock immediately.
@@ -571,7 +574,7 @@ TEST(LockerImpl, ModeIXAndXLockParticipatesInTwoPhaseLocking) {
ASSERT_EQ(locker.getLockMode(globalResId), MODE_NONE);
}
-TEST(LockerImpl, RSTLUnlocksWithNestedLock) {
+TEST_F(LockerImplTest, RSTLUnlocksWithNestedLock) {
LockerImpl locker;
locker.lock(resourceIdReplicationStateTransitionLock, MODE_IX);
@@ -596,7 +599,7 @@ TEST(LockerImpl, RSTLUnlocksWithNestedLock) {
ASSERT_FALSE(locker.unlock(resourceIdReplicationStateTransitionLock));
}
-TEST(LockerImpl, RSTLModeIXWithTwoPhaseLockingCanBeUnlockedWhenPrepared) {
+TEST_F(LockerImplTest, RSTLModeIXWithTwoPhaseLockingCanBeUnlockedWhenPrepared) {
LockerImpl locker;
locker.lock(resourceIdReplicationStateTransitionLock, MODE_IX);
@@ -620,7 +623,7 @@ TEST(LockerImpl, RSTLModeIXWithTwoPhaseLockingCanBeUnlockedWhenPrepared) {
ASSERT_FALSE(locker.unlock(resourceIdReplicationStateTransitionLock));
}
-TEST(LockerImpl, RSTLModeISWithTwoPhaseLockingCanBeUnlockedWhenPrepared) {
+TEST_F(LockerImplTest, RSTLModeISWithTwoPhaseLockingCanBeUnlockedWhenPrepared) {
LockerImpl locker;
locker.lock(resourceIdReplicationStateTransitionLock, MODE_IS);
@@ -641,7 +644,7 @@ TEST(LockerImpl, RSTLModeISWithTwoPhaseLockingCanBeUnlockedWhenPrepared) {
ASSERT_FALSE(locker.unlock(resourceIdReplicationStateTransitionLock));
}
-TEST(LockerImpl, RSTLTwoPhaseLockingBehaviorModeIS) {
+TEST_F(LockerImplTest, RSTLTwoPhaseLockingBehaviorModeIS) {
LockerImpl locker;
locker.lock(resourceIdReplicationStateTransitionLock, MODE_IS);
@@ -662,7 +665,7 @@ TEST(LockerImpl, RSTLTwoPhaseLockingBehaviorModeIS) {
ASSERT_FALSE(locker.unlock(resourceIdReplicationStateTransitionLock));
}
-TEST(LockerImpl, OverrideLockRequestTimeout) {
+TEST_F(LockerImplTest, OverrideLockRequestTimeout) {
const ResourceId resIdFirstDB(RESOURCE_DATABASE, "FirstDB"_sd);
const ResourceId resIdSecondDB(RESOURCE_DATABASE, "SecondDB"_sd);
@@ -697,7 +700,7 @@ TEST(LockerImpl, OverrideLockRequestTimeout) {
ASSERT(locker2.unlockGlobal());
}
-TEST(LockerImpl, DoNotWaitForLockAcquisition) {
+TEST_F(LockerImplTest, DoNotWaitForLockAcquisition) {
const ResourceId resIdFirstDB(RESOURCE_DATABASE, "FirstDB"_sd);
const ResourceId resIdSecondDB(RESOURCE_DATABASE, "SecondDB"_sd);
@@ -749,7 +752,7 @@ bool lockerInfoContainsLock(const Locker::LockerInfo& lockerInfo,
}
} // namespace
-TEST(LockerImpl, GetLockerInfoShouldReportHeldLocks) {
+TEST_F(LockerImplTest, GetLockerInfoShouldReportHeldLocks) {
const ResourceId globalId(RESOURCE_GLOBAL, ResourceId::SINGLETON_GLOBAL);
const ResourceId dbId(RESOURCE_DATABASE, "TestDB"_sd);
const ResourceId collectionId(RESOURCE_COLLECTION, "TestDB.collection"_sd);
@@ -774,7 +777,7 @@ TEST(LockerImpl, GetLockerInfoShouldReportHeldLocks) {
ASSERT(locker.unlockGlobal());
}
-TEST(LockerImpl, GetLockerInfoShouldReportPendingLocks) {
+TEST_F(LockerImplTest, GetLockerInfoShouldReportPendingLocks) {
const ResourceId globalId(RESOURCE_GLOBAL, ResourceId::SINGLETON_GLOBAL);
const ResourceId dbId(RESOURCE_DATABASE, "TestDB"_sd);
const ResourceId collectionId(RESOURCE_COLLECTION, "TestDB.collection"_sd);
@@ -817,7 +820,7 @@ TEST(LockerImpl, GetLockerInfoShouldReportPendingLocks) {
ASSERT(conflictingLocker.unlockGlobal());
}
-TEST(LockerImpl, ReaquireLockPendingUnlock) {
+TEST_F(LockerImplTest, ReaquireLockPendingUnlock) {
const ResourceId resId(RESOURCE_COLLECTION, "TestDB.collection"_sd);
LockerImpl locker;
@@ -845,7 +848,7 @@ TEST(LockerImpl, ReaquireLockPendingUnlock) {
locker.unlockGlobal();
}
-TEST(LockerImpl, AcquireLockPendingUnlockWithCoveredMode) {
+TEST_F(LockerImplTest, AcquireLockPendingUnlockWithCoveredMode) {
const ResourceId resId(RESOURCE_COLLECTION, "TestDB.collection"_sd);
LockerImpl locker;
@@ -873,7 +876,7 @@ TEST(LockerImpl, AcquireLockPendingUnlockWithCoveredMode) {
locker.unlockGlobal();
}
-TEST(LockerImpl, ConvertLockPendingUnlock) {
+TEST_F(LockerImplTest, ConvertLockPendingUnlock) {
const ResourceId resId(RESOURCE_COLLECTION, "TestDB.collection"_sd);
LockerImpl locker;
@@ -903,7 +906,7 @@ TEST(LockerImpl, ConvertLockPendingUnlock) {
locker.unlockGlobal();
}
-TEST(LockerImpl, ConvertLockPendingUnlockAndUnlock) {
+TEST_F(LockerImplTest, ConvertLockPendingUnlockAndUnlock) {
const ResourceId resId(RESOURCE_COLLECTION, "TestDB.collection"_sd);
LockerImpl locker;
diff --git a/src/mongo/db/concurrency/lock_stats_test.cpp b/src/mongo/db/concurrency/lock_stats_test.cpp
index 2c6fdb7d019..5e03af8c3e2 100644
--- a/src/mongo/db/concurrency/lock_stats_test.cpp
+++ b/src/mongo/db/concurrency/lock_stats_test.cpp
@@ -31,11 +31,14 @@
#include "mongo/bson/bsonobjbuilder.h"
#include "mongo/db/concurrency/lock_manager_test_help.h"
+#include "mongo/db/service_context_test_fixture.h"
#include "mongo/unittest/unittest.h"
namespace mongo {
-TEST(LockStats, NoWait) {
+class LockStatsTest : public unittest::Test, public ScopedGlobalServiceContextForTest {};
+
+TEST_F(LockStatsTest, NoWait) {
const ResourceId resId(RESOURCE_COLLECTION, std::string("LockStats.NoWait"));
resetGlobalLockStats();
@@ -53,7 +56,7 @@ TEST(LockStats, NoWait) {
ASSERT_EQUALS(0, stats.get(resId, MODE_X).combinedWaitTimeMicros);
}
-TEST(LockStats, Wait) {
+TEST_F(LockStatsTest, Wait) {
const ResourceId resId(RESOURCE_COLLECTION, std::string("LockStats.Wait"));
resetGlobalLockStats();
@@ -86,7 +89,7 @@ TEST(LockStats, Wait) {
ASSERT_GREATER_THAN(stats.get(resId, MODE_S).combinedWaitTimeMicros, 0);
}
-TEST(LockStats, Reporting) {
+TEST_F(LockStatsTest, Reporting) {
const ResourceId resId(RESOURCE_COLLECTION, std::string("LockStats.Reporting"));
resetGlobalLockStats();
@@ -103,7 +106,7 @@ TEST(LockStats, Reporting) {
stats.report(&builder);
}
-TEST(LockStats, Subtraction) {
+TEST_F(LockStatsTest, Subtraction) {
const ResourceId resId(RESOURCE_COLLECTION, std::string("LockStats.Subtraction"));
resetGlobalLockStats();
@@ -173,13 +176,13 @@ void assertAcquisitionStats(ResourceId rid) {
}
} // namespace
-TEST(LockStats, GlobalRetrievableSeparately) {
+TEST_F(LockStatsTest, GlobalRetrievableSeparately) {
assertAcquisitionStats(resourceIdGlobal);
assertAcquisitionStats(resourceIdParallelBatchWriterMode);
assertAcquisitionStats(resourceIdReplicationStateTransitionLock);
}
-TEST(LockStats, ServerStatusAggregatesAllGlobal) {
+TEST_F(LockStatsTest, ServerStatusAggregatesAllGlobal) {
resetGlobalLockStats();
SingleThreadedLockStats stats;