diff options
-rw-r--r-- | src/mongo/client/dbclient.cpp | 8 | ||||
-rw-r--r-- | src/mongo/client/dbclientinterface.h | 8 | ||||
-rw-r--r-- | src/mongo/db/catalog/index_catalog_impl.h | 6 | ||||
-rw-r--r-- | src/mongo/db/catalog/index_create.h | 8 | ||||
-rw-r--r-- | src/mongo/db/catalog/index_create_impl.cpp | 6 | ||||
-rw-r--r-- | src/mongo/db/catalog/index_create_impl.h | 2 | ||||
-rw-r--r-- | src/mongo/db/commands/create_indexes.cpp | 10 | ||||
-rw-r--r-- | src/mongo/dbtests/storage_timestamp_tests.cpp | 136 |
8 files changed, 158 insertions, 26 deletions
diff --git a/src/mongo/client/dbclient.cpp b/src/mongo/client/dbclient.cpp index 3a7e95e819c..4455a371e13 100644 --- a/src/mongo/client/dbclient.cpp +++ b/src/mongo/client/dbclient.cpp @@ -1383,14 +1383,14 @@ string DBClientBase::genIndexName(const BSONObj& keys) { return ss.str(); } -void DBClientBase::createIndex(StringData ns, const IndexSpec& descriptor) { - const BSONObj descriptorObj = descriptor.toBSON(); - +void DBClientBase::createIndexes(StringData ns, const std::vector<const IndexSpec*>& descriptors) { BSONObjBuilder command; command.append("createIndexes", nsToCollectionSubstring(ns)); { BSONArrayBuilder indexes(command.subarrayStart("indexes")); - indexes.append(descriptorObj); + for (const auto& desc : descriptors) { + indexes.append(desc->toBSON()); + } } const BSONObj commandObj = command.done(); diff --git a/src/mongo/client/dbclientinterface.h b/src/mongo/client/dbclientinterface.h index 6048c89f348..a0b4782062b 100644 --- a/src/mongo/client/dbclientinterface.h +++ b/src/mongo/client/dbclientinterface.h @@ -637,7 +637,13 @@ public: * @param descriptor Configuration object describing the index to create. The * descriptor must describe at least one key and index type. */ - virtual void createIndex(StringData ns, const IndexSpec& descriptor); + virtual void createIndex(StringData ns, const IndexSpec& descriptor) { + std::vector<const IndexSpec*> toBuild; + toBuild.push_back(&descriptor); + createIndexes(ns, toBuild); + } + + virtual void createIndexes(StringData ns, const std::vector<const IndexSpec*>& descriptor); virtual std::list<BSONObj> getIndexSpecs(const std::string& ns, int options = 0); diff --git a/src/mongo/db/catalog/index_catalog_impl.h b/src/mongo/db/catalog/index_catalog_impl.h index 27aa657318d..9c751c17692 100644 --- a/src/mongo/db/catalog/index_catalog_impl.h +++ b/src/mongo/db/catalog/index_catalog_impl.h @@ -305,10 +305,14 @@ public: return _entry; } - const std::string& getIndexName() { + const std::string& getIndexName() const { return _indexName; } + const BSONObj& getSpec() const { + return _spec; + } + private: Collection* const _collection; IndexCatalog* const _catalog; diff --git a/src/mongo/db/catalog/index_create.h b/src/mongo/db/catalog/index_create.h index acdf744da9e..238d3811958 100644 --- a/src/mongo/db/catalog/index_create.h +++ b/src/mongo/db/catalog/index_create.h @@ -82,7 +82,7 @@ public: virtual Status doneInserting(std::set<RecordId>* dupsOut = NULL) = 0; - virtual void commit() = 0; + virtual void commit(stdx::function<void(const BSONObj& spec)> onCreateFn) = 0; virtual void abortWithoutCleanup() = 0; @@ -235,10 +235,12 @@ public: * Should be called inside of a WriteUnitOfWork. If the index building is to be logOp'd, * logOp() should be called from the same unit of work as commit(). * + * `onCreateFn` will be called on each index before writes that mark the index as "ready". + * * Requires holding an exclusive database lock. */ - inline void commit() { - return this->_impl().commit(); + inline void commit(stdx::function<void(const BSONObj& spec)> onCreateFn = nullptr) { + return this->_impl().commit(onCreateFn); } /** diff --git a/src/mongo/db/catalog/index_create_impl.cpp b/src/mongo/db/catalog/index_create_impl.cpp index 88d0a246d6d..76cef288fd5 100644 --- a/src/mongo/db/catalog/index_create_impl.cpp +++ b/src/mongo/db/catalog/index_create_impl.cpp @@ -506,7 +506,7 @@ void MultiIndexBlockImpl::abortWithoutCleanup() { _needToCleanup = false; } -void MultiIndexBlockImpl::commit() { +void MultiIndexBlockImpl::commit(stdx::function<void(const BSONObj& spec)> onCreateFn) { // Do not interfere with writing multikey information when committing index builds. auto restartTracker = MakeGuard([this] { MultikeyPathTracker::get(_opCtx).startTrackingMultikeyPathInfo(); }); @@ -516,6 +516,10 @@ void MultiIndexBlockImpl::commit() { MultikeyPathTracker::get(_opCtx).stopTrackingMultikeyPathInfo(); for (size_t i = 0; i < _indexes.size(); i++) { + if (onCreateFn) { + onCreateFn(_indexes[i].block->getSpec()); + } + _indexes[i].block->success(); // The bulk builder will track multikey information itself. Non-bulk builders re-use the diff --git a/src/mongo/db/catalog/index_create_impl.h b/src/mongo/db/catalog/index_create_impl.h index 6f54d56e035..85b54b270f9 100644 --- a/src/mongo/db/catalog/index_create_impl.h +++ b/src/mongo/db/catalog/index_create_impl.h @@ -166,7 +166,7 @@ public: * * Requires holding an exclusive database lock. */ - void commit() override; + void commit(stdx::function<void(const BSONObj& spec)> onCreateFn) override; /** * May be called at any time after construction but before a successful commit(). Suppresses diff --git a/src/mongo/db/commands/create_indexes.cpp b/src/mongo/db/commands/create_indexes.cpp index bb8e978f6c1..e71d703b488 100644 --- a/src/mongo/db/commands/create_indexes.cpp +++ b/src/mongo/db/commands/create_indexes.cpp @@ -387,12 +387,10 @@ public: writeConflictRetry(opCtx, kCommandName, ns.ns(), [&] { WriteUnitOfWork wunit(opCtx); - indexer.commit(); - - for (auto&& infoObj : indexInfoObjs) { - getGlobalServiceContext()->getOpObserver()->onCreateIndex( - opCtx, ns, collection->uuid(), infoObj, false); - } + indexer.commit([opCtx, &ns, collection](const BSONObj& spec) { + opCtx->getServiceContext()->getOpObserver()->onCreateIndex( + opCtx, ns, collection->uuid(), spec, false); + }); wunit.commit(); }); diff --git a/src/mongo/dbtests/storage_timestamp_tests.cpp b/src/mongo/dbtests/storage_timestamp_tests.cpp index 426a0d74e6f..fd69f6e4efc 100644 --- a/src/mongo/dbtests/storage_timestamp_tests.cpp +++ b/src/mongo/dbtests/storage_timestamp_tests.cpp @@ -307,6 +307,17 @@ public: return {result.obj()}; } + BSONObj queryOplog(const BSONObj& query) { + OneOffRead oor(_opCtx, Timestamp::min()); + BSONObj ret; + ASSERT_TRUE(Helpers::findOne( + _opCtx, + AutoGetCollectionForRead(_opCtx, NamespaceString::kRsOplogNamespace).getCollection(), + query, + ret)); + return ret; + } + void assertMinValidDocumentAtTimestamp(Collection* coll, const Timestamp& ts, const repl::MinValidDocument& expectedDoc) { @@ -380,23 +391,41 @@ public: } } - std::string getNewIndexIdent(KVCatalog* kvCatalog, std::vector<std::string>& origIdents) { - OneOffRead oor(_opCtx, Timestamp::min()); + /** + * Use `ts` = Timestamp::min to observe all indexes. + */ + std::string getNewIndexIdentAtTime(KVCatalog* kvCatalog, + std::vector<std::string>& origIdents, + Timestamp ts) { + auto ret = getNewIndexIdentsAtTime(kvCatalog, origIdents, ts); + ASSERT_EQ(static_cast<std::size_t>(1), ret.size()) << " Num idents: " << ret.size(); + return ret[0]; + } + + /** + * Use `ts` = Timestamp::min to observe all indexes. + */ + std::vector<std::string> getNewIndexIdentsAtTime(KVCatalog* kvCatalog, + std::vector<std::string>& origIdents, + Timestamp ts) { + OneOffRead oor(_opCtx, ts); // Find the collection and index ident by performing a set difference on the original // idents and the current idents. std::vector<std::string> identsWithColl = kvCatalog->getAllIdents(_opCtx); std::sort(origIdents.begin(), origIdents.end()); std::sort(identsWithColl.begin(), identsWithColl.end()); - std::vector<std::string> collAndIdxIdents; + std::vector<std::string> idxIdents; std::set_difference(identsWithColl.begin(), identsWithColl.end(), origIdents.begin(), origIdents.end(), - std::back_inserter(collAndIdxIdents)); + std::back_inserter(idxIdents)); - ASSERT(collAndIdxIdents.size() == 1) << "Num idents: " << collAndIdxIdents.size(); - return collAndIdxIdents[0]; + for (const auto& ident : idxIdents) { + ASSERT(ident.find("index-") == 0) << "Ident is not an index: " << ident; + } + return idxIdents; } std::string getDroppedIndexIdent(KVCatalog* kvCatalog, std::vector<std::string>& origIdents) { @@ -1871,7 +1900,8 @@ public: const Timestamp afterIndexBuild = _clock->reserveTicks(1).asTimestamp(); - const std::string indexIdent = getNewIndexIdent(kvCatalog, origIdents); + const std::string indexIdent = + getNewIndexIdentAtTime(kvCatalog, origIdents, Timestamp::min()); assertIdentsMissingAtTimestamp(kvCatalog, "", indexIdent, beforeIndexBuild.asTimestamp()); // Assert that the index entry exists after init and `ready: false`. @@ -1900,6 +1930,92 @@ public: } }; +class TimestampMultiIndexBuilds : public StorageTimestampTest { +public: + void run() { + // Only run on 'wiredTiger'. No other storage engines to-date support timestamp writes. + if (mongo::storageGlobalParams.engine != "wiredTiger") { + return; + } + + auto kvStorageEngine = + dynamic_cast<KVStorageEngine*>(_opCtx->getServiceContext()->getStorageEngine()); + KVCatalog* kvCatalog = kvStorageEngine->getCatalog(); + + NamespaceString nss("unittests.timestampMultiIndexBuilds"); + reset(nss); + + AutoGetCollection autoColl(_opCtx, nss, LockMode::MODE_X, LockMode::MODE_X); + + const LogicalTime insertTimestamp = _clock->reserveTicks(1); + { + WriteUnitOfWork wuow(_opCtx); + insertDocument(autoColl.getCollection(), + InsertStatement(BSON("_id" << 0 << "a" << 1 << "b" << 2 << "c" << 3), + insertTimestamp.asTimestamp(), + 0LL)); + wuow.commit(); + ASSERT_EQ(1, itCount(autoColl.getCollection())); + } + + // Save the pre-state idents so we can capture the specific ident related to index + // creation. + std::vector<std::string> origIdents = kvCatalog->getAllIdents(_opCtx); + + DBDirectClient client(_opCtx); + { + IndexSpec index1; + // Name this index for easier querying. + index1.addKeys(BSON("a" << 1)).name("a_1"); + IndexSpec index2; + index2.addKeys(BSON("b" << 1)).name("b_1"); + + std::vector<const IndexSpec*> indexes; + indexes.push_back(&index1); + indexes.push_back(&index2); + client.createIndexes(nss.ns(), indexes); + } + + const Timestamp indexCreateInitTs = queryOplog(BSON("op" + << "n"))["ts"] + .timestamp(); + + const Timestamp indexAComplete = queryOplog(BSON("op" + << "c" + << "o.createIndexes" + << nss.coll() + << "o.name" + << "a_1"))["ts"] + .timestamp(); + + const auto indexBComplete = + Timestamp(indexAComplete.getSecs(), indexAComplete.getInc() + 1); + + // The idents are created and persisted with the "ready: false" write. There should be two + // new index idents visible at this time. + const std::vector<std::string> indexes = + getNewIndexIdentsAtTime(kvCatalog, origIdents, indexCreateInitTs); + ASSERT_EQ(static_cast<std::size_t>(2), indexes.size()) << " Num idents: " << indexes.size(); + + ASSERT_FALSE( + getIndexMetaData(getMetaDataAtTime(kvCatalog, nss, indexCreateInitTs), "a_1").ready); + ASSERT_FALSE( + getIndexMetaData(getMetaDataAtTime(kvCatalog, nss, indexCreateInitTs), "b_1").ready); + + // Assert the `a_1` index becomes ready at the next oplog entry time. + ASSERT_TRUE( + getIndexMetaData(getMetaDataAtTime(kvCatalog, nss, indexAComplete), "a_1").ready); + ASSERT_FALSE( + getIndexMetaData(getMetaDataAtTime(kvCatalog, nss, indexAComplete), "b_1").ready); + + // Assert the `b_1` index becomes ready at the last oplog entry time. + ASSERT_TRUE( + getIndexMetaData(getMetaDataAtTime(kvCatalog, nss, indexBComplete), "a_1").ready); + ASSERT_TRUE( + getIndexMetaData(getMetaDataAtTime(kvCatalog, nss, indexBComplete), "b_1").ready); + } +}; + class TimestampIndexDrops : public StorageTimestampTest { public: void run() { @@ -1944,7 +2060,7 @@ public: afterCreateTimestamps.push_back(_clock->reserveTicks(1).asTimestamp()); // Add the new ident to the vector and reset the current idents. - indexIdents.push_back(getNewIndexIdent(kvCatalog, origIdents)); + indexIdents.push_back(getNewIndexIdentAtTime(kvCatalog, origIdents, Timestamp::min())); origIdents = kvCatalog->getAllIdents(_opCtx); } @@ -2169,7 +2285,8 @@ public: ASSERT_OK(doAtomicApplyOps(nss.db().toString(), {createIndexOp})); AutoGetCollection autoColl(_opCtx, nss, LockMode::MODE_IS, LockMode::MODE_IS); - const std::string indexIdent = getNewIndexIdent(kvCatalog, origIdents); + const std::string indexIdent = + getNewIndexIdentAtTime(kvCatalog, origIdents, Timestamp::min()); assertIdentsMissingAtTimestamp( kvCatalog, "", indexIdent, beforeBuildTime.asTimestamp()); assertIdentsExistAtTimestamp(kvCatalog, "", indexIdent, startBuildTs); @@ -2223,6 +2340,7 @@ public: // TimestampIndexBuilds<SimulatePrimary> add<TimestampIndexBuilds<false>>(); add<TimestampIndexBuilds<true>>(); + add<TimestampMultiIndexBuilds>(); add<TimestampIndexDrops>(); // TimestampIndexBuilderOnPrimary<Background> add<TimestampIndexBuilderOnPrimary<false>>(); |