1
2 /**
3 * Copyright (C) 2018-present MongoDB, Inc.
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the Server Side Public License, version 1,
7 * as published by MongoDB, Inc.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * Server Side Public License for more details.
13 *
14 * You should have received a copy of the Server Side Public License
15 * along with this program. If not, see
16 * <http://www.mongodb.com/licensing/server-side-public-license>.
17 *
18 * As a special exception, the copyright holders give permission to link the
19 * code of portions of this program with the OpenSSL library under certain
20 * conditions as described in each individual source file and distribute
21 * linked combinations including the program with the OpenSSL library. You
22 * must comply with the Server Side Public License in all respects for
23 * all of the code used other than as permitted herein. If you modify file(s)
24 * with this exception, you may extend this exception to your version of the
25 * file(s), but you are not obligated to do so. If you do not wish to do so,
26 * delete this exception statement from your version. If you delete this
27 * exception statement from all source files in the program, then also delete
28 * it in the license file.
29 */
30
31 #include "mongo/platform/basic.h"
32
33 #include "mongo/db/s/migration_destination_manager.h"
34 #include "mongo/s/shard_server_test_fixture.h"
35 #include "mongo/unittest/unittest.h"
36
37 namespace mongo {
38 namespace {
39
40 using unittest::assertGet;
41
42 class MigrationDestinationManagerTest : public ShardServerTestFixture {
43 protected:
44 /**
45 * Instantiates a BSON object in which both "_id" and "X" are set to value.
46 */
createDocument(int value)47 static BSONObj createDocument(int value) {
48 return BSON("_id" << value << "X" << value);
49 }
50
51 /**
52 * Creates a list of documents to clone.
53 */
createDocumentsToClone()54 static std::vector<BSONObj> createDocumentsToClone() {
55 return {createDocument(1), createDocument(2), createDocument(3)};
56 }
57
58 /**
59 * Creates a list of documents to clone and converts it to a BSONArray.
60 */
createDocumentsToCloneArray()61 static BSONArray createDocumentsToCloneArray() {
62 BSONArrayBuilder arrayBuilder;
63 for (auto& doc : createDocumentsToClone()) {
64 arrayBuilder.append(doc);
65 }
66 return arrayBuilder.arr();
67 }
68 };
69
70 // Tests that documents will ferry from the fetch logic to the insert logic successfully.
TEST_F(MigrationDestinationManagerTest,CloneDocumentsFromDonorWorksCorrectly)71 TEST_F(MigrationDestinationManagerTest, CloneDocumentsFromDonorWorksCorrectly) {
72 bool ranOnce = false;
73
74 auto fetchBatchFn = [&](OperationContext* opCtx) {
75 BSONObjBuilder fetchBatchResultBuilder;
76
77 if (ranOnce) {
78 fetchBatchResultBuilder.append("objects", BSONObj());
79 } else {
80 ranOnce = true;
81 fetchBatchResultBuilder.append("objects", createDocumentsToCloneArray());
82 }
83
84 return fetchBatchResultBuilder.obj();
85 };
86
87 std::vector<BSONObj> resultDocs;
88
89 auto insertBatchFn = [&](OperationContext* opCtx, BSONObj docs) {
90 for (auto&& docToClone : docs) {
91 resultDocs.push_back(docToClone.Obj().getOwned());
92 }
93 };
94
95 MigrationDestinationManager::cloneDocumentsFromDonor(
96 operationContext(), insertBatchFn, fetchBatchFn);
97
98 std::vector<BSONObj> originalDocs = createDocumentsToClone();
99
100 ASSERT_EQ(originalDocs.size(), resultDocs.size());
101
102 for (auto originalDocsIt = originalDocs.begin(), resultDocsIt = resultDocs.begin();
103 originalDocsIt != originalDocs.end() && resultDocsIt != resultDocs.end();
104 ++originalDocsIt, ++resultDocsIt) {
105 ASSERT_BSONOBJ_EQ(*originalDocsIt, *resultDocsIt);
106 }
107 }
108
109 // Tests that an exception in the fetch logic will successfully throw an exception on the main
110 // thread.
TEST_F(MigrationDestinationManagerTest,CloneDocumentsThrowsFetchErrors)111 TEST_F(MigrationDestinationManagerTest, CloneDocumentsThrowsFetchErrors) {
112 bool ranOnce = false;
113
114 auto fetchBatchFn = [&](OperationContext* opCtx) {
115 BSONObjBuilder fetchBatchResultBuilder;
116
117 if (ranOnce) {
118 uasserted(ErrorCodes::NetworkTimeout, "network error");
119 }
120
121 ranOnce = true;
122 fetchBatchResultBuilder.append("objects", createDocumentsToCloneArray());
123
124 return fetchBatchResultBuilder.obj();
125 };
126
127 auto insertBatchFn = [&](OperationContext* opCtx, BSONObj docs) {};
128
129 ASSERT_THROWS_CODE_AND_WHAT(MigrationDestinationManager::cloneDocumentsFromDonor(
130 operationContext(), insertBatchFn, fetchBatchFn),
131 DBException,
132 ErrorCodes::NetworkTimeout,
133 "network error");
134 }
135
136 // Tests that an exception in the insertion logic will successfully throw an exception on the
137 // main thread.
TEST_F(MigrationDestinationManagerTest,CloneDocumentsCatchesInsertErrors)138 TEST_F(MigrationDestinationManagerTest, CloneDocumentsCatchesInsertErrors) {
139 auto fetchBatchFn = [&](OperationContext* opCtx) {
140 BSONObjBuilder fetchBatchResultBuilder;
141 fetchBatchResultBuilder.append("objects", createDocumentsToCloneArray());
142 return fetchBatchResultBuilder.obj();
143 };
144
145 auto insertBatchFn = [&](OperationContext* opCtx, BSONObj docs) {
146 uasserted(ErrorCodes::FailedToParse, "insertion error");
147 };
148
149 // Since the error is thrown on another thread, the message becomes "operation was interrupted"
150 // on the main thread.
151
152 ASSERT_THROWS_CODE_AND_WHAT(MigrationDestinationManager::cloneDocumentsFromDonor(
153 operationContext(), insertBatchFn, fetchBatchFn),
154 DBException,
155 ErrorCodes::FailedToParse,
156 "operation was interrupted");
157
158 ASSERT_EQ(operationContext()->getKillStatus(), ErrorCodes::FailedToParse);
159 }
160
161 } // namespace
162 } // namespace mongo
163