diff options
author | Gregory Wlodarek <gregory.wlodarek@mongodb.com> | 2019-04-02 14:06:20 -0400 |
---|---|---|
committer | Gregory Wlodarek <gregory.wlodarek@mongodb.com> | 2019-04-08 14:35:47 -0400 |
commit | 28a361799cf6a0d7ea1c3196b19aa9a75795b56d (patch) | |
tree | 0e9426b0faa3e6fa80e88d726336632c16e696f5 | |
parent | 02a87ee5b1942d24e1d7a20502c79d36218929fe (diff) | |
download | mongo-28a361799cf6a0d7ea1c3196b19aa9a75795b56d.tar.gz |
SERVER-39902 lockInfo should use UUIDCatalog to map resourceIds to collection names
-rw-r--r-- | src/mongo/db/catalog/SConscript | 1 | ||||
-rw-r--r-- | src/mongo/db/catalog/uuid_catalog.cpp | 85 | ||||
-rw-r--r-- | src/mongo/db/catalog/uuid_catalog.h | 24 | ||||
-rw-r--r-- | src/mongo/db/catalog/uuid_catalog_test.cpp | 229 | ||||
-rw-r--r-- | src/mongo/db/concurrency/SConscript | 1 | ||||
-rw-r--r-- | src/mongo/db/concurrency/lock_manager.cpp | 56 | ||||
-rw-r--r-- | src/mongo/db/concurrency/lock_manager_defs.h | 41 | ||||
-rw-r--r-- | src/mongo/db/concurrency/lock_state_test.cpp | 63 | ||||
-rw-r--r-- | src/mongo/db/concurrency/lock_stats_test.cpp | 15 |
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; |