summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPierlauro Sciarelli <pierlauro.sciarelli@mongodb.com>2022-08-05 12:38:05 +0000
committerEvergreen Agent <no-reply@evergreen.mongodb.com>2022-08-05 13:02:31 +0000
commit0fe254641cd91bf7087d77440028432219029ab6 (patch)
tree7212d960bfaed0016cf0f5f0f8d7c63e71989015
parentf0472aaf726ddcb0f0271b86aaf194b09374f492 (diff)
downloadmongo-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.js64
-rw-r--r--src/mongo/db/s/move_primary_source_manager.cpp79
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();
}