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