diff options
author | Benety Goh <benety@mongodb.com> | 2017-04-04 16:15:03 -0400 |
---|---|---|
committer | Benety Goh <benety@mongodb.com> | 2017-04-13 16:12:59 -0400 |
commit | b02b7f7bb78d4fd0bb006591769faaa216e6f8a7 (patch) | |
tree | 2ff381ed5f1003bb7a563dc51f2c26dc8565d9ca /src/mongo | |
parent | 4677c2b4659ab634959d7866586cc9aaffa41745 (diff) | |
download | mongo-b02b7f7bb78d4fd0bb006591769faaa216e6f8a7.tar.gz |
SERVER-28211 RollbackFixUpInfo handles create, drop and rename collection commands
Diffstat (limited to 'src/mongo')
-rw-r--r-- | src/mongo/db/repl/rollback_fix_up_info.cpp | 59 | ||||
-rw-r--r-- | src/mongo/db/repl/rollback_fix_up_info.h | 67 | ||||
-rw-r--r-- | src/mongo/db/repl/rollback_fix_up_info_descriptions.cpp | 11 | ||||
-rw-r--r-- | src/mongo/db/repl/rollback_fix_up_info_descriptions.h | 21 | ||||
-rw-r--r-- | src/mongo/db/repl/rollback_fix_up_info_descriptions_test.cpp | 24 | ||||
-rw-r--r-- | src/mongo/db/repl/rollback_fix_up_info_test.cpp | 167 |
6 files changed, 337 insertions, 12 deletions
diff --git a/src/mongo/db/repl/rollback_fix_up_info.cpp b/src/mongo/db/repl/rollback_fix_up_info.cpp index 2db40e71200..0994f0b7399 100644 --- a/src/mongo/db/repl/rollback_fix_up_info.cpp +++ b/src/mongo/db/repl/rollback_fix_up_info.cpp @@ -41,7 +41,16 @@ namespace mongo { namespace repl { -const NamespaceString RollbackFixUpInfo::kRollbackDocsNamespace("local.system.rollback.docs"); +namespace { + +const auto kRollbackNamespacePrefix = "local.system.rollback."_sd; + +} // namespace + +const NamespaceString RollbackFixUpInfo::kRollbackDocsNamespace(kRollbackNamespacePrefix + "docs"); + +const NamespaceString RollbackFixUpInfo::kRollbackCollectionUuidNamespace(kRollbackNamespacePrefix + + "collectionUuid"); RollbackFixUpInfo::RollbackFixUpInfo(StorageInterface* storageInterface) : _storageInterface(storageInterface) { @@ -53,9 +62,53 @@ Status RollbackFixUpInfo::processSingleDocumentOplogEntry(OperationContext* opCt const BSONElement& docId, SingleDocumentOpType opType) { SingleDocumentOperationDescription desc(collectionUuid, docId, opType); - auto update = desc.toBSON(); + return _upsertById(opCtx, kRollbackDocsNamespace, desc.toBSON()); +} + +Status RollbackFixUpInfo::processCreateCollectionOplogEntry(OperationContext* opCtx, + const UUID& collectionUuid) { + // TODO: Remove references to this collection UUID from other rollback fix up info collections. + + CollectionUuidDescription desc(collectionUuid, {}); + return _upsertById(opCtx, kRollbackCollectionUuidNamespace, desc.toBSON()); +} + +Status RollbackFixUpInfo::processDropCollectionOplogEntry(OperationContext* opCtx, + const UUID& collectionUuid, + const NamespaceString& nss) { + CollectionUuidDescription desc(collectionUuid, nss); + return _upsertById(opCtx, kRollbackCollectionUuidNamespace, desc.toBSON()); +} + +Status RollbackFixUpInfo::processRenameCollectionOplogEntry( + OperationContext* opCtx, + const UUID& sourceCollectionUuid, + const NamespaceString& sourceNss, + boost::optional<CollectionUuidAndNss> targetCollectionUuidAndNss) { + CollectionUuidDescription sourceDesc(sourceCollectionUuid, sourceNss); + + auto status = _upsertById(opCtx, kRollbackCollectionUuidNamespace, sourceDesc.toBSON()); + if (!status.isOK()) { + return status; + } + + // If target collection is not dropped during the rename operation, there is nothing further to + // do. + if (!targetCollectionUuidAndNss) { + return Status::OK(); + } + + CollectionUuidDescription targetDesc(targetCollectionUuidAndNss->first, + targetCollectionUuidAndNss->second); + return _upsertById(opCtx, kRollbackCollectionUuidNamespace, targetDesc.toBSON()); +} + +Status RollbackFixUpInfo::_upsertById(OperationContext* opCtx, + const NamespaceString& nss, + const BSONObj& update) { auto key = update["_id"]; - return _storageInterface->upsertById(opCtx, kRollbackDocsNamespace, key, update); + invariant(!key.eoo()); + return _storageInterface->upsertById(opCtx, nss, key, update); } } // namespace repl diff --git a/src/mongo/db/repl/rollback_fix_up_info.h b/src/mongo/db/repl/rollback_fix_up_info.h index ee64754338f..989023e8dfa 100644 --- a/src/mongo/db/repl/rollback_fix_up_info.h +++ b/src/mongo/db/repl/rollback_fix_up_info.h @@ -28,15 +28,19 @@ #pragma once +#include <boost/optional.hpp> +#include <utility> + #include "mongo/base/disallow_copying.h" #include "mongo/base/status.h" +#include "mongo/base/status_with.h" #include "mongo/db/namespace_string.h" +#include "mongo/util/uuid.h" namespace mongo { class BSONElement; class OperationContext; -class UUID; namespace repl { @@ -64,6 +68,16 @@ public: static const NamespaceString kRollbackDocsNamespace; /** + * Contains mappings of collection UUID -> namespace. + * This collection is used to roll back create, drop and rename operations. + * For drops and renames, the namespace in the mapping will be the full namespace to restore + * in the database catalog. + * For collection creation, the namespace will be set to empty to indicate that the collection + * should be dropped. + */ + static const NamespaceString kRollbackCollectionUuidNamespace; + + /** * Creates an instance of RollbackFixUpInfo. */ explicit RollbackFixUpInfo(StorageInterface* storageInterface); @@ -87,7 +101,58 @@ public: const BSONElement& docId, SingleDocumentOpType opType); + /** + * Processes an oplog entry representing a create collection command. Stores information about + * this operation into "kRollbackCollectionUuidNamespace" to allow us to roll back this + * operation later by dropping the collection from the catalog by UUID. + * + * The mapping in the "kRollbackCollectionUuidNamespace" collection will contain the + * empty namespace. + */ + class CollectionUuidDescription; + Status processCreateCollectionOplogEntry(OperationContext* opCtx, const UUID& collectionUuid); + + /** + * Processes an oplog entry representing a drop collection command. Stores information about + * this operation into "kRollbackCollectionUuidNamespace" to allow us to roll back this + * operation later by restoring the UUID mapping in the catalog. + */ + Status processDropCollectionOplogEntry(OperationContext* opCtx, + const UUID& collectionUuid, + const NamespaceString& nss); + + /** + * Processes an oplog entry representing a rename collection command. Stores information about + * this operation into "kRollbackCollectionUuidNamespace" to allow us to roll back this + * operation later by restoring the UUID mapping(s) in the catalog. + * + * "targetCollectionUuidAndNss" is derived from the "dropTarget" and "to" fields of the + * renameCollection oplogEntry. + * + * If the target collection did not exist when the rename operation was applied + * (targetCollectionUuidAndNss is valid), this function will insert one document into + * "kRollbackCollectionUuidNamespace": + * source UUID -> namespace mapping. + * + * If the target collection was dropped when the rename operation was applied + * (targetCollectionUuidAndNss is valid), this function will insert two documents into + * "kRollbackCollectionUuidNamespace": + * source UUID -> namespace mapping; and + * dropped target UUID -> namespace mapping + */ + using CollectionUuidAndNss = std::pair<UUID, NamespaceString>; + Status processRenameCollectionOplogEntry( + OperationContext* opCtx, + const UUID& sourceCollectionUuid, + const NamespaceString& sourceNss, + boost::optional<CollectionUuidAndNss> targetCollectionUuidAndNss); + private: + /** + * Upserts a single document using the _id field of the document in "update". + */ + Status _upsertById(OperationContext* opCtx, const NamespaceString& nss, const BSONObj& update); + StorageInterface* const _storageInterface; }; diff --git a/src/mongo/db/repl/rollback_fix_up_info_descriptions.cpp b/src/mongo/db/repl/rollback_fix_up_info_descriptions.cpp index 657463237d8..bd5c5f230b5 100644 --- a/src/mongo/db/repl/rollback_fix_up_info_descriptions.cpp +++ b/src/mongo/db/repl/rollback_fix_up_info_descriptions.cpp @@ -89,5 +89,16 @@ BSONObj RollbackFixUpInfo::SingleDocumentOperationDescription::toBSON() const { return bob.obj(); } +RollbackFixUpInfo::CollectionUuidDescription::CollectionUuidDescription(const UUID& collectionUuid, + const NamespaceString& nss) + : _collectionUuid(collectionUuid), _nss(nss) {} + +BSONObj RollbackFixUpInfo::CollectionUuidDescription::toBSON() const { + BSONObjBuilder bob; + _collectionUuid.appendToBuilder(&bob, "_id"); + bob.append("ns", _nss.ns()); + return bob.obj(); +} + } // namespace repl } // namespace mongo diff --git a/src/mongo/db/repl/rollback_fix_up_info_descriptions.h b/src/mongo/db/repl/rollback_fix_up_info_descriptions.h index a0b91a3a252..3355289a8f6 100644 --- a/src/mongo/db/repl/rollback_fix_up_info_descriptions.h +++ b/src/mongo/db/repl/rollback_fix_up_info_descriptions.h @@ -30,6 +30,7 @@ #include "mongo/base/disallow_copying.h" #include "mongo/bson/bsonobj.h" +#include "mongo/db/namespace_string.h" #include "mongo/db/repl/rollback_fix_up_info.h" #include "mongo/util/uuid.h" @@ -58,5 +59,25 @@ private: RollbackFixUpInfo::SingleDocumentOpType _opType; }; +/** + * Represents a document in the "kCollectionUuidNamespace" namespace. + * Contains information to roll back collection drops and renames. + */ +class RollbackFixUpInfo::CollectionUuidDescription { + MONGO_DISALLOW_COPYING(CollectionUuidDescription); + +public: + CollectionUuidDescription(const UUID& collectionUuid, const NamespaceString& nss); + + /** + * Returns a BSON representation of this object. + */ + BSONObj toBSON() const; + +private: + UUID _collectionUuid; + NamespaceString _nss; +}; + } // namespace repl } // namespace mongo diff --git a/src/mongo/db/repl/rollback_fix_up_info_descriptions_test.cpp b/src/mongo/db/repl/rollback_fix_up_info_descriptions_test.cpp index df8a2498907..01b8afaa1e9 100644 --- a/src/mongo/db/repl/rollback_fix_up_info_descriptions_test.cpp +++ b/src/mongo/db/repl/rollback_fix_up_info_descriptions_test.cpp @@ -58,4 +58,28 @@ TEST(RollbackFixUpInfoDescriptionsTest, SingleDocumentDescriptionToBson) { ASSERT_BSONOBJ_EQ(expectedDocument, description.toBSON()); } +TEST(RollbackFixUpInfoDescriptionsTest, CollectionUuidDescriptionToBson) { + auto collectionUuid = UUID::gen(); + NamespaceString nss("mydb.mylostcoll"); + + RollbackFixUpInfo::CollectionUuidDescription description(collectionUuid, nss); + + auto expectedDocument = + BSON("_id" << collectionUuid.toBSON().firstElement() << "ns" << nss.ns()); + + ASSERT_BSONOBJ_EQ(expectedDocument, description.toBSON()); +} + +TEST(RollbackFixUpInfoDescriptionsTest, CollectionUuidDescriptionWithEmptyNamespaceToBson) { + auto collectionUuid = UUID::gen(); + NamespaceString emptyNss; + + RollbackFixUpInfo::CollectionUuidDescription description(collectionUuid, emptyNss); + + auto expectedDocument = BSON("_id" << collectionUuid.toBSON().firstElement() << "ns" + << ""); + + ASSERT_BSONOBJ_EQ(expectedDocument, description.toBSON()); +} + } // namespace diff --git a/src/mongo/db/repl/rollback_fix_up_info_test.cpp b/src/mongo/db/repl/rollback_fix_up_info_test.cpp index e26e3a3a5e0..a9347fbcc14 100644 --- a/src/mongo/db/repl/rollback_fix_up_info_test.cpp +++ b/src/mongo/db/repl/rollback_fix_up_info_test.cpp @@ -28,10 +28,12 @@ #include "mongo/platform/basic.h" +#include "mongo/bson/simple_bsonobj_comparator.h" #include "mongo/db/catalog/collection_options.h" #include "mongo/db/client.h" #include "mongo/db/jsobj.h" #include "mongo/db/namespace_string.h" +#include "mongo/db/repl/oplog_entry.h" #include "mongo/db/repl/replication_coordinator.h" #include "mongo/db/repl/replication_coordinator_mock.h" #include "mongo/db/repl/rollback_fix_up_info.h" @@ -71,10 +73,11 @@ private: protected: /** * Check collection contents against given vector of documents. + * Ordering of documents in collection does not need to match order in provided vector. */ void _assertDocumentsInCollectionEquals(OperationContext* opCtx, const NamespaceString& nss, - const std::vector<BSONObj>& expectedDocs); + std::initializer_list<BSONObj> expectedDocs); std::unique_ptr<StorageInterface> _storageInterface; }; @@ -90,6 +93,8 @@ void RollbackFixUpInfoTest::setUp() { auto opCtx = makeOpCtx(); ASSERT_OK(_storageInterface->createCollection( opCtx.get(), RollbackFixUpInfo::kRollbackDocsNamespace, {})); + ASSERT_OK(_storageInterface->createCollection( + opCtx.get(), RollbackFixUpInfo::kRollbackCollectionUuidNamespace, {})); } void RollbackFixUpInfoTest::tearDown() { @@ -100,7 +105,8 @@ void RollbackFixUpInfoTest::tearDown() { /** * Returns string representation of a vector of BSONObj. */ -std::string _toString(const std::vector<BSONObj>& docs) { +template <typename T> +std::string _toString(const T& docs) { str::stream ss; ss << "["; bool first = true; @@ -117,7 +123,9 @@ std::string _toString(const std::vector<BSONObj>& docs) { } void RollbackFixUpInfoTest::_assertDocumentsInCollectionEquals( - OperationContext* opCtx, const NamespaceString& nss, const std::vector<BSONObj>& expectedDocs) { + OperationContext* opCtx, + const NamespaceString& nss, + std::initializer_list<BSONObj> expectedDocs) { auto indexName = "_id_"_sd; const auto actualDocs = unittest::assertGet( _storageInterface->findDocuments(opCtx, @@ -127,14 +135,17 @@ void RollbackFixUpInfoTest::_assertDocumentsInCollectionEquals( {}, BoundInclusion::kIncludeStartKeyOnly, 10000U)); - auto iter = actualDocs.cbegin(); std::string msg = str::stream() << "expected: " << _toString(expectedDocs) << "; actual: " << _toString(actualDocs); - for (const auto& doc : expectedDocs) { - ASSERT_TRUE(iter != actualDocs.cend()) << msg; - ASSERT_BSONOBJ_EQ(doc, *(iter++)); + ASSERT_EQUALS(expectedDocs.size(), actualDocs.size()) << msg; + + auto unorderedExpectedDocsSet = + mongo::SimpleBSONObjComparator::kInstance.makeBSONObjUnorderedSet(expectedDocs); + for (const auto& doc : actualDocs) { + std::string docMsg = str::stream() << "Unexpected document " << doc << " in collection " + << nss.ns() << ": " << msg; + ASSERT_TRUE(unorderedExpectedDocsSet.find(doc) != unorderedExpectedDocsSet.end()) << docMsg; } - ASSERT_TRUE(iter == actualDocs.cend()) << msg; } TEST_F(RollbackFixUpInfoTest, @@ -236,4 +247,144 @@ TEST_F(RollbackFixUpInfoTest, opCtx.get(), RollbackFixUpInfo::kRollbackDocsNamespace, {expectedDocument}); } +TEST_F( + RollbackFixUpInfoTest, + ProcessCreateCollectionOplogEntryInsertsDocumentIntoRollbackCollectionUuidCollectionWithEmptyNamespace) { + auto operation = BSON("ts" << Timestamp(Seconds(1), 0) << "h" << 1LL << "op" + << "c" + << "ns" + << "mydb.$cmd" + << "ui" + << UUID::gen().toBSON().firstElement() + << "o" + << BSON("create" + << "mynewcoll" + << "idIndex" + << BSON("v" << 2 << "key" << BSON("_id" << 1) << "name" + << "_id_" + << "ns" + << "mydb.mynewcoll"))); + auto collectionUuid = unittest::assertGet(UUID::parse(operation["ui"])); + NamespaceString commandNss(operation["ns"].String()); + auto collectionName = operation["o"].Obj().firstElement().String(); + + ASSERT_TRUE(OplogEntry(operation).isCommand()); + + auto opCtx = makeOpCtx(); + RollbackFixUpInfo rollbackFixUpInfo(_storageInterface.get()); + ASSERT_OK(rollbackFixUpInfo.processCreateCollectionOplogEntry(opCtx.get(), collectionUuid)); + + auto expectedDocument = BSON("_id" << collectionUuid.toBSON().firstElement() << "ns" + << ""); + + _assertDocumentsInCollectionEquals( + opCtx.get(), RollbackFixUpInfo::kRollbackCollectionUuidNamespace, {expectedDocument}); +} + +TEST_F(RollbackFixUpInfoTest, + ProcessDropCollectionOplogEntryInsertsDocumentIntoRollbackCollectionUuidCollection) { + auto operation = BSON("ts" << Timestamp(Seconds(1), 0) << "h" << 1LL << "op" + << "c" + << "ns" + << "mydb.$cmd" + << "ui" + << UUID::gen().toBSON().firstElement() + << "o" + << BSON("drop" + << "mydroppedcoll")); + auto collectionUuid = unittest::assertGet(UUID::parse(operation["ui"])); + NamespaceString commandNss(operation["ns"].String()); + auto collectionName = operation["o"].Obj().firstElement().String(); + NamespaceString nss(commandNss.db(), collectionName); + + ASSERT_TRUE(OplogEntry(operation).isCommand()); + + auto opCtx = makeOpCtx(); + RollbackFixUpInfo rollbackFixUpInfo(_storageInterface.get()); + ASSERT_OK(rollbackFixUpInfo.processDropCollectionOplogEntry(opCtx.get(), collectionUuid, nss)); + + auto expectedDocument = + BSON("_id" << collectionUuid.toBSON().firstElement() << "ns" << nss.ns()); + + _assertDocumentsInCollectionEquals( + opCtx.get(), RollbackFixUpInfo::kRollbackCollectionUuidNamespace, {expectedDocument}); +} + +TEST_F( + RollbackFixUpInfoTest, + ProcessRenameCollectionOplogEntryWithDropTargetFalseInsertsOneDocumentIntoRollbackCollectionUuidCollection) { + auto operation = BSON("ts" << Timestamp(Seconds(1), 0) << "h" << 1LL << "op" + << "c" + << "ns" + << "mydb.$cmd" + << "ui" + << UUID::gen().toBSON().firstElement() + << "o" + << BSON("renameCollection" + << "mydb.prevCollName" + << "to" + << "mydb.newCollName" + << "stayTemp" + << false + << "dropTarget" + << false)); + auto collectionUuid = unittest::assertGet(UUID::parse(operation["ui"])); + NamespaceString sourceNss(operation["o"].Obj().firstElement().String()); + ASSERT_EQUALS(ErrorCodes::UnknownError, UUID::parse(operation["o"].Obj()["dropTarget"])); + + ASSERT_TRUE(OplogEntry(operation).isCommand()); + + auto opCtx = makeOpCtx(); + RollbackFixUpInfo rollbackFixUpInfo(_storageInterface.get()); + ASSERT_OK(rollbackFixUpInfo.processRenameCollectionOplogEntry( + opCtx.get(), collectionUuid, sourceNss, boost::none)); + + auto expectedDocument = + BSON("_id" << collectionUuid.toBSON().firstElement() << "ns" << sourceNss.ns()); + + _assertDocumentsInCollectionEquals( + opCtx.get(), RollbackFixUpInfo::kRollbackCollectionUuidNamespace, {expectedDocument}); +} + +TEST_F( + RollbackFixUpInfoTest, + ProcessRenameCollectionOplogEntryWithValidDropTargetUuidInsertsTwoDocumentsIntoRollbackCollectionUuidCollection) { + auto operation = BSON("ts" << Timestamp(Seconds(1), 0) << "h" << 1LL << "op" + << "c" + << "ns" + << "mydb.$cmd" + << "ui" + << UUID::gen().toBSON().firstElement() + << "o" + << BSON("renameCollection" + << "mydb.prevCollName" + << "to" + << "mydb.newCollName" + << "stayTemp" + << false + << "dropTarget" + << UUID::gen().toBSON().firstElement())); + auto collectionUuid = unittest::assertGet(UUID::parse(operation["ui"])); + NamespaceString sourceNss(operation["o"].Obj().firstElement().String()); + NamespaceString targetNss(operation["o"].Obj()["to"].String()); + auto droppedCollectionUuid = + unittest::assertGet(UUID::parse(operation["o"].Obj()["dropTarget"])); + + ASSERT_TRUE(OplogEntry(operation).isCommand()); + + auto opCtx = makeOpCtx(); + RollbackFixUpInfo rollbackFixUpInfo(_storageInterface.get()); + ASSERT_OK(rollbackFixUpInfo.processRenameCollectionOplogEntry( + opCtx.get(), collectionUuid, sourceNss, std::make_pair(droppedCollectionUuid, targetNss))); + + auto expectedDocument1 = + BSON("_id" << collectionUuid.toBSON().firstElement() << "ns" << sourceNss.ns()); + auto expectedDocument2 = + BSON("_id" << droppedCollectionUuid.toBSON().firstElement() << "ns" << targetNss.ns()); + + _assertDocumentsInCollectionEquals(opCtx.get(), + RollbackFixUpInfo::kRollbackCollectionUuidNamespace, + {expectedDocument1, expectedDocument2}); +} + } // namespace |