summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBenety Goh <benety@mongodb.com>2022-08-28 20:02:36 -0400
committerEvergreen Agent <no-reply@evergreen.mongodb.com>2022-10-12 13:01:02 +0000
commit6e10f7705e4eff4738291af5a5fc2aa58bca9d5c (patch)
treea31268b0145605e580b0967274dd3d5144bef721
parent241025fb42cf3760adfa6e266621af4a9f0bbe9f (diff)
downloadmongo-6e10f7705e4eff4738291af5a5fc2aa58bca9d5c.tar.gz
SERVER-68477 remove epoch restriction on ttl indexes
(cherry picked from commit eb2f7f03f8c0522f85a9cae2c61bec4673251103) (cherry picked from commit 17d15eb7f3d8b044456ea9191d5777af3fbc5651)
-rw-r--r--jstests/core/ttl_index_options.js12
-rw-r--r--src/mongo/db/catalog/coll_mod.cpp7
-rw-r--r--src/mongo/db/catalog/create_collection.cpp5
-rw-r--r--src/mongo/db/catalog/index_key_validate.cpp42
-rw-r--r--src/mongo/db/catalog/index_key_validate.h9
5 files changed, 53 insertions, 22 deletions
diff --git a/jstests/core/ttl_index_options.js b/jstests/core/ttl_index_options.js
index 47ae2709073..8095c809a10 100644
--- a/jstests/core/ttl_index_options.js
+++ b/jstests/core/ttl_index_options.js
@@ -1,7 +1,10 @@
/**
* Ensures that the options passed in for TTL indexes are validated during index creation.
*
- * @tags: [requires_ttl_index]
+ * @tags: [
+ * requires_fcv_50,
+ * requires_ttl_index,
+ * ]
*/
(function() {
'use strict';
@@ -16,11 +19,10 @@ assert.commandFailedWithCode(
assert.commandFailedWithCode(coll.createIndexes([{x: 1}], {expireAfterSeconds: 9999999999999999}),
ErrorCodes.CannotCreateIndex);
-// Ensure that we cannot provide a time that is larger than the current epoch time.
+// Ensure that we can provide a time that is larger than the current epoch time.
let secondsSinceEpoch = Date.now() / 1000;
-assert.commandFailedWithCode(
- coll.createIndexes([{x: 1}], {expireAfterSeconds: secondsSinceEpoch + 1000}),
- ErrorCodes.CannotCreateIndex);
+assert.commandWorked(
+ coll.createIndexes([{x_before_epoch: 1}], {expireAfterSeconds: secondsSinceEpoch + 1000}));
// 'expireAfterSeconds' cannot be less than 0.
assert.commandFailedWithCode(coll.createIndexes([{x: 1}], {expireAfterSeconds: -1}),
diff --git a/src/mongo/db/catalog/coll_mod.cpp b/src/mongo/db/catalog/coll_mod.cpp
index a2b4505a04f..248a663806d 100644
--- a/src/mongo/db/catalog/coll_mod.cpp
+++ b/src/mongo/db/catalog/coll_mod.cpp
@@ -229,6 +229,9 @@ StatusWith<CollModRequest> parseCollModRequest(OperationContext* opCtx,
"existing expireAfterSeconds field is not a number");
}
}
+
+ // No additional validation is required on 'expireAfterSeconds' because this option
+ // is defined as a safeInt in the IDL (coll_mod.idl).
}
if (cmr.indexHidden) {
@@ -329,7 +332,9 @@ StatusWith<CollModRequest> parseCollModRequest(OperationContext* opCtx,
} else {
invariant(e.type() == mongo::NumberLong);
const int64_t elemNum = e.safeNumberLong();
- uassertStatusOK(index_key_validate::validateExpireAfterSeconds(elemNum));
+ uassertStatusOK(index_key_validate::validateExpireAfterSeconds(
+ elemNum,
+ index_key_validate::ValidateExpireAfterSecondsMode::kClusteredTTLIndex));
}
cmr.clusteredIndexExpireAfterSeconds = e;
diff --git a/src/mongo/db/catalog/create_collection.cpp b/src/mongo/db/catalog/create_collection.cpp
index 461efd0bcb6..52c5aef567c 100644
--- a/src/mongo/db/catalog/create_collection.cpp
+++ b/src/mongo/db/catalog/create_collection.cpp
@@ -257,8 +257,9 @@ Status _createTimeseries(OperationContext* opCtx,
auto expireAfterSeconds = options.expireAfterSeconds;
if (useClusteredIdIndex) {
if (expireAfterSeconds) {
- uassertStatusOK(
- index_key_validate::validateExpireAfterSeconds(*expireAfterSeconds));
+ uassertStatusOK(index_key_validate::validateExpireAfterSeconds(
+ *expireAfterSeconds,
+ index_key_validate::ValidateExpireAfterSecondsMode::kClusteredTTLIndex));
bucketsOptions.expireAfterSeconds = expireAfterSeconds;
}
bucketsOptions.clusteredIndex = true;
diff --git a/src/mongo/db/catalog/index_key_validate.cpp b/src/mongo/db/catalog/index_key_validate.cpp
index e85fa1336d7..c6509e3a4b9 100644
--- a/src/mongo/db/catalog/index_key_validate.cpp
+++ b/src/mongo/db/catalog/index_key_validate.cpp
@@ -689,7 +689,8 @@ StatusWith<BSONObj> validateIndexSpecCollation(OperationContext* opCtx,
return indexSpec;
}
-Status validateExpireAfterSeconds(std::int64_t expireAfterSeconds) {
+Status validateExpireAfterSeconds(std::int64_t expireAfterSeconds,
+ ValidateExpireAfterSecondsMode mode) {
if (expireAfterSeconds < 0) {
return {ErrorCodes::InvalidOptions,
str::stream() << "TTL index '" << IndexDescriptor::kExpireAfterSecondsFieldName
@@ -700,16 +701,31 @@ Status validateExpireAfterSeconds(std::int64_t expireAfterSeconds) {
<< "TTL index '" << IndexDescriptor::kExpireAfterSecondsFieldName
<< "' option must be within an acceptable range, try a lower number";
- // There are two cases where we can encounter an issue here.
- // The first case is when we try to cast to millseconds from seconds, which could cause an
- // overflow. The second case is where 'expireAfterSeconds' is larger than the current epoch
- // time.
- if (expireAfterSeconds > std::numeric_limits<std::int64_t>::max() / 1000) {
- return {ErrorCodes::InvalidOptions, tooLargeErr};
- }
- auto expireAfterMillis = duration_cast<Milliseconds>(Seconds(expireAfterSeconds));
- if (expireAfterMillis > Date_t::now().toDurationSinceEpoch()) {
- return {ErrorCodes::InvalidOptions, tooLargeErr};
+ if (mode == ValidateExpireAfterSecondsMode::kSecondaryTTLIndex) {
+ // Relax epoch restriction on TTL indexes. This allows us to export and import existing
+ // TTL indexes with large values or NaN for the 'expireAfterSeconds' field.
+ // Additionally, the 'expireAfterSeconds' for TTL indexes is defined as safeInt (int32_t)
+ // in the IDL for listIndexes and collMod. See list_indexes.idl and coll_mod.idl.
+ if (expireAfterSeconds > std::numeric_limits<std::int32_t>::max()) {
+ return {ErrorCodes::InvalidOptions, tooLargeErr};
+ }
+ } else {
+ // Clustered collections with TTL.
+ // Note that 'expireAfterSeconds' is defined as safeInt64 in the IDL for the create and
+ // collMod commands. See create.idl and coll_mod.idl.
+ // There are two cases where we can encounter an issue here.
+ // The first case is when we try to cast to millseconds from seconds, which could cause an
+ // overflow. The second case is where 'expireAfterSeconds' is larger than the current epoch
+ // time. This isn't necessarily problematic for the general case, but for the specific case
+ // of time series collections, we cluster the collection by an OID value, where the
+ // timestamp portion is only a 32-bit unsigned integer offset of seconds since the epoch.
+ if (expireAfterSeconds > std::numeric_limits<std::int64_t>::max() / 1000) {
+ return {ErrorCodes::InvalidOptions, tooLargeErr};
+ }
+ auto expireAfterMillis = duration_cast<Milliseconds>(Seconds(expireAfterSeconds));
+ if (expireAfterMillis > Date_t::now().toDurationSinceEpoch()) {
+ return {ErrorCodes::InvalidOptions, tooLargeErr};
+ }
}
return Status::OK();
}
@@ -729,7 +745,9 @@ Status validateIndexSpecTTL(const BSONObj& indexSpec) {
<< "'. Index spec: " << indexSpec};
}
- if (auto status = validateExpireAfterSeconds(expireAfterSecondsElt.safeNumberLong());
+ if (auto status =
+ validateExpireAfterSeconds(expireAfterSecondsElt.safeNumberLong(),
+ ValidateExpireAfterSecondsMode::kSecondaryTTLIndex);
!status.isOK()) {
return {ErrorCodes::CannotCreateIndex,
str::stream() << status.reason() << "index spec: " << indexSpec};
diff --git a/src/mongo/db/catalog/index_key_validate.h b/src/mongo/db/catalog/index_key_validate.h
index bc344e42041..dc3dc2bd6e6 100644
--- a/src/mongo/db/catalog/index_key_validate.h
+++ b/src/mongo/db/catalog/index_key_validate.h
@@ -126,9 +126,14 @@ StatusWith<BSONObj> validateIndexSpecCollation(OperationContext* opCtx,
const CollatorInterface* defaultCollator);
/**
- * Validates the the 'expireAfterSeconds' value for a TTL index..
+ * Validates the the 'expireAfterSeconds' value for a TTL index or clustered collection.
*/
-Status validateExpireAfterSeconds(std::int64_t expireAfterSeconds);
+enum class ValidateExpireAfterSecondsMode {
+ kSecondaryTTLIndex,
+ kClusteredTTLIndex,
+};
+Status validateExpireAfterSeconds(std::int64_t expireAfterSeconds,
+ ValidateExpireAfterSecondsMode mode);
/**
* Validates the key pattern and the 'expireAfterSeconds' duration in the index specification