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-09-11 18:36:57 +0000
commit17d15eb7f3d8b044456ea9191d5777af3fbc5651 (patch)
tree47818747ccb1bb1952c5ca96a9392d80d7064f5e
parent6a1b08c8108635be9a7624243ca9f4ff191fd48c (diff)
downloadmongo-17d15eb7f3d8b044456ea9191d5777af3fbc5651.tar.gz
SERVER-68477 remove epoch restriction on ttl indexes
(cherry picked from commit eb2f7f03f8c0522f85a9cae2c61bec4673251103)
-rw-r--r--jstests/core/collmod_convert_to_ttl.js7
-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.cpp9
-rw-r--r--src/mongo/db/catalog/index_key_validate.cpp42
-rw-r--r--src/mongo/db/catalog/index_key_validate.h9
6 files changed, 55 insertions, 31 deletions
diff --git a/jstests/core/collmod_convert_to_ttl.js b/jstests/core/collmod_convert_to_ttl.js
index ab617d3eeb7..c4c8b7d2bbd 100644
--- a/jstests/core/collmod_convert_to_ttl.js
+++ b/jstests/core/collmod_convert_to_ttl.js
@@ -39,13 +39,6 @@ assert.commandFailedWithCode(
db.runCommand({"collMod": collName, "index": {"keyPattern": {a: 1}, "expireAfterSeconds": -1}}),
ErrorCodes.InvalidOptions);
-// Tries to modify with an 'expireAfterSeconds' value too large.
-assert.commandFailedWithCode(db.runCommand({
- "collMod": collName,
- "index": {"keyPattern": {a: 1}, "expireAfterSeconds": 10000000000000}
-}),
- ErrorCodes.InvalidOptions);
-
// Successfully converts to a TTL index.
assert.commandWorked(db.runCommand(
{"collMod": collName, "index": {"keyPattern": {a: 1}, "expireAfterSeconds": 100}}));
diff --git a/jstests/core/ttl_index_options.js b/jstests/core/ttl_index_options.js
index 47ae2709073..ea35a93bec2 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_60,
+ * 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 21b5d6dc920..9fc32c239cd 100644
--- a/src/mongo/db/catalog/coll_mod.cpp
+++ b/src/mongo/db/catalog/coll_mod.cpp
@@ -250,7 +250,8 @@ StatusWith<std::pair<ParsedCollModRequest, BSONObj>> parseCollModRequest(Operati
"TTL indexes are not supported for capped collections."};
}
if (auto status = index_key_validate::validateExpireAfterSeconds(
- *cmdIndex.getExpireAfterSeconds());
+ *cmdIndex.getExpireAfterSeconds(),
+ index_key_validate::ValidateExpireAfterSecondsMode::kSecondaryTTLIndex);
!status.isOK()) {
return {ErrorCodes::InvalidOptions, status.reason()};
}
@@ -533,7 +534,9 @@ StatusWith<std::pair<ParsedCollModRequest, BSONObj>> parseCollModRequest(Operati
},
[&oplogEntryBuilder](std::int64_t value) {
oplogEntryBuilder.append(CollMod::kExpireAfterSecondsFieldName, value);
- return index_key_validate::validateExpireAfterSeconds(value);
+ return index_key_validate::validateExpireAfterSeconds(
+ value,
+ index_key_validate::ValidateExpireAfterSecondsMode::kClusteredTTLIndex);
},
},
*expireAfterSeconds);
diff --git a/src/mongo/db/catalog/create_collection.cpp b/src/mongo/db/catalog/create_collection.cpp
index 74be2bd73e4..0a4400680b2 100644
--- a/src/mongo/db/catalog/create_collection.cpp
+++ b/src/mongo/db/catalog/create_collection.cpp
@@ -117,7 +117,9 @@ Status validateClusteredIndexSpec(OperationContext* opCtx,
if (expireAfterSeconds) {
// Not included in the indexSpec itself.
- auto status = index_key_validate::validateExpireAfterSeconds(*expireAfterSeconds);
+ auto status = index_key_validate::validateExpireAfterSeconds(
+ *expireAfterSeconds,
+ index_key_validate::ValidateExpireAfterSecondsMode::kClusteredTTLIndex);
if (!status.isOK()) {
return status;
}
@@ -321,8 +323,9 @@ Status _createTimeseries(OperationContext* opCtx,
// Cluster time-series buckets collections by _id.
auto expireAfterSeconds = options.expireAfterSeconds;
if (expireAfterSeconds) {
- uassertStatusOK(
- index_key_validate::validateExpireAfterSeconds(*expireAfterSeconds));
+ uassertStatusOK(index_key_validate::validateExpireAfterSeconds(
+ *expireAfterSeconds,
+ index_key_validate::ValidateExpireAfterSecondsMode::kClusteredTTLIndex));
bucketsOptions.expireAfterSeconds = expireAfterSeconds;
}
diff --git a/src/mongo/db/catalog/index_key_validate.cpp b/src/mongo/db/catalog/index_key_validate.cpp
index 501df1931bb..af71c30bea7 100644
--- a/src/mongo/db/catalog/index_key_validate.cpp
+++ b/src/mongo/db/catalog/index_key_validate.cpp
@@ -778,7 +778,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
@@ -789,16 +790,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();
}
@@ -822,7 +838,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 49ffa120e60..af819f4286a 100644
--- a/src/mongo/db/catalog/index_key_validate.h
+++ b/src/mongo/db/catalog/index_key_validate.h
@@ -127,9 +127,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);
/**
* Returns true if 'indexSpec' refers to a TTL index.