Skip to content

Commit dab2ae2

Browse files
EshaMaharishievergreen
authored andcommitted
SERVER-42112 uassert on _flushDatabaseCacheUpdates cmdResponse in configsvrDropDatabase and configsvrCreateDatabase if FCV 4.4
1 parent 66cc9d9 commit dab2ae2

File tree

4 files changed

+151
-3
lines changed

4 files changed

+151
-3
lines changed
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// Tests that create database and drop database succeed when the config servers are v4.4 and shard
2+
// servers are v4.2.
3+
4+
(function() {
5+
"use strict";
6+
7+
const st = new ShardingTest({
8+
shards: [
9+
{binVersion: "last-stable"},
10+
],
11+
mongos: 1,
12+
other: {mongosOptions: {binVersion: "last-stable"}}
13+
});
14+
15+
// Create a database by inserting into a collection.
16+
assert.commandWorked(st.s.getDB("test").getCollection("foo").insert({x: 1}));
17+
18+
// Drop the database.
19+
assert.commandWorked(st.s.getDB("test").dropDatabase());
20+
21+
st.stop();
22+
})();

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

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -183,7 +183,16 @@ class ConfigSvrDropDatabaseCommand : public BasicCommand {
183183
"admin",
184184
BSON("_flushDatabaseCacheUpdates" << dbname),
185185
Shard::RetryPolicy::kIdempotent));
186-
// TODO SERVER-42112: uassert on the cmdResponse.
186+
187+
// If the shard had binary version v4.2 when it received the
188+
// _flushDatabaseCacheUpdates, it will have responded with NamespaceNotFound,
189+
// because the shard no longer has the database (see SERVER-34431). Ignore this
190+
// error, since once the shard is restarted in v4.4, its in-memory database version
191+
// will be cleared anyway.
192+
if (cmdResponse.commandStatus == ErrorCodes::NamespaceNotFound) {
193+
continue;
194+
}
195+
uassertStatusOK(cmdResponse.commandStatus);
187196
}
188197

189198
ShardingLogging::get(opCtx)->logChange(

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

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,103 @@ TEST_F(CreateDatabaseTest, createDatabaseSuccess) {
159159
future.default_timed_get();
160160
}
161161

162+
TEST_F(CreateDatabaseTest,
163+
createDatabaseShardReturnsNamespaceNotFoundForFlushDatabaseCacheUpdates) {
164+
const std::string dbname = "db1";
165+
166+
ShardType s0;
167+
s0.setName("shard0000");
168+
s0.setHost("ShardHost0:27017");
169+
setupShards(vector<ShardType>{s0});
170+
171+
ShardType s1;
172+
s1.setName("shard0001");
173+
s1.setHost("ShardHost1:27017");
174+
setupShards(vector<ShardType>{s1});
175+
176+
ShardType s2;
177+
s2.setName("shard0002");
178+
s2.setHost("ShardHost2:27017");
179+
setupShards(vector<ShardType>{s2});
180+
181+
// Prime the shard registry with information about the existing shards
182+
shardRegistry()->reload(operationContext());
183+
184+
// Set up all the target mocks return values.
185+
RemoteCommandTargeterMock::get(
186+
uassertStatusOK(shardRegistry()->getShard(operationContext(), s0.getName()))->getTargeter())
187+
->setFindHostReturnValue(HostAndPort(s0.getHost()));
188+
RemoteCommandTargeterMock::get(
189+
uassertStatusOK(shardRegistry()->getShard(operationContext(), s1.getName()))->getTargeter())
190+
->setFindHostReturnValue(HostAndPort(s1.getHost()));
191+
RemoteCommandTargeterMock::get(
192+
uassertStatusOK(shardRegistry()->getShard(operationContext(), s2.getName()))->getTargeter())
193+
->setFindHostReturnValue(HostAndPort(s2.getHost()));
194+
195+
// Now actually start the createDatabase work.
196+
197+
auto future = launchAsync([this, dbname] {
198+
ThreadClient tc("Test", getGlobalServiceContext());
199+
auto opCtx = cc().makeOperationContext();
200+
ShardingCatalogManager::get(opCtx.get())->createDatabase(opCtx.get(), dbname);
201+
});
202+
203+
// Return size information about first shard
204+
onCommand([&](const RemoteCommandRequest& request) {
205+
ASSERT_EQUALS(s0.getHost(), request.target.toString());
206+
ASSERT_EQUALS("admin", request.dbname);
207+
std::string cmdName = request.cmdObj.firstElement().fieldName();
208+
ASSERT_EQUALS("listDatabases", cmdName);
209+
ASSERT_FALSE(request.cmdObj.hasField(repl::ReadConcernArgs::kReadConcernFieldName));
210+
211+
ASSERT_BSONOBJ_EQ(
212+
ReadPreferenceSetting(ReadPreference::PrimaryPreferred).toContainingBSON(),
213+
rpc::TrackingMetadata::removeTrackingData(request.metadata));
214+
215+
return BSON("ok" << 1 << "totalSize" << 10);
216+
});
217+
218+
// Return size information about second shard
219+
onCommand([&](const RemoteCommandRequest& request) {
220+
ASSERT_EQUALS(s1.getHost(), request.target.toString());
221+
ASSERT_EQUALS("admin", request.dbname);
222+
std::string cmdName = request.cmdObj.firstElement().fieldName();
223+
ASSERT_EQUALS("listDatabases", cmdName);
224+
ASSERT_FALSE(request.cmdObj.hasField(repl::ReadConcernArgs::kReadConcernFieldName));
225+
226+
ASSERT_BSONOBJ_EQ(
227+
ReadPreferenceSetting(ReadPreference::PrimaryPreferred).toContainingBSON(),
228+
rpc::TrackingMetadata::removeTrackingData(request.metadata));
229+
230+
return BSON("ok" << 1 << "totalSize" << 1);
231+
});
232+
233+
// Return size information about third shard
234+
onCommand([&](const RemoteCommandRequest& request) {
235+
ASSERT_EQUALS(s2.getHost(), request.target.toString());
236+
ASSERT_EQUALS("admin", request.dbname);
237+
std::string cmdName = request.cmdObj.firstElement().fieldName();
238+
ASSERT_EQUALS("listDatabases", cmdName);
239+
240+
ASSERT_BSONOBJ_EQ(
241+
ReadPreferenceSetting(ReadPreference::PrimaryPreferred).toContainingBSON(),
242+
rpc::TrackingMetadata::removeTrackingData(request.metadata));
243+
244+
return BSON("ok" << 1 << "totalSize" << 100);
245+
});
246+
247+
// Return NamespaceNotFound for _flushDatabaseCacheUpdates
248+
onCommand([&](const RemoteCommandRequest& request) {
249+
std::string cmdName = request.cmdObj.firstElement().fieldName();
250+
ASSERT_EQUALS("_flushDatabaseCacheUpdates", cmdName);
251+
252+
return BSON("ok" << 0 << "code" << ErrorCodes::NamespaceNotFound << "errmsg"
253+
<< "dummy");
254+
});
255+
256+
future.default_timed_get();
257+
}
258+
162259
TEST_F(CreateDatabaseTest, createDatabaseDBExists) {
163260
const std::string dbname = "db3";
164261

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

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,19 @@ DatabaseType ShardingCatalogManager::createDatabase(OperationContext* opCtx,
119119
uassertStatusOK(Grid::get(opCtx)->catalogClient()->insertConfigDocument(
120120
opCtx, DatabaseType::ConfigNS, db.toBSON(), ShardingCatalogClient::kMajorityWriteConcern));
121121

122-
// Send _flushDatabaseCacheUpdates to the primary shard
122+
// Note, making the primary shard refresh its databaseVersion here is not required for
123+
// correctness, since either:
124+
// 1) This is the first time this database is being created. The primary shard will not have a
125+
// databaseVersion already cached.
126+
// 2) The database was dropped and is being re-created. Since dropping a database also sends
127+
// _flushDatabaseCacheUpdates to all shards, the primary shard should not have a database
128+
// version cached. (Note, it is possible that dropping a database will skip sending
129+
// _flushDatabaseCacheUpdates if the config server fails over while dropping the database.)
130+
// However, routers don't support retrying internally on StaleDbVersion in transactions
131+
// (SERVER-39704), so if the first operation run against the database is in a transaction, it
132+
// would fail with StaleDbVersion. Making the primary shard refresh here allows that first
133+
// transaction to succeed. This allows our transaction passthrough suites and transaction demos
134+
// to succeed without additional special logic.
123135
const auto shard =
124136
uassertStatusOK(Grid::get(opCtx)->shardRegistry()->getShard(opCtx, db.getPrimary()));
125137
auto cmdResponse = uassertStatusOK(
@@ -128,7 +140,15 @@ DatabaseType ShardingCatalogManager::createDatabase(OperationContext* opCtx,
128140
"admin",
129141
BSON("_flushDatabaseCacheUpdates" << dbName),
130142
Shard::RetryPolicy::kIdempotent));
131-
// TODO SERVER-42112: uassert on the cmdResponse.
143+
144+
// If the shard had binary version v4.2 when it received the _flushDatabaseCacheUpdates, it will
145+
// have responded with NamespaceNotFound, because the shard does not have the database (see
146+
// SERVER-34431). Ignore this error, since the _flushDatabaseCacheUpdates is only a nicety for
147+
// users testing transactions, and the transaction passthrough suites do not change shard binary
148+
// versions.
149+
if (cmdResponse.commandStatus != ErrorCodes::NamespaceNotFound) {
150+
uassertStatusOK(cmdResponse.commandStatus);
151+
}
132152

133153
return db;
134154
}

0 commit comments

Comments
 (0)