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/bson/bsonobj.h"
34 #include "mongo/bson/bsonobjbuilder.h"
35 #include "mongo/client/read_preference.h"
36 #include "mongo/db/namespace_string.h"
37 #include "mongo/s/catalog/sharding_catalog_manager.h"
38 #include "mongo/s/catalog/type_chunk.h"
39 #include "mongo/s/catalog/type_shard.h"
40 #include "mongo/s/client/shard_registry.h"
41 #include "mongo/s/config_server_test_fixture.h"
42
43 namespace mongo {
44 namespace {
45
46 using unittest::assertGet;
47
48 using CommitChunkMigrate = ConfigServerTestFixture;
49
TEST_F(CommitChunkMigrate,ChunksUpdatedCorrectlyWithControlChunk)50 TEST_F(CommitChunkMigrate, ChunksUpdatedCorrectlyWithControlChunk) {
51 std::string const nss = "TestDB.TestColl";
52
53 ShardType shard0;
54 shard0.setName("shard0");
55 shard0.setHost("shard0:12");
56
57 ShardType shard1;
58 shard1.setName("shard1");
59 shard1.setHost("shard1:12");
60
61 setupShards({shard0, shard1});
62
63 ChunkType migratedChunk, controlChunk;
64 {
65 ChunkVersion origVersion(12, 7, OID::gen());
66
67 migratedChunk.setNS(nss);
68 migratedChunk.setVersion(origVersion);
69 migratedChunk.setShard(shard0.getName());
70 migratedChunk.setMin(BSON("a" << 1));
71 migratedChunk.setMax(BSON("a" << 10));
72
73 origVersion.incMinor();
74
75 controlChunk.setNS(nss);
76 controlChunk.setVersion(origVersion);
77 controlChunk.setShard(shard0.getName());
78 controlChunk.setMin(BSON("a" << 10));
79 controlChunk.setMax(BSON("a" << 20));
80 controlChunk.setJumbo(true);
81 }
82
83 setupChunks({migratedChunk, controlChunk});
84
85 Timestamp validAfter{101, 0};
86 BSONObj versions = assertGet(ShardingCatalogManager::get(operationContext())
87 ->commitChunkMigration(operationContext(),
88 NamespaceString(nss),
89 migratedChunk,
90 controlChunk,
91 migratedChunk.getVersion().epoch(),
92 ShardId(shard0.getName()),
93 ShardId(shard1.getName())));
94
95 // Verify the versions returned match expected values.
96 auto mver = assertGet(
97 ChunkVersion::parseFromBSONWithFieldForCommands(versions, "migratedChunkVersion"));
98 ASSERT_EQ(ChunkVersion(migratedChunk.getVersion().majorVersion() + 1,
99 0,
100 migratedChunk.getVersion().epoch()),
101 mver);
102
103 auto cver =
104 assertGet(ChunkVersion::parseFromBSONWithFieldForCommands(versions, "controlChunkVersion"));
105 ASSERT_EQ(ChunkVersion(migratedChunk.getVersion().majorVersion() + 1,
106 1,
107 migratedChunk.getVersion().epoch()),
108 cver);
109
110 // Verify the chunks ended up in the right shards, and versions match the values returned.
111 auto chunkDoc0 = uassertStatusOK(getChunkDoc(operationContext(), migratedChunk.getMin()));
112 ASSERT_EQ("shard1", chunkDoc0.getShard().toString());
113 ASSERT_EQ(mver, chunkDoc0.getVersion());
114
115 auto chunkDoc1 = uassertStatusOK(getChunkDoc(operationContext(), controlChunk.getMin()));
116 ASSERT_EQ("shard0", chunkDoc1.getShard().toString());
117 ASSERT_EQ(cver, chunkDoc1.getVersion());
118
119 // The control chunk's jumbo status should be unchanged.
120 ASSERT(chunkDoc1.getJumbo());
121 }
122
TEST_F(CommitChunkMigrate,CheckCorrectOpsCommandNoCtl)123 TEST_F(CommitChunkMigrate, CheckCorrectOpsCommandNoCtl) {
124
125 std::string const nss = "TestDB.TestColl";
126
127 ShardType shard0;
128 shard0.setName("shard0");
129 shard0.setHost("shard0:12");
130
131 ShardType shard1;
132 shard1.setName("shard1");
133 shard1.setHost("shard1:12");
134
135 setupShards({shard0, shard1});
136
137 int origMajorVersion = 15;
138 auto const origVersion = ChunkVersion(origMajorVersion, 4, OID::gen());
139
140 ChunkType chunk0;
141 chunk0.setNS(nss);
142 chunk0.setVersion(origVersion);
143 chunk0.setShard(shard0.getName());
144
145 // apportion
146 auto chunkMin = BSON("a" << 1);
147 chunk0.setMin(chunkMin);
148 auto chunkMax = BSON("a" << 10);
149 chunk0.setMax(chunkMax);
150
151 setupChunks({chunk0});
152
153 StatusWith<BSONObj> resultBSON = ShardingCatalogManager::get(operationContext())
154 ->commitChunkMigration(operationContext(),
155 NamespaceString(chunk0.getNS()),
156 chunk0,
157 boost::none,
158 origVersion.epoch(),
159 ShardId(shard0.getName()),
160 ShardId(shard1.getName()));
161
162 ASSERT_OK(resultBSON.getStatus());
163
164 // Verify the version returned matches expected value.
165 BSONObj versions = resultBSON.getValue();
166 auto mver = ChunkVersion::parseFromBSONWithFieldForCommands(versions, "migratedChunkVersion");
167 ASSERT_OK(mver.getStatus());
168 ASSERT_EQ(ChunkVersion(origMajorVersion + 1, 0, origVersion.epoch()), mver.getValue());
169
170 auto cver = ChunkVersion::parseFromBSONWithFieldForCommands(versions, "controlChunkVersion");
171 ASSERT_NOT_OK(cver.getStatus());
172
173 // Verify the chunk ended up in the right shard, and version matches the value returned.
174 auto chunkDoc0 = uassertStatusOK(getChunkDoc(operationContext(), chunkMin));
175 ASSERT_EQ("shard1", chunkDoc0.getShard().toString());
176 ASSERT_EQ(mver.getValue(), chunkDoc0.getVersion());
177 }
178
TEST_F(CommitChunkMigrate,RejectWrongCollectionEpoch0)179 TEST_F(CommitChunkMigrate, RejectWrongCollectionEpoch0) {
180
181 std::string const nss = "TestDB.TestColl";
182
183 ShardType shard0;
184 shard0.setName("shard0");
185 shard0.setHost("shard0:12");
186
187 ShardType shard1;
188 shard1.setName("shard1");
189 shard1.setHost("shard1:12");
190
191 setupShards({shard0, shard1});
192
193 int origMajorVersion = 12;
194 auto const origVersion = ChunkVersion(origMajorVersion, 7, OID::gen());
195
196 ChunkType chunk0;
197 chunk0.setNS(nss);
198 chunk0.setVersion(origVersion);
199 chunk0.setShard(shard0.getName());
200
201 // apportion
202 auto chunkMin = BSON("a" << 1);
203 chunk0.setMin(chunkMin);
204 auto chunkMax = BSON("a" << 10);
205 chunk0.setMax(chunkMax);
206
207 ChunkType chunk1;
208 chunk1.setNS(nss);
209 chunk1.setVersion(origVersion);
210 chunk1.setShard(shard0.getName());
211
212 chunk1.setMin(chunkMax);
213 auto chunkMaxax = BSON("a" << 20);
214 chunk1.setMax(chunkMaxax);
215
216 setupChunks({chunk0, chunk1});
217
218 StatusWith<BSONObj> resultBSON = ShardingCatalogManager::get(operationContext())
219 ->commitChunkMigration(operationContext(),
220 NamespaceString(chunk0.getNS()),
221 chunk0,
222 chunk1,
223 OID::gen(),
224 ShardId(shard0.getName()),
225 ShardId(shard1.getName()));
226
227 ASSERT_EQ(ErrorCodes::StaleEpoch, resultBSON.getStatus());
228 }
229
TEST_F(CommitChunkMigrate,RejectWrongCollectionEpoch1)230 TEST_F(CommitChunkMigrate, RejectWrongCollectionEpoch1) {
231
232 std::string const nss = "TestDB.TestColl";
233
234 ShardType shard0;
235 shard0.setName("shard0");
236 shard0.setHost("shard0:12");
237
238 ShardType shard1;
239 shard1.setName("shard1");
240 shard1.setHost("shard1:12");
241
242 setupShards({shard0, shard1});
243
244 int origMajorVersion = 12;
245 auto const origVersion = ChunkVersion(origMajorVersion, 7, OID::gen());
246 auto const otherVersion = ChunkVersion(origMajorVersion, 7, OID::gen());
247
248 ChunkType chunk0;
249 chunk0.setNS(nss);
250 chunk0.setVersion(origVersion);
251 chunk0.setShard(shard0.getName());
252
253 // apportion
254 auto chunkMin = BSON("a" << 1);
255 chunk0.setMin(chunkMin);
256 auto chunkMax = BSON("a" << 10);
257 chunk0.setMax(chunkMax);
258
259 ChunkType chunk1;
260 chunk1.setNS(nss);
261 chunk1.setVersion(otherVersion);
262 chunk1.setShard(shard0.getName());
263
264 chunk1.setMin(chunkMax);
265 auto chunkMaxax = BSON("a" << 20);
266 chunk1.setMax(chunkMaxax);
267
268 // get version from the control chunk this time
269 setupChunks({chunk1, chunk0});
270
271 StatusWith<BSONObj> resultBSON = ShardingCatalogManager::get(operationContext())
272 ->commitChunkMigration(operationContext(),
273 NamespaceString(chunk0.getNS()),
274 chunk0,
275 chunk1,
276 origVersion.epoch(),
277 ShardId(shard0.getName()),
278 ShardId(shard1.getName()));
279
280 ASSERT_EQ(ErrorCodes::StaleEpoch, resultBSON.getStatus());
281 }
282
TEST_F(CommitChunkMigrate,RejectChunkMissing0)283 TEST_F(CommitChunkMigrate, RejectChunkMissing0) {
284
285 std::string const nss = "TestDB.TestColl";
286
287 ShardType shard0;
288 shard0.setName("shard0");
289 shard0.setHost("shard0:12");
290
291 ShardType shard1;
292 shard1.setName("shard1");
293 shard1.setHost("shard1:12");
294
295 setupShards({shard0, shard1});
296
297 int origMajorVersion = 12;
298 auto const origVersion = ChunkVersion(origMajorVersion, 7, OID::gen());
299
300 ChunkType chunk0;
301 chunk0.setNS(nss);
302 chunk0.setVersion(origVersion);
303 chunk0.setShard(shard0.getName());
304
305 // apportion
306 auto chunkMin = BSON("a" << 1);
307 chunk0.setMin(chunkMin);
308 auto chunkMax = BSON("a" << 10);
309 chunk0.setMax(chunkMax);
310
311 ChunkType chunk1;
312 chunk1.setNS(nss);
313 chunk1.setVersion(origVersion);
314 chunk1.setShard(shard0.getName());
315
316 chunk1.setMin(chunkMax);
317 auto chunkMaxax = BSON("a" << 20);
318 chunk1.setMax(chunkMaxax);
319
320 setupChunks({chunk1});
321
322 StatusWith<BSONObj> resultBSON = ShardingCatalogManager::get(operationContext())
323 ->commitChunkMigration(operationContext(),
324 NamespaceString(chunk0.getNS()),
325 chunk0,
326 chunk1,
327 origVersion.epoch(),
328 ShardId(shard0.getName()),
329 ShardId(shard1.getName()));
330
331 ASSERT_EQ(40165, resultBSON.getStatus().code());
332 }
333
TEST_F(CommitChunkMigrate,RejectChunkMissing1)334 TEST_F(CommitChunkMigrate, RejectChunkMissing1) {
335
336 std::string const nss = "TestDB.TestColl";
337
338 ShardType shard0;
339 shard0.setName("shard0");
340 shard0.setHost("shard0:12");
341
342 ShardType shard1;
343 shard1.setName("shard1");
344 shard1.setHost("shard1:12");
345
346 setupShards({shard0, shard1});
347
348 int origMajorVersion = 12;
349 auto const origVersion = ChunkVersion(origMajorVersion, 7, OID::gen());
350
351 ChunkType chunk0;
352 chunk0.setNS(nss);
353 chunk0.setVersion(origVersion);
354 chunk0.setShard(shard0.getName());
355
356 // apportion
357 auto chunkMin = BSON("a" << 1);
358 chunk0.setMin(chunkMin);
359 auto chunkMax = BSON("a" << 10);
360 chunk0.setMax(chunkMax);
361
362 ChunkType chunk1;
363 chunk1.setNS(nss);
364 chunk1.setVersion(origVersion);
365 chunk1.setShard(shard0.getName());
366
367 chunk1.setMin(chunkMax);
368 auto chunkMaxax = BSON("a" << 20);
369 chunk1.setMax(chunkMaxax);
370
371 setupChunks({chunk0});
372
373 StatusWith<BSONObj> resultBSON = ShardingCatalogManager::get(operationContext())
374 ->commitChunkMigration(operationContext(),
375 NamespaceString(chunk0.getNS()),
376 chunk0,
377 chunk1,
378 origVersion.epoch(),
379 ShardId(shard0.getName()),
380 ShardId(shard1.getName()));
381
382 ASSERT_EQ(40165, resultBSON.getStatus().code());
383 }
384
385 } // namespace
386 } // namespace mongo
387