summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/mongo/db/SConscript1
-rw-r--r--src/mongo/db/catalog/collection.h3
-rw-r--r--src/mongo/db/catalog/collection_mock.h276
-rw-r--r--src/mongo/db/catalog/database_impl.cpp13
-rw-r--r--src/mongo/db/catalog/rename_collection.cpp10
-rw-r--r--src/mongo/db/commands/mr.cpp6
-rw-r--r--src/mongo/db/op_observer.h1
-rw-r--r--src/mongo/db/op_observer_impl.cpp22
-rw-r--r--src/mongo/db/op_observer_impl.h1
-rw-r--r--src/mongo/db/op_observer_noop.cpp1
-rw-r--r--src/mongo/db/op_observer_noop.h1
-rw-r--r--src/mongo/util/SConscript26
-rw-r--r--src/mongo/util/namespace_uuid_cache.cpp10
-rw-r--r--src/mongo/util/namespace_uuid_cache.h21
-rw-r--r--src/mongo/util/namespace_uuid_cache_test.cpp4
-rw-r--r--src/mongo/util/uuid_catalog.cpp79
-rw-r--r--src/mongo/util/uuid_catalog.h85
-rw-r--r--src/mongo/util/uuid_catalog_test.cpp85
18 files changed, 624 insertions, 21 deletions
diff --git a/src/mongo/db/SConscript b/src/mongo/db/SConscript
index 05c31829d07..63119c54019 100644
--- a/src/mongo/db/SConscript
+++ b/src/mongo/db/SConscript
@@ -602,6 +602,7 @@ env.Library(
'commands/dcommands',
'repl/serveronly',
'views/views_mongod',
+ '$BUILD_DIR/mongo/util/uuid_catalog',
],
)
diff --git a/src/mongo/db/catalog/collection.h b/src/mongo/db/catalog/collection.h
index 1f4bd4bf6bb..25fb8930290 100644
--- a/src/mongo/db/catalog/collection.h
+++ b/src/mongo/db/catalog/collection.h
@@ -340,6 +340,9 @@ public:
this->_impl().init(opCtx);
}
+ // Use this constructor only for testing/mocks
+ explicit inline Collection(std::unique_ptr<Impl> mock) : _pimpl(std::move(mock)) {}
+
inline ~Collection() = default;
inline bool ok() const {
diff --git a/src/mongo/db/catalog/collection_mock.h b/src/mongo/db/catalog/collection_mock.h
new file mode 100644
index 00000000000..7b46367a766
--- /dev/null
+++ b/src/mongo/db/catalog/collection_mock.h
@@ -0,0 +1,276 @@
+/**
+ * Copyright (C) 2017 MongoDB Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the GNU Affero General Public License in all respects for
+ * all of the code used other than as permitted herein. If you modify file(s)
+ * with this exception, you may extend this exception to your version of the
+ * file(s), but you are not obligated to do so. If you do not wish to do so,
+ * delete this exception statement from your version. If you delete this
+ * exception statement from all source files in the program, then also delete
+ * it in the license file.
+ */
+
+#pragma once
+
+#include "mongo/db/catalog/collection.h"
+
+namespace mongo {
+
+/**
+ * This class comprises a mock Collection for use by UUIDCatalog unit tests.
+ */
+class CollectionMock : virtual public Collection::Impl,
+ virtual CappedCallback,
+ virtual UpdateNotifier {
+public:
+ CollectionMock(const NamespaceString& ns) : _ns(ns) {}
+ ~CollectionMock() = default;
+
+ void init(OperationContext* opCtx) {
+ std::abort();
+ }
+
+private:
+ DatabaseCatalogEntry* dbce() const {
+ std::abort();
+ }
+
+ CollectionCatalogEntry* details() const {
+ std::abort();
+ }
+
+ Status aboutToDeleteCapped(OperationContext* opCtx, const RecordId& loc, RecordData data) {
+ std::abort();
+ }
+
+ Status recordStoreGoingToUpdateInPlace(OperationContext* opCtx, const RecordId& loc) {
+ std::abort();
+ }
+ const NamespaceString _ns;
+
+public:
+ const NamespaceString& ns() const {
+ return _ns;
+ }
+ bool ok() const {
+ std::abort();
+ }
+
+ CollectionCatalogEntry* getCatalogEntry() {
+ std::abort();
+ }
+ const CollectionCatalogEntry* getCatalogEntry() const {
+ std::abort();
+ }
+
+ CollectionInfoCache* infoCache() {
+ std::abort();
+ }
+ const CollectionInfoCache* infoCache() const {
+ std::abort();
+ }
+
+ const IndexCatalog* getIndexCatalog() const {
+ std::abort();
+ }
+ IndexCatalog* getIndexCatalog() {
+ std::abort();
+ }
+
+ const RecordStore* getRecordStore() const {
+ std::abort();
+ }
+ RecordStore* getRecordStore() {
+ std::abort();
+ }
+
+ CursorManager* getCursorManager() const {
+ std::abort();
+ }
+
+ bool requiresIdIndex() const {
+ std::abort();
+ }
+
+ Snapshotted<BSONObj> docFor(OperationContext* opCtx, const RecordId& loc) const {
+ std::abort();
+ }
+
+ bool findDoc(OperationContext* opCtx, const RecordId& loc, Snapshotted<BSONObj>* out) const {
+ std::abort();
+ }
+
+ std::unique_ptr<SeekableRecordCursor> getCursor(OperationContext* opCtx, bool forward) const {
+ std::abort();
+ }
+
+ std::vector<std::unique_ptr<RecordCursor>> getManyCursors(OperationContext* opCtx) const {
+ std::abort();
+ }
+
+ void deleteDocument(OperationContext* opCtx,
+ const RecordId& loc,
+ OpDebug* opDebug,
+ bool fromMigrate,
+ bool noWarn) {
+ std::abort();
+ }
+
+ Status insertDocuments(OperationContext* opCtx,
+ std::vector<BSONObj>::const_iterator begin,
+ std::vector<BSONObj>::const_iterator end,
+ OpDebug* opDebug,
+ bool enforceQuota,
+ bool fromMigrate) {
+ std::abort();
+ }
+
+ Status insertDocument(OperationContext* opCtx,
+ const BSONObj& doc,
+ OpDebug* opDebug,
+ bool enforceQuota,
+ bool fromMigrate) {
+ std::abort();
+ }
+
+ Status insertDocumentsForOplog(OperationContext* opCtx,
+ const DocWriter* const* docs,
+ size_t nDocs) {
+ std::abort();
+ }
+
+ Status insertDocument(OperationContext* opCtx,
+ const BSONObj& doc,
+ const std::vector<MultiIndexBlock*>& indexBlocks,
+ bool enforceQuota) {
+ std::abort();
+ }
+
+ StatusWith<RecordId> updateDocument(OperationContext* opCtx,
+ const RecordId& oldLocation,
+ const Snapshotted<BSONObj>& oldDoc,
+ const BSONObj& newDoc,
+ bool enforceQuota,
+ bool indexesAffected,
+ OpDebug* opDebug,
+ OplogUpdateEntryArgs* args) {
+ std::abort();
+ }
+
+ bool updateWithDamagesSupported() const {
+ std::abort();
+ }
+
+ StatusWith<RecordData> updateDocumentWithDamages(OperationContext* opCtx,
+ const RecordId& loc,
+ const Snapshotted<RecordData>& oldRec,
+ const char* damageSource,
+ const mutablebson::DamageVector& damages,
+ OplogUpdateEntryArgs* args) {
+ std::abort();
+ }
+
+ StatusWith<CompactStats> compact(OperationContext* opCtx, const CompactOptions* options) {
+ std::abort();
+ }
+ Status truncate(OperationContext* opCtx) {
+ std::abort();
+ }
+
+ Status validate(OperationContext* opCtx,
+ ValidateCmdLevel level,
+ ValidateResults* results,
+ BSONObjBuilder* output) {
+ std::abort();
+ }
+
+ Status touch(OperationContext* opCtx,
+ bool touchData,
+ bool touchIndexes,
+ BSONObjBuilder* output) const {
+ std::abort();
+ }
+
+ void cappedTruncateAfter(OperationContext* opCtx, RecordId end, bool inclusive) {
+ std::abort();
+ }
+
+ StatusWithMatchExpression parseValidator(const BSONObj& validator) const {
+ std::abort();
+ }
+
+ Status setValidator(OperationContext* opCtx, BSONObj validator) {
+ std::abort();
+ }
+
+ Status setValidationLevel(OperationContext* opCtx, StringData newLevel) {
+ std::abort();
+ }
+ Status setValidationAction(OperationContext* opCtx, StringData newAction) {
+ std::abort();
+ }
+
+ StringData getValidationLevel() const {
+ std::abort();
+ }
+ StringData getValidationAction() const {
+ std::abort();
+ }
+
+ bool isCapped() const {
+ std::abort();
+ }
+
+ std::shared_ptr<CappedInsertNotifier> getCappedInsertNotifier() const {
+ std::abort();
+ }
+
+ uint64_t numRecords(OperationContext* opCtx) const {
+ std::abort();
+ }
+
+ uint64_t dataSize(OperationContext* opCtx) const {
+ std::abort();
+ }
+
+ uint64_t getIndexSize(OperationContext* opCtx, BSONObjBuilder* details, int scale) {
+ std::abort();
+ }
+
+ boost::optional<SnapshotName> getMinimumVisibleSnapshot() {
+ std::abort();
+ }
+
+ void setMinimumVisibleSnapshot(SnapshotName name) {
+ std::abort();
+ }
+
+ void notifyCappedWaitersIfNeeded() {
+ std::abort();
+ }
+
+ const CollatorInterface* getDefaultCollator() const {
+ std::abort();
+ }
+
+ OptionalCollectionUUID uuid(OperationContext* opCtx) const {
+ std::abort();
+ }
+};
+} // namespace mongo
diff --git a/src/mongo/db/catalog/database_impl.cpp b/src/mongo/db/catalog/database_impl.cpp
index 59ba8c1ed53..ab8ba32ca7f 100644
--- a/src/mongo/db/catalog/database_impl.cpp
+++ b/src/mongo/db/catalog/database_impl.cpp
@@ -66,6 +66,7 @@
#include "mongo/util/assert_util.h"
#include "mongo/util/log.h"
#include "mongo/util/namespace_uuid_cache.h"
+#include "mongo/util/uuid_catalog.h"
namespace mongo {
namespace {
@@ -462,11 +463,6 @@ Status DatabaseImpl::dropCollectionEvenIfSystem(OperationContext* opCtx,
getGlobalServiceContext()->getOpObserver()->onDropCollection(opCtx, fullns, uuid);
- // Evict namespace entry from the namespace/uuid cache.
- if (enableCollectionUUIDs) {
- NamespaceUUIDCache& cache = NamespaceUUIDCache::get(opCtx);
- cache.evictNamespace(fullns);
- }
return Status::OK();
}
@@ -548,11 +544,6 @@ Status DatabaseImpl::renameCollection(OperationContext* opCtx,
Status s = _dbEntry->renameCollection(opCtx, fromNS, toNS, stayTemp);
_collections[toNS] = _getOrCreateCollectionInstance(opCtx, toNSS);
- // Evict namespace entry from the namespace/uuid cache.
- if (enableCollectionUUIDs) {
- NamespaceUUIDCache& cache = NamespaceUUIDCache::get(opCtx);
- cache.evictNamespace(fromNSS);
- }
return s;
}
@@ -648,7 +639,7 @@ Collection* DatabaseImpl::createCollection(OperationContext* opCtx,
}
getGlobalServiceContext()->getOpObserver()->onCreateCollection(
- opCtx, nss, options, fullIdIndexSpec);
+ opCtx, collection, nss, options, fullIdIndexSpec);
return collection;
}
diff --git a/src/mongo/db/catalog/rename_collection.cpp b/src/mongo/db/catalog/rename_collection.cpp
index d9240302534..37e35387249 100644
--- a/src/mongo/db/catalog/rename_collection.cpp
+++ b/src/mongo/db/catalog/rename_collection.cpp
@@ -190,14 +190,16 @@ Status renameCollection(OperationContext* opCtx,
// write lock acquired at the top.
NamespaceString tmpName(target.db(), "tmp.renameCollection");
Collection* tmpColl = nullptr;
- OptionalCollectionUUID tmpUUID;
+ OptionalCollectionUUID newUUID;
{
CollectionOptions options = sourceColl->getCatalogEntry()->getCollectionOptions(opCtx);
// Renaming across databases will result in a new UUID, as otherwise we'd require
// two collections with the same uuid (temporarily).
options.temp = true;
- if (enableCollectionUUIDs)
- tmpUUID = UUID::gen();
+ if (enableCollectionUUIDs) {
+ newUUID = UUID::gen();
+ options.uuid = newUUID;
+ }
MONGO_WRITE_CONFLICT_RETRY_LOOP_BEGIN {
WriteUnitOfWork wunit(opCtx);
@@ -294,7 +296,7 @@ Status renameCollection(OperationContext* opCtx,
opCtx,
source,
target,
- tmpUUID,
+ newUUID,
dropTarget,
dropTargetUUID,
/*dropSourceUUID*/ sourceUUID,
diff --git a/src/mongo/db/commands/mr.cpp b/src/mongo/db/commands/mr.cpp
index 7b64dadbbdb..8993e12db51 100644
--- a/src/mongo/db/commands/mr.cpp
+++ b/src/mongo/db/commands/mr.cpp
@@ -425,6 +425,9 @@ void State::prepTempCollection() {
CollectionOptions options;
options.setNoIdIndex();
options.temp = true;
+ if (enableCollectionUUIDs) {
+ options.uuid.emplace(UUID::gen());
+ }
incColl = incCtx.db()->createCollection(_opCtx, _config.incLong.ns(), options);
invariant(incColl);
@@ -496,6 +499,9 @@ void State::prepTempCollection() {
CollectionOptions options = finalOptions;
options.temp = true;
+ if (enableCollectionUUIDs) {
+ options.uuid.emplace(UUID::gen());
+ }
tempColl = tempCtx.db()->createCollection(_opCtx, _config.tempNamespace.ns(), options);
for (vector<BSONObj>::iterator it = indexesToInsert.begin(); it != indexesToInsert.end();
diff --git a/src/mongo/db/op_observer.h b/src/mongo/db/op_observer.h
index 5a3e089f261..33d2a76ba16 100644
--- a/src/mongo/db/op_observer.h
+++ b/src/mongo/db/op_observer.h
@@ -106,6 +106,7 @@ public:
bool fromMigrate) = 0;
virtual void onOpMessage(OperationContext* opCtx, const BSONObj& msgObj) = 0;
virtual void onCreateCollection(OperationContext* opCtx,
+ Collection* coll,
const NamespaceString& collectionName,
const CollectionOptions& options,
const BSONObj& idIndex) = 0;
diff --git a/src/mongo/db/op_observer_impl.cpp b/src/mongo/db/op_observer_impl.cpp
index 2f0cf6eef00..bde44ed01ca 100644
--- a/src/mongo/db/op_observer_impl.cpp
+++ b/src/mongo/db/op_observer_impl.cpp
@@ -43,6 +43,8 @@
#include "mongo/db/server_options.h"
#include "mongo/db/views/durable_view_catalog.h"
#include "mongo/scripting/engine.h"
+#include "mongo/util/namespace_uuid_cache.h"
+#include "mongo/util/uuid_catalog.h"
namespace mongo {
@@ -185,6 +187,7 @@ void OpObserverImpl::onOpMessage(OperationContext* opCtx, const BSONObj& msgObj)
}
void OpObserverImpl::onCreateCollection(OperationContext* opCtx,
+ Collection* coll,
const NamespaceString& collectionName,
const CollectionOptions& options,
const BSONObj& idIndex) {
@@ -217,6 +220,11 @@ void OpObserverImpl::onCreateCollection(OperationContext* opCtx,
getGlobalAuthorizationManager()->logOp(opCtx, "c", dbName, cmdObj, nullptr);
logOpForDbHash(opCtx, dbName);
+
+ if (options.uuid) {
+ UUIDCatalog& catalog = UUIDCatalog::get(opCtx->getServiceContext());
+ catalog.onCreateCollection(opCtx, coll, options.uuid.get());
+ }
}
namespace {
@@ -320,6 +328,16 @@ void OpObserverImpl::onDropCollection(OperationContext* opCtx,
auto css = CollectionShardingState::get(opCtx, collectionName);
css->onDropCollection(opCtx, collectionName);
+ // Evict namespace entry from the namespace/uuid cache if it exists.
+ NamespaceUUIDCache& cache = NamespaceUUIDCache::get(opCtx);
+ cache.onDropCollection(collectionName);
+
+ // Remove collection from the uuid catalog.
+ if (uuid) {
+ UUIDCatalog& catalog = UUIDCatalog::get(opCtx->getServiceContext());
+ catalog.onDropCollection(opCtx, uuid.get());
+ }
+
logOpForDbHash(opCtx, dbName);
}
@@ -368,6 +386,10 @@ void OpObserverImpl::onRenameCollection(OperationContext* opCtx,
getGlobalAuthorizationManager()->logOp(opCtx, "c", cmdNss, cmdObj, nullptr);
logOpForDbHash(opCtx, cmdNss);
+
+ // Evict namespace entry from the namespace/uuid cache if it exists.
+ NamespaceUUIDCache& cache = NamespaceUUIDCache::get(opCtx);
+ cache.onRenameCollection(fromCollection);
}
void OpObserverImpl::onApplyOps(OperationContext* opCtx,
diff --git a/src/mongo/db/op_observer_impl.h b/src/mongo/db/op_observer_impl.h
index aaff2de5f64..525a94bda71 100644
--- a/src/mongo/db/op_observer_impl.h
+++ b/src/mongo/db/op_observer_impl.h
@@ -61,6 +61,7 @@ public:
bool fromMigrate) override;
void onOpMessage(OperationContext* opCtx, const BSONObj& msgObj) override;
void onCreateCollection(OperationContext* opCtx,
+ Collection* coll,
const NamespaceString& collectionName,
const CollectionOptions& options,
const BSONObj& idIndex) override;
diff --git a/src/mongo/db/op_observer_noop.cpp b/src/mongo/db/op_observer_noop.cpp
index 34234940816..5a5b04ecc53 100644
--- a/src/mongo/db/op_observer_noop.cpp
+++ b/src/mongo/db/op_observer_noop.cpp
@@ -59,6 +59,7 @@ void OpObserverNoop::onDelete(OperationContext*,
void OpObserverNoop::onOpMessage(OperationContext*, const BSONObj&) {}
void OpObserverNoop::onCreateCollection(OperationContext*,
+ Collection*,
const NamespaceString&,
const CollectionOptions&,
const BSONObj&) {}
diff --git a/src/mongo/db/op_observer_noop.h b/src/mongo/db/op_observer_noop.h
index 1701a875f3a..b624282fd7d 100644
--- a/src/mongo/db/op_observer_noop.h
+++ b/src/mongo/db/op_observer_noop.h
@@ -61,6 +61,7 @@ public:
bool fromMigrate) override;
void onOpMessage(OperationContext* opCtx, const BSONObj& msgObj) override;
void onCreateCollection(OperationContext* opCtx,
+ Collection* coll,
const NamespaceString& collectionName,
const CollectionOptions& options,
const BSONObj& idIndex) override;
diff --git a/src/mongo/util/SConscript b/src/mongo/util/SConscript
index 87f7226d33b..ebe9ae87d3d 100644
--- a/src/mongo/util/SConscript
+++ b/src/mongo/util/SConscript
@@ -100,6 +100,20 @@ env.Library(
],
LIBDEPS=[
'$BUILD_DIR/mongo/base',
+ '$BUILD_DIR/mongo/db/catalog/collection',
+ '$BUILD_DIR/mongo/util/decorable',
+ '$BUILD_DIR/third_party/murmurhash3/murmurhash3',
+ ],
+)
+
+env.Library(
+ target='uuid_catalog',
+ source=[
+ 'uuid_catalog.cpp'
+ ],
+ LIBDEPS=[
+ '$BUILD_DIR/mongo/base',
+ '$BUILD_DIR/mongo/db/catalog/collection',
'$BUILD_DIR/mongo/util/decorable',
'$BUILD_DIR/third_party/murmurhash3/murmurhash3',
],
@@ -126,6 +140,18 @@ env.CppUnitTest(
],
)
+env.CppUnitTest(
+ target='uuid_catalog_test',
+ source=[
+ 'uuid_catalog_test.cpp',
+ ],
+ LIBDEPS=[
+ 'uuid_catalog',
+ 'uuid',
+ '$BUILD_DIR/mongo/db/service_context',
+ ]
+ )
+
env.Library(
target='summation',
source=[
diff --git a/src/mongo/util/namespace_uuid_cache.cpp b/src/mongo/util/namespace_uuid_cache.cpp
index 0cf1d9c30a1..59e40b73166 100644
--- a/src/mongo/util/namespace_uuid_cache.cpp
+++ b/src/mongo/util/namespace_uuid_cache.cpp
@@ -49,7 +49,15 @@ void NamespaceUUIDCache::ensureNamespaceInCache(const NamespaceString& nss, Coll
it->second == uuid);
}
}
-void NamespaceUUIDCache::evictNamespace(const NamespaceString& nss) {
+void NamespaceUUIDCache::onDropCollection(const NamespaceString& nss) {
+ _evictNamespace(nss);
+}
+
+void NamespaceUUIDCache::onRenameCollection(const NamespaceString& nss) {
+ _evictNamespace(nss);
+}
+
+void NamespaceUUIDCache::_evictNamespace(const NamespaceString& nss) {
invariant(_cache.erase(nss.ns()) <= 1);
}
} // namespace mongo
diff --git a/src/mongo/util/namespace_uuid_cache.h b/src/mongo/util/namespace_uuid_cache.h
index a38394787de..10f4bd75206 100644
--- a/src/mongo/util/namespace_uuid_cache.h
+++ b/src/mongo/util/namespace_uuid_cache.h
@@ -52,17 +52,32 @@ public:
/**
* This function adds the pair nss.ns(), uuid to the namespace uuid cache
* if it does not yet exist. If nss.ns() already exists in the cache with
- * a different uuid, a UserException is thrown.
+ * a different uuid, a UserException is thrown, so we can guarantee that
+ * an operation will always resolve the same name to the same collection,
+ * even in presence of drops and renames.
*/
void ensureNamespaceInCache(const NamespaceString& nss, CollectionUUID uuid);
/**
* This function removes the entry for nss.ns() from the namespace uuid
- * cache. Does nothing if the entry doesn't exist.
+ * cache. Does nothing if the entry doesn't exist. It is called via the
+ * op observer when a collection is dropped.
+ */
+ void onDropCollection(const NamespaceString& nss);
+
+ /**
+ * This function removes the entry for nss.ns() from the namespace uuid
+ * cache. Does nothing if the entry doesn't exist. It is called via the
+ * op observer when a collection is renamed.
*/
- void evictNamespace(const NamespaceString& nss);
+ void onRenameCollection(const NamespaceString& nss);
private:
+ /**
+ * This function removes the entry for nss.ns() from the namespace uuid
+ * cache. Does nothing if the entry doesn't exist.
+ */
+ void _evictNamespace(const NamespaceString& nss);
using CollectionUUIDMap = StringMap<CollectionUUID>;
CollectionUUIDMap _cache;
};
diff --git a/src/mongo/util/namespace_uuid_cache_test.cpp b/src/mongo/util/namespace_uuid_cache_test.cpp
index 906cfebaebb..7834dcb4968 100644
--- a/src/mongo/util/namespace_uuid_cache_test.cpp
+++ b/src/mongo/util/namespace_uuid_cache_test.cpp
@@ -47,13 +47,13 @@ TEST(NamespaceUUIDCache, ensureNamespaceInCache) {
ASSERT_THROWS(cache.ensureNamespaceInCache(nss, uuidConflict), UserException);
}
-TEST(NamespaceUUIDCache, evictEntry) {
+TEST(NamespaceUUIDCache, onDropCollection) {
NamespaceUUIDCache cache;
CollectionUUID uuid = CollectionUUID::gen();
CollectionUUID newUuid = CollectionUUID::gen();
NamespaceString nss("test", "test_collection_ns");
cache.ensureNamespaceInCache(nss, uuid);
- cache.evictNamespace(nss);
+ cache.onDropCollection(nss);
// Add nss to the cache with a different uuid. This should not throw since
// we evicted the previous entry from the cache.
cache.ensureNamespaceInCache(nss, newUuid);
diff --git a/src/mongo/util/uuid_catalog.cpp b/src/mongo/util/uuid_catalog.cpp
new file mode 100644
index 00000000000..6ec799414c1
--- /dev/null
+++ b/src/mongo/util/uuid_catalog.cpp
@@ -0,0 +1,79 @@
+/**
+ * Copyright (C) 2017 MongoDB Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the GNU Affero General Public License in all respects for
+ * all of the code used other than as permitted herein. If you modify file(s)
+ * with this exception, you may extend this exception to your version of the
+ * file(s), but you are not obligated to do so. If you do not wish to do so,
+ * delete this exception statement from your version. If you delete this
+ * exception statement from all source files in the program, then also delete
+ * it in the license file.
+ */
+
+#include "uuid_catalog.h"
+
+#include "mongo/db/storage/recovery_unit.h"
+#include "mongo/util/uuid.h"
+
+namespace mongo {
+
+const ServiceContext::Decoration<UUIDCatalog> UUIDCatalog::get =
+ ServiceContext::declareDecoration<UUIDCatalog>();
+
+void UUIDCatalog::onCreateCollection(OperationContext* opCtx,
+ Collection* coll,
+ CollectionUUID uuid) {
+ _registerUUIDCatalogEntry(uuid, coll);
+ opCtx->recoveryUnit()->onRollback([this, uuid] { _removeUUIDCatalogEntry(uuid); });
+}
+
+Collection* UUIDCatalog::lookupCollectionByUUID(CollectionUUID uuid) {
+ stdx::lock_guard<stdx::mutex> lock(_catalogLock);
+ Collection* foundCol = _catalog[uuid];
+ return foundCol;
+}
+
+NamespaceString UUIDCatalog::lookupNSSByUUID(CollectionUUID uuid) {
+ stdx::lock_guard<stdx::mutex> lock(_catalogLock);
+ Collection* foundCol = _catalog[uuid];
+ NamespaceString nss = foundCol ? foundCol->ns() : NamespaceString();
+ return nss;
+}
+
+void UUIDCatalog::onDropCollection(OperationContext* opCtx, CollectionUUID uuid) {
+ Collection* foundCol = _removeUUIDCatalogEntry(uuid);
+ opCtx->recoveryUnit()->onRollback(
+ [this, foundCol, uuid] { _registerUUIDCatalogEntry(uuid, foundCol); });
+}
+
+void UUIDCatalog::_registerUUIDCatalogEntry(CollectionUUID uuid, Collection* coll) {
+ stdx::lock_guard<stdx::mutex> lock(_catalogLock);
+ if (coll) {
+ std::pair<CollectionUUID, Collection*> entry = std::make_pair(uuid, coll);
+ invariant(_catalog.insert(entry).second == true);
+ }
+}
+
+Collection* UUIDCatalog::_removeUUIDCatalogEntry(CollectionUUID uuid) {
+ stdx::lock_guard<stdx::mutex> lock(_catalogLock);
+ Collection* foundCol = _catalog[uuid];
+ invariant(_catalog.erase(uuid) <= 1);
+ return foundCol;
+}
+} // namespace mongo
diff --git a/src/mongo/util/uuid_catalog.h b/src/mongo/util/uuid_catalog.h
new file mode 100644
index 00000000000..f79b2ded90f
--- /dev/null
+++ b/src/mongo/util/uuid_catalog.h
@@ -0,0 +1,85 @@
+/**
+ * Copyright (C) 2017 MongoDB Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the GNU Affero General Public License in all respects for
+ * all of the code used other than as permitted herein. If you modify file(s)
+ * with this exception, you may extend this exception to your version of the
+ * file(s), but you are not obligated to do so. If you do not wish to do so,
+ * delete this exception statement from your version. If you delete this
+ * exception statement from all source files in the program, then also delete
+ * it in the license file.
+ */
+
+#pragma once
+
+#include <unordered_map>
+
+#include "mongo/base/disallow_copying.h"
+#include "mongo/db/catalog/collection.h"
+#include "mongo/db/service_context.h"
+#include "mongo/util/uuid.h"
+
+namespace mongo {
+
+/**
+ * This class comprises a UUID to collection catalog, allowing for efficient
+ * collection lookup by UUID.
+ */
+using CollectionUUID = UUID;
+
+class UUIDCatalog {
+ MONGO_DISALLOW_COPYING(UUIDCatalog);
+
+public:
+ static const ServiceContext::Decoration<UUIDCatalog> get;
+ UUIDCatalog() = default;
+
+ /* This function inserts the entry for uuid, coll into the UUID
+ * Collection. It is called by the op observer when a collection
+ * is created.
+ */
+ void onCreateCollection(OperationContext* opCtx, Collection* coll, CollectionUUID uuid);
+
+ /* This function gets the Collection* pointer that corresponds to
+ * CollectionUUID uuid. The required locks should be obtained prior
+ * to calling this function, or else the found Collection pointer
+ * might no longer be valid when the call returns.
+ */
+ Collection* lookupCollectionByUUID(CollectionUUID uuid);
+
+ /* This function gets the NamespaceString from the Collection* pointer that
+ * corresponds to CollectionUUID uuid. If there is no such pointer, an empty
+ * NamespaceString is returned.
+ */
+ NamespaceString lookupNSSByUUID(CollectionUUID uuid);
+
+ /* This function removes the entry for uuid from the UUID catalog. It
+ * is called by the op observer when a collection is dropped.
+ */
+ void onDropCollection(OperationContext* opCtx, CollectionUUID uuid);
+
+private:
+ mongo::stdx::mutex _catalogLock;
+ mongo::stdx::unordered_map<CollectionUUID, Collection*, CollectionUUID::Hash> _catalog;
+
+ void _registerUUIDCatalogEntry(CollectionUUID uuid, Collection* coll);
+ Collection* _removeUUIDCatalogEntry(CollectionUUID uuid);
+};
+
+} // namespace mongo
diff --git a/src/mongo/util/uuid_catalog_test.cpp b/src/mongo/util/uuid_catalog_test.cpp
new file mode 100644
index 00000000000..eb1afba9b99
--- /dev/null
+++ b/src/mongo/util/uuid_catalog_test.cpp
@@ -0,0 +1,85 @@
+/**
+ * Copyright (C) 2017 MongoDB Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the GNU Affero General Public License in all respects for
+ * all of the code used other than as permitted herein. If you modify file(s)
+ * with this exception, you may extend this exception to your version of the
+ * file(s), but you are not obligated to do so. If you do not wish to do so,
+ * delete this exception statement from your version. If you delete this
+ * exception statement from all source files in the program, then also delete
+ * it in the license file.
+ */
+
+#include "mongo/util/uuid_catalog.h"
+
+#include "mongo/db/catalog/collection_mock.h"
+#include "mongo/db/operation_context_noop.h"
+#include "mongo/unittest/unittest.h"
+
+using namespace mongo;
+
+/**
+ * A test fixture that creates a UUID Catalog and Collection* pointer to store in it.
+ */
+class UUIDCatalogTest : public unittest::Test {
+public:
+ UUIDCatalogTest()
+ : uuid(CollectionUUID::gen()),
+ nss("testdb", "testcol"),
+ col(stdx::make_unique<CollectionMock>(nss)) {
+ // Register dummy collection in catalog.
+ catalog.onCreateCollection(&opCtx, &col, uuid);
+ }
+
+protected:
+ UUIDCatalog catalog;
+ OperationContextNoop opCtx;
+ CollectionUUID uuid;
+ NamespaceString nss;
+ Collection col;
+};
+
+namespace {
+
+TEST_F(UUIDCatalogTest, onCreateCollection) {
+ ASSERT(catalog.lookupCollectionByUUID(uuid) != nullptr);
+}
+
+TEST_F(UUIDCatalogTest, lookupCollectionByUUID) {
+ ASSERT(catalog.lookupCollectionByUUID(uuid) != nullptr);
+ // Ensure the string value of the NamespaceString of the obtained Collection is equal to
+ // nss.ns().
+ ASSERT_EQUALS(catalog.lookupCollectionByUUID(uuid)->ns().ns(), nss.ns());
+ // Ensure lookups of unknown UUIDs result in null pointers.
+ ASSERT(catalog.lookupCollectionByUUID(CollectionUUID::gen()) == nullptr);
+}
+
+TEST_F(UUIDCatalogTest, lookupNSSByUUID) {
+ // Ensure the string value of the obtained NamespaceString is equal to nss.ns().
+ ASSERT_EQUALS(catalog.lookupNSSByUUID(uuid).ns(), nss.ns());
+ // Ensure namespace lookups of unknown UUIDs result in empty NamespaceStrings.
+ ASSERT_EQUALS(catalog.lookupNSSByUUID(CollectionUUID::gen()).ns(), NamespaceString().ns());
+}
+
+TEST_F(UUIDCatalogTest, onDropCollection) {
+ catalog.onDropCollection(&opCtx, uuid);
+ // Ensure the lookup returns a null pointer upon removing the uuid entry.
+ ASSERT(catalog.lookupCollectionByUUID(uuid) == nullptr);
+}
+} // namespace