summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGregory Wlodarek <gregory.wlodarek@mongodb.com>2021-02-23 15:26:00 +0000
committerEvergreen Agent <no-reply@evergreen.mongodb.com>2021-02-24 15:52:05 +0000
commit31c6ccd2a320dde3af9e5a0e2f32682e8e0412fa (patch)
tree9bc6e293418f6fa6b726c30af671b51b344e9025
parente87efcb9acec942047711c13701ffae4cea7160f (diff)
downloadmongo-31c6ccd2a320dde3af9e5a0e2f32682e8e0412fa.tar.gz
SERVER-54727 Make resumable index builds work on time-series collections
-rw-r--r--jstests/noPassthrough/libs/index_build.js38
-rw-r--r--jstests/noPassthrough/resumable_timeseries_index_build_collection_scan_phase.js64
-rw-r--r--src/mongo/db/catalog/multi_index_block.cpp11
-rw-r--r--src/mongo/db/index_builds_coordinator.cpp17
-rw-r--r--src/mongo/db/resumable_index_builds.idl3
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"