summaryrefslogtreecommitdiff
path: root/src/mongo/db
diff options
context:
space:
mode:
authorArun Banala <arun.banala@mongodb.com>2021-09-20 17:03:29 +0100
committerEvergreen Agent <no-reply@evergreen.mongodb.com>2021-09-30 17:15:26 +0000
commit5e0dffb6d2723470e1f1bd93de6565c7a4e0353a (patch)
tree3ee6ca0e236a67c1a8a687774b0c53dda9a268b2 /src/mongo/db
parent9f8e95a95b592c3686dcb1aa82ebcd9b7515974b (diff)
downloadmongo-5e0dffb6d2723470e1f1bd93de6565c7a4e0353a.tar.gz
SERVER-60144 Handle stale routing info on mongos for sharded time-series collections
Diffstat (limited to 'src/mongo/db')
-rw-r--r--src/mongo/db/catalog/coll_mod.cpp7
-rw-r--r--src/mongo/db/coll_mod.idl6
-rw-r--r--src/mongo/db/commands/create_indexes.cpp5
-rw-r--r--src/mongo/db/commands/dbcommands.cpp13
-rw-r--r--src/mongo/db/commands/drop_indexes.cpp5
-rw-r--r--src/mongo/db/commands/list_indexes.cpp9
-rw-r--r--src/mongo/db/commands/write_commands.cpp11
-rw-r--r--src/mongo/db/create_indexes.idl6
-rw-r--r--src/mongo/db/drop_indexes.idl6
-rw-r--r--src/mongo/db/list_indexes.idl6
-rw-r--r--src/mongo/db/s/create_collection_coordinator.cpp2
-rw-r--r--src/mongo/db/service_entry_point_common.cpp13
-rw-r--r--src/mongo/db/timeseries/catalog_helper.cpp5
-rw-r--r--src/mongo/db/timeseries/catalog_helper.h3
-rw-r--r--src/mongo/db/timeseries/timeseries_commands_conversion_helper.cpp18
-rw-r--r--src/mongo/db/timeseries/timeseries_commands_conversion_helper.h3
16 files changed, 97 insertions, 21 deletions
diff --git a/src/mongo/db/catalog/coll_mod.cpp b/src/mongo/db/catalog/coll_mod.cpp
index a683d603d7c..a052e22d81f 100644
--- a/src/mongo/db/catalog/coll_mod.cpp
+++ b/src/mongo/db/catalog/coll_mod.cpp
@@ -491,8 +491,15 @@ Status _collModInternal(OperationContext* opCtx,
.throwIfReshardingInProgress(nss);
}
+
// If db/collection/view does not exist, short circuit and return.
if (!db || (!coll && !view)) {
+ if (nss.isTimeseriesBucketsCollection()) {
+ // If a sharded time-series collection is dropped, it's possible that a stale mongos
+ // sends the request on the buckets namespace instead of the view namespace. Ensure that
+ // the shardVersion is upto date before throwing an error.
+ CollectionShardingState::get(opCtx, nss)->checkShardVersionOrThrow(opCtx);
+ }
return Status(ErrorCodes::NamespaceNotFound, "ns does not exist");
}
diff --git a/src/mongo/db/coll_mod.idl b/src/mongo/db/coll_mod.idl
index 0a71b5522d1..e1f193294d3 100644
--- a/src/mongo/db/coll_mod.idl
+++ b/src/mongo/db/coll_mod.idl
@@ -150,4 +150,10 @@ commands:
description: "Adjusts parameters for timeseries collections"
optional: true
type: CollModTimeseries
+ isTimeseriesNamespace:
+ description: "This flag is set to true when the command was originally sent to
+ mongos on the time-series view, but got rewritten to target
+ time-series buckets namespace before being sent to shards."
+ optional: true
+ type: bool
reply_type: CollModReply
diff --git a/src/mongo/db/commands/create_indexes.cpp b/src/mongo/db/commands/create_indexes.cpp
index 00a74a47efb..50addeb0673 100644
--- a/src/mongo/db/commands/create_indexes.cpp
+++ b/src/mongo/db/commands/create_indexes.cpp
@@ -652,7 +652,10 @@ public:
// If the request namespace refers to a time-series collection, transforms the user
// time-series index request to one on the underlying bucket.
boost::optional<CreateIndexesCommand> timeseriesCmdOwnership;
- if (auto options = timeseries::getTimeseriesOptions(opCtx, origCmd.getNamespace())) {
+ auto isCommandOnTimeseriesBucketNamespace =
+ origCmd.getIsTimeseriesNamespace() && *origCmd.getIsTimeseriesNamespace();
+ if (auto options = timeseries::getTimeseriesOptions(
+ opCtx, origCmd.getNamespace(), !isCommandOnTimeseriesBucketNamespace)) {
timeseriesCmdOwnership =
timeseries::makeTimeseriesCreateIndexesCommand(opCtx, origCmd, *options);
cmd = &timeseriesCmdOwnership.get();
diff --git a/src/mongo/db/commands/dbcommands.cpp b/src/mongo/db/commands/dbcommands.cpp
index fbf3dbaad96..bb14f4e07c2 100644
--- a/src/mongo/db/commands/dbcommands.cpp
+++ b/src/mongo/db/commands/dbcommands.cpp
@@ -121,7 +121,10 @@ std::unique_ptr<CollMod> makeTimeseriesBucketsCollModCommand(OperationContext* o
const CollMod& origCmd) {
const auto& origNs = origCmd.getNamespace();
- auto timeseriesOptions = timeseries::getTimeseriesOptions(opCtx, origNs);
+ auto isCommandOnTimeseriesBucketNamespace =
+ origCmd.getIsTimeseriesNamespace() && *origCmd.getIsTimeseriesNamespace();
+ auto timeseriesOptions =
+ timeseries::getTimeseriesOptions(opCtx, origNs, !isCommandOnTimeseriesBucketNamespace);
// Return early with null if we are not working with a time-series collection.
if (!timeseriesOptions) {
@@ -141,7 +144,8 @@ std::unique_ptr<CollMod> makeTimeseriesBucketsCollModCommand(OperationContext* o
index->setKeyPattern(std::move(bucketsIndexSpecWithStatus.getValue()));
}
- auto ns = origNs.makeTimeseriesBucketsNamespace();
+ auto ns =
+ isCommandOnTimeseriesBucketNamespace ? origNs : origNs.makeTimeseriesBucketsNamespace();
auto cmd = std::make_unique<CollMod>(ns);
cmd->setIndex(index);
cmd->setValidator(origCmd.getValidator());
@@ -166,7 +170,10 @@ std::unique_ptr<CollMod> makeTimeseriesViewCollModCommand(OperationContext* opCt
const CollMod& origCmd) {
const auto& ns = origCmd.getNamespace();
- auto timeseriesOptions = timeseries::getTimeseriesOptions(opCtx, ns);
+ auto isCommandOnTimeseriesBucketNamespace =
+ origCmd.getIsTimeseriesNamespace() && *origCmd.getIsTimeseriesNamespace();
+ auto timeseriesOptions =
+ timeseries::getTimeseriesOptions(opCtx, ns, !isCommandOnTimeseriesBucketNamespace);
// Return early with null if we are not working with a time-series collection.
if (!timeseriesOptions) {
diff --git a/src/mongo/db/commands/drop_indexes.cpp b/src/mongo/db/commands/drop_indexes.cpp
index e06cdaec2f4..ce3bbfbb0fa 100644
--- a/src/mongo/db/commands/drop_indexes.cpp
+++ b/src/mongo/db/commands/drop_indexes.cpp
@@ -96,7 +96,10 @@ public:
Reply typedRun(OperationContext* opCtx) final {
// If the request namespace refers to a time-series collection, transform the user
// time-series index request to one on the underlying bucket.
- if (auto options = timeseries::getTimeseriesOptions(opCtx, request().getNamespace())) {
+ auto isCommandOnTimeseriesBucketNamespace =
+ request().getIsTimeseriesNamespace() && *request().getIsTimeseriesNamespace();
+ if (auto options = timeseries::getTimeseriesOptions(
+ opCtx, request().getNamespace(), !isCommandOnTimeseriesBucketNamespace)) {
auto timeseriesCmd =
timeseries::makeTimeseriesDropIndexesCommand(opCtx, request(), *options);
return dropIndexes(opCtx, timeseriesCmd.getNamespace(), timeseriesCmd.getIndex());
diff --git a/src/mongo/db/commands/list_indexes.cpp b/src/mongo/db/commands/list_indexes.cpp
index 54725eb7fb5..c9a42882944 100644
--- a/src/mongo/db/commands/list_indexes.cpp
+++ b/src/mongo/db/commands/list_indexes.cpp
@@ -74,8 +74,13 @@ IndexSpecsWithNamespaceString getIndexSpecsWithNamespaceString(OperationContext*
// Since time-series collections don't have UUIDs, we skip the time-series lookup
// if the target collection is specified as a UUID.
if (const auto& origNss = origNssOrUUID.nss()) {
- if (auto timeseriesOptions = timeseries::getTimeseriesOptions(opCtx, *origNss)) {
- auto bucketsNss = origNss->makeTimeseriesBucketsNamespace();
+ auto isCommandOnTimeseriesBucketNamespace =
+ cmd.getIsTimeseriesNamespace() && *cmd.getIsTimeseriesNamespace();
+ if (auto timeseriesOptions = timeseries::getTimeseriesOptions(
+ opCtx, *origNss, !isCommandOnTimeseriesBucketNamespace)) {
+ auto bucketsNss = isCommandOnTimeseriesBucketNamespace
+ ? *origNss
+ : origNss->makeTimeseriesBucketsNamespace();
AutoGetCollectionForReadCommandMaybeLockFree autoColl(opCtx, bucketsNss);
const CollectionPtr& coll = autoColl.getCollection();
diff --git a/src/mongo/db/commands/write_commands.cpp b/src/mongo/db/commands/write_commands.cpp
index 64c8b9d3f7b..d794025b2c0 100644
--- a/src/mongo/db/commands/write_commands.cpp
+++ b/src/mongo/db/commands/write_commands.cpp
@@ -121,6 +121,9 @@ bool isTimeseries(OperationContext* opCtx, const Request& request) {
"system.buckets namespace",
!request.getIsTimeseriesNamespace() ||
request.getNamespace().isTimeseriesBucketsCollection());
+ const auto bucketNss = request.getIsTimeseriesNamespace()
+ ? request.getNamespace()
+ : request.getNamespace().makeTimeseriesBucketsNamespace();
// If the buckets collection exists now, the time-series insert path will check for the
// existence of the buckets collection later on with a lock.
@@ -128,11 +131,9 @@ bool isTimeseries(OperationContext* opCtx, const Request& request) {
// collection does not yet exist, this check may return false unnecessarily. As a result, an
// insert attempt into the time-series namespace will either succeed or fail, depending on who
// wins the race.
- return request.getIsTimeseriesNamespace() ||
- CollectionCatalog::get(opCtx)
- ->lookupCollectionByNamespaceForRead(
- opCtx, request.getNamespace().makeTimeseriesBucketsNamespace())
- .get();
+ return CollectionCatalog::get(opCtx)
+ ->lookupCollectionByNamespaceForRead(opCtx, bucketNss)
+ .get();
}
NamespaceString makeTimeseriesBucketsNamespace(const NamespaceString& nss) {
diff --git a/src/mongo/db/create_indexes.idl b/src/mongo/db/create_indexes.idl
index 12f629ed7e2..e7b60e73790 100644
--- a/src/mongo/db/create_indexes.idl
+++ b/src/mongo/db/create_indexes.idl
@@ -183,3 +183,9 @@ commands:
description: 'Commit Quorum options'
type: CommitQuorum
optional: true
+ isTimeseriesNamespace:
+ description: "This flag is set to true when the command was originally sent to
+ mongos on the time-series view, but got rewritten to target
+ time-series buckets namespace before being sent to shards."
+ type: bool
+ optional: true
diff --git a/src/mongo/db/drop_indexes.idl b/src/mongo/db/drop_indexes.idl
index 76d17a53e9e..cb4ea9eddb2 100644
--- a/src/mongo/db/drop_indexes.idl
+++ b/src/mongo/db/drop_indexes.idl
@@ -71,4 +71,10 @@ commands:
- string
- array<string>
- object
+ isTimeseriesNamespace:
+ description: "This flag is set to true when the command was originally sent to
+ mongos on the time-series view, but got rewritten to target
+ time-series buckets namespace before being sent to shards."
+ type: bool
+ optional: true
reply_type: DropIndexesReply
diff --git a/src/mongo/db/list_indexes.idl b/src/mongo/db/list_indexes.idl
index 48900398be6..d4f2caf9d96 100644
--- a/src/mongo/db/list_indexes.idl
+++ b/src/mongo/db/list_indexes.idl
@@ -179,4 +179,10 @@ commands:
type: safeBool
optional: true
unstable: true
+ isTimeseriesNamespace:
+ description: "This flag is set to true when the command was originally sent to
+ mongos on the time-series view, but got rewritten to target
+ time-series buckets namespace before being sent to shards."
+ type: bool
+ optional: true
reply_type: ListIndexesReply
diff --git a/src/mongo/db/s/create_collection_coordinator.cpp b/src/mongo/db/s/create_collection_coordinator.cpp
index 2b40e7df204..9cc00c593f2 100644
--- a/src/mongo/db/s/create_collection_coordinator.cpp
+++ b/src/mongo/db/s/create_collection_coordinator.cpp
@@ -623,7 +623,7 @@ void CreateCollectionCoordinator::_checkCommandArguments(OperationContext* opCtx
str::stream() << "can't shard time-series collection " << nss(),
feature_flags::gFeatureFlagShardedTimeSeries.isEnabled(
serverGlobalParams.featureCompatibility) ||
- !timeseries::getTimeseriesOptions(opCtx, nss().getTimeseriesViewNamespace()));
+ !timeseries::getTimeseriesOptions(opCtx, nss(), false));
}
// Ensure the namespace is valid.
diff --git a/src/mongo/db/service_entry_point_common.cpp b/src/mongo/db/service_entry_point_common.cpp
index b0b2a905864..df094434a39 100644
--- a/src/mongo/db/service_entry_point_common.cpp
+++ b/src/mongo/db/service_entry_point_common.cpp
@@ -1506,7 +1506,18 @@ void ExecCommandDatabase::_initiateCommand() {
if (!opCtx->getClient()->isInDirectClient() &&
readConcernArgs.getLevel() != repl::ReadConcernLevel::kAvailableReadConcern &&
(iAmPrimary || (readConcernArgs.hasLevel() || readConcernArgs.getArgsAfterClusterTime()))) {
- oss.initializeClientRoutingVersionsFromCommand(_invocation->ns(), request.body);
+ // If a timeseries collection is sharded, only the buckets collection would be sharded. We
+ // expect all versioned commands to be sent over 'system.buckets' namespace. But it is
+ // possible that a stale mongos may send the request over a view namespace. In this case, we
+ // initialize the 'OperationShardingState' with buckets namespace.
+ auto bucketNss = _invocation->ns().makeTimeseriesBucketsNamespace();
+ auto namespaceForSharding = CollectionCatalog::get(opCtx)
+ ->lookupCollectionByNamespaceForRead(opCtx, bucketNss)
+ .get()
+ ? bucketNss
+ : _invocation->ns();
+
+ oss.initializeClientRoutingVersionsFromCommand(namespaceForSharding, request.body);
auto const shardingState = ShardingState::get(opCtx);
if (OperationShardingState::isOperationVersioned(opCtx) || oss.hasDbVersion()) {
diff --git a/src/mongo/db/timeseries/catalog_helper.cpp b/src/mongo/db/timeseries/catalog_helper.cpp
index 5ac55527f1a..4b577d55d30 100644
--- a/src/mongo/db/timeseries/catalog_helper.cpp
+++ b/src/mongo/db/timeseries/catalog_helper.cpp
@@ -38,8 +38,9 @@ namespace mongo {
namespace timeseries {
boost::optional<TimeseriesOptions> getTimeseriesOptions(OperationContext* opCtx,
- const NamespaceString& nss) {
- auto bucketsNs = nss.makeTimeseriesBucketsNamespace();
+ const NamespaceString& nss,
+ bool convertToBucketsNamespace) {
+ auto bucketsNs = convertToBucketsNamespace ? nss.makeTimeseriesBucketsNamespace() : nss;
auto bucketsColl =
CollectionCatalog::get(opCtx)->lookupCollectionByNamespaceForRead(opCtx, bucketsNs);
if (!bucketsColl) {
diff --git a/src/mongo/db/timeseries/catalog_helper.h b/src/mongo/db/timeseries/catalog_helper.h
index 969410d1c45..14cc681b4c3 100644
--- a/src/mongo/db/timeseries/catalog_helper.h
+++ b/src/mongo/db/timeseries/catalog_helper.h
@@ -46,7 +46,8 @@ namespace timeseries {
* collection. Otherwise returns boost::none.
*/
boost::optional<TimeseriesOptions> getTimeseriesOptions(OperationContext* opCtx,
- const NamespaceString& nss);
+ const NamespaceString& nss,
+ bool convertToBucketsNamespace);
} // namespace timeseries
} // namespace mongo
diff --git a/src/mongo/db/timeseries/timeseries_commands_conversion_helper.cpp b/src/mongo/db/timeseries/timeseries_commands_conversion_helper.cpp
index 233add4ada4..79d8483ab6f 100644
--- a/src/mongo/db/timeseries/timeseries_commands_conversion_helper.cpp
+++ b/src/mongo/db/timeseries/timeseries_commands_conversion_helper.cpp
@@ -41,9 +41,17 @@
namespace mongo::timeseries {
+namespace {
+NamespaceString makeTimeseriesBucketsNamespace(const NamespaceString& nss) {
+ return nss.isTimeseriesBucketsCollection() ? nss : nss.makeTimeseriesBucketsNamespace();
+}
+} // namespace
+
+
BSONObj makeTimeseriesCommand(const BSONObj& origCmd,
const NamespaceString& ns,
- const StringData nsFieldName) {
+ const StringData nsFieldName,
+ boost::optional<StringData> appendTimeSeriesFlag) {
// Translate time-series collection view namespace to bucket namespace.
const auto bucketNs = ns.makeTimeseriesBucketsNamespace();
BSONObjBuilder builder;
@@ -54,6 +62,10 @@ BSONObj makeTimeseriesCommand(const BSONObj& origCmd,
builder.append(entry);
}
}
+
+ if (appendTimeSeriesFlag) {
+ builder.append(*appendTimeSeriesFlag, true);
+ }
return builder.obj();
}
@@ -137,7 +149,7 @@ CreateIndexesCommand makeTimeseriesCreateIndexesCommand(OperationContext* opCtx,
indexes.push_back(builder.obj());
}
- auto ns = origNs.makeTimeseriesBucketsNamespace();
+ auto ns = makeTimeseriesBucketsNamespace(origNs);
auto cmd = CreateIndexesCommand(ns, std::move(indexes));
cmd.setV(origCmd.getV());
cmd.setIgnoreUnknownIndexOptions(origCmd.getIgnoreUnknownIndexOptions());
@@ -150,7 +162,7 @@ DropIndexes makeTimeseriesDropIndexesCommand(OperationContext* opCtx,
const DropIndexes& origCmd,
const TimeseriesOptions& options) {
const auto& origNs = origCmd.getNamespace();
- auto ns = origNs.makeTimeseriesBucketsNamespace();
+ auto ns = makeTimeseriesBucketsNamespace(origNs);
const auto& origIndex = origCmd.getIndex();
if (auto keyPtr = stdx::get_if<BSONObj>(&origIndex)) {
diff --git a/src/mongo/db/timeseries/timeseries_commands_conversion_helper.h b/src/mongo/db/timeseries/timeseries_commands_conversion_helper.h
index eef1de61621..d2641a17d65 100644
--- a/src/mongo/db/timeseries/timeseries_commands_conversion_helper.h
+++ b/src/mongo/db/timeseries/timeseries_commands_conversion_helper.h
@@ -43,7 +43,8 @@ namespace mongo::timeseries {
*/
BSONObj makeTimeseriesCommand(const BSONObj& origCmd,
const NamespaceString& ns,
- StringData nsFieldName);
+ StringData nsFieldName,
+ boost::optional<StringData> appendTimeSeriesFlag);
/*
* Returns a CreateIndexesCommand for creating indexes on the bucket collection.