diff options
Diffstat (limited to 'src')
8 files changed, 109 insertions, 30 deletions
diff --git a/src/mongo/db/commands/list_databases.cpp b/src/mongo/db/commands/list_databases.cpp index f97988641fa..7088a4248b5 100644 --- a/src/mongo/db/commands/list_databases.cpp +++ b/src/mongo/db/commands/list_databases.cpp @@ -34,11 +34,16 @@ #include "mongo/db/client.h" #include "mongo/db/commands.h" #include "mongo/db/concurrency/d_concurrency.h" +#include "mongo/db/matcher/expression.h" +#include "mongo/db/matcher/extensions_callback_disallow_extensions.h" #include "mongo/db/operation_context.h" #include "mongo/db/service_context.h" #include "mongo/db/storage/storage_engine.h" namespace mongo { +namespace { +static const StringData kFilterField{"filter"}; +} // namespace using std::set; using std::string; @@ -81,6 +86,27 @@ public: int, string& errmsg, BSONObjBuilder& result) { + // Parse the filter. + std::unique_ptr<MatchExpression> filter; + if (auto filterElt = jsobj[kFilterField]) { + if (filterElt.type() != BSONType::Object) { + return appendCommandStatus(result, + {ErrorCodes::TypeMismatch, + str::stream() << "Field '" << kFilterField + << "' must be of type Object in: " + << jsobj}); + } + // The collator is null because database metadata objects are compared using simple + // binary comparison. + const CollatorInterface* collator = nullptr; + auto statusWithMatcher = MatchExpressionParser::parse( + filterElt.Obj(), ExtensionsCallbackDisallowExtensions(), collator); + if (!statusWithMatcher.isOK()) { + return appendCommandStatus(result, statusWithMatcher.getStatus()); + } + filter = std::move(statusWithMatcher.getValue()); + } + vector<string> dbNames; StorageEngine* storageEngine = getGlobalServiceContext()->getGlobalStorageEngine(); { @@ -91,7 +117,6 @@ public: vector<BSONObj> dbInfos; - set<string> seen; intmax_t totalSize = 0; for (vector<string>::iterator i = dbNames.begin(); i != dbNames.end(); ++i) { const string& dbname = *i; @@ -99,6 +124,8 @@ public: BSONObjBuilder b; b.append("name", dbname); + BSONObj curDbObj; + int64_t size = 0; { ScopedTransaction transaction(txn, MODE_IS); Lock::DBLock dbLock(txn->lockState(), dbname, MODE_IS); @@ -110,16 +137,18 @@ public: const DatabaseCatalogEntry* entry = db->getDatabaseCatalogEntry(); invariant(entry); - int64_t size = entry->sizeOnDisk(txn); + size = entry->sizeOnDisk(txn); b.append("sizeOnDisk", static_cast<double>(size)); - totalSize += size; b.appendBool("empty", entry->isEmpty()); - } - dbInfos.push_back(b.obj()); + curDbObj = b.obj(); + } - seen.insert(i->c_str()); + if (!filter || filter->matchesBSON(curDbObj)) { + totalSize += size; + dbInfos.push_back(curDbObj); + } } result.append("databases", dbInfos); diff --git a/src/mongo/s/catalog/sharding_catalog_append_db_stats_test.cpp b/src/mongo/s/catalog/sharding_catalog_append_db_stats_test.cpp index d3f46f56f63..5e2259b56fb 100644 --- a/src/mongo/s/catalog/sharding_catalog_append_db_stats_test.cpp +++ b/src/mongo/s/catalog/sharding_catalog_append_db_stats_test.cpp @@ -63,8 +63,8 @@ TEST_F(ShardingCatalogClientAppendDbStatsTest, BasicAppendDBStats) { BSONArrayBuilder builder; auto future = launchAsync([this, &builder] { - ASSERT_OK( - catalogClient()->appendInfoForConfigServerDatabases(operationContext(), &builder)); + ASSERT_OK(catalogClient()->appendInfoForConfigServerDatabases( + operationContext(), BSON("listDatabases" << 1), &builder)); }); onCommand([](const RemoteCommandRequest& request) { @@ -118,13 +118,49 @@ TEST_F(ShardingCatalogClientAppendDbStatsTest, BasicAppendDBStats) { ASSERT_TRUE(localIter == dbMap.end()); } +TEST_F(ShardingCatalogClientAppendDbStatsTest, AppendDBStatsWithFilter) { + configTargeter()->setFindHostReturnValue(HostAndPort("TestHost1")); + + BSONArrayBuilder builder; + auto future = launchAsync([this, &builder] { + ASSERT_OK(catalogClient()->appendInfoForConfigServerDatabases( + operationContext(), + BSON("listDatabases" << 1 << "filter" << BSON("name" + << "config")), + &builder)); + }); + + onCommand([](const RemoteCommandRequest& request) { + ASSERT_BSONOBJ_EQ(kReplSecondaryOkMetadata, + rpc::TrackingMetadata::removeTrackingData(request.metadata)); + + ASSERT_EQ("admin", request.dbname); + ASSERT_BSONOBJ_EQ(BSON("listDatabases" << 1 << "filter" << BSON("name" + << "config")), + request.cmdObj); + + return fromjson(R"({ + databases: [ + { + name: 'config', + empty: false, + sizeOnDisk: 40000 + } + ], + ok: 1 + })"); + }); + + future.timed_get(kFutureTimeout); +} + TEST_F(ShardingCatalogClientAppendDbStatsTest, ErrorRunningListDatabases) { configTargeter()->setFindHostReturnValue(HostAndPort("TestHost1")); BSONArrayBuilder builder; auto future = launchAsync([this, &builder] { - auto status = - catalogClient()->appendInfoForConfigServerDatabases(operationContext(), &builder); + auto status = catalogClient()->appendInfoForConfigServerDatabases( + operationContext(), BSON("listDatabases" << 1), &builder); ASSERT_NOT_OK(status); ASSERT_EQ(ErrorCodes::AuthenticationFailed, status.code()); ASSERT_FALSE(status.reason().empty()); @@ -142,8 +178,8 @@ TEST_F(ShardingCatalogClientAppendDbStatsTest, MalformedListDatabasesResponse) { BSONArrayBuilder builder; auto future = launchAsync([this, &builder] { - auto status = - catalogClient()->appendInfoForConfigServerDatabases(operationContext(), &builder); + auto status = catalogClient()->appendInfoForConfigServerDatabases( + operationContext(), BSON("listDatabases" << 1), &builder); ASSERT_NOT_OK(status); ASSERT_EQ(ErrorCodes::NoSuchKey, status.code()); ASSERT_FALSE(status.reason().empty()); @@ -159,8 +195,8 @@ TEST_F(ShardingCatalogClientAppendDbStatsTest, MalformedListDatabasesEntryInResp BSONArrayBuilder builder; auto future = launchAsync([this, &builder] { - auto status = - catalogClient()->appendInfoForConfigServerDatabases(operationContext(), &builder); + auto status = catalogClient()->appendInfoForConfigServerDatabases( + operationContext(), BSON("listDatabases" << 1), &builder); ASSERT_NOT_OK(status); ASSERT_EQ(ErrorCodes::NoSuchKey, status.code()); ASSERT_FALSE(status.reason().empty()); diff --git a/src/mongo/s/catalog/sharding_catalog_client.h b/src/mongo/s/catalog/sharding_catalog_client.h index 1d736fb222d..f334c05a477 100644 --- a/src/mongo/s/catalog/sharding_catalog_client.h +++ b/src/mongo/s/catalog/sharding_catalog_client.h @@ -429,10 +429,12 @@ public: const WriteConcernOptions& writeConcern) = 0; /** - * Appends the information about the config and admin databases in the config server - * with the format for listDatabase. + * Appends the information about the config and admin databases in the config server with the + * format for listDatabases, based on the listDatabases command parameters in + * 'listDatabasesCmd'. */ virtual Status appendInfoForConfigServerDatabases(OperationContext* txn, + const BSONObj& listDatabasesCmd, BSONArrayBuilder* builder) = 0; /** diff --git a/src/mongo/s/catalog/sharding_catalog_client_impl.cpp b/src/mongo/s/catalog/sharding_catalog_client_impl.cpp index 0f364b61e84..01bb420f49a 100644 --- a/src/mongo/s/catalog/sharding_catalog_client_impl.cpp +++ b/src/mongo/s/catalog/sharding_catalog_client_impl.cpp @@ -1683,14 +1683,14 @@ void ShardingCatalogClientImpl::_appendReadConcern(BSONObjBuilder* builder) { readConcern.appendInfo(builder); } -Status ShardingCatalogClientImpl::appendInfoForConfigServerDatabases(OperationContext* txn, - BSONArrayBuilder* builder) { +Status ShardingCatalogClientImpl::appendInfoForConfigServerDatabases( + OperationContext* txn, const BSONObj& listDatabasesCmd, BSONArrayBuilder* builder) { auto configShard = Grid::get(txn)->shardRegistry()->getConfigShard(); auto resultStatus = configShard->runCommandWithFixedRetryAttempts(txn, kConfigPrimaryPreferredSelector, "admin", - BSON("listDatabases" << 1), + listDatabasesCmd, Shard::RetryPolicy::kIdempotent); if (!resultStatus.isOK()) { diff --git a/src/mongo/s/catalog/sharding_catalog_client_impl.h b/src/mongo/s/catalog/sharding_catalog_client_impl.h index c49b1bd6432..898b3774456 100644 --- a/src/mongo/s/catalog/sharding_catalog_client_impl.h +++ b/src/mongo/s/catalog/sharding_catalog_client_impl.h @@ -174,6 +174,7 @@ public: DistLockManager* getDistLockManager() override; Status appendInfoForConfigServerDatabases(OperationContext* txn, + const BSONObj& listDatabasesCmd, BSONArrayBuilder* builder) override; /** diff --git a/src/mongo/s/catalog/sharding_catalog_client_mock.cpp b/src/mongo/s/catalog/sharding_catalog_client_mock.cpp index 7058bff8c2b..11e03fb4c70 100644 --- a/src/mongo/s/catalog/sharding_catalog_client_mock.cpp +++ b/src/mongo/s/catalog/sharding_catalog_client_mock.cpp @@ -227,8 +227,8 @@ DistLockManager* ShardingCatalogClientMock::getDistLockManager() { return _distLockManager.get(); } -Status ShardingCatalogClientMock::appendInfoForConfigServerDatabases(OperationContext* txn, - BSONArrayBuilder* builder) { +Status ShardingCatalogClientMock::appendInfoForConfigServerDatabases( + OperationContext* txn, const BSONObj& listDatabasesCmd, BSONArrayBuilder* builder) { return Status::OK(); } diff --git a/src/mongo/s/catalog/sharding_catalog_client_mock.h b/src/mongo/s/catalog/sharding_catalog_client_mock.h index fd913a267c3..658681fd37a 100644 --- a/src/mongo/s/catalog/sharding_catalog_client_mock.h +++ b/src/mongo/s/catalog/sharding_catalog_client_mock.h @@ -159,6 +159,7 @@ public: DistLockManager* getDistLockManager() override; Status appendInfoForConfigServerDatabases(OperationContext* txn, + const BSONObj& listDatabasesCmd, BSONArrayBuilder* builder) override; private: diff --git a/src/mongo/s/commands/cluster_list_databases_cmd.cpp b/src/mongo/s/commands/cluster_list_databases_cmd.cpp index 9151d244bfe..2ae555e8593 100644 --- a/src/mongo/s/commands/cluster_list_databases_cmd.cpp +++ b/src/mongo/s/commands/cluster_list_databases_cmd.cpp @@ -32,6 +32,7 @@ #include <string> #include <vector> +#include "mongo/bson/util/bson_extract.h" #include "mongo/client/read_preference.h" #include "mongo/client/remote_command_targeter.h" #include "mongo/db/commands.h" @@ -105,7 +106,7 @@ public: txn, ReadPreferenceSetting{ReadPreference::PrimaryPreferred}, "admin", - BSON("listDatabases" << 1), + cmdObj, Shard::RetryPolicy::kIdempotent)); uassertStatusOK(response.commandStatus); BSONObj x = std::move(response.response); @@ -117,13 +118,13 @@ public: const string name = dbObj["name"].String(); const long long size = dbObj["sizeOnDisk"].numberLong(); - long long& totalSize = sizes[name]; + long long& sizeSumForDbAcrossShards = sizes[name]; if (size == 1) { - if (totalSize <= 1) { - totalSize = 1; + if (sizeSumForDbAcrossShards <= 1) { + sizeSumForDbAcrossShards = 1; } } else { - totalSize += size; + sizeSumForDbAcrossShards += size; } unique_ptr<BSONObjBuilder>& bb = dbShardInfo[name]; @@ -135,8 +136,6 @@ public: } } - long long totalSize = 0; - BSONArrayBuilder dbListBuilder(result.subarrayStart("databases")); for (map<string, long long>::iterator i = sizes.begin(); i != sizes.end(); ++i) { const string name = i->first; @@ -152,7 +151,6 @@ public: } long long size = i->second; - totalSize += size; BSONObjBuilder temp; temp.append("name", name); @@ -165,13 +163,25 @@ public: // Get information for config and admin dbs from the config servers. auto catalogClient = grid.catalogClient(txn); - auto appendStatus = catalogClient->appendInfoForConfigServerDatabases(txn, &dbListBuilder); + auto appendStatus = + catalogClient->appendInfoForConfigServerDatabases(txn, cmdObj, &dbListBuilder); if (!appendStatus.isOK()) { return Command::appendCommandStatus(result, appendStatus); } dbListBuilder.done(); + // Compute the combined total size based on the response we've built so far. + long long totalSize = 0; + for (auto&& dbElt : result.asTempObj()["databases"].Obj()) { + long long sizeOnDisk; + uassertStatusOK(bsonExtractIntegerField(dbElt.Obj(), "sizeOnDisk"_sd, &sizeOnDisk)); + uassert(ErrorCodes::BadValue, + str::stream() << "Found negative 'sizeOnDisk' in: " << dbElt.Obj(), + sizeOnDisk >= 0); + totalSize += sizeOnDisk; + } + result.appendNumber("totalSize", totalSize); result.appendNumber("totalSizeMb", totalSize / (1024 * 1024)); |