summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGregory Wlodarek <gregory.wlodarek@mongodb.com>2021-04-30 14:52:12 +0000
committerEvergreen Agent <no-reply@evergreen.mongodb.com>2021-05-03 22:34:19 +0000
commitca5df3a380da8e4230260aef1f0c2c24362efda9 (patch)
tree255846f83eb403a9c3733859d9098a1d4c2850cd
parent960f5deb14520af3076c9164fbf8b3cbcca0560f (diff)
downloadmongo-ca5df3a380da8e4230260aef1f0c2c24362efda9.tar.gz
SERVER-56062 CappedPositionLost during an index builds collection scan phase restarts
-rw-r--r--jstests/noPassthrough/index_build_capped_position_lost.js51
-rw-r--r--src/mongo/db/catalog/multi_index_block.cpp7
2 files changed, 56 insertions, 2 deletions
diff --git a/jstests/noPassthrough/index_build_capped_position_lost.js b/jstests/noPassthrough/index_build_capped_position_lost.js
new file mode 100644
index 00000000000..a1c7d454fb8
--- /dev/null
+++ b/jstests/noPassthrough/index_build_capped_position_lost.js
@@ -0,0 +1,51 @@
+/**
+ * Capped cursors return CappedPositionLost when the document they were positioned on gets deleted.
+ * When this occurs during the collection scan phase of an index build, it will get restarted.
+ */
+(function() {
+"use strict";
+
+load("jstests/libs/fail_point_util.js");
+load("jstests/noPassthrough/libs/index_build.js");
+
+const conn = MongoRunner.runMongod({});
+
+const dbName = "test";
+const collName = "index_build_capped_position_lost";
+
+const db = conn.getDB(dbName);
+assert.commandWorked(
+ db.createCollection(collName, {capped: true, size: 1024 * 1024 * 1024, max: 5}));
+
+const coll = db.getCollection(collName);
+
+for (let i = 0; i < 5; i++) {
+ assert.commandWorked(coll.insert({a: i}));
+}
+
+// Hang the collection scan phase of the index build when it's halfway finished.
+let fp = configureFailPoint(
+ conn, "hangIndexBuildDuringCollectionScanPhaseAfterInsertion", {fieldsToMatch: {a: 3}});
+
+const awaitCreateIndex = IndexBuildTest.startIndexBuild(conn, coll.getFullName(), {a: 1});
+fp.wait();
+
+// Rollover the capped collection.
+for (let i = 5; i < 10; i++) {
+ assert.commandWorked(coll.insert({a: i}));
+}
+
+fp.off();
+checkLog.containsJson(conn, 5470300, {
+ error: function(error) {
+ return error.code === ErrorCodes.CappedPositionLost;
+ }
+}); // Collection scan restarted.
+checkLog.containsJson(conn, 20391, {totalRecords: 5}); // Collection scan complete.
+
+awaitCreateIndex();
+
+IndexBuildTest.assertIndexes(coll, 2, ["_id_", "a_1"]);
+
+MongoRunner.stopMongod(conn);
+}());
diff --git a/src/mongo/db/catalog/multi_index_block.cpp b/src/mongo/db/catalog/multi_index_block.cpp
index a4c39f1d56f..fb51a69d4e7 100644
--- a/src/mongo/db/catalog/multi_index_block.cpp
+++ b/src/mongo/db/catalog/multi_index_block.cpp
@@ -444,10 +444,13 @@ Status MultiIndexBlock::insertAllDocumentsInCollection(
RecoveryUnit::toString(opCtx->recoveryUnit()->getTimestampReadSource()),
"duration"_attr = duration_cast<Milliseconds>(Seconds(timer.seconds())));
} catch (DBException& ex) {
- if (ex.code() == ErrorCodes::ReadConcernMajorityNotAvailableYet) {
+ if (ex.code() == ErrorCodes::ReadConcernMajorityNotAvailableYet ||
+ ex.code() == ErrorCodes::CappedPositionLost) {
// Forced replica set re-configs will clear the majority committed snapshot,
// which may be used by the collection scan. The collection scan will restart
- // from the beginning in this case.
+ // from the beginning in this case. Capped cursors are invalidated when the document
+ // they were positioned on gets deleted. The collection scan will restart in both
+ // cases.
restartCollectionScan = true;
logAndBackoff(
5470300,