summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGregory Noma <gregory.noma@gmail.com>2022-02-24 19:33:56 +0000
committerEvergreen Agent <no-reply@evergreen.mongodb.com>2022-02-24 20:34:37 +0000
commitab0af4dc877ea795069b720b5900e313f7e36f02 (patch)
tree650d3eb8b3826017a35c3c44f70957ecd8df7ba7
parent972d3f082e84c82002c15b9ad3bbed05d77db514 (diff)
downloadmongo-ab0af4dc877ea795069b720b5900e313f7e36f02.tar.gz
SERVER-60562 Include cursor value in duplicate key error when testing is enabled
-rw-r--r--jstests/core/uniqueness.js4
-rw-r--r--src/mongo/db/s/dist_lock_catalog_replset_test.cpp5
-rw-r--r--src/mongo/db/storage/duplicate_key_error_info.cpp38
-rw-r--r--src/mongo/db/storage/duplicate_key_error_info.h16
-rw-r--r--src/mongo/db/storage/index_entry_comparison.cpp22
-rw-r--r--src/mongo/db/storage/index_entry_comparison.h4
-rw-r--r--src/mongo/db/storage/index_entry_comparison_test.cpp40
-rw-r--r--src/mongo/db/storage/wiredtiger/wiredtiger_index.cpp17
-rw-r--r--src/mongo/db/storage/wiredtiger/wiredtiger_record_store.cpp11
-rw-r--r--src/mongo/s/catalog/sharding_catalog_write_retry_test.cpp2
10 files changed, 133 insertions, 26 deletions
diff --git a/jstests/core/uniqueness.js b/jstests/core/uniqueness.js
index f86da4e7abd..1be519487f1 100644
--- a/jstests/core/uniqueness.js
+++ b/jstests/core/uniqueness.js
@@ -59,9 +59,7 @@ res = t.insert(key);
assert.commandFailedWithCode(res, ErrorCodes.DuplicateKey);
assert.eq(res.nInserted, 0, tojson(res));
const writeError = res.getWriteError();
-assert.eq(writeError.errmsg,
- expectedMessage,
- "The duplicate key error message must exactly match." + tojson(res));
+assert.includes(writeError.errmsg, expectedMessage, tojson(res));
/* Check that if we update and remove _id, it gets added back by the DB */
diff --git a/src/mongo/db/s/dist_lock_catalog_replset_test.cpp b/src/mongo/db/s/dist_lock_catalog_replset_test.cpp
index aedba30df96..a05f6db250d 100644
--- a/src/mongo/db/s/dist_lock_catalog_replset_test.cpp
+++ b/src/mongo/db/s/dist_lock_catalog_replset_test.cpp
@@ -503,8 +503,9 @@ TEST_F(DistLockCatalogReplSetTest, GrabLockDupKeyError) {
});
onCommand([](const RemoteCommandRequest& request) -> StatusWith<BSONObj> {
- return Status({DuplicateKeyErrorInfo(BSON("x" << 1), BSON("" << 1), BSONObj{}),
- "Mock duplicate key error"});
+ return Status(
+ {DuplicateKeyErrorInfo(BSON("x" << 1), BSON("" << 1), BSONObj{}, stdx::monostate{}),
+ "Mock duplicate key error"});
});
future.default_timed_get();
diff --git a/src/mongo/db/storage/duplicate_key_error_info.cpp b/src/mongo/db/storage/duplicate_key_error_info.cpp
index e32bf59fe37..4a724f2ef09 100644
--- a/src/mongo/db/storage/duplicate_key_error_info.cpp
+++ b/src/mongo/db/storage/duplicate_key_error_info.cpp
@@ -36,6 +36,7 @@
#include "mongo/util/assert_util.h"
#include "mongo/util/hex.h"
#include "mongo/util/text.h"
+#include "mongo/util/visit_helper.h"
namespace mongo {
namespace {
@@ -44,6 +45,19 @@ MONGO_INIT_REGISTER_ERROR_EXTRA_INFO(DuplicateKeyErrorInfo);
} // namespace
+DuplicateKeyErrorInfo::DuplicateKeyErrorInfo(const BSONObj& keyPattern,
+ const BSONObj& keyValue,
+ const BSONObj& collation,
+ FoundValue&& foundValue)
+ : _keyPattern(keyPattern.getOwned()),
+ _keyValue(keyValue.getOwned()),
+ _collation(collation.getOwned()),
+ _foundValue(std::move(foundValue)) {
+ if (auto foundValueObj = stdx::get_if<BSONObj>(&_foundValue)) {
+ _foundValue = foundValueObj->getOwned();
+ }
+}
+
void DuplicateKeyErrorInfo::serialize(BSONObjBuilder* bob) const {
bob->append("keyPattern", _keyPattern);
@@ -79,6 +93,18 @@ void DuplicateKeyErrorInfo::serialize(BSONObjBuilder* bob) const {
if (!_collation.isEmpty()) {
bob->append("collation", _collation);
}
+
+ stdx::visit(
+ visit_helper::Overloaded{
+ [](stdx::monostate) {},
+ [bob](const RecordId& rid) { rid.serializeToken("foundValue", bob); },
+ [bob](const BSONObj& obj) {
+ if (obj.objsize() < BSONObjMaxUserSize / 2) {
+ bob->append("foundValue", obj);
+ }
+ },
+ },
+ _foundValue);
}
std::shared_ptr<const ErrorExtraInfo> DuplicateKeyErrorInfo::parse(const BSONObj& obj) {
@@ -111,7 +137,17 @@ std::shared_ptr<const ErrorExtraInfo> DuplicateKeyErrorInfo::parse(const BSONObj
collation = collationElt.Obj();
}
- return std::make_shared<DuplicateKeyErrorInfo>(keyPattern, keyValue, collation);
+ FoundValue foundValue;
+ if (auto foundValueElt = obj["foundValue"]) {
+ if (foundValueElt.isABSONObj()) {
+ foundValue = foundValueElt.Obj();
+ } else {
+ foundValue = RecordId::deserializeToken(foundValueElt);
+ }
+ }
+
+ return std::make_shared<DuplicateKeyErrorInfo>(
+ keyPattern, keyValue, collation, std::move(foundValue));
}
} // namespace mongo
diff --git a/src/mongo/db/storage/duplicate_key_error_info.h b/src/mongo/db/storage/duplicate_key_error_info.h
index 8c21088f50e..38175c9cd23 100644
--- a/src/mongo/db/storage/duplicate_key_error_info.h
+++ b/src/mongo/db/storage/duplicate_key_error_info.h
@@ -32,6 +32,8 @@
#include "mongo/base/error_extra_info.h"
#include "mongo/bson/bsonobj.h"
#include "mongo/bson/bsonobjbuilder.h"
+#include "mongo/db/record_id.h"
+#include "mongo/stdx/variant.h"
namespace mongo {
@@ -41,16 +43,16 @@ namespace mongo {
*/
class DuplicateKeyErrorInfo final : public ErrorExtraInfo {
public:
+ using FoundValue = stdx::variant<stdx::monostate, RecordId, BSONObj>;
+
static constexpr auto code = ErrorCodes::DuplicateKey;
static std::shared_ptr<const ErrorExtraInfo> parse(const BSONObj&);
explicit DuplicateKeyErrorInfo(const BSONObj& keyPattern,
const BSONObj& keyValue,
- const BSONObj& collation)
- : _keyPattern(keyPattern.getOwned()),
- _keyValue(keyValue.getOwned()),
- _collation(collation.getOwned()) {}
+ const BSONObj& collation,
+ FoundValue&& foundValue);
void serialize(BSONObjBuilder* bob) const override;
@@ -75,6 +77,12 @@ private:
// An empty object if the index which resulted in the duplicate key error has the simple
// collation, otherwise gives the index's collation.
BSONObj _collation;
+
+ // Optionally, the value found at the cursor which produced the DuplicateKey error, for
+ // diagnostic use. If the error came from an _id index, then the value will be the record id of
+ // the duplicate document. If the error came from a clustered collection, then the value will be
+ // the duplicate document itself.
+ FoundValue _foundValue;
};
} // namespace mongo
diff --git a/src/mongo/db/storage/index_entry_comparison.cpp b/src/mongo/db/storage/index_entry_comparison.cpp
index 1e5d53eecf7..feceb088a38 100644
--- a/src/mongo/db/storage/index_entry_comparison.cpp
+++ b/src/mongo/db/storage/index_entry_comparison.cpp
@@ -34,7 +34,6 @@
#include "mongo/db/jsobj.h"
#include "mongo/db/namespace_string.h"
-#include "mongo/db/storage/duplicate_key_error_info.h"
#include "mongo/db/storage/key_string.h"
#include "mongo/util/hex.h"
#include "mongo/util/text.h"
@@ -188,7 +187,8 @@ Status buildDupKeyErrorStatus(const BSONObj& key,
const NamespaceString& collectionNamespace,
const std::string& indexName,
const BSONObj& keyPattern,
- const BSONObj& indexCollation) {
+ const BSONObj& indexCollation,
+ DuplicateKeyErrorInfo::FoundValue&& foundValue) {
const bool hasCollation = !indexCollation.isEmpty();
StringBuilder sb;
@@ -250,8 +250,22 @@ Status buildDupKeyErrorStatus(const BSONObj& key,
sb << builderForErrmsg.obj();
- return Status(DuplicateKeyErrorInfo(keyPattern, builderForErrorExtraInfo.obj(), indexCollation),
- sb.str());
+ stdx::visit(
+ visit_helper::Overloaded{
+ [](stdx::monostate) {},
+ [&sb](const RecordId& rid) { sb << " found value: " << rid; },
+ [&sb](const BSONObj& obj) {
+ if (obj.objsize() < BSONObjMaxUserSize / 2) {
+ sb << " found value: " << obj;
+ }
+ },
+ },
+ foundValue);
+
+ return Status(
+ DuplicateKeyErrorInfo(
+ keyPattern, builderForErrorExtraInfo.obj(), indexCollation, std::move(foundValue)),
+ sb.str());
}
Status buildDupKeyErrorStatus(const KeyString::Value& keyString,
diff --git a/src/mongo/db/storage/index_entry_comparison.h b/src/mongo/db/storage/index_entry_comparison.h
index e021bb26f20..a6291c3f91b 100644
--- a/src/mongo/db/storage/index_entry_comparison.h
+++ b/src/mongo/db/storage/index_entry_comparison.h
@@ -38,6 +38,7 @@
#include "mongo/db/jsobj.h"
#include "mongo/db/namespace_string.h"
#include "mongo/db/record_id.h"
+#include "mongo/db/storage/duplicate_key_error_info.h"
#include "mongo/db/storage/key_string.h"
namespace mongo {
@@ -283,7 +284,8 @@ Status buildDupKeyErrorStatus(const BSONObj& key,
const NamespaceString& collectionNamespace,
const std::string& indexName,
const BSONObj& keyPattern,
- const BSONObj& indexCollation);
+ const BSONObj& indexCollation,
+ DuplicateKeyErrorInfo::FoundValue&& foundValue = stdx::monostate{});
Status buildDupKeyErrorStatus(const KeyString::Value& keyString,
const NamespaceString& collectionNamespace,
diff --git a/src/mongo/db/storage/index_entry_comparison_test.cpp b/src/mongo/db/storage/index_entry_comparison_test.cpp
index 0181428ea62..eef2b5c2651 100644
--- a/src/mongo/db/storage/index_entry_comparison_test.cpp
+++ b/src/mongo/db/storage/index_entry_comparison_test.cpp
@@ -37,14 +37,30 @@
namespace mongo {
-TEST(IndexEntryComparison, BuildDupKeyErrorStatusProducesExpectedErrorObject) {
+void buildDupKeyErrorStatusProducesExpectedErrorObject(
+ DuplicateKeyErrorInfo::FoundValue&& foundValue) {
NamespaceString collNss("test.foo");
std::string indexName("a_1_b_1");
auto keyPattern = BSON("a" << 1 << "b" << 1);
auto keyValue = BSON("" << 10 << ""
<< "abc");
+ auto keyValueWithFieldName = BSON("a" << 10 << "b"
+ << "abc");
- auto dupKeyStatus = buildDupKeyErrorStatus(keyValue, collNss, indexName, keyPattern, BSONObj{});
+ BSONObjBuilder expectedObjBuilder;
+ expectedObjBuilder.append("keyPattern", keyPattern);
+ expectedObjBuilder.append("keyValue", keyValueWithFieldName);
+ stdx::visit(
+ visit_helper::Overloaded{
+ [](stdx::monostate) {},
+ [&](const RecordId& rid) { rid.serializeToken("foundValue", &expectedObjBuilder); },
+ [&](const BSONObj& obj) { expectedObjBuilder.append("foundValue", obj); },
+ },
+ foundValue);
+ auto expectedObj = expectedObjBuilder.obj();
+
+ auto dupKeyStatus = buildDupKeyErrorStatus(
+ keyValue, collNss, indexName, keyPattern, BSONObj{}, std::move(foundValue));
ASSERT_NOT_OK(dupKeyStatus);
ASSERT_EQUALS(dupKeyStatus.code(), ErrorCodes::DuplicateKey);
@@ -52,15 +68,25 @@ TEST(IndexEntryComparison, BuildDupKeyErrorStatusProducesExpectedErrorObject) {
ASSERT(extraInfo);
ASSERT_BSONOBJ_EQ(extraInfo->getKeyPattern(), keyPattern);
-
- auto keyValueWithFieldName = BSON("a" << 10 << "b"
- << "abc");
ASSERT_BSONOBJ_EQ(extraInfo->getDuplicatedKeyValue(), keyValueWithFieldName);
BSONObjBuilder objBuilder;
extraInfo->serialize(&objBuilder);
- ASSERT_BSONOBJ_EQ(objBuilder.obj(),
- BSON("keyPattern" << keyPattern << "keyValue" << keyValueWithFieldName));
+ auto obj = objBuilder.obj();
+ ASSERT_BSONOBJ_EQ(obj, expectedObj);
+
+ // Ensure the object is the same after parsing and serializing again.
+ auto parsedExtraInfo =
+ std::dynamic_pointer_cast<const DuplicateKeyErrorInfo>(DuplicateKeyErrorInfo::parse(obj));
+ BSONObjBuilder afterParseObjBuilder;
+ parsedExtraInfo->serialize(&afterParseObjBuilder);
+ ASSERT_BSONOBJ_EQ(afterParseObjBuilder.obj(), expectedObj);
+}
+
+TEST(IndexEntryComparison, BuildDupKeyErrorStatusProducesExpectedErrorObject) {
+ buildDupKeyErrorStatusProducesExpectedErrorObject(stdx::monostate{});
+ buildDupKeyErrorStatusProducesExpectedErrorObject(RecordId{1});
+ buildDupKeyErrorStatusProducesExpectedErrorObject(BSON("c" << 1));
}
void duplicateKeyErrorSerializationAndParseReturnTheSameObject(
diff --git a/src/mongo/db/storage/wiredtiger/wiredtiger_index.cpp b/src/mongo/db/storage/wiredtiger/wiredtiger_index.cpp
index d17238bb8c7..51f11aadc6c 100644
--- a/src/mongo/db/storage/wiredtiger/wiredtiger_index.cpp
+++ b/src/mongo/db/storage/wiredtiger/wiredtiger_index.cpp
@@ -1582,9 +1582,22 @@ StatusWith<bool> WiredTigerIdIndex::_insert(OperationContext* opCtx,
});
}
+ DuplicateKeyErrorInfo::FoundValue foundValueRecordId;
+ if (TestingProctor::instance().isEnabled()) {
+ WT_ITEM foundValue;
+ invariantWTOK(c->get_value(c, &foundValue), c->session);
+
+ BufReader reader(foundValue.data, foundValue.size);
+ foundValueRecordId = KeyString::decodeRecordIdLong(&reader);
+ }
+
auto key = KeyString::toBson(keyString, _ordering);
- return buildDupKeyErrorStatus(
- key, _desc->getEntry()->getNSSFromCatalog(opCtx), _indexName, _keyPattern, _collation);
+ return buildDupKeyErrorStatus(key,
+ _desc->getEntry()->getNSSFromCatalog(opCtx),
+ _indexName,
+ _keyPattern,
+ _collation,
+ std::move(foundValueRecordId));
}
StatusWith<bool> WiredTigerIndexUnique::_insert(OperationContext* opCtx,
diff --git a/src/mongo/db/storage/wiredtiger/wiredtiger_record_store.cpp b/src/mongo/db/storage/wiredtiger/wiredtiger_record_store.cpp
index a0d96de1529..0f4806c32a4 100644
--- a/src/mongo/db/storage/wiredtiger/wiredtiger_record_store.cpp
+++ b/src/mongo/db/storage/wiredtiger/wiredtiger_record_store.cpp
@@ -1397,6 +1397,14 @@ Status WiredTigerRecordStore::_insertRecords(OperationContext* opCtx,
invariant(!_overwrite);
invariant(_keyFormat == KeyFormat::String);
+ DuplicateKeyErrorInfo::FoundValue foundValueObj;
+ if (TestingProctor::instance().isEnabled()) {
+ WT_ITEM foundValue;
+ invariantWTOK(c->get_value(c, &foundValue), c->session);
+
+ foundValueObj.emplace<BSONObj>(reinterpret_cast<const char*>(foundValue.data));
+ }
+
// Generate a useful error message that is consistent with duplicate key error messages
// on indexes.
BSONObj obj = record_id_helpers::toBSONAs(record.id, "");
@@ -1404,7 +1412,8 @@ Status WiredTigerRecordStore::_insertRecords(OperationContext* opCtx,
NamespaceString(ns()),
"" /* indexName */,
BSON("_id" << 1),
- BSONObj() /* collation */);
+ BSONObj() /* collation */,
+ std::move(foundValueObj));
}
if (ret)
diff --git a/src/mongo/s/catalog/sharding_catalog_write_retry_test.cpp b/src/mongo/s/catalog/sharding_catalog_write_retry_test.cpp
index efac61fbbda..ad8bfbe1ec0 100644
--- a/src/mongo/s/catalog/sharding_catalog_write_retry_test.cpp
+++ b/src/mongo/s/catalog/sharding_catalog_write_retry_test.cpp
@@ -75,7 +75,7 @@ const HostAndPort kTestHosts[] = {
HostAndPort("TestHost1:12345"), HostAndPort("TestHost2:12345"), HostAndPort("TestHost3:12345")};
Status getMockDuplicateKeyError() {
- return {DuplicateKeyErrorInfo(BSON("mock" << 1), BSON("" << 1), BSONObj{}),
+ return {DuplicateKeyErrorInfo(BSON("mock" << 1), BSON("" << 1), BSONObj{}, stdx::monostate{}),
"Mock duplicate key error"};
}