summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/mongo/client/dbclient.cpp8
-rw-r--r--src/mongo/client/dbclientinterface.h8
-rw-r--r--src/mongo/db/catalog/index_catalog_impl.h6
-rw-r--r--src/mongo/db/catalog/index_create.h8
-rw-r--r--src/mongo/db/catalog/index_create_impl.cpp6
-rw-r--r--src/mongo/db/catalog/index_create_impl.h2
-rw-r--r--src/mongo/db/commands/create_indexes.cpp10
-rw-r--r--src/mongo/dbtests/storage_timestamp_tests.cpp136
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>>();