diff options
author | Benety Goh <benety@mongodb.com> | 2022-08-28 20:02:36 -0400 |
---|---|---|
committer | Evergreen Agent <no-reply@evergreen.mongodb.com> | 2022-10-12 13:01:02 +0000 |
commit | 6e10f7705e4eff4738291af5a5fc2aa58bca9d5c (patch) | |
tree | a31268b0145605e580b0967274dd3d5144bef721 | |
parent | 241025fb42cf3760adfa6e266621af4a9f0bbe9f (diff) | |
download | mongo-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.js | 12 | ||||
-rw-r--r-- | src/mongo/db/catalog/coll_mod.cpp | 7 | ||||
-rw-r--r-- | src/mongo/db/catalog/create_collection.cpp | 5 | ||||
-rw-r--r-- | src/mongo/db/catalog/index_key_validate.cpp | 42 | ||||
-rw-r--r-- | src/mongo/db/catalog/index_key_validate.h | 9 |
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 |