summaryrefslogtreecommitdiff
path: root/src/mongo
diff options
context:
space:
mode:
authorBenety Goh <benety@mongodb.com>2017-04-04 16:15:03 -0400
committerBenety Goh <benety@mongodb.com>2017-04-13 16:12:59 -0400
commitb02b7f7bb78d4fd0bb006591769faaa216e6f8a7 (patch)
tree2ff381ed5f1003bb7a563dc51f2c26dc8565d9ca /src/mongo
parent4677c2b4659ab634959d7866586cc9aaffa41745 (diff)
downloadmongo-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.cpp59
-rw-r--r--src/mongo/db/repl/rollback_fix_up_info.h67
-rw-r--r--src/mongo/db/repl/rollback_fix_up_info_descriptions.cpp11
-rw-r--r--src/mongo/db/repl/rollback_fix_up_info_descriptions.h21
-rw-r--r--src/mongo/db/repl/rollback_fix_up_info_descriptions_test.cpp24
-rw-r--r--src/mongo/db/repl/rollback_fix_up_info_test.cpp167
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