diff options
Diffstat (limited to 'src/mongo/db')
-rw-r--r-- | src/mongo/db/s/collection_metadata.cpp | 33 | ||||
-rw-r--r-- | src/mongo/db/s/collection_metadata.h | 6 | ||||
-rw-r--r-- | src/mongo/db/s/collection_metadata_test.cpp | 60 | ||||
-rw-r--r-- | src/mongo/db/s/resharding/resharding_donor_service.cpp | 57 | ||||
-rw-r--r-- | src/mongo/db/s/resharding/resharding_donor_service.h | 4 | ||||
-rw-r--r-- | src/mongo/db/s/scoped_collection_metadata.h | 6 |
6 files changed, 115 insertions, 51 deletions
diff --git a/src/mongo/db/s/collection_metadata.cpp b/src/mongo/db/s/collection_metadata.cpp index d72b013b9ac..43233ae94ca 100644 --- a/src/mongo/db/s/collection_metadata.cpp +++ b/src/mongo/db/s/collection_metadata.cpp @@ -33,6 +33,8 @@ #include "mongo/db/s/collection_metadata.h" +#include <fmt/format.h> + #include "mongo/bson/simple_bsonobj_comparator.h" #include "mongo/bson/util/builder.h" #include "mongo/db/bson/dotted_path_support.h" @@ -41,6 +43,8 @@ namespace mongo { +using namespace fmt::literals; + CollectionMetadata::CollectionMetadata(ChunkManager cm, const ShardId& thisShardId) : _cm(std::move(cm)), _thisShardId(thisShardId) {} @@ -90,8 +94,8 @@ boost::optional<ShardKeyPattern> CollectionMetadata::getReshardingKeyIfShouldFor return ShardKeyPattern(donorFields->getReshardingKey()); } -bool CollectionMetadata::writesShouldRunInDistributedTransaction(const OID& originalEpoch, - const OID& reshardingEpoch) const { +bool CollectionMetadata::writesShouldRunInDistributedTransaction(const UUID& originalUUID, + const UUID& reshardingUUID) const { auto reshardingFields = getReshardingFields(); if (!reshardingFields) return false; @@ -114,23 +118,20 @@ bool CollectionMetadata::writesShouldRunInDistributedTransaction(const OID& orig } // Handle kRenaming: - auto chunkVersion = getCollVersion(); - uassert(ErrorCodes::NamespaceNotSharded, - str::stream() << "Collection is not sharded, original epoch: " << originalEpoch, - chunkVersion != ChunkVersion::UNSHARDED()); + auto currentCollectionUUID = *_cm->getUUID(); - const auto& collectionCurrentEpoch = chunkVersion.epoch(); - - // Renaming has not completed: - if (collectionCurrentEpoch == originalEpoch) + // Renaming has not completed + if (currentCollectionUUID == originalUUID) { return true; + } - // Else, renaming must have completed, and myEpoch must be equal to reshardingEpoch. - uassert(5169400, - str::stream() << "Invalid epoch; current epoch " << collectionCurrentEpoch - << " does not match original epoch " << originalEpoch - << " or resharding epoch " << reshardingEpoch, - collectionCurrentEpoch == reshardingEpoch); + // Else, renaming must have completed, and the new UUID must be equal to the resharding UUID. + uassert(ErrorCodes::InvalidUUID, + "Expected collection to have either the original UUID {} or the resharding UUID {}, " + "but the collection instead has UUID {}"_format(originalUUID.toString(), + reshardingUUID.toString(), + currentCollectionUUID.toString()), + currentCollectionUUID == reshardingUUID); return false; } diff --git a/src/mongo/db/s/collection_metadata.h b/src/mongo/db/s/collection_metadata.h index a0b7d5cc6c7..e225dd570cd 100644 --- a/src/mongo/db/s/collection_metadata.h +++ b/src/mongo/db/s/collection_metadata.h @@ -80,10 +80,10 @@ public: /** * Writes should run in distributed transactions when * 1. The coordinator is between the mirroring and committed states, OR - * 2. The coordinator is in the renaming state, but the epoch is still the original epoch. + * 2. The coordinator is in the renaming state, but the UUID is still the original UUID. */ - bool writesShouldRunInDistributedTransaction(const OID& originalEpoch, - const OID& reshardingEpoch) const; + bool writesShouldRunInDistributedTransaction(const UUID& originalUUID, + const UUID& reshardingUUID) const; /** * Returns the current shard version for the collection or UNSHARDED if it is not sharded. diff --git a/src/mongo/db/s/collection_metadata_test.cpp b/src/mongo/db/s/collection_metadata_test.cpp index fe0935fe41d..c42989221ef 100644 --- a/src/mongo/db/s/collection_metadata_test.cpp +++ b/src/mongo/db/s/collection_metadata_test.cpp @@ -46,6 +46,7 @@ CollectionMetadata makeCollectionMetadataImpl( const KeyPattern& shardKeyPattern, const std::vector<std::pair<BSONObj, BSONObj>>& thisShardsChunks, bool staleChunkManager, + UUID uuid = UUID::gen(), CoordinatorStateEnum state = CoordinatorStateEnum::kInitializing) { const OID epoch = OID::gen(); @@ -85,10 +86,10 @@ CollectionMetadata makeCollectionMetadataImpl( return CollectionMetadata( ChunkManager(kThisShard, - DatabaseVersion(UUID::gen()), + DatabaseVersion(uuid), ShardingTestFixtureCommon::makeStandaloneRoutingTableHistory( RoutingTableHistory::makeNew(kNss, - UUID::gen(), + uuid, shardKeyPattern, nullptr, false, @@ -108,8 +109,9 @@ struct ConstructedRangeMap : public RangeMap { class NoChunkFixture : public unittest::Test { protected: CollectionMetadata makeCollectionMetadata( + UUID uuid = UUID::gen(), CoordinatorStateEnum state = CoordinatorStateEnum::kInitializing) const { - return makeCollectionMetadataImpl(KeyPattern(BSON("a" << 1)), {}, false, state); + return makeCollectionMetadataImpl(KeyPattern(BSON("a" << 1)), {}, false, uuid, state); } }; @@ -180,28 +182,44 @@ TEST_F(NoChunkFixture, OrphanedDataRangeEnd) { ASSERT(!metadata.getNextOrphanRange(pending, metadata.getMaxKey())); } +TEST_F(NoChunkFixture, WritesShouldRunInDistributedTxnRenamingOrigUUID) { + UUID originalUUID = UUID::gen(); + UUID reshardingUUID = UUID::gen(); + + // kRenaming is the only state where UUIDs will be compared. + auto metadata(makeCollectionMetadata(originalUUID, CoordinatorStateEnum::kRenaming)); + + // Writes should run in a distributed txn if the collection metadata's UUID matches + // the original collection's UUID. + ASSERT(metadata.writesShouldRunInDistributedTransaction(originalUUID, reshardingUUID)); +} + +TEST_F(NoChunkFixture, WritesShouldRunInDistributedTxnRenamingCheckReshardingUUID) { + UUID originalUUID = UUID::gen(); + UUID reshardingUUID = UUID::gen(); + + // kRenaming is the only state where UUIDs will be compared. + auto metadata(makeCollectionMetadata(reshardingUUID, CoordinatorStateEnum::kRenaming)); + + // Writes should NOT run in a distributed txn when the UUID matches the temp collection's + // UUID. + ASSERT(!metadata.writesShouldRunInDistributedTransaction(originalUUID, reshardingUUID)); +} + TEST_F(NoChunkFixture, WritesShouldRunInDistributedTxnRenamingCheck) { - auto metadata(makeCollectionMetadata(CoordinatorStateEnum::kRenaming)); - // We are in kRenaming by default. - OID originalEpoch = OID::gen(); - OID reshardingEpoch = OID::gen(); - - // Writes should run in a distributed txn if the collection metadata's epoch matches - // the original collection's epoch. - ASSERT(metadata.writesShouldRunInDistributedTransaction(metadata.getCollVersion().epoch(), - reshardingEpoch)); - - // Writes should NOT run in a distributed txn when the epoch matches the temp collection's - // epoch. - ASSERT(!metadata.writesShouldRunInDistributedTransaction(originalEpoch, - metadata.getCollVersion().epoch())); - - // If the collection's epoch matches neither the original epoch nor the resharding epoch, + UUID originalUUID = UUID::gen(); + UUID reshardingUUID = UUID::gen(); + UUID rogueUUID = UUID::gen(); + + // kRenaming is the only state where UUIDs will be compared. + auto metadata(makeCollectionMetadata(rogueUUID, CoordinatorStateEnum::kRenaming)); + + // If the collection's UUID matches neither the original UUID nor the resharding UUID, // expect a throw. ASSERT_THROWS_CODE( - metadata.writesShouldRunInDistributedTransaction(originalEpoch, reshardingEpoch), + metadata.writesShouldRunInDistributedTransaction(originalUUID, reshardingUUID), AssertionException, - 5169400); + ErrorCodes::InvalidUUID); } /** diff --git a/src/mongo/db/s/resharding/resharding_donor_service.cpp b/src/mongo/db/s/resharding/resharding_donor_service.cpp index 62e7ecfd334..c2bc7ed409b 100644 --- a/src/mongo/db/s/resharding/resharding_donor_service.cpp +++ b/src/mongo/db/s/resharding/resharding_donor_service.cpp @@ -31,6 +31,9 @@ #include "mongo/db/s/resharding/resharding_donor_service.h" +#include <fmt/format.h> + +#include "mongo/db/catalog/drop_collection.h" #include "mongo/db/catalog_raii.h" #include "mongo/db/concurrency/write_conflict_exception.h" #include "mongo/db/dbdirectclient.h" @@ -45,19 +48,27 @@ #include "mongo/s/grid.h" namespace mongo { + +using namespace fmt::literals; + namespace { -void refreshTemporaryReshardingCollection(const ReshardingDonorDocument& donorDoc) { +ChunkManager getShardedCollectionRoutingInfoWithRefreshAndFlush(const NamespaceString& nss) { auto opCtx = cc().makeOperationContext(); + auto swRoutingInfo = Grid::get(opCtx.get()) + ->catalogCache() + ->getShardedCollectionRoutingInfoWithRefresh(opCtx.get(), nss); + auto routingInfo = uassertStatusOK(swRoutingInfo); + + CatalogCacheLoader::get(opCtx.get()).waitForCollectionFlush(opCtx.get(), nss); + + return routingInfo; +} + +void refreshTemporaryReshardingCollection(const ReshardingDonorDocument& donorDoc) { auto tempNss = constructTemporaryReshardingNss(donorDoc.getNss().db(), donorDoc.getExistingUUID()); - - auto tempNssRoutingInfo = - Grid::get(opCtx.get()) - ->catalogCache() - ->getShardedCollectionRoutingInfoWithRefresh(opCtx.get(), tempNss); - uassertStatusOK(tempNssRoutingInfo); - CatalogCacheLoader::get(opCtx.get()).waitForCollectionFlush(opCtx.get(), tempNss); + std::ignore = getShardedCollectionRoutingInfoWithRefreshAndFlush(tempNss); } Timestamp generateMinFetchTimestamp(const ReshardingDonorDocument& donorDoc) { @@ -269,9 +280,39 @@ void ReshardingDonorService::DonorStateMachine::_dropOriginalCollectionThenDelet return; } + auto origNssRoutingInfo = + getShardedCollectionRoutingInfoWithRefreshAndFlush(_donorDoc.getNss()); + auto currentCollectionUUID = + getCollectionUUIDFromChunkManger(_donorDoc.getNss(), origNssRoutingInfo); + + if (currentCollectionUUID == _donorDoc.getExistingUUID()) { + _dropOriginalCollection(); + } else { + uassert(ErrorCodes::InvalidUUID, + "Expected collection {} to have either the original UUID {} or the resharding UUID" + " {}, but the collection instead has UUID {}"_format( + _donorDoc.getNss().toString(), + _donorDoc.getExistingUUID().toString(), + _donorDoc.get_id().toString(), + currentCollectionUUID.toString()), + currentCollectionUUID == _donorDoc.get_id()); + } + _transitionStateAndUpdateCoordinator(DonorStateEnum::kDone); } +void ReshardingDonorService::DonorStateMachine::_dropOriginalCollection() { + DBDirectClient client(cc().makeOperationContext().get()); + BSONObj dropResult; + if (!client.dropCollection( + _donorDoc.getNss().toString(), WriteConcerns::kMajorityWriteConcern, &dropResult)) { + auto dropStatus = getStatusFromCommandResult(dropResult); + if (dropStatus != ErrorCodes::NamespaceNotFound) { + uassertStatusOK(dropStatus); + } + } +} + void ReshardingDonorService::DonorStateMachine::_transitionState( DonorStateEnum endState, boost::optional<Timestamp> minFetchTimestamp) { ReshardingDonorDocument replacementDoc(_donorDoc); diff --git a/src/mongo/db/s/resharding/resharding_donor_service.h b/src/mongo/db/s/resharding/resharding_donor_service.h index b1b837f5381..0a0d39c13cd 100644 --- a/src/mongo/db/s/resharding/resharding_donor_service.h +++ b/src/mongo/db/s/resharding/resharding_donor_service.h @@ -117,6 +117,10 @@ private: void _dropOriginalCollectionThenDeleteLocalState(); + // Drops the original collection and throws if the returned status is not either Status::OK() + // or NamespaceNotFound. + void _dropOriginalCollection(); + // Transitions the state on-disk and in-memory to 'endState'. void _transitionState(DonorStateEnum endState, boost::optional<Timestamp> minFetchTimestamp = boost::none); diff --git a/src/mongo/db/s/scoped_collection_metadata.h b/src/mongo/db/s/scoped_collection_metadata.h index 40cef70599e..55252ba6bf9 100644 --- a/src/mongo/db/s/scoped_collection_metadata.h +++ b/src/mongo/db/s/scoped_collection_metadata.h @@ -57,9 +57,9 @@ public: return _impl->get().isSharded(); } - bool writesShouldRunInDistributedTransaction(const OID& originalEpoch, - const OID& reshardingEpoch) const { - return _impl->get().writesShouldRunInDistributedTransaction(originalEpoch, reshardingEpoch); + bool writesShouldRunInDistributedTransaction(const UUID& originalUUID, + const UUID& reshardingUUID) const { + return _impl->get().writesShouldRunInDistributedTransaction(originalUUID, reshardingUUID); } bool isValidKey(const BSONObj& key) const { |