diff options
Diffstat (limited to 'src/mongo/s/chunk_manager_refresh_test.cpp')
-rw-r--r-- | src/mongo/s/chunk_manager_refresh_test.cpp | 333 |
1 files changed, 204 insertions, 129 deletions
diff --git a/src/mongo/s/chunk_manager_refresh_test.cpp b/src/mongo/s/chunk_manager_refresh_test.cpp index ef6f6672f47..087baa65471 100644 --- a/src/mongo/s/chunk_manager_refresh_test.cpp +++ b/src/mongo/s/chunk_manager_refresh_test.cpp @@ -35,6 +35,8 @@ #include "mongo/db/query/query_request.h" #include "mongo/s/catalog/type_chunk.h" #include "mongo/s/catalog/type_collection.h" +#include "mongo/s/catalog/type_database.h" +#include "mongo/s/catalog/type_shard.h" #include "mongo/s/catalog/type_shard.h" #include "mongo/s/catalog_cache.h" #include "mongo/s/chunk_manager_test_fixture.h" @@ -45,28 +47,60 @@ namespace { using executor::RemoteCommandRequest; using unittest::assertGet; -using ChunkManagerLoadTest = ChunkManagerTestFixture; +const NamespaceString kNss("TestDB", "TestColl"); + +class ChunkManagerLoadTest : public ChunkManagerTestFixture { +protected: + void setUp() override { + ChunkManagerTestFixture::setUp(); + + setupShards([&]() { + ShardType shard0; + shard0.setName("0"); + shard0.setHost("Host0:12345"); + + ShardType shard1; + shard1.setName("1"); + shard1.setHost("Host1:12345"); + + return std::vector<ShardType>{shard0, shard1}; + }()); + } + + void expectGetDatabase() { + expectFindOnConfigSendBSONObjVector([&]() { + DatabaseType db; + db.setName(kNss.db().toString()); + db.setPrimary({"0"}); + db.setSharded(true); + + return std::vector<BSONObj>{db.toBSON()}; + }()); + } + + void expectGetCollection(OID epoch, const ShardKeyPattern& shardKeyPattern) { + expectFindOnConfigSendBSONObjVector([&]() { + CollectionType collType; + collType.setNs(kNss); + collType.setEpoch(epoch); + collType.setKeyPattern(shardKeyPattern.toBSON()); + collType.setUnique(false); + + return std::vector<BSONObj>{collType.toBSON()}; + }()); + } +}; TEST_F(ChunkManagerLoadTest, FullLoad) { const OID epoch = OID::gen(); const ShardKeyPattern shardKeyPattern(BSON("_id" << 1)); - auto future = launchAsync([&] { - auto client = serviceContext()->makeClient("Test"); - auto opCtx = client->makeOperationContext(); - return CatalogCache::refreshCollectionRoutingInfo(opCtx.get(), kNss, nullptr); - }); - - expectFindOnConfigSendBSONObjVector([&]() { - CollectionType collType; - collType.setNs(kNss); - collType.setEpoch(epoch); - collType.setKeyPattern(shardKeyPattern.toBSON()); - collType.setUnique(false); + auto future = scheduleRoutingInfoRefresh(kNss); - return std::vector<BSONObj>{collType.toBSON()}; - }()); + expectGetDatabase(); + expectGetCollection(epoch, shardKeyPattern); + expectGetCollection(epoch, shardKeyPattern); expectFindOnConfigSendBSONObjVector([&]() { ChunkVersion version(1, 0, epoch); @@ -94,93 +128,145 @@ TEST_F(ChunkManagerLoadTest, FullLoad) { chunk4.toConfigBSON()}; }()); - expectFindOnConfigSendBSONObjVector([&]() { - ShardType shard1; - shard1.setName("0"); - shard1.setHost(str::stream() << "Host0:12345"); + auto routingInfo = future.timed_get(kFutureTimeout); + ASSERT(routingInfo->cm()); + auto cm = routingInfo->cm(); - ShardType shard2; - shard2.setName("1"); - shard2.setHost(str::stream() << "Host1:12345"); + ASSERT_EQ(4, cm->numChunks()); +} - return std::vector<BSONObj>{shard1.toBSON(), shard2.toBSON()}; - }()); +TEST_F(ChunkManagerLoadTest, DatabaseNotFound) { + auto future = scheduleRoutingInfoRefresh(kNss); - auto routingInfo = future.timed_get(kFutureTimeout); - ASSERT_EQ(4, routingInfo->numChunks()); + // Return an empty database (need to return it twice because for missing databases, the + // CatalogClient tries twice) + expectFindOnConfigSendBSONObjVector({}); + expectFindOnConfigSendBSONObjVector({}); + + try { + auto routingInfo = future.timed_get(kFutureTimeout); + auto cm = routingInfo->cm(); + auto primary = routingInfo->primary(); + + FAIL(str::stream() << "Returning no database did not fail and returned " + << (cm ? cm->toString() : routingInfo->primaryId().toString())); + } catch (const DBException& ex) { + ASSERT_EQ(ErrorCodes::NamespaceNotFound, ex.getCode()); + } } TEST_F(ChunkManagerLoadTest, CollectionNotFound) { - auto future = launchAsync([&] { - auto client = serviceContext()->makeClient("Test"); - auto opCtx = client->makeOperationContext(); - return CatalogCache::refreshCollectionRoutingInfo(opCtx.get(), kNss, nullptr); - }); + auto future = scheduleRoutingInfoRefresh(kNss); + + expectGetDatabase(); // Return an empty collection expectFindOnConfigSendBSONObjVector({}); - ASSERT(nullptr == future.timed_get(kFutureTimeout)); + auto routingInfo = future.timed_get(kFutureTimeout); + ASSERT(!routingInfo->cm()); + ASSERT(routingInfo->primary()); + ASSERT_EQ(ShardId{"0"}, routingInfo->primaryId()); } TEST_F(ChunkManagerLoadTest, NoChunksFoundForCollection) { const OID epoch = OID::gen(); const ShardKeyPattern shardKeyPattern(BSON("_id" << 1)); - auto future = launchAsync([&] { - auto client = serviceContext()->makeClient("Test"); - auto opCtx = client->makeOperationContext(); - return CatalogCache::refreshCollectionRoutingInfo(opCtx.get(), kNss, nullptr); - }); + auto future = scheduleRoutingInfoRefresh(kNss); - expectFindOnConfigSendBSONObjVector([&]() { - CollectionType collType; - collType.setNs(kNss); - collType.setEpoch(epoch); - collType.setKeyPattern(shardKeyPattern.toBSON()); - collType.setUnique(false); + expectGetDatabase(); + expectGetCollection(epoch, shardKeyPattern); - return std::vector<BSONObj>{collType.toBSON()}; - }()); + // Return no chunks three times, which is how frequently the catalog cache retries + expectGetCollection(epoch, shardKeyPattern); + expectFindOnConfigSendBSONObjVector({}); + + expectGetCollection(epoch, shardKeyPattern); + expectFindOnConfigSendBSONObjVector({}); - // Return no chunks + expectGetCollection(epoch, shardKeyPattern); expectFindOnConfigSendBSONObjVector({}); try { auto routingInfo = future.timed_get(kFutureTimeout); + auto cm = routingInfo->cm(); + auto primary = routingInfo->primary(); + FAIL(str::stream() << "Returning no chunks for collection did not fail and returned " - << (routingInfo ? routingInfo->toString() : "nullptr")); + << (cm ? cm->toString() : routingInfo->primaryId().toString())); } catch (const DBException& ex) { ASSERT_EQ(ErrorCodes::ConflictingOperationInProgress, ex.getCode()); } } -TEST_F(ChunkManagerLoadTest, ChunkEpochChangeDuringIncrementalLoad) { +TEST_F(ChunkManagerLoadTest, IncompleteChunksFoundForCollection) { + const OID epoch = OID::gen(); const ShardKeyPattern shardKeyPattern(BSON("_id" << 1)); - auto initialRoutingInfo(makeChunkManager(shardKeyPattern, nullptr, true, {})); - ASSERT_EQ(1, initialRoutingInfo->numChunks()); + auto future = scheduleRoutingInfoRefresh(kNss); - auto future = launchAsync([&] { - auto client = serviceContext()->makeClient("Test"); - auto opCtx = client->makeOperationContext(); - return CatalogCache::refreshCollectionRoutingInfo(opCtx.get(), kNss, initialRoutingInfo); - }); + expectGetDatabase(); + expectGetCollection(epoch, shardKeyPattern); - expectFindOnConfigSendBSONObjVector([&]() { - CollectionType collType; - collType.setNs(kNss); - collType.setEpoch(initialRoutingInfo->getVersion().epoch()); - collType.setKeyPattern(shardKeyPattern.toBSON()); - collType.setUnique(false); + const auto incompleteChunks = [&]() { + ChunkVersion version(1, 0, epoch); - return std::vector<BSONObj>{collType.toBSON()}; - }()); + // Chunk from (MinKey, -100) is missing (as if someone is dropping the collection + // concurrently) + version.incMinor(); + + ChunkType chunk2(kNss, {BSON("_id" << -100), BSON("_id" << 0)}, version, {"1"}); + version.incMinor(); + + ChunkType chunk3(kNss, {BSON("_id" << 0), BSON("_id" << 100)}, version, {"0"}); + version.incMinor(); + + ChunkType chunk4(kNss, + {BSON("_id" << 100), shardKeyPattern.getKeyPattern().globalMax()}, + version, + {"1"}); + version.incMinor(); + + return std::vector<BSONObj>{ + chunk2.toConfigBSON(), chunk3.toConfigBSON(), chunk4.toConfigBSON()}; + }(); + + // Return incomplete set of chunks three times, which is how frequently the catalog cache + // retries + expectGetCollection(epoch, shardKeyPattern); + expectFindOnConfigSendBSONObjVector(incompleteChunks); + + expectGetCollection(epoch, shardKeyPattern); + expectFindOnConfigSendBSONObjVector(incompleteChunks); + + expectGetCollection(epoch, shardKeyPattern); + expectFindOnConfigSendBSONObjVector(incompleteChunks); + + try { + auto routingInfo = future.timed_get(kFutureTimeout); + auto cm = routingInfo->cm(); + auto primary = routingInfo->primary(); + + FAIL( + str::stream() << "Returning incomplete chunks for collection did not fail and returned " + << (cm ? cm->toString() : routingInfo->primaryId().toString())); + } catch (const DBException& ex) { + ASSERT_EQ(ErrorCodes::ConflictingOperationInProgress, ex.getCode()); + } +} + +TEST_F(ChunkManagerLoadTest, ChunkEpochChangeDuringIncrementalLoad) { + const ShardKeyPattern shardKeyPattern(BSON("_id" << 1)); + + auto initialRoutingInfo(makeChunkManager(kNss, shardKeyPattern, nullptr, true, {})); + ASSERT_EQ(1, initialRoutingInfo->numChunks()); + + auto future = scheduleRoutingInfoRefresh(kNss); ChunkVersion version = initialRoutingInfo->getVersion(); - // Return set of chunks, one of which has different epoch - expectFindOnConfigSendBSONObjVector([&]() { + const auto inconsistentChunks = [&]() { version.incMajor(); ChunkType chunk1( kNss, {shardKeyPattern.getKeyPattern().globalMin(), BSON("_id" << 0)}, version, {"0"}); @@ -191,12 +277,27 @@ TEST_F(ChunkManagerLoadTest, ChunkEpochChangeDuringIncrementalLoad) { {"0"}); return std::vector<BSONObj>{chunk1.toConfigBSON(), chunk2.toConfigBSON()}; - }()); + }(); + + // Return set of chunks, one of which has different epoch. Do it three times, which is how + // frequently the catalog cache retries. + expectGetCollection(initialRoutingInfo->getVersion().epoch(), shardKeyPattern); + expectFindOnConfigSendBSONObjVector(inconsistentChunks); + + expectGetCollection(initialRoutingInfo->getVersion().epoch(), shardKeyPattern); + expectFindOnConfigSendBSONObjVector(inconsistentChunks); + + expectGetCollection(initialRoutingInfo->getVersion().epoch(), shardKeyPattern); + expectFindOnConfigSendBSONObjVector(inconsistentChunks); try { auto routingInfo = future.timed_get(kFutureTimeout); - FAIL(str::stream() << "Returning chunks with different epoch did not fail and returned " - << (routingInfo ? routingInfo->toString() : "nullptr")); + auto cm = routingInfo->cm(); + auto primary = routingInfo->primary(); + + FAIL(str::stream() + << "Returning chunks with different epoch for collection did not fail and returned " + << (cm ? cm->toString() : routingInfo->primaryId().toString())); } catch (const DBException& ex) { ASSERT_EQ(ErrorCodes::ConflictingOperationInProgress, ex.getCode()); } @@ -205,26 +306,14 @@ TEST_F(ChunkManagerLoadTest, ChunkEpochChangeDuringIncrementalLoad) { TEST_F(ChunkManagerLoadTest, IncrementalLoadAfterSplit) { const ShardKeyPattern shardKeyPattern(BSON("_id" << 1)); - auto initialRoutingInfo(makeChunkManager(shardKeyPattern, nullptr, true, {})); + auto initialRoutingInfo(makeChunkManager(kNss, shardKeyPattern, nullptr, true, {})); ASSERT_EQ(1, initialRoutingInfo->numChunks()); - auto future = launchAsync([&] { - auto client = serviceContext()->makeClient("Test"); - auto opCtx = client->makeOperationContext(); - return CatalogCache::refreshCollectionRoutingInfo(opCtx.get(), kNss, initialRoutingInfo); - }); - ChunkVersion version = initialRoutingInfo->getVersion(); - expectFindOnConfigSendBSONObjVector([&]() { - CollectionType collType; - collType.setNs(kNss); - collType.setEpoch(version.epoch()); - collType.setKeyPattern(shardKeyPattern.toBSON()); - collType.setUnique(false); + auto future = scheduleRoutingInfoRefresh(kNss); - return std::vector<BSONObj>{collType.toBSON()}; - }()); + expectGetCollection(version.epoch(), shardKeyPattern); // Return set of chunks, which represent a split onFindCommand([&](const RemoteCommandRequest& request) { @@ -247,39 +336,31 @@ TEST_F(ChunkManagerLoadTest, IncrementalLoadAfterSplit) { return std::vector<BSONObj>{chunk1.toConfigBSON(), chunk2.toConfigBSON()}; }); - auto newRoutingInfo(future.timed_get(kFutureTimeout)); - ASSERT_EQ(2, newRoutingInfo->numChunks()); - ASSERT_EQ(version, newRoutingInfo->getVersion()); - ASSERT_EQ(version, newRoutingInfo->getVersion({"0"})); - ASSERT_EQ(ChunkVersion(0, 0, version.epoch()), newRoutingInfo->getVersion({"1"})); + auto routingInfo = future.timed_get(kFutureTimeout); + ASSERT(routingInfo->cm()); + auto cm = routingInfo->cm(); + + ASSERT_EQ(2, cm->numChunks()); + ASSERT_EQ(version, cm->getVersion()); + ASSERT_EQ(version, cm->getVersion({"0"})); + ASSERT_EQ(ChunkVersion(0, 0, version.epoch()), cm->getVersion({"1"})); } TEST_F(ChunkManagerLoadTest, IncrementalLoadAfterMove) { const ShardKeyPattern shardKeyPattern(BSON("_id" << 1)); - auto initialRoutingInfo(makeChunkManager(shardKeyPattern, nullptr, true, {BSON("_id" << 0)})); + auto initialRoutingInfo( + makeChunkManager(kNss, shardKeyPattern, nullptr, true, {BSON("_id" << 0)})); ASSERT_EQ(2, initialRoutingInfo->numChunks()); - auto future = launchAsync([&] { - auto client = serviceContext()->makeClient("Test"); - auto opCtx = client->makeOperationContext(); - return CatalogCache::refreshCollectionRoutingInfo(opCtx.get(), kNss, initialRoutingInfo); - }); - ChunkVersion version = initialRoutingInfo->getVersion(); - expectFindOnConfigSendBSONObjVector([&]() { - CollectionType collType; - collType.setNs(kNss); - collType.setEpoch(version.epoch()); - collType.setKeyPattern(shardKeyPattern.toBSON()); - collType.setUnique(false); - - return std::vector<BSONObj>{collType.toBSON()}; - }()); + auto future = scheduleRoutingInfoRefresh(kNss); ChunkVersion expectedDestShardVersion; + expectGetCollection(version.epoch(), shardKeyPattern); + // Return set of chunks, which represent a move expectFindOnConfigSendBSONObjVector([&]() { version.incMajor(); @@ -294,36 +375,27 @@ TEST_F(ChunkManagerLoadTest, IncrementalLoadAfterMove) { return std::vector<BSONObj>{chunk1.toConfigBSON(), chunk2.toConfigBSON()}; }()); - auto newRoutingInfo(future.timed_get(kFutureTimeout)); - ASSERT_EQ(2, newRoutingInfo->numChunks()); - ASSERT_EQ(version, newRoutingInfo->getVersion()); - ASSERT_EQ(version, newRoutingInfo->getVersion({"0"})); - ASSERT_EQ(expectedDestShardVersion, newRoutingInfo->getVersion({"1"})); + auto routingInfo = future.timed_get(kFutureTimeout); + ASSERT(routingInfo->cm()); + auto cm = routingInfo->cm(); + + ASSERT_EQ(2, cm->numChunks()); + ASSERT_EQ(version, cm->getVersion()); + ASSERT_EQ(version, cm->getVersion({"0"})); + ASSERT_EQ(expectedDestShardVersion, cm->getVersion({"1"})); } TEST_F(ChunkManagerLoadTest, IncrementalLoadAfterMoveLastChunk) { const ShardKeyPattern shardKeyPattern(BSON("_id" << 1)); - auto initialRoutingInfo(makeChunkManager(shardKeyPattern, nullptr, true, {})); + auto initialRoutingInfo(makeChunkManager(kNss, shardKeyPattern, nullptr, true, {})); ASSERT_EQ(1, initialRoutingInfo->numChunks()); - auto future = launchAsync([&] { - auto client = serviceContext()->makeClient("Test"); - auto opCtx = client->makeOperationContext(); - return CatalogCache::refreshCollectionRoutingInfo(opCtx.get(), kNss, initialRoutingInfo); - }); - ChunkVersion version = initialRoutingInfo->getVersion(); - expectFindOnConfigSendBSONObjVector([&]() { - CollectionType collType; - collType.setNs(kNss); - collType.setEpoch(version.epoch()); - collType.setKeyPattern(shardKeyPattern.toBSON()); - collType.setUnique(false); + auto future = scheduleRoutingInfoRefresh(kNss); - return std::vector<BSONObj>{collType.toBSON()}; - }()); + expectGetCollection(version.epoch(), shardKeyPattern); // Return set of chunks, which represent a move expectFindOnConfigSendBSONObjVector([&]() { @@ -349,11 +421,14 @@ TEST_F(ChunkManagerLoadTest, IncrementalLoadAfterMoveLastChunk) { return std::vector<BSONObj>{shard1.toBSON(), shard2.toBSON()}; }()); - auto newRoutingInfo(future.timed_get(kFutureTimeout)); - ASSERT_EQ(1, newRoutingInfo->numChunks()); - ASSERT_EQ(version, newRoutingInfo->getVersion()); - ASSERT_EQ(ChunkVersion(0, 0, version.epoch()), newRoutingInfo->getVersion({"0"})); - ASSERT_EQ(version, newRoutingInfo->getVersion({"1"})); + auto routingInfo = future.timed_get(kFutureTimeout); + ASSERT(routingInfo->cm()); + auto cm = routingInfo->cm(); + + ASSERT_EQ(1, cm->numChunks()); + ASSERT_EQ(version, cm->getVersion()); + ASSERT_EQ(ChunkVersion(0, 0, version.epoch()), cm->getVersion({"0"})); + ASSERT_EQ(version, cm->getVersion({"1"})); } } // namespace |