diff options
author | Gregory Wlodarek <gregory.wlodarek@mongodb.com> | 2021-02-23 15:26:00 +0000 |
---|---|---|
committer | Evergreen Agent <no-reply@evergreen.mongodb.com> | 2021-02-24 15:52:05 +0000 |
commit | 31c6ccd2a320dde3af9e5a0e2f32682e8e0412fa (patch) | |
tree | 9bc6e293418f6fa6b726c30af671b51b344e9025 | |
parent | e87efcb9acec942047711c13701ffae4cea7160f (diff) | |
download | mongo-31c6ccd2a320dde3af9e5a0e2f32682e8e0412fa.tar.gz |
SERVER-54727 Make resumable index builds work on time-series collections
-rw-r--r-- | jstests/noPassthrough/libs/index_build.js | 38 | ||||
-rw-r--r-- | jstests/noPassthrough/resumable_timeseries_index_build_collection_scan_phase.js | 64 | ||||
-rw-r--r-- | src/mongo/db/catalog/multi_index_block.cpp | 11 | ||||
-rw-r--r-- | src/mongo/db/index_builds_coordinator.cpp | 17 | ||||
-rw-r--r-- | src/mongo/db/resumable_index_builds.idl | 3 |
5 files changed, 115 insertions, 18 deletions
diff --git a/jstests/noPassthrough/libs/index_build.js b/jstests/noPassthrough/libs/index_build.js index b3ef206858d..7fe4ed625c8 100644 --- a/jstests/noPassthrough/libs/index_build.js +++ b/jstests/noPassthrough/libs/index_build.js @@ -137,6 +137,28 @@ var IndexBuildTest = class { } /** + * Returns true if the passed in collection is clustered. + */ + static isCollectionClustered(coll) { + const res = assert.commandWorked( + coll.getDB().runCommand({listCollections: 1, filter: {name: coll.getName()}})); + assert.eq(1, res.cursor.firstBatch.length); + + const collInfo = res.cursor.firstBatch[0]; + return collInfo.options.hasOwnProperty("clusteredIndex"); + } + + static assertIndexesIdHelper(coll, numIndexes, readyIndexes, notReadyIndexes, options) { + if (!IndexBuildTest.isCollectionClustered(coll)) { + numIndexes++; + readyIndexes.concat("_id_"); + } + + return IndexBuildTest.assertIndexes( + coll, numIndexes, readyIndexes, notReadyIndexes, options); + } + + /** * Runs listIndexes command on collection. * If 'options' is provided, these will be sent along with the command request. * Asserts that all the indexes on this collection fit within the first batch of results. @@ -253,7 +275,7 @@ const ResumableIndexBuildTest = class { indexNamesFlatSinglePerBuild[i] = indexNames[i][0]; buildUUIDs[i] = extractUUIDFromObject( IndexBuildTest - .assertIndexes(coll, indexNamesFlat.length + 1, ["_id_"], indexNamesFlat, { + .assertIndexesIdHelper(coll, indexNamesFlat.length, [], indexNamesFlat, { includeBuildUUIDs: true })[indexNames[i][0]] .buildUUID); @@ -371,11 +393,9 @@ const ResumableIndexBuildTest = class { const indexNamesFlat = ResumableIndexBuildTest.flatten(indexNames); let indexes; assert.soonNoExcept(function() { - indexes = IndexBuildTest.assertIndexes(coll, - indexNamesFlat.length + 1, - ["_id_"], - indexNamesFlat, - {includeBuildUUIDs: true}); + indexes = IndexBuildTest.assertIndexesIdHelper( + coll, indexNamesFlat.length, [], indexNamesFlat, {includeBuildUUIDs: true}); + return true; }); @@ -430,8 +450,7 @@ const ResumableIndexBuildTest = class { namespace: coll.getFullName() }); } - - IndexBuildTest.assertIndexes(coll, indexNames.length + 1, indexNames.concat("_id_")); + IndexBuildTest.assertIndexesIdHelper(coll, indexNames.length, indexNames); } /** @@ -712,7 +731,8 @@ const ResumableIndexBuildTest = class { const buildUUID = extractUUIDFromObject( IndexBuildTest - .assertIndexes(coll, 2, ["_id_"], [indexName], {includeBuildUUIDs: true})[indexName] + .assertIndexesIdHelper( + coll, 1, [], [indexName], {includeBuildUUIDs: true})[indexName] .buildUUID); clearRawMongoProgramOutput(); diff --git a/jstests/noPassthrough/resumable_timeseries_index_build_collection_scan_phase.js b/jstests/noPassthrough/resumable_timeseries_index_build_collection_scan_phase.js new file mode 100644 index 00000000000..a6f9bf3e2d9 --- /dev/null +++ b/jstests/noPassthrough/resumable_timeseries_index_build_collection_scan_phase.js @@ -0,0 +1,64 @@ +/** + * Tests that resumable index builds on time-series collections in the collection scan phase write + * their state to disk upon clean shutdown and are resumed from the same phase to completion when + * the node is started back up. + * + * @tags: [ + * requires_majority_read_concern, + * requires_persistence, + * requires_replication, + * requires_wiredtiger, + * sbe_incompatible, + * ] + */ +(function() { +"use strict"; + +load("jstests/noPassthrough/libs/index_build.js"); +load("jstests/core/timeseries/libs/timeseries.js"); + +const rst = new ReplSetTest({nodes: 1}); +rst.startSet(); +rst.initiate(); + +const dbName = "test"; +const db = rst.getPrimary().getDB(dbName); + +if (!TimeseriesTest.timeseriesCollectionsEnabled(db.getMongo())) { + jsTestLog("Skipping test because the time-series collection feature flag is disabled"); + rst.stopSet(); + return; +} + +const coll = db.timeseries_resumable_index_build; +coll.drop(); + +const timeFieldName = "time"; +const metaFieldName = 'meta'; +assert.commandWorked(db.createCollection( + coll.getName(), {timeseries: {timeField: timeFieldName, metaField: metaFieldName}})); + +// Use different metadata fields to guarantee creating three individual buckets in the buckets +// collection. +for (let i = 0; i < 3; i++) { + assert.commandWorked(coll.insert({ + _id: i, + measurement: "measurement", + time: ISODate(), + meta: i, + })); +} + +const bucketColl = db.getCollection("system.buckets." + coll.getName()); +ResumableIndexBuildTest.run( + rst, + dbName, + bucketColl.getName(), + [[{"control.min.time": 1}, {"control.max.time": 1}]], + [{name: "hangIndexBuildDuringCollectionScanPhaseAfterInsertion", logIdWithBuildUUID: 20386}], + /*iteration=*/0, + ["collection scan"], + [{numScannedAferResume: 2}]); + +rst.stopSet(); +})();
\ No newline at end of file diff --git a/src/mongo/db/catalog/multi_index_block.cpp b/src/mongo/db/catalog/multi_index_block.cpp index 506136851e4..6076b502305 100644 --- a/src/mongo/db/catalog/multi_index_block.cpp +++ b/src/mongo/db/catalog/multi_index_block.cpp @@ -897,8 +897,15 @@ BSONObj MultiIndexBlock::_constructStateObject(OperationContext* opCtx, // We can be interrupted by shutdown before inserting the first document from the collection // scan, in which case there is no _lastRecordIdInserted. - if (_phase == IndexBuildPhaseEnum::kCollectionScan && _lastRecordIdInserted) - builder.append("collectionScanPosition", _lastRecordIdInserted->asLong()); + if (_phase == IndexBuildPhaseEnum::kCollectionScan && _lastRecordIdInserted) { + _lastRecordIdInserted->withFormat( + [](RecordId::Null n) { invariant(false); }, + [&](int64_t rid) { builder.append("collectionScanPosition", rid); }, + [&](const char* str, int size) { + OID oid = OID::from(str); + builder.appendOID("collectionScanPosition", &oid); + }); + } BSONArrayBuilder indexesArray(builder.subarrayStart("indexes")); for (const auto& index : _indexes) { diff --git a/src/mongo/db/index_builds_coordinator.cpp b/src/mongo/db/index_builds_coordinator.cpp index 63632382041..4fa1338c73e 100644 --- a/src/mongo/db/index_builds_coordinator.cpp +++ b/src/mongo/db/index_builds_coordinator.cpp @@ -2178,12 +2178,17 @@ void IndexBuildsCoordinator::_resumeIndexBuildFromPhase( if (resumeInfo.getPhase() == IndexBuildPhaseEnum::kInitialized || resumeInfo.getPhase() == IndexBuildPhaseEnum::kCollectionScan) { - _scanCollectionAndInsertSortedKeysIntoIndex( - opCtx, - replState, - resumeInfo.getCollectionScanPosition() - ? boost::make_optional<RecordId>(RecordId(*resumeInfo.getCollectionScanPosition())) - : boost::none); + boost::optional<RecordId> resumeAfterRecordId; + if (resumeInfo.getCollectionScanPosition()) { + auto scanPosition = *resumeInfo.getCollectionScanPosition(); + if (auto recordIdOIDPtr = stdx::get_if<OID>(&scanPosition)) { + resumeAfterRecordId.emplace(recordIdOIDPtr->view().view(), OID::kOIDSize); + } else if (auto recordIdLongPtr = stdx::get_if<int64_t>(&scanPosition)) { + resumeAfterRecordId.emplace(RecordId(*recordIdLongPtr)); + } + } + + _scanCollectionAndInsertSortedKeysIntoIndex(opCtx, replState, resumeAfterRecordId); } else if (resumeInfo.getPhase() == IndexBuildPhaseEnum::kBulkLoad) { _insertSortedKeysIntoIndexForResume(opCtx, replState); } diff --git a/src/mongo/db/resumable_index_builds.idl b/src/mongo/db/resumable_index_builds.idl index 8ae9bb0844e..6e940dc3c02 100644 --- a/src/mongo/db/resumable_index_builds.idl +++ b/src/mongo/db/resumable_index_builds.idl @@ -116,7 +116,8 @@ structs: type: uuid collectionScanPosition: description: "The last record id inserted into the sorter before shutdown" - type: long + type: + variant: [objectid, long] optional: true indexes: description: "The information needed to resume each specific index in this build" |