Skip to content

Commit 71fd21e

Browse files
jsmulrowevergreen
authored andcommitted
SERVER-42299 Batch chunk and tag upgrade/downgrade modification transactions
1 parent 9867626 commit 71fd21e

File tree

6 files changed

+191
-172
lines changed

6 files changed

+191
-172
lines changed

jstests/multiVersion/config_chunks_tags_set_fcv.js

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,5 +138,36 @@ function runInProgressSetFCVTest(st, {initialFCV, desiredFCV}) {
138138
runInProgressSetFCVTest(st, {initialFCV: latestFCV, desiredFCV: lastStableFCV});
139139
runInProgressSetFCVTest(st, {initialFCV: lastStableFCV, desiredFCV: latestFCV});
140140

141+
//
142+
// Test setFCV with many chunks and tags.
143+
//
144+
145+
// Set up collections with the same number of chunks and zones as the batch limit for the
146+
// transactions used to modify chunks and zones documents and with more than the limit to verify the
147+
// batching logic in both cases.
148+
const txnBatchSize = 100;
149+
setUpCollectionWithManyChunksAndZones(
150+
st, dbName + ".many_at_batch_size", txnBatchSize /* numChunks */, txnBatchSize /* numZones */);
151+
setUpCollectionWithManyChunksAndZones(st,
152+
dbName + ".many_over_batch_size",
153+
txnBatchSize + 5 /* numChunks */,
154+
txnBatchSize + 5 /* numZones */);
155+
156+
checkFCV(configPrimary.getDB("admin"), latestFCV);
157+
158+
verifyChunks(st, {expectNewFormat: true});
159+
160+
jsTestLog("Downgrading FCV to last stable with many chunks and zones");
161+
assert.commandWorked(st.s.adminCommand({setFeatureCompatibilityVersion: lastStableFCV}));
162+
checkFCV(configPrimary.getDB("admin"), lastStableFCV);
163+
164+
verifyChunks(st, {expectNewFormat: false});
165+
166+
jsTestLog("Upgrading FCV to latest with many chunks and zones");
167+
assert.commandWorked(st.s.adminCommand({setFeatureCompatibilityVersion: latestFCV}));
168+
checkFCV(configPrimary.getDB("admin"), latestFCV);
169+
170+
verifyChunks(st, {expectNewFormat: true});
171+
141172
st.stop();
142173
}());

jstests/multiVersion/config_chunks_tags_upgrade_downgrade_cluster.js

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,17 @@ setUpCollectionForZoneTesting(st, zoneNs);
5353
// for every sharded collection.
5454
setUpExtraShardedCollections(st, "extra_db" /* dbName */);
5555

56+
// Set up collections with the same number of chunks and zones as the batch limit for the
57+
// transactions used to modify chunks and zones documents and with more than the limit to verify the
58+
// batching logic in both cases.
59+
const txnBatchSize = 100;
60+
setUpCollectionWithManyChunksAndZones(
61+
st, dbName + ".many_at_batch_size", txnBatchSize /* numChunks */, txnBatchSize /* numZones */);
62+
setUpCollectionWithManyChunksAndZones(st,
63+
dbName + ".many_over_batch_size",
64+
txnBatchSize + 5 /* numChunks */,
65+
txnBatchSize + 5 /* numZones */);
66+
5667
//
5768
// Upgrade back to the latest version.
5869
//

jstests/multiVersion/libs/config_chunks_tags_shared.js

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,22 @@ function setUpCollectionForZoneTesting(st, ns) {
2424
st.s.adminCommand({updateZoneKeyRange: ns, min: {_id: 0}, max: {_id: 50}, zone: "zone1"}));
2525
}
2626

27+
// Sets up a sharded collection with the given number of chunks and zones.
28+
function setUpCollectionWithManyChunksAndZones(st, ns, numChunks, numZones) {
29+
assert.commandWorked(st.s.adminCommand({shardCollection: ns, key: {_id: 1}}));
30+
31+
for (let i = 0; i < numChunks - 1; i++) {
32+
assert.commandWorked(st.s.adminCommand({split: ns, middle: {_id: i}}));
33+
}
34+
35+
for (let i = 0; i < numZones; i++) {
36+
assert.commandWorked(
37+
st.s.adminCommand({addShardToZone: st.shard0.shardName, zone: "many_zones-" + i}));
38+
assert.commandWorked(st.s.adminCommand(
39+
{updateZoneKeyRange: ns, min: {_id: i}, max: {_id: i + 1}, zone: "many_zones-" + i}));
40+
}
41+
}
42+
2743
function setUpExtraShardedCollections(st, dbName) {
2844
assert.commandWorked(st.s.adminCommand({enableSharding: dbName}));
2945
st.ensurePrimaryShard(dbName, st.shard1.shardName);

src/mongo/db/commands/set_feature_compatibility_version_command.cpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -199,7 +199,8 @@ class SetFeatureCompatibilityVersionCommand : public BasicCommand {
199199
log() << "Hit pauseBeforeUpgradingConfigMetadata";
200200
pauseBeforeUpgradingConfigMetadata.pauseWhileSet(opCtx);
201201
}
202-
ShardingCatalogManager::get(opCtx)->upgradeChunksAndTags(opCtx);
202+
ShardingCatalogManager::get(opCtx)->upgradeOrDowngradeChunksAndTags(
203+
opCtx, ShardingCatalogManager::ConfigUpgradeType::kUpgrade);
203204
}
204205

205206
FeatureCompatibilityVersion::unsetTargetUpgradeOrDowngrade(opCtx, requestedVersion);
@@ -255,7 +256,8 @@ class SetFeatureCompatibilityVersionCommand : public BasicCommand {
255256
log() << "Hit pauseBeforeDowngradingConfigMetadata";
256257
pauseBeforeDowngradingConfigMetadata.pauseWhileSet(opCtx);
257258
}
258-
ShardingCatalogManager::get(opCtx)->downgradeChunksAndTags(opCtx);
259+
ShardingCatalogManager::get(opCtx)->upgradeOrDowngradeChunksAndTags(
260+
opCtx, ShardingCatalogManager::ConfigUpgradeType::kDowngrade);
259261
}
260262

261263
FeatureCompatibilityVersion::unsetTargetUpgradeOrDowngrade(opCtx, requestedVersion);

src/mongo/db/s/config/sharding_catalog_manager.cpp

Lines changed: 118 additions & 153 deletions
Original file line numberDiff line numberDiff line change
@@ -44,11 +44,13 @@
4444
#include "mongo/s/catalog/type_chunk.h"
4545
#include "mongo/s/catalog/type_collection.h"
4646
#include "mongo/s/catalog/type_config_version.h"
47+
#include "mongo/s/catalog/type_database.h"
4748
#include "mongo/s/catalog/type_lockpings.h"
4849
#include "mongo/s/catalog/type_locks.h"
4950
#include "mongo/s/catalog/type_shard.h"
5051
#include "mongo/s/catalog/type_tags.h"
5152
#include "mongo/s/client/shard_registry.h"
53+
#include "mongo/s/database_version_gen.h"
5254
#include "mongo/s/grid.h"
5355
#include "mongo/s/write_ops/batched_command_request.h"
5456
#include "mongo/s/write_ops/batched_command_response.h"
@@ -405,171 +407,134 @@ Lock::ExclusiveLock ShardingCatalogManager::lockZoneMutex(OperationContext* opCt
405407
return lk;
406408
}
407409

408-
void ShardingCatalogManager::upgradeChunksAndTags(OperationContext* opCtx) {
409-
// Upgrade each chunk document by deleting and re-inserting with the 4.4 _id format.
410-
{
411-
Lock::ExclusiveLock lk(opCtx->lockState(), _kChunkOpLock);
412-
413-
auto const configShard = Grid::get(opCtx)->shardRegistry()->getConfigShard();
414-
auto findResponse = uassertStatusOK(
415-
configShard->exhaustiveFindOnConfig(opCtx,
416-
ReadPreferenceSetting{ReadPreference::PrimaryOnly},
417-
repl::ReadConcernLevel::kLocalReadConcern,
418-
ChunkType::ConfigNS,
419-
{},
420-
{},
421-
boost::none /* limit */));
422-
423-
AlternativeSessionRegion asr(opCtx);
424-
AuthorizationSession::get(asr.opCtx()->getClient())
425-
->grantInternalAuthorization(asr.opCtx()->getClient());
426-
TxnNumber txnNumber = 0;
427-
for (const auto& chunkObj : findResponse.docs) {
428-
auto chunk = uassertStatusOK(ChunkType::fromConfigBSON(chunkObj));
429-
430-
removeDocumentsInLocalTxn(
431-
asr.opCtx(),
432-
ChunkType::ConfigNS,
433-
BSON(ChunkType::ns(chunk.getNS().ns()) << ChunkType::min(chunk.getMin())),
434-
true /* startTransaction */,
435-
txnNumber);
436-
437-
// Note that ChunkType::toConfigBSON() will not include an _id if one hasn't been set,
438-
// which will be the case for chunks written in the 4.2 format because parsing ignores
439-
// _ids in the 4.2 format, so the insert path will generate one for us.
440-
insertDocumentsInLocalTxn(asr.opCtx(),
441-
ChunkType::ConfigNS,
442-
{chunk.toConfigBSON()},
443-
false /* startTransaction */,
444-
txnNumber);
445-
446-
commitLocalTxn(asr.opCtx(), txnNumber);
410+
// TODO SERVER-44034: Remove this function.
411+
void deleteAndInsertChunk(OperationContext* opCtx,
412+
const BSONObj& chunkDoc,
413+
bool startTransaction,
414+
TxnNumber txnNumber,
415+
ShardingCatalogManager::ConfigUpgradeType upgradeType) {
416+
auto chunk = uassertStatusOK(ChunkType::fromConfigBSON(chunkDoc));
447417

448-
txnNumber += 1;
449-
}
450-
}
418+
removeDocumentsInLocalTxn(
419+
opCtx,
420+
ChunkType::ConfigNS,
421+
BSON(ChunkType::ns(chunk.getNS().ns()) << ChunkType::min(chunk.getMin())),
422+
startTransaction,
423+
txnNumber);
451424

452-
// Upgrade each tag document by deleting and re-inserting with the 4.4 _id format.
453-
{
454-
Lock::ExclusiveLock lk(opCtx->lockState(), _kZoneOpLock);
455-
456-
auto const configShard = Grid::get(opCtx)->shardRegistry()->getConfigShard();
457-
auto findResponse = uassertStatusOK(
458-
configShard->exhaustiveFindOnConfig(opCtx,
459-
ReadPreferenceSetting{ReadPreference::PrimaryOnly},
460-
repl::ReadConcernLevel::kLocalReadConcern,
461-
TagsType::ConfigNS,
462-
{},
463-
{},
464-
boost::none /* limit */));
465-
466-
AlternativeSessionRegion asr(opCtx);
467-
AuthorizationSession::get(asr.opCtx()->getClient())
468-
->grantInternalAuthorization(asr.opCtx()->getClient());
469-
TxnNumber txnNumber = 0;
470-
for (const auto& tagObj : findResponse.docs) {
471-
auto tag = uassertStatusOK(TagsType::fromBSON(tagObj));
472-
473-
removeDocumentsInLocalTxn(
474-
asr.opCtx(),
475-
TagsType::ConfigNS,
476-
BSON(TagsType::ns(tag.getNS().ns()) << TagsType::min(tag.getMinKey())),
477-
true /* startTransaction */,
478-
txnNumber);
479-
480-
// Note that TagsType::toBSON() will not include an _id, so the insert path will
481-
// generate one for us.
482-
insertDocumentsInLocalTxn(asr.opCtx(),
483-
TagsType::ConfigNS,
484-
{tag.toBSON()},
485-
false /* startTransaction */,
486-
txnNumber);
425+
insertDocumentsInLocalTxn(
426+
opCtx,
427+
ChunkType::ConfigNS,
428+
{upgradeType == ShardingCatalogManager::ConfigUpgradeType::kUpgrade
429+
// Note that ChunkType::toConfigBSON() will not include an _id if one hasn't been set,
430+
// which will be the case for chunks written in the 4.2 format because parsing ignores
431+
// _ids in the 4.2 format, so the insert path will generate one for us.
432+
? chunk.toConfigBSON()
433+
: chunk.toConfigBSONLegacyID()},
434+
false /* startTransaction */,
435+
txnNumber);
436+
}
487437

488-
commitLocalTxn(asr.opCtx(), txnNumber);
438+
// TODO SERVER-44034: Remove this function.
439+
void deleteAndInsertTag(OperationContext* opCtx,
440+
const BSONObj& tagDoc,
441+
bool startTransaction,
442+
TxnNumber txnNumber,
443+
ShardingCatalogManager::ConfigUpgradeType upgradeType) {
444+
auto tag = uassertStatusOK(TagsType::fromBSON(tagDoc));
489445

490-
txnNumber += 1;
491-
}
492-
}
446+
removeDocumentsInLocalTxn(
447+
opCtx,
448+
TagsType::ConfigNS,
449+
BSON(TagsType::ns(tag.getNS().ns()) << TagsType::min(tag.getMinKey())),
450+
startTransaction,
451+
txnNumber);
452+
453+
insertDocumentsInLocalTxn(opCtx,
454+
TagsType::ConfigNS,
455+
{upgradeType == ShardingCatalogManager::ConfigUpgradeType::kUpgrade
456+
// Note that TagsType::toBSON() will not include an _id, so the
457+
// insert path will generate one for us.
458+
? tag.toBSON()
459+
: tag.toBSONLegacyID()},
460+
false /* startTransaction */,
461+
txnNumber);
493462
}
494463

495-
void ShardingCatalogManager::downgradeChunksAndTags(OperationContext* opCtx) {
496-
// Downgrade each chunk document by deleting and re-inserting with the 4.2 _id format.
497-
{
498-
Lock::ExclusiveLock lk(opCtx->lockState(), _kChunkOpLock);
499-
500-
auto const configShard = Grid::get(opCtx)->shardRegistry()->getConfigShard();
501-
auto findResponse = uassertStatusOK(
502-
configShard->exhaustiveFindOnConfig(opCtx,
503-
ReadPreferenceSetting{ReadPreference::PrimaryOnly},
504-
repl::ReadConcernLevel::kLocalReadConcern,
505-
ChunkType::ConfigNS,
506-
{},
507-
{},
508-
boost::none /* limit */));
509-
510-
AlternativeSessionRegion asr(opCtx);
511-
AuthorizationSession::get(asr.opCtx()->getClient())
512-
->grantInternalAuthorization(asr.opCtx()->getClient());
513-
TxnNumber txnNumber = 0;
514-
for (const auto& chunkObj : findResponse.docs) {
515-
auto chunk = uassertStatusOK(ChunkType::fromConfigBSON(chunkObj));
516-
517-
removeDocumentsInLocalTxn(
518-
asr.opCtx(),
519-
ChunkType::ConfigNS,
520-
BSON(ChunkType::ns(chunk.getNS().ns()) << ChunkType::min(chunk.getMin())),
521-
true /* startTransaction */,
522-
txnNumber);
523-
524-
insertDocumentsInLocalTxn(asr.opCtx(),
525-
ChunkType::ConfigNS,
526-
{chunk.toConfigBSONLegacyID()},
527-
false /* startTransaction */,
528-
txnNumber);
529-
464+
// TODO SERVER-44034: Remove this function and type.
465+
using ConfigDocModFunction = std::function<void(
466+
OperationContext*, BSONObj, bool, TxnNumber, ShardingCatalogManager::ConfigUpgradeType)>;
467+
void forEachConfigDocInBatchedTransactions(OperationContext* opCtx,
468+
const NamespaceString& configNss,
469+
const NamespaceString& shardedCollNss,
470+
ConfigDocModFunction configDocModFn,
471+
ShardingCatalogManager::ConfigUpgradeType upgradeType) {
472+
const auto configShard = Grid::get(opCtx)->shardRegistry()->getConfigShard();
473+
auto findResponse = uassertStatusOK(
474+
configShard->exhaustiveFindOnConfig(opCtx,
475+
ReadPreferenceSetting{ReadPreference::PrimaryOnly},
476+
repl::ReadConcernLevel::kLocalReadConcern,
477+
configNss,
478+
BSON("ns" << shardedCollNss.ns()),
479+
{},
480+
boost::none /* limit */));
481+
482+
AlternativeSessionRegion asr(opCtx);
483+
AuthorizationSession::get(asr.opCtx()->getClient())
484+
->grantInternalAuthorization(asr.opCtx()->getClient());
485+
TxnNumber txnNumber = 0;
486+
487+
const auto batchSizeLimit = 100;
488+
auto currentBatchSize = 0;
489+
for (const auto& doc : findResponse.docs) {
490+
auto startTransaction = currentBatchSize == 0;
491+
492+
configDocModFn(asr.opCtx(), doc, startTransaction, txnNumber, upgradeType);
493+
494+
currentBatchSize += 1;
495+
if (currentBatchSize == batchSizeLimit) {
530496
commitLocalTxn(asr.opCtx(), txnNumber);
531-
532497
txnNumber += 1;
498+
currentBatchSize = 0;
533499
}
534500
}
535501

536-
// Downgrade each tag document by deleting and re-inserting with the 4.2 _id format.
537-
{
538-
Lock::ExclusiveLock lk(opCtx->lockState(), _kZoneOpLock);
539-
540-
auto const configShard = Grid::get(opCtx)->shardRegistry()->getConfigShard();
541-
auto findResponse = uassertStatusOK(
542-
configShard->exhaustiveFindOnConfig(opCtx,
543-
ReadPreferenceSetting{ReadPreference::PrimaryOnly},
544-
repl::ReadConcernLevel::kLocalReadConcern,
545-
TagsType::ConfigNS,
546-
{},
547-
{},
548-
boost::none /* limit */));
549-
550-
AlternativeSessionRegion asr(opCtx);
551-
AuthorizationSession::get(asr.opCtx()->getClient())
552-
->grantInternalAuthorization(asr.opCtx()->getClient());
553-
TxnNumber txnNumber = 0;
554-
for (const auto& tagObj : findResponse.docs) {
555-
auto tag = uassertStatusOK(TagsType::fromBSON(tagObj));
556-
557-
removeDocumentsInLocalTxn(
558-
asr.opCtx(),
559-
TagsType::ConfigNS,
560-
BSON(TagsType::ns(tag.getNS().ns()) << TagsType::min(tag.getMinKey())),
561-
true /* startTransaction */,
562-
txnNumber);
563-
564-
insertDocumentsInLocalTxn(asr.opCtx(),
565-
TagsType::ConfigNS,
566-
{tag.toBSONLegacyID()},
567-
false /* startTransaction */,
568-
txnNumber);
569-
570-
commitLocalTxn(asr.opCtx(), txnNumber);
502+
if (currentBatchSize != 0) {
503+
commitLocalTxn(asr.opCtx(), txnNumber);
504+
}
505+
}
571506

572-
txnNumber += 1;
507+
void ShardingCatalogManager::upgradeOrDowngradeChunksAndTags(OperationContext* opCtx,
508+
ConfigUpgradeType upgradeType) {
509+
const auto grid = Grid::get(opCtx);
510+
auto allDbs = uassertStatusOK(grid->catalogClient()->getAllDBs(
511+
opCtx, repl::ReadConcernLevel::kLocalReadConcern))
512+
.value;
513+
514+
// The 'config' database contains the sharded 'config.system.sessions' collection but does not
515+
// have an entry in config.databases.
516+
allDbs.emplace_back("config", ShardId("config"), true, DatabaseVersion());
517+
518+
for (const auto& db : allDbs) {
519+
auto collections = uassertStatusOK(grid->catalogClient()->getCollections(
520+
opCtx, &db.getName(), nullptr, repl::ReadConcernLevel::kLocalReadConcern));
521+
522+
for (const auto& coll : collections) {
523+
if (coll.getDropped()) {
524+
continue;
525+
}
526+
527+
{
528+
Lock::ExclusiveLock lk(opCtx->lockState(), _kChunkOpLock);
529+
forEachConfigDocInBatchedTransactions(
530+
opCtx, ChunkType::ConfigNS, coll.getNs(), deleteAndInsertChunk, upgradeType);
531+
}
532+
533+
{
534+
Lock::ExclusiveLock lk(opCtx->lockState(), _kZoneOpLock);
535+
forEachConfigDocInBatchedTransactions(
536+
opCtx, TagsType::ConfigNS, coll.getNs(), deleteAndInsertTag, upgradeType);
537+
}
573538
}
574539
}
575540
}

0 commit comments

Comments
 (0)