diff options
Diffstat (limited to 'src/mongo/db')
-rw-r--r-- | src/mongo/db/s/dist_lock_catalog_replset_test.cpp | 5 | ||||
-rw-r--r-- | src/mongo/db/storage/duplicate_key_error_info.cpp | 38 | ||||
-rw-r--r-- | src/mongo/db/storage/duplicate_key_error_info.h | 16 | ||||
-rw-r--r-- | src/mongo/db/storage/index_entry_comparison.cpp | 22 | ||||
-rw-r--r-- | src/mongo/db/storage/index_entry_comparison.h | 4 | ||||
-rw-r--r-- | src/mongo/db/storage/index_entry_comparison_test.cpp | 40 | ||||
-rw-r--r-- | src/mongo/db/storage/wiredtiger/wiredtiger_index.cpp | 17 | ||||
-rw-r--r-- | src/mongo/db/storage/wiredtiger/wiredtiger_record_store.cpp | 11 |
8 files changed, 131 insertions, 22 deletions
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) |