summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGregory Noma <gregory.noma@gmail.com>2021-11-17 22:22:31 +0000
committerEvergreen Agent <no-reply@evergreen.mongodb.com>2021-12-03 21:30:06 +0000
commitc797110b90fd5d950fa5d78aa26ae198c76883c6 (patch)
treeafeb24e2f4e045d42c36bd998bdd4381b58fe2fa
parent1151e6fec016a7abed435b8a9c81cc9a00aea980 (diff)
downloadmongo-c797110b90fd5d950fa5d78aa26ae198c76883c6.tar.gz
SERVER-61427 Skip duplicate index key handler when exact key already exists in index
(cherry picked from commit 9f15b2b1eeb356f4e3d0c6e06302941ec685cc0a) (cherry picked from commit cbfe08edee4a6aa41694b73be690b4e56de0bb3a) (cherry picked from commit e729f76f2c5de716e0724651e14a51b7e424258a)
-rw-r--r--jstests/noPassthrough/unique_index_insert_during_collection_scan.js36
-rw-r--r--src/mongo/db/index/index_access_method.cpp7
-rw-r--r--src/mongo/db/storage/biggie/biggie_sorted_impl.cpp5
-rw-r--r--src/mongo/db/storage/ephemeral_for_test/ephemeral_for_test_btree_impl.cpp4
-rw-r--r--src/mongo/db/storage/sorted_data_interface.h6
-rw-r--r--src/mongo/db/storage/wiredtiger/wiredtiger_index.cpp32
6 files changed, 69 insertions, 21 deletions
diff --git a/jstests/noPassthrough/unique_index_insert_during_collection_scan.js b/jstests/noPassthrough/unique_index_insert_during_collection_scan.js
new file mode 100644
index 00000000000..e3c0ce98af4
--- /dev/null
+++ b/jstests/noPassthrough/unique_index_insert_during_collection_scan.js
@@ -0,0 +1,36 @@
+/**
+ * Tests inserting into collection while a unique index build is in the collection scan phase.
+ * Ensures that even though the insert is seen by both the collection scan and the side writes
+ * table, the index build does not need to resolve any duplicate keys.
+ */
+(function() {
+"use strict";
+
+load('jstests/libs/fail_point_util.js');
+load('jstests/libs/parallel_shell_helpers.js');
+
+const conn = MongoRunner.runMongod();
+const coll = conn.getDB('test')[jsTestName()];
+
+assert.commandWorked(coll.insert({_id: 0, a: 0}));
+
+const fp = configureFailPoint(conn, 'hangAfterSettingUpIndexBuild');
+const awaitCreateIndex =
+ startParallelShell(funWithArgs(function(collName) {
+ assert.commandWorked(db[collName].createIndex({a: 1}, {unique: true}));
+ }, coll.getName()), conn.port);
+fp.wait();
+assert.commandWorked(coll.insert({_id: 1, a: 1}));
+fp.off();
+
+awaitCreateIndex();
+
+// Ensure that the second document was seen by both the collection scan and the side writes table.
+checkLog.contains(conn, 'index build: inserted 2 keys from external sorter into index');
+checkLog.contains(conn, 'index build: drain applied 1 side writes');
+
+// Ensure that there were no duplicates to resolve.
+assert(!checkLog.checkContainsOnce(conn, 'duplicate key conflicts for unique index'));
+
+MongoRunner.stopMongod(conn);
+})();
diff --git a/src/mongo/db/index/index_access_method.cpp b/src/mongo/db/index/index_access_method.cpp
index b9024a5fd94..41ff27a6ddb 100644
--- a/src/mongo/db/index/index_access_method.cpp
+++ b/src/mongo/db/index/index_access_method.cpp
@@ -264,8 +264,13 @@ Status AbstractIndexAccessMethod::insertKeys(OperationContext* opCtx,
ret = _newInterface->insert(opCtx, key, loc, true /* dupsAllowed */);
status = ret.getStatus();
- if (status.isOK() && onDuplicateKey)
+ if (status.isOK() && ret.getValue() != SpecialFormatInserted::NothingInserted &&
+ onDuplicateKey) {
+ // Only run the duplicate key handler if we inserted the key ourselves. Someone
+ // else could have already inserted this exact key, but in that case we don't
+ // count it as a duplicate.
status = onDuplicateKey(key);
+ }
}
if (status.isOK() && ret.getValue() == SpecialFormatInserted::LongTypeBitsInserted)
diff --git a/src/mongo/db/storage/biggie/biggie_sorted_impl.cpp b/src/mongo/db/storage/biggie/biggie_sorted_impl.cpp
index 32f75c0ee6f..5946243cee2 100644
--- a/src/mongo/db/storage/biggie/biggie_sorted_impl.cpp
+++ b/src/mongo/db/storage/biggie/biggie_sorted_impl.cpp
@@ -329,8 +329,7 @@ StatusWith<SpecialFormatInserted> SortedDataInterface::insert(OperationContext*
key, _collectionNamespace, _indexName, _keyPattern, _collation);
}
} else {
- return StatusWith<SpecialFormatInserted>(
- SpecialFormatInserted::NoSpecialFormatInserted);
+ return StatusWith<SpecialFormatInserted>(SpecialFormatInserted::NothingInserted);
}
}
} else {
@@ -338,7 +337,7 @@ StatusWith<SpecialFormatInserted> SortedDataInterface::insert(OperationContext*
}
if (workingCopy->find(insertKeyString) != workingCopy->end())
- return StatusWith<SpecialFormatInserted>(SpecialFormatInserted::NoSpecialFormatInserted);
+ return StatusWith<SpecialFormatInserted>(SpecialFormatInserted::NothingInserted);
// The value we insert is the RecordId followed by the typebits.
std::string internalTbString = std::string(workingCopyInternalKs->getTypeBits().getBuffer(),
diff --git a/src/mongo/db/storage/ephemeral_for_test/ephemeral_for_test_btree_impl.cpp b/src/mongo/db/storage/ephemeral_for_test/ephemeral_for_test_btree_impl.cpp
index 3b5f6f2cd02..536aa443315 100644
--- a/src/mongo/db/storage/ephemeral_for_test/ephemeral_for_test_btree_impl.cpp
+++ b/src/mongo/db/storage/ephemeral_for_test/ephemeral_for_test_btree_impl.cpp
@@ -189,8 +189,10 @@ public:
if (_data->insert(entry).second) {
_currentKeySize += key.objsize();
opCtx->recoveryUnit()->registerChange(new IndexChange(_data, entry, true));
+ return StatusWith<SpecialFormatInserted>(
+ SpecialFormatInserted::NoSpecialFormatInserted);
}
- return StatusWith<SpecialFormatInserted>(SpecialFormatInserted::NoSpecialFormatInserted);
+ return StatusWith<SpecialFormatInserted>(SpecialFormatInserted::NothingInserted);
}
virtual void unindex(OperationContext* opCtx,
diff --git a/src/mongo/db/storage/sorted_data_interface.h b/src/mongo/db/storage/sorted_data_interface.h
index ee745bf7cfe..3f5d1b91a63 100644
--- a/src/mongo/db/storage/sorted_data_interface.h
+++ b/src/mongo/db/storage/sorted_data_interface.h
@@ -50,7 +50,11 @@ struct ValidateResults;
* is a way to inform the callers to do something when special format exists on disk.
* TODO SERVER-36385: Remove this enum in 4.4.
*/
-enum SpecialFormatInserted { NoSpecialFormatInserted = 0, LongTypeBitsInserted = 1 };
+enum SpecialFormatInserted {
+ NothingInserted = 0,
+ NoSpecialFormatInserted = 1,
+ LongTypeBitsInserted = 2,
+};
/**
* This is the uniform interface for storing indexes and supporting point queries as well as range
diff --git a/src/mongo/db/storage/wiredtiger/wiredtiger_index.cpp b/src/mongo/db/storage/wiredtiger/wiredtiger_index.cpp
index 9c1da2c5a3d..472269c05be 100644
--- a/src/mongo/db/storage/wiredtiger/wiredtiger_index.cpp
+++ b/src/mongo/db/storage/wiredtiger/wiredtiger_index.cpp
@@ -1441,16 +1441,13 @@ StatusWith<SpecialFormatInserted> WiredTigerIndexUnique::_insertTimestampUnsafe(
c->set_value(c, valueItem.Get());
int ret = WT_OP_CHECK(c->insert(c));
- if (ret != WT_DUPLICATE_KEY) {
- if (ret == 0) {
- if (data.getTypeBits().isLongEncoding())
- return StatusWith<SpecialFormatInserted>(
- SpecialFormatInserted::LongTypeBitsInserted);
- else
- return StatusWith<SpecialFormatInserted>(
- SpecialFormatInserted::NoSpecialFormatInserted);
- }
-
+ if (!ret) {
+ if (data.getTypeBits().isLongEncoding())
+ return StatusWith<SpecialFormatInserted>(SpecialFormatInserted::LongTypeBitsInserted);
+ else
+ return StatusWith<SpecialFormatInserted>(
+ SpecialFormatInserted::NoSpecialFormatInserted);
+ } else if (ret != WT_DUPLICATE_KEY) {
return wtRCToStatus(ret);
}
@@ -1472,7 +1469,7 @@ StatusWith<SpecialFormatInserted> WiredTigerIndexUnique::_insertTimestampUnsafe(
RecordId idInIndex = KeyString::decodeRecordId(&br);
if (id == idInIndex)
return StatusWith<SpecialFormatInserted>(
- SpecialFormatInserted::NoSpecialFormatInserted); // already in index
+ SpecialFormatInserted::NothingInserted); // already in index
if (!insertedId && id < idInIndex) {
value.appendRecordId(id);
@@ -1563,8 +1560,10 @@ StatusWith<SpecialFormatInserted> WiredTigerIndexUnique::_insertTimestampSafe(
ret = WT_OP_CHECK(c->insert(c));
// It is possible that this key is already present during a concurrent background index build.
- if (ret != WT_DUPLICATE_KEY)
- invariantWTOK(ret);
+ if (ret == WT_DUPLICATE_KEY) {
+ return StatusWith<SpecialFormatInserted>(SpecialFormatInserted::NothingInserted);
+ }
+ invariantWTOK(ret);
if (tableKey.getTypeBits().isLongEncoding())
return StatusWith<SpecialFormatInserted>(SpecialFormatInserted::LongTypeBitsInserted);
@@ -1768,11 +1767,14 @@ StatusWith<SpecialFormatInserted> WiredTigerIndexStandard::_insert(OperationCont
c->set_value(c, valueItem.Get());
int ret = WT_OP_CHECK(c->insert(c));
- // If the record was already in the index, we just return OK.
+ // If the record was already in the index, return NothingInserted.
// This can happen, for example, when building a background index while documents are being
// written and reindexed.
- if (ret != 0 && ret != WT_DUPLICATE_KEY)
+ if (ret == WT_DUPLICATE_KEY) {
+ return StatusWith<SpecialFormatInserted>(SpecialFormatInserted::NothingInserted);
+ } else if (ret) {
return wtRCToStatus(ret);
+ }
if (key.getTypeBits().isLongEncoding())
return StatusWith<SpecialFormatInserted>(SpecialFormatInserted::LongTypeBitsInserted);