diff options
Diffstat (limited to 'src/mongo/db/catalog/create_collection.cpp')
-rw-r--r-- | src/mongo/db/catalog/create_collection.cpp | 69 |
1 files changed, 40 insertions, 29 deletions
diff --git a/src/mongo/db/catalog/create_collection.cpp b/src/mongo/db/catalog/create_collection.cpp index 26d24f6f812..d64c3567ed2 100644 --- a/src/mongo/db/catalog/create_collection.cpp +++ b/src/mongo/db/catalog/create_collection.cpp @@ -118,10 +118,13 @@ Status _createView(OperationContext* opCtx, Status _createTimeseries(OperationContext* opCtx, const NamespaceString& ns, - CollectionOptions&& options) { - auto bucketsNs = ns.makeTimeseriesBucketsNamespace(); + const CollectionOptions& options) { + // This path should only be taken when a user creates a new time-series collection on the + // primary. Secondaries replicate individual oplog entries. + invariant(!ns.isTimeseriesBucketsCollection()); + invariant(opCtx->writesAreReplicated()); - options.viewOn = bucketsNs.coll().toString(); + auto bucketsNs = ns.makeTimeseriesBucketsNamespace(); auto granularity = options.timeseries->getGranularity(); uassert(ErrorCodes::InvalidOptions, @@ -133,18 +136,6 @@ Status _createTimeseries(OperationContext* opCtx, "Time-series 'bucketMaxSpanSeconds' is required to be 3600", bucketMaxSpan == 3600); - if (options.timeseries->getMetaField()) { - options.pipeline = - BSON_ARRAY(BSON("$_internalUnpackBucket" - << BSON("timeField" << options.timeseries->getTimeField() << "metaField" - << *options.timeseries->getMetaField() << "exclude" - << BSONArray()))); - } else { - options.pipeline = BSON_ARRAY( - BSON("$_internalUnpackBucket" << BSON("timeField" << options.timeseries->getTimeField() - << "exclude" << BSONArray()))); - } - return writeConflictRetry(opCtx, "create", ns.ns(), [&]() -> Status { AutoGetCollection autoColl(opCtx, ns, MODE_IX, AutoGetCollectionViewMode::kViewsPermitted); Lock::CollectionLock bucketsCollLock(opCtx, bucketsNs, MODE_IX); @@ -202,7 +193,8 @@ Status _createTimeseries(OperationContext* opCtx, Top::get(serviceContext).collectionDropped(bucketsNs); }); - CollectionOptions bucketsOptions; + // Use the provided options to create the buckets collection. + CollectionOptions bucketsOptions = options; // Set the validator option to a JSON schema enforcing constraints on bucket documents. // This validation is only structural to prevent accidental corruption by users and cannot @@ -259,18 +251,19 @@ Status _createTimeseries(OperationContext* opCtx, bucketsOptions.clusteredIndex = clusteredOptions; } + // Once accepted by the create command, the 'expireAfterSeconds' option is either stored + // in the 'clusteredIndex' document or in the TTL index. To avoid storing redundant data, + // clear this field. + bucketsOptions.timeseries->setExpireAfterSeconds(boost::none); + // Create the buckets collection that will back the view. const bool createIdIndex = !useClusteredIdIndex; - auto bucketsCollection = - db->createCollection(opCtx, bucketsNs, bucketsOptions, createIdIndex); - invariant(bucketsCollection, - str::stream() << "Failed to create buckets collection " << bucketsNs - << " for time-series collection " << ns); + uassertStatusOK(db->userCreateNS(opCtx, bucketsNs, bucketsOptions, createIdIndex)); // Create a TTL index on 'control.min.[timeField]' if 'expireAfterSeconds' is provided and // the collection is not clustered by _id. - if (expireAfterSeconds && !bucketsOptions.clusteredIndex) { - CollectionWriter collectionWriter(opCtx, bucketsCollection->uuid()); + if (expireAfterSeconds && !useClusteredIdIndex) { + CollectionWriter collectionWriter(opCtx, bucketsNs); auto indexBuildCoord = IndexBuildsCoordinator::get(opCtx); const std::string controlMinTimeField = str::stream() << "control.min." << options.timeseries->getTimeField(); @@ -292,13 +285,28 @@ Status _createTimeseries(OperationContext* opCtx, } } - // Create the time-series view. Even though 'options' is passed by rvalue reference, it is - // not safe to move because 'userCreateNS' may throw a WriteConflictException. - auto status = db->userCreateNS(opCtx, ns, options); + CollectionOptions viewOptions; + viewOptions.viewOn = bucketsNs.coll().toString(); + viewOptions.collation = options.collation; + + if (options.timeseries->getMetaField()) { + viewOptions.pipeline = + BSON_ARRAY(BSON("$_internalUnpackBucket" << BSON( + "timeField" << options.timeseries->getTimeField() << "metaField" + << *options.timeseries->getMetaField() << "exclude" + << BSONArray()))); + } else { + viewOptions.pipeline = BSON_ARRAY(BSON( + "$_internalUnpackBucket" << BSON("timeField" << options.timeseries->getTimeField() + << "exclude" << BSONArray()))); + } + + // Create the time-series view. + auto status = db->userCreateNS(opCtx, ns, viewOptions); if (!status.isOK()) { return status.withContext(str::stream() << "Failed to create view on " << bucketsNs << " for time-series collection " << ns - << " with options " << options.toBSON()); + << " with options " << viewOptions.toBSON()); } wuow.commit(); @@ -401,12 +409,15 @@ Status createCollection(OperationContext* opCtx, "transaction.", !opCtx->inMultiDocumentTransaction()); return _createView(opCtx, ns, std::move(options)); - } else if (options.timeseries) { + } else if (options.timeseries && !ns.isTimeseriesBucketsCollection()) { + // This helper is designed for user-created time-series collections on primaries. If a + // time-series buckets collection is created explicitly or during replication, treat this as + // a normal collection creation. uassert(ErrorCodes::OperationNotSupportedInTransaction, str::stream() << "Cannot create a time-series collection in a multi-document transaction.", !opCtx->inMultiDocumentTransaction()); - return _createTimeseries(opCtx, ns, std::move(options)); + return _createTimeseries(opCtx, ns, options); } else { uassert(ErrorCodes::OperationNotSupportedInTransaction, str::stream() << "Cannot create system collection " << ns |