diff options
author | Gregory Noma <gregory.noma@gmail.com> | 2020-07-21 16:52:17 -0400 |
---|---|---|
committer | Evergreen Agent <no-reply@evergreen.mongodb.com> | 2020-07-21 21:18:08 +0000 |
commit | 98a7db0a562b818426c19203e8d16cc93980b279 (patch) | |
tree | 85421e21ff3d43e46cbb0db1b7bce1ab2a889480 | |
parent | f200a0b9dc462acad59dabead90ae35fe309e205 (diff) | |
download | mongo-98a7db0a562b818426c19203e8d16cc93980b279.tar.gz |
SERVER-48067 Reduce memory consumption for unique index builds with large numbers of non-unique keys
(cherry picked from commit 3c2951938d65667d675c48511d9d1046655809a5)
-rw-r--r-- | src/mongo/db/catalog/index_catalog_impl.cpp | 7 | ||||
-rw-r--r-- | src/mongo/db/catalog/multi_index_block.cpp | 42 | ||||
-rw-r--r-- | src/mongo/db/index/duplicate_key_tracker.cpp | 48 | ||||
-rw-r--r-- | src/mongo/db/index/duplicate_key_tracker.h | 4 | ||||
-rw-r--r-- | src/mongo/db/index/index_access_method.cpp | 40 | ||||
-rw-r--r-- | src/mongo/db/index/index_access_method.h | 42 | ||||
-rw-r--r-- | src/mongo/db/index/index_build_interceptor.cpp | 36 | ||||
-rw-r--r-- | src/mongo/db/index/index_build_interceptor.h | 4 | ||||
-rw-r--r-- | src/mongo/dbtests/validate_tests.cpp | 88 |
9 files changed, 145 insertions, 166 deletions
diff --git a/src/mongo/db/catalog/index_catalog_impl.cpp b/src/mongo/db/catalog/index_catalog_impl.cpp index 13c194b6c98..266fac5b46b 100644 --- a/src/mongo/db/catalog/index_catalog_impl.cpp +++ b/src/mongo/db/catalog/index_catalog_impl.cpp @@ -1312,7 +1312,7 @@ Status IndexCatalogImpl::_indexKeys(OperationContext* opCtx, *keysInsertedOut += inserted; } } else { - InsertResult result; + int64_t numInserted; status = index->accessMethod()->insertKeys( opCtx, keys, @@ -1320,9 +1320,10 @@ Status IndexCatalogImpl::_indexKeys(OperationContext* opCtx, multikeyPaths, loc, options, - &result); + nullptr, + &numInserted); if (keysInsertedOut) { - *keysInsertedOut += result.numInserted; + *keysInsertedOut += numInserted; } } diff --git a/src/mongo/db/catalog/multi_index_block.cpp b/src/mongo/db/catalog/multi_index_block.cpp index 158dd471849..cb902dec15b 100644 --- a/src/mongo/db/catalog/multi_index_block.cpp +++ b/src/mongo/db/catalog/multi_index_block.cpp @@ -648,7 +648,6 @@ Status MultiIndexBlock::insert(OperationContext* opCtx, const BSONObj& doc, cons continue; } - InsertResult result; Status idxStatus(ErrorCodes::InternalError, ""); if (_indexes[i].bulk) { // When calling insert, BulkBuilderImpl's Sorter performs file I/O that may result in an @@ -659,7 +658,8 @@ Status MultiIndexBlock::insert(OperationContext* opCtx, const BSONObj& doc, cons return exceptionToStatus(); } } else { - idxStatus = _indexes[i].real->insert(opCtx, doc, loc, _indexes[i].options, &result); + idxStatus = + _indexes[i].real->insert(opCtx, doc, loc, _indexes[i].options, nullptr, nullptr); } if (!idxStatus.isOK()) @@ -684,11 +684,6 @@ Status MultiIndexBlock::dumpInsertsFromBulk(OperationContext* opCtx, if (_indexes[i].bulk == NULL) continue; - // If 'dupRecords' is provided, it will be used to store all records that would result in - // duplicate key errors. Only pass 'dupKeysInserted', which stores inserted duplicate keys, - // when 'dupRecords' is not used because these two vectors are mutually incompatible. - std::vector<BSONObj> dupKeysInserted; - // When dupRecords is passed, 'dupsAllowed' should be passed to reflect whether or not the // index is unique. bool dupsAllowed = (dupRecords) ? !_indexes[i].block->getEntry()->descriptor()->unique() @@ -701,30 +696,23 @@ Status MultiIndexBlock::dumpInsertsFromBulk(OperationContext* opCtx, // SERVER-41918 This call to commitBulk() results in file I/O that may result in an // exception. try { - Status status = _indexes[i].real->commitBulk(opCtx, - _indexes[i].bulk.get(), - dupsAllowed, - dupRecords, - (dupRecords) ? nullptr : &dupKeysInserted); + Status status = _indexes[i].real->commitBulk( + opCtx, + _indexes[i].bulk.get(), + dupsAllowed, + [=](const BSONObj& duplicateKey) { + // Do not record duplicates when explicitly ignored. This may be the case on + // secondaries. + return dupsAllowed && !dupRecords && !_ignoreUnique && + entry->indexBuildInterceptor() + ? entry->indexBuildInterceptor()->recordDuplicateKey(opCtx, duplicateKey) + : Status::OK(); + }, + dupRecords); if (!status.isOK()) { return status; } - - // Do not record duplicates when explicitly ignored. This may be the case on - // secondaries. - auto interceptor = entry->indexBuildInterceptor(); - if (!interceptor || _ignoreUnique) { - continue; - } - - // Record duplicate key insertions for later verification. - if (dupKeysInserted.size()) { - status = interceptor->recordDuplicateKeys(opCtx, dupKeysInserted); - if (!status.isOK()) { - return status; - } - } } catch (...) { return exceptionToStatus(); } diff --git a/src/mongo/db/index/duplicate_key_tracker.cpp b/src/mongo/db/index/duplicate_key_tracker.cpp index ac06389c2d3..71311c05899 100644 --- a/src/mongo/db/index/duplicate_key_tracker.cpp +++ b/src/mongo/db/index/duplicate_key_tracker.cpp @@ -58,39 +58,25 @@ void DuplicateKeyTracker::deleteTemporaryTable(OperationContext* opCtx) { _keyConstraintsTable->deleteTemporaryTable(opCtx); } -Status DuplicateKeyTracker::recordKeys(OperationContext* opCtx, const std::vector<BSONObj>& keys) { - if (keys.size() == 0) - return Status::OK(); +Status DuplicateKeyTracker::recordKey(OperationContext* opCtx, const BSONObj& key) { + invariant(opCtx->lockState()->inAWriteUnitOfWork()); - std::vector<BSONObj> toInsert; - toInsert.reserve(keys.size()); - for (auto&& key : keys) { - BSONObjBuilder builder; - builder.append(kKeyField, key); - - BSONObj obj = builder.obj(); - - toInsert.emplace_back(std::move(obj)); - } - - std::vector<Record> records; - records.reserve(keys.size()); - for (auto&& obj : toInsert) { - records.emplace_back(Record{RecordId(), RecordData(obj.objdata(), obj.objsize())}); - } - - LOG(1) << "recording " << records.size() << " duplicate key conflicts on unique index: " + LOG(1) << "Index build: recording duplicate key conflict on unique index: " << _indexCatalogEntry->descriptor()->indexName(); - WriteUnitOfWork wuow(opCtx); - std::vector<Timestamp> timestamps(records.size()); - Status s = _keyConstraintsTable->rs()->insertRecords(opCtx, &records, timestamps); - if (!s.isOK()) - return s; + auto status = + _keyConstraintsTable->rs()->insertRecord(opCtx, key.objdata(), key.objsize(), Timestamp()); + if (!status.isOK()) + return status.getStatus(); - wuow.commit(); + auto numDuplicates = _duplicateCounter.addAndFetch(1); + opCtx->recoveryUnit()->onRollback([this]() { _duplicateCounter.fetchAndAdd(-1); }); - _duplicateCounter.fetchAndAdd(records.size()); + if (numDuplicates % 1000 == 0) { + log() << "Index build: high number (" << numDuplicates + << ") of duplicate keys on unique index: " + << _indexCatalogEntry->descriptor()->indexName(); + } return Status::OK(); } @@ -111,14 +97,12 @@ Status DuplicateKeyTracker::checkConstraints(OperationContext* opCtx) const { CurOp::get(opCtx)->setProgress_inlock(curopMessage, _duplicateCounter.load(), 1)); } - int resolved = 0; while (record) { resolved++; - BSONObj conflict = record->data.toBson(); - BSONObj keyObj = conflict[kKeyField].Obj(); + auto key = record->data.toBson(); - auto status = index->dupKeyCheck(opCtx, keyObj); + auto status = index->dupKeyCheck(opCtx, key); if (!status.isOK()) return status; diff --git a/src/mongo/db/index/duplicate_key_tracker.h b/src/mongo/db/index/duplicate_key_tracker.h index c5399620984..61d93a5871a 100644 --- a/src/mongo/db/index/duplicate_key_tracker.h +++ b/src/mongo/db/index/duplicate_key_tracker.h @@ -63,9 +63,9 @@ public: void deleteTemporaryTable(OperationContext* opCtx); /** - * Given a set of duplicate keys, insert them into the key constraint table. + * Given a duplicate key, insert it into the key constraint table. */ - Status recordKeys(OperationContext* opCtx, const std::vector<BSONObj>& keys); + Status recordKey(OperationContext* opCtx, const BSONObj& key); /** * Returns Status::OK if all previously recorded duplicate key constraint violations have been diff --git a/src/mongo/db/index/index_access_method.cpp b/src/mongo/db/index/index_access_method.cpp index 5f4a6d7dd92..cc050e63dd8 100644 --- a/src/mongo/db/index/index_access_method.cpp +++ b/src/mongo/db/index/index_access_method.cpp @@ -184,7 +184,8 @@ Status AbstractIndexAccessMethod::insert(OperationContext* opCtx, const BSONObj& obj, const RecordId& loc, const InsertDeleteOptions& options, - InsertResult* result) { + KeyHandlerFn&& onDuplicateKey, + int64_t* numInserted) { invariant(options.fromIndexBuilder || !_btreeState->isHybridBuilding()); BSONObjSet multikeyMetadataKeys = SimpleBSONObjComparator::kInstance.makeBSONObjSet(); @@ -205,7 +206,8 @@ Status AbstractIndexAccessMethod::insert(OperationContext* opCtx, multikeyPaths, loc, options, - result); + std::move(onDuplicateKey), + numInserted); } Status AbstractIndexAccessMethod::insertKeys(OperationContext* opCtx, @@ -214,7 +216,8 @@ Status AbstractIndexAccessMethod::insertKeys(OperationContext* opCtx, const MultikeyPaths& multikeyPaths, const RecordId& loc, const InsertDeleteOptions& options, - InsertResult* result) { + KeyHandlerFn&& onDuplicateKey, + int64_t* numInserted) { bool checkIndexKeySize = shouldCheckIndexKeySize(opCtx); // Add all new data keys, and all new multikey metadata keys, into the index. When iterating @@ -230,20 +233,15 @@ Status AbstractIndexAccessMethod::insertKeys(OperationContext* opCtx, _newInterface->insert(opCtx, key, recordId, !unique /* dupsAllowed */); status = ret.getStatus(); - // When duplicates are encountered and allowed, retry with dupsAllowed. Add the - // key to the output vector so callers know which duplicate keys were inserted. + // When duplicates are encountered and allowed, retry with dupsAllowed. Call + // onDuplicateKey() with the inserted duplicate key. if (ErrorCodes::DuplicateKey == status.code() && options.dupsAllowed) { invariant(unique); ret = _newInterface->insert(opCtx, key, recordId, true /* dupsAllowed */); status = ret.getStatus(); - // This is speculative in that the 'dupsInserted' vector is not used by any code - // today. It is currently in place to test detecting duplicate key errors during - // hybrid index builds. Duplicate detection in the future will likely not take - // place in this insert() method. - if (status.isOK() && result) { - result->dupsInserted.push_back(key); - } + if (status.isOK() && onDuplicateKey) + status = onDuplicateKey(key); } if (status.isOK() && ret.getValue() == SpecialFormatInserted::LongTypeBitsInserted) @@ -256,8 +254,8 @@ Status AbstractIndexAccessMethod::insertKeys(OperationContext* opCtx, } } - if (result) { - result->numInserted += keys.size() + multikeyMetadataKeys.size(); + if (numInserted) { + *numInserted = keys.size() + multikeyMetadataKeys.size(); } if (shouldMarkIndexAsMultikey(keys, multikeyMetadataKeys, multikeyPaths)) { @@ -650,12 +648,8 @@ int64_t AbstractIndexAccessMethod::BulkBuilderImpl::getKeysInserted() const { Status AbstractIndexAccessMethod::commitBulk(OperationContext* opCtx, BulkBuilder* bulk, bool dupsAllowed, - set<RecordId>* dupRecords, - std::vector<BSONObj>* dupKeysInserted) { - // Cannot simultaneously report uninserted duplicates 'dupRecords' and inserted duplicates - // 'dupKeysInserted'. - invariant(!(dupRecords && dupKeysInserted)); - + KeyHandlerFn&& onDuplicateKey, + set<RecordId>* dupRecords) { Timer timer; std::unique_ptr<BulkBuilder::Sorter::Iterator> it(bulk->done()); @@ -726,8 +720,10 @@ Status AbstractIndexAccessMethod::commitBulk(OperationContext* opCtx, previousKey = data.first.getOwned(); - if (isDup && dupsAllowed && dupKeysInserted) { - dupKeysInserted->push_back(data.first.getOwned()); + if (isDup) { + status = onDuplicateKey(data.first); + if (!status.isOK()) + return status; } // If we're here either it's a dup and we're cool with it or the addKey went just fine. diff --git a/src/mongo/db/index/index_access_method.h b/src/mongo/db/index/index_access_method.h index d7d96588d18..5fe38414125 100644 --- a/src/mongo/db/index/index_access_method.h +++ b/src/mongo/db/index/index_access_method.h @@ -48,7 +48,6 @@ namespace mongo { class BSONObjBuilder; class MatchExpression; struct UpdateTicket; -struct InsertResult; struct InsertDeleteOptions; bool failIndexKeyTooLongParam(); @@ -68,6 +67,8 @@ class IndexAccessMethod { IndexAccessMethod& operator=(const IndexAccessMethod&) = delete; public: + using KeyHandlerFn = std::function<Status(const BSONObj&)>; + IndexAccessMethod() = default; virtual ~IndexAccessMethod() = default; @@ -90,7 +91,8 @@ public: const BSONObj& obj, const RecordId& loc, const InsertDeleteOptions& options, - InsertResult* result) = 0; + KeyHandlerFn&& onDuplicateKey, + int64_t* numInserted) = 0; virtual Status insertKeys(OperationContext* opCtx, const std::vector<BSONObj>& keys, @@ -98,7 +100,8 @@ public: const MultikeyPaths& multikeyPaths, const RecordId& loc, const InsertDeleteOptions& options, - InsertResult* result) = 0; + KeyHandlerFn&& onDuplicateKey, + int64_t* numInserted) = 0; /** * Analogous to insertKeys above, but remove the keys instead of inserting them. @@ -260,22 +263,14 @@ public: * @param mayInterrupt - Is this commit interruptible (will cancel) * @param dupsAllowed - If false and 'dupRecords' is not null, append with the RecordIds of * the uninserted duplicates. - * If true and 'dupKeys' is not null, append with the keys of the inserted - * duplicates. + * @param onDuplicateKey - Will be called for each duplicate key inserted into the index. * @param dupRecords - If not null, is filled with the RecordIds of uninserted duplicate keys. - * If null, duplicate keys will return errors. - * @param dupKeys - If not null and 'dupsAllowed' is true, is filled with the keys of inserted - * duplicates. - * If null, duplicates are inserted but not recorded. - * - * It is invalid and contradictory to pass both 'dupRecords' and 'dupKeys'. */ - virtual Status commitBulk(OperationContext* opCtx, BulkBuilder* bulk, bool dupsAllowed, - std::set<RecordId>* dupRecords, - std::vector<BSONObj>* dupKeys) = 0; + KeyHandlerFn&& onDuplicateKey, + std::set<RecordId>* dupRecords) = 0; /** * Specifies whether getKeys should relax the index constraints or not, in order of most @@ -376,15 +371,6 @@ public: }; /** - * Records number of keys inserted and duplicate keys inserted, if applicable. - */ -struct InsertResult { -public: - std::int64_t numInserted{0}; - std::vector<BSONObj> dupsInserted; -}; - -/** * Updates are two steps: verify that it's a valid update, and perform it. * prepareUpdate fills out the UpdateStatus and update actually applies it. */ @@ -462,7 +448,8 @@ public: const BSONObj& obj, const RecordId& loc, const InsertDeleteOptions& options, - InsertResult* result) final; + KeyHandlerFn&& onDuplicateKey, + int64_t* numInserted) final; Status insertKeys(OperationContext* opCtx, const std::vector<BSONObj>& keys, @@ -470,7 +457,8 @@ public: const MultikeyPaths& multikeyPaths, const RecordId& loc, const InsertDeleteOptions& options, - InsertResult* result) final; + KeyHandlerFn&& onDuplicateKey, + int64_t* numInserted) final; Status removeKeys(OperationContext* opCtx, const std::vector<BSONObj>& keys, @@ -522,8 +510,8 @@ public: Status commitBulk(OperationContext* opCtx, BulkBuilder* bulk, bool dupsAllowed, - std::set<RecordId>* dupRecords, - std::vector<BSONObj>* dupKeys) final; + KeyHandlerFn&& onDuplicateKey, + std::set<RecordId>* dupRecords) final; void getKeys(const BSONObj& obj, GetKeysMode mode, diff --git a/src/mongo/db/index/index_build_interceptor.cpp b/src/mongo/db/index/index_build_interceptor.cpp index 704b7d81d00..b7eb5b336b5 100644 --- a/src/mongo/db/index/index_build_interceptor.cpp +++ b/src/mongo/db/index/index_build_interceptor.cpp @@ -94,10 +94,9 @@ void IndexBuildInterceptor::deleteTemporaryTables(OperationContext* opCtx) { } } -Status IndexBuildInterceptor::recordDuplicateKeys(OperationContext* opCtx, - const std::vector<BSONObj>& keys) { +Status IndexBuildInterceptor::recordDuplicateKey(OperationContext* opCtx, const BSONObj& key) { invariant(_indexCatalogEntry->descriptor()->unique()); - return _duplicateKeyTracker->recordKeys(opCtx, keys); + return _duplicateKeyTracker->recordKey(opCtx, key); } Status IndexBuildInterceptor::checkDuplicateKeyConstraints(OperationContext* opCtx) const { @@ -276,27 +275,24 @@ Status IndexBuildInterceptor::_applyWrite(OperationContext* opCtx, auto accessMethod = _indexCatalogEntry->accessMethod(); if (opType == Op::kInsert) { - InsertResult result; - auto status = accessMethod->insertKeys(opCtx, - {keySet.begin(), keySet.end()}, - {}, - MultikeyPaths{}, - opRecordId, - options, - &result); + int64_t numInserted; + auto status = accessMethod->insertKeys( + opCtx, + {keySet.begin(), keySet.end()}, + {}, + MultikeyPaths{}, + opRecordId, + options, + [=](const BSONObj& duplicateKey) { + return options.getKeysMode == IndexAccessMethod::GetKeysMode::kEnforceConstraints + ? recordDuplicateKey(opCtx, duplicateKey) + : Status::OK(); + }, + &numInserted); if (!status.isOK()) { return status; } - if (result.dupsInserted.size() && - options.getKeysMode == IndexAccessMethod::GetKeysMode::kEnforceConstraints) { - status = recordDuplicateKeys(opCtx, result.dupsInserted); - if (!status.isOK()) { - return status; - } - } - - int64_t numInserted = result.numInserted; *keysInserted += numInserted; opCtx->recoveryUnit()->onRollback( [keysInserted, numInserted] { *keysInserted -= numInserted; }); diff --git a/src/mongo/db/index/index_build_interceptor.h b/src/mongo/db/index/index_build_interceptor.h index 8f8d54bd737..70a7d28d1f6 100644 --- a/src/mongo/db/index/index_build_interceptor.h +++ b/src/mongo/db/index/index_build_interceptor.h @@ -81,10 +81,10 @@ public: int64_t* const numKeysOut); /** - * Given a set of duplicate keys, record the keys for later verification by a call to + * Given a duplicate key, record the key for later verification by a call to * checkDuplicateKeyConstraints(); */ - Status recordDuplicateKeys(OperationContext* opCtx, const std::vector<BSONObj>& keys); + Status recordDuplicateKey(OperationContext* opCtx, const BSONObj& key); /** * Returns Status::OK if all previously recorded duplicate key constraint violations have been diff --git a/src/mongo/dbtests/validate_tests.cpp b/src/mongo/dbtests/validate_tests.cpp index e0f92c2c757..8968eb7ec71 100644 --- a/src/mongo/dbtests/validate_tests.cpp +++ b/src/mongo/dbtests/validate_tests.cpp @@ -37,6 +37,7 @@ #include "mongo/db/db_raii.h" #include "mongo/db/dbdirectclient.h" #include "mongo/db/index/index_access_method.h" +#include "mongo/db/index/index_build_interceptor.h" #include "mongo/db/index/index_descriptor.h" #include "mongo/db/service_context.h" #include "mongo/dbtests/dbtests.h" @@ -822,7 +823,7 @@ public: { WriteUnitOfWork wunit(&_opCtx); int64_t numDeleted; - InsertResult insertResult; + int64_t numInserted; const BSONObj actualKey = BSON("a" << 1); const BSONObj badKey = BSON("a" << -1); InsertDeleteOptions options; @@ -838,10 +839,10 @@ public: nullptr); auto removeStatus = iam->removeKeys(&_opCtx, {keys.begin(), keys.end()}, id1, options, &numDeleted); - auto insertStatus = iam->insert(&_opCtx, badKey, id1, options, &insertResult); + auto insertStatus = iam->insert(&_opCtx, badKey, id1, options, nullptr, &numInserted); ASSERT_EQUALS(numDeleted, 1); - ASSERT_EQUALS(insertResult.numInserted, 1); + ASSERT_EQUALS(numInserted, 1); ASSERT_OK(removeStatus); ASSERT_OK(insertStatus); wunit.commit(); @@ -1570,11 +1571,12 @@ public: IndexCatalog* indexCatalog = coll->getIndexCatalog(); - WriteUnitOfWork wunit(&_opCtx); InsertDeleteOptions options; options.logIfError = true; options.dupsAllowed = true; + WriteUnitOfWork wunit(&_opCtx); + // Insert a record and its keys separately. We do this to bypass duplicate constraint // checking. Inserting a record and all of its keys ensures that validation fails // because there are duplicate keys, and not just because there are keys without @@ -1583,11 +1585,17 @@ public: &_opCtx, dupObj.objdata(), dupObj.objsize(), Timestamp()); ASSERT_OK(swRecordId); + wunit.commit(); + // Insert the key on _id. { + WriteUnitOfWork wunit(&_opCtx); + auto descriptor = indexCatalog->findIdIndex(&_opCtx); - auto iam = const_cast<IndexAccessMethod*>( - indexCatalog->getEntry(descriptor)->accessMethod()); + auto entry = const_cast<IndexCatalogEntry*>(indexCatalog->getEntry(descriptor)); + auto iam = entry->accessMethod(); + auto interceptor = std::make_unique<IndexBuildInterceptor>(&_opCtx, entry); + BSONObjSet keys = SimpleBSONObjComparator::kInstance.makeBSONObjSet(); iam->getKeys(dupObj, IndexAccessMethod::GetKeysMode::kRelaxConstraints, @@ -1597,28 +1605,39 @@ public: nullptr); ASSERT_EQ(1ul, keys.size()); - InsertResult result; - auto insertStatus = iam->insertKeys(&_opCtx, - {keys.begin(), keys.end()}, - {}, - MultikeyPaths{}, - swRecordId.getValue(), - options, - &result); - - ASSERT_EQUALS(result.dupsInserted.size(), 0ul); - ASSERT_EQUALS(result.numInserted, 1); + int64_t numInserted; + auto insertStatus = iam->insertKeys( + &_opCtx, + {keys.begin(), keys.end()}, + {}, + MultikeyPaths{}, + swRecordId.getValue(), + options, + [this, &interceptor](const BSONObj& duplicateKey) { + return interceptor->recordDuplicateKey(&_opCtx, duplicateKey); + }, + &numInserted); + + wunit.commit(); + + ASSERT_OK(interceptor->checkDuplicateKeyConstraints(&_opCtx)); + ASSERT_EQUALS(numInserted, 1); ASSERT_OK(insertStatus); + + interceptor->deleteTemporaryTables(&_opCtx); } // Insert the key on "a". { + WriteUnitOfWork wunit(&_opCtx); + auto descriptor = indexCatalog->findIndexByName(&_opCtx, indexName); - auto iam = const_cast<IndexAccessMethod*>( - indexCatalog->getEntry(descriptor)->accessMethod()); + auto entry = const_cast<IndexCatalogEntry*>(indexCatalog->getEntry(descriptor)); + auto iam = entry->accessMethod(); + auto interceptor = std::make_unique<IndexBuildInterceptor>(&_opCtx, entry); BSONObjSet keys = SimpleBSONObjComparator::kInstance.makeBSONObjSet(); - InsertResult result; + int64_t numInserted; iam->getKeys(dupObj, IndexAccessMethod::GetKeysMode::kRelaxConstraints, IndexAccessMethod::GetKeysContext::kReadOrAddKeys, @@ -1626,19 +1645,26 @@ public: nullptr, nullptr); ASSERT_EQ(1ul, keys.size()); - auto insertStatus = iam->insertKeys(&_opCtx, - {keys.begin(), keys.end()}, - {}, - MultikeyPaths{}, - swRecordId.getValue(), - options, - &result); - - ASSERT_EQUALS(result.dupsInserted.size(), 1ul); - ASSERT_EQUALS(result.numInserted, 1); + auto insertStatus = iam->insertKeys( + &_opCtx, + {keys.begin(), keys.end()}, + {}, + MultikeyPaths{}, + swRecordId.getValue(), + options, + [this, &interceptor](const BSONObj& duplicateKey) { + return interceptor->recordDuplicateKey(&_opCtx, duplicateKey); + }, + &numInserted); + + wunit.commit(); + + ASSERT_NOT_OK(interceptor->checkDuplicateKeyConstraints(&_opCtx)); + ASSERT_EQUALS(numInserted, 1); ASSERT_OK(insertStatus); + + interceptor->deleteTemporaryTables(&_opCtx); } - wunit.commit(); releaseDb(); } |