diff options
author | Pierlauro Sciarelli <pierlauro.sciarelli@mongodb.com> | 2022-08-05 12:38:05 +0000 |
---|---|---|
committer | Evergreen Agent <no-reply@evergreen.mongodb.com> | 2022-08-05 13:02:31 +0000 |
commit | 0fe254641cd91bf7087d77440028432219029ab6 (patch) | |
tree | 7212d960bfaed0016cf0f5f0f8d7c63e71989015 | |
parent | f0472aaf726ddcb0f0271b86aaf194b09374f492 (diff) | |
download | mongo-0fe254641cd91bf7087d77440028432219029ab6.tar.gz |
SERVER-68511 MovePrimary update of config.databases entry must use dotted fields notation (backport v5.0)
-rw-r--r-- | jstests/multiVersion/genericSetFCVUsage/move_primary_setFCV.js | 64 | ||||
-rw-r--r-- | src/mongo/db/s/move_primary_source_manager.cpp | 79 |
2 files changed, 115 insertions, 28 deletions
diff --git a/jstests/multiVersion/genericSetFCVUsage/move_primary_setFCV.js b/jstests/multiVersion/genericSetFCVUsage/move_primary_setFCV.js new file mode 100644 index 00000000000..c167603a0b3 --- /dev/null +++ b/jstests/multiVersion/genericSetFCVUsage/move_primary_setFCV.js @@ -0,0 +1,64 @@ + +/** + * Test that `movePrimary` works for databases created under a different FCV + */ +(function() { + +"use strict"; + +let st = new ShardingTest({shards: 2, mongos: 1, rs: {nodes: 1}}); + +const mongos = st.s; +const kBeforeDowngradingDbName = 'createdBeforeDowngrading'; +const kBeforeUpgradingDbName = 'createdBeforeUpgrading'; +const shard0 = st.shard0.shardName; +const shard1 = st.shard1.shardName; + +const createdBeforeDowngradingDB = mongos.getDB(kBeforeDowngradingDbName); +const createdBeforeUpgradingDB = mongos.getDB(kBeforeUpgradingDbName); + +function testMovePrimary(db) { + const dbName = db.getName(); + const currentPrimary = mongos.getDB('config').databases.findOne({_id: dbName}).primary; + const newPrimary = currentPrimary == shard0 ? shard1 : shard0; + assert.eq(db.coll.countDocuments({}), 1); + assert.commandWorked(mongos.adminCommand({movePrimary: dbName, to: newPrimary})); + assert.eq(newPrimary, mongos.getDB('config').databases.findOne({_id: dbName}).primary); + assert.eq(db.coll.countDocuments({}), 1); +} + +// Latest FCV +assert.commandWorked(mongos.adminCommand({setFeatureCompatibilityVersion: latestFCV})); + +// Create database `createdBeforeDowngrading` under latest FCV +assert.commandWorked( + mongos.adminCommand({enableSharding: kBeforeDowngradingDbName, primaryShard: shard0})); +assert.commandWorked(createdBeforeDowngradingDB.coll.insert({_id: 'foo'})); + +// Downgrade FCV +assert.commandWorked(mongos.adminCommand({setFeatureCompatibilityVersion: lastLTSFCV})); + +// Make sure movePrimary works for `createdBeforeDowngrading` +testMovePrimary(createdBeforeDowngradingDB); + +// Create database `createdBeforeUpgrading` under downgraded FCV +assert.commandWorked(mongos.adminCommand({setFeatureCompatibilityVersion: latestFCV})); +assert.commandWorked( + mongos.adminCommand({enableSharding: kBeforeUpgradingDbName, primaryShard: shard0})); +assert.commandWorked(createdBeforeUpgradingDB.coll.insert({_id: 'foo'})); + +// Upgrade FCV +assert.commandWorked(mongos.adminCommand({setFeatureCompatibilityVersion: latestFCV})); + +// Make sure movePrimary works (again) for `createdBeforeDowngrading` +testMovePrimary(createdBeforeDowngradingDB); + +// Make sure movePrimary works for `createdBeforeUpgrading` +testMovePrimary(createdBeforeUpgradingDB); + +// Drop databases for next round +assert.commandWorked(createdBeforeDowngradingDB.dropDatabase()); +assert.commandWorked(createdBeforeUpgradingDB.dropDatabase()); + +st.stop(); +})(); diff --git a/src/mongo/db/s/move_primary_source_manager.cpp b/src/mongo/db/s/move_primary_source_manager.cpp index 99bf70f5ba7..108871e0f4d 100644 --- a/src/mongo/db/s/move_primary_source_manager.cpp +++ b/src/mongo/db/s/move_primary_source_manager.cpp @@ -332,42 +332,56 @@ Status MovePrimarySourceManager::commitOnConfig(OperationContext* opCtx) { Status MovePrimarySourceManager::_commitOnConfig(OperationContext* opCtx) { auto const configShard = Grid::get(opCtx)->shardRegistry()->getConfigShard(); - auto findResponse = uassertStatusOK( - configShard->exhaustiveFindOnConfig(opCtx, - ReadPreferenceSetting{ReadPreference::PrimaryOnly}, - repl::ReadConcernLevel::kMajorityReadConcern, - DatabaseType::ConfigNS, - BSON(DatabaseType::name << _dbname), - BSON(DatabaseType::name << -1), - 1)); - - const auto databasesVector = std::move(findResponse.docs); - uassert(ErrorCodes::IncompatibleShardingMetadata, - str::stream() << "Tried to find max database version for database '" << _dbname - << "', but found no databases", - !databasesVector.empty()); - - const auto dbType = uassertStatusOK(DatabaseType::fromBSON(databasesVector.front())); - - if (dbType.getPrimary() == _toShard) { + auto getDatabaseEntry = [&]() { + auto findResponse = uassertStatusOK( + configShard->exhaustiveFindOnConfig(opCtx, + ReadPreferenceSetting{ReadPreference::PrimaryOnly}, + repl::ReadConcernLevel::kMajorityReadConcern, + DatabaseType::ConfigNS, + BSON(DatabaseType::name << _dbname), + BSON(DatabaseType::name << -1), + 1)); + + const auto databasesVector = std::move(findResponse.docs); + uassert(ErrorCodes::IncompatibleShardingMetadata, + str::stream() << "Tried to find max database version for database '" << _dbname + << "', but found no databases", + !databasesVector.empty()); + + return uassertStatusOK(DatabaseType::fromBSON(databasesVector.front())); + }; + + const auto originalDbType = getDatabaseEntry(); + + if (originalDbType.getPrimary() == _toShard) { return Status::OK(); } - auto newDbType = dbType; - newDbType.setPrimary(_toShard); - - auto const currentDatabaseVersion = dbType.getVersion(); - - newDbType.setVersion(currentDatabaseVersion.makeUpdated()); + auto const newDbVersion = [&]() { + auto version = originalDbType.getVersion(); + return version.makeUpdated(); + }(); - auto updateQueryBuilder = BSONObjBuilder(BSON(DatabaseType::name << _dbname)); - updateQueryBuilder.append(DatabaseType::version.name(), currentDatabaseVersion.toBSON()); + auto getDottedVersionField = [](const StringData& fieldName) { + return DatabaseType::version.name() + "." + fieldName; + }; + + // Include the version in the update filter to be resilient to potential network retries + auto const updateQuery = + BSON(DatabaseType::name << _dbname << getDottedVersionField(DatabaseVersion::kUuidFieldName) + << newDbVersion.getUuid() + << getDottedVersionField(DatabaseVersion::kLastModFieldName) + << originalDbType.getVersion().getLastMod()); + auto const update = + BSON("$set" << BSON(DatabaseType::primary + << _toShard << getDottedVersionField(DatabaseVersion::kLastModFieldName) + << newDbVersion.getLastMod())); auto updateStatus = Grid::get(opCtx)->catalogClient()->updateConfigDocument( opCtx, DatabaseType::ConfigNS, - updateQueryBuilder.obj(), - newDbType.toBSON(), + updateQuery, + update, false, ShardingCatalogClient::kMajorityWriteConcern); @@ -380,6 +394,15 @@ Status MovePrimarySourceManager::_commitOnConfig(OperationContext* opCtx) { return updateStatus.getStatus(); } + const auto updatedDbType = getDatabaseEntry(); + tassert(6851100, + "Error committing movePrimary: database version went backwards", + updatedDbType.getVersion().getUuid() != originalDbType.getVersion().getUuid() || + updatedDbType.getVersion().getLastMod() > originalDbType.getVersion().getLastMod()); + uassert(6851101, + "Error committing movePrimary: update of `config.databases` failed", + updatedDbType.getPrimary() != _fromShard); + return Status::OK(); } |