diff options
Diffstat (limited to 'src/mongo')
-rw-r--r-- | src/mongo/client/dbclient.cpp | 31 | ||||
-rw-r--r-- | src/mongo/client/dbclientinterface.h | 8 | ||||
-rw-r--r-- | src/mongo/db/repl/rollback_source.h | 9 | ||||
-rw-r--r-- | src/mongo/db/repl/rollback_source_impl.cpp | 9 | ||||
-rw-r--r-- | src/mongo/db/repl/rollback_source_impl.h | 2 | ||||
-rw-r--r-- | src/mongo/db/repl/rs_rollback.cpp | 195 | ||||
-rw-r--r-- | src/mongo/db/repl/rs_rollback.h | 20 | ||||
-rw-r--r-- | src/mongo/db/repl/rs_rollback_no_uuid_test.cpp | 9 | ||||
-rw-r--r-- | src/mongo/db/repl/rs_rollback_test.cpp | 155 |
9 files changed, 248 insertions, 190 deletions
diff --git a/src/mongo/client/dbclient.cpp b/src/mongo/client/dbclient.cpp index 74d26aac7d5..44db52dc622 100644 --- a/src/mongo/client/dbclient.cpp +++ b/src/mongo/client/dbclient.cpp @@ -651,6 +651,37 @@ BSONObj DBClientBase::findOne(const string& ns, return v.empty() ? BSONObj() : v[0]; } +BSONObj DBClientBase::findOneByUUID(const std::string& db, UUID uuid, const BSONObj& filter) { + list<BSONObj> results; + BSONObj res; + + BSONObjBuilder cmdBuilder; + uuid.appendToBuilder(&cmdBuilder, "find"); + cmdBuilder.append("filter", filter); + cmdBuilder.append("limit", 1); + cmdBuilder.append("singleBatch", true); + + BSONObj cmd = cmdBuilder.obj(); + + if (runCommand(db, cmd, res, QueryOption_SlaveOk)) { + BSONObj cursorObj = res.getObjectField("cursor"); + BSONObj docs = cursorObj.getObjectField("firstBatch"); + BSONObjIterator it(docs); + while (it.more()) { + BSONElement e = it.next(); + results.push_back(e.Obj().getOwned()); + } + invariant(results.size() <= 1); + if (results.empty()) { + return BSONObj(); + } + return results.front(); + } + uasserted( + 40586, + str::stream() << "find command using UUID failed. Command: " << cmd << " Result: " << res); +} + namespace { /** diff --git a/src/mongo/client/dbclientinterface.h b/src/mongo/client/dbclientinterface.h index ab9a4864cb6..f57618bca00 100644 --- a/src/mongo/client/dbclientinterface.h +++ b/src/mongo/client/dbclientinterface.h @@ -198,6 +198,14 @@ public: const BSONObj* fieldsToReturn = 0, int queryOptions = 0); + /** + * @return a single object that matches the filter within the collection specified by the UUID. + * If the command fails, an assertion error is thrown. Otherwise, if no document matches + * the query, an empty BSONObj is returned. + * @throws AssertionException + */ + virtual BSONObj findOneByUUID(const std::string& db, UUID uuid, const BSONObj& filter); + virtual std::string getServerAddress() const = 0; /** helper function. run a simple command where the command expression is simply diff --git a/src/mongo/db/repl/rollback_source.h b/src/mongo/db/repl/rollback_source.h index cafba0eb74c..0c55bcb9ae2 100644 --- a/src/mongo/db/repl/rollback_source.h +++ b/src/mongo/db/repl/rollback_source.h @@ -76,11 +76,18 @@ public: virtual BSONObj getLastOperation() const = 0; /** - * Fetch a single document from the sync source. + * Fetch a single document from the sync source using the namespace. */ virtual BSONObj findOne(const NamespaceString& nss, const BSONObj& filter) const = 0; /** + * Fetch a single document from the sync source using the UUID. + */ + virtual BSONObj findOneByUUID(const std::string& db, + UUID uuid, + const BSONObj& filter) const = 0; + + /** * Clones a single collection from the sync source. */ virtual void copyCollectionFromRemote(OperationContext* opCtx, diff --git a/src/mongo/db/repl/rollback_source_impl.cpp b/src/mongo/db/repl/rollback_source_impl.cpp index cbb8e66aa90..3e05e8a41be 100644 --- a/src/mongo/db/repl/rollback_source_impl.cpp +++ b/src/mongo/db/repl/rollback_source_impl.cpp @@ -73,6 +73,12 @@ BSONObj RollbackSourceImpl::findOne(const NamespaceString& nss, const BSONObj& f return _getConnection()->findOne(nss.toString(), filter, NULL, QueryOption_SlaveOk).getOwned(); } +BSONObj RollbackSourceImpl::findOneByUUID(const std::string& db, + UUID uuid, + const BSONObj& filter) const { + return _getConnection()->findOneByUUID(db, uuid, filter); +} + void RollbackSourceImpl::copyCollectionFromRemote(OperationContext* opCtx, const NamespaceString& nss) const { std::string errmsg; @@ -92,8 +98,7 @@ void RollbackSourceImpl::copyCollectionFromRemote(OperationContext* opCtx, StatusWith<BSONObj> RollbackSourceImpl::getCollectionInfoByUUID(const std::string& db, const UUID& uuid) const { - std::list<BSONObj> info = - _getConnection()->getCollectionInfos(db, BSON("options.uuid" << uuid)); + std::list<BSONObj> info = _getConnection()->getCollectionInfos(db, BSON("info.uuid" << uuid)); if (info.empty()) { return StatusWith<BSONObj>(ErrorCodes::NoSuchKey, str::stream() diff --git a/src/mongo/db/repl/rollback_source_impl.h b/src/mongo/db/repl/rollback_source_impl.h index e9995bb5981..1654194ea5d 100644 --- a/src/mongo/db/repl/rollback_source_impl.h +++ b/src/mongo/db/repl/rollback_source_impl.h @@ -66,6 +66,8 @@ public: BSONObj findOne(const NamespaceString& nss, const BSONObj& filter) const override; + BSONObj findOneByUUID(const std::string& db, UUID uuid, const BSONObj& filter) const override; + void copyCollectionFromRemote(OperationContext* opCtx, const NamespaceString& nss) const override; diff --git a/src/mongo/db/repl/rs_rollback.cpp b/src/mongo/db/repl/rs_rollback.cpp index b6fbd0e4b50..891262f6f4f 100644 --- a/src/mongo/db/repl/rs_rollback.cpp +++ b/src/mongo/db/repl/rs_rollback.cpp @@ -93,7 +93,7 @@ namespace repl { using namespace rollback_internal; bool DocID::operator<(const DocID& other) const { - int comp = strcmp(ns, other.ns); + int comp = uuid.toString().compare(other.uuid.toString()); if (comp < 0) return true; if (comp > 0) @@ -110,28 +110,18 @@ bool DocID::operator==(const DocID& other) const { return !(*this < other || other < *this); } -void FixUpInfo::removeAllDocsToRefetchFor(const std::string& collection) { - docsToRefetch.erase(docsToRefetch.lower_bound(DocID::minFor(collection.c_str())), - docsToRefetch.upper_bound(DocID::maxFor(collection.c_str()))); +void FixUpInfo::removeAllDocsToRefetchFor(UUID collectionUUID) { + docsToRefetch.erase(docsToRefetch.lower_bound(DocID::minFor(collectionUUID)), + docsToRefetch.upper_bound(DocID::maxFor(collectionUUID))); } -// TODO: This function will not be fully functional until the entire -// rollback via refetch for non-WT project is complete. See SERVER-30171. void FixUpInfo::removeRedundantOperations() { - // These loops and their bodies can be done in any order. The final result of the FixUpInfo - // members will be the same. - - // for (const auto& collection : collectionsToDrop) { - // removeAllDocsToRefetchFor(collection); - // indexesToDrop.erase(collection); - // collectionsToResyncMetadata.erase(collection); - // } - - for (const auto& collection : collectionsToResyncData) { - removeAllDocsToRefetchFor(collection); - // indexesToDrop.erase(collection); - // collectionsToResyncMetadata.erase(collection); - // collectionsToDrop.erase(collection); + for (const auto& collectionUUID : collectionsToDrop) { + removeAllDocsToRefetchFor(collectionUUID); + indexesToDrop.erase(collectionUUID); + indexesToCreate.erase(collectionUUID); + collectionsToRename.erase(collectionUUID); + collectionsToResyncMetadata.erase(collectionUUID); } } @@ -240,9 +230,9 @@ Status rollback_internal::updateFixUpInfoFromLocalOplogEntry(FixUpInfo& fixUpInf if (oplogEntry.getOpType() == OpTypeEnum::kNoop) return Status::OK(); - DocID doc; - doc.ownedObj = oplogEntry.raw; - doc.ns = oplogEntry.raw.getStringField("ns"); + // If we are inserting/updating/deleting a document in the oplog entry, we will update + // the doc._id field when we actually insert the docID into the docsToRefetch set. + DocID doc = DocID(oplogEntry.raw, BSONElement(), *uuid); if (oplogEntry.getNamespace().isEmpty()) { throw RSFatalException(str::stream() << "Local op on rollback has no ns: " @@ -266,18 +256,19 @@ Status rollback_internal::updateFixUpInfoFromLocalOplogEntry(FixUpInfo& fixUpInf invariant(sessionId); invariant(oplogEntry.getStatementId()); - DocID txnDoc; - BSONObjBuilder txnBob; - txnBob.append("_id", sessionId->toBSON()); - txnDoc.ownedObj = txnBob.obj(); - txnDoc._id = txnDoc.ownedObj.firstElement(); - // TODO: SERVER-29667 + // TODO: SERVER-30076 // Once collection uuids replace namespace strings for rollback, this will need to be - // changed to the uuid of the session transaction table collection. - txnDoc.ns = NamespaceString::kSessionTransactionsTableNamespace.ns().c_str(); - - fixUpInfo.docsToRefetch.insert(txnDoc); - fixUpInfo.refetchTransactionDocs = true; + // changed to the uuid of the session transaction table collection. Need to add + // txnDoc.uuid with the proper uuid. + // DocID txnDoc; + // BSONObjBuilder txnBob; + // txnBob.append("_id", sessionId->toBSON()); + // txnDoc.ownedObj = txnBob.obj(); + // txnDoc._id = txnDoc.ownedObj.firstElement(); + // txnDoc.ns = NamespaceString::kSessionTransactionsTableNamespace.ns().c_str(); + // + // fixUpInfo.docsToRefetch.insert(txnDoc); + // fixUpInfo.refetchTransactionDocs = true; } if (oplogEntry.getOpType() == OpTypeEnum::kCommand) { @@ -350,7 +341,7 @@ Status rollback_internal::updateFixUpInfoFromLocalOplogEntry(FixUpInfo& fixUpInf "Missing index name in dropIndexes operation on rollback."); } - BSONObj obj2 = *(oplogEntry.getObject2()); + BSONObj obj2 = oplogEntry.getObject2().get().getOwned(); // Inserts the index name and the index spec of the index to be created into the map // of index name and index specs that need to be created for the given collection. @@ -434,14 +425,14 @@ Status rollback_internal::updateFixUpInfoFromLocalOplogEntry(FixUpInfo& fixUpInf } } - // Checks if the renameColl ection is a cross-database rename. If the dropSource + // Checks if the renameCollection is a cross-database rename. If the dropSource // field is present in the oplog entry then the renameCollection must be a // cross-database rename. The field will be absent in renames in the same // database. auto dropSource = obj.getField("dropSource"); if (!dropSource.eoo()) { return fixUpInfo.recordCrossDatabaseRenameRollbackInfo( - dropSource, obj, nss, *uuid, oplogEntry.getOpTime()); + dropSource, obj, NamespaceString(ns), *uuid, oplogEntry.getOpTime()); } RenameCollectionInfo info; @@ -571,11 +562,10 @@ Status rollback_internal::updateFixUpInfoFromLocalOplogEntry(FixUpInfo& fixUpInf doc._id = oplogEntry.getIdElement(); if (doc._id.eoo()) { - std::string message = str::stream() << "Cannot roll back op with no _id. ns: " << doc.ns; + std::string message = str::stream() << "Cannot roll back op with no _id. ns: " << nss.ns(); severe() << message << ", document: " << redact(oplogEntry.toBSON()); throw RSFatalException(message); } - fixUpInfo.docsToRefetch.insert(doc); return Status::OK(); } @@ -659,8 +649,9 @@ void dropIndex(OperationContext* opCtx, */ void rollbackCreateIndexes(OperationContext* opCtx, UUID uuid, std::set<std::string> indexNames) { - Collection* collection = UUIDCatalog::get(opCtx).lookupCollectionByUUID(uuid); NamespaceString nss = UUIDCatalog::get(opCtx).lookupNSSByUUID(uuid); + Lock::DBLock dbLock(opCtx, nss.db(), MODE_X); + Collection* collection = UUIDCatalog::get(opCtx).lookupCollectionByUUID(uuid); // If we cannot find the collection, we skip over dropping the index. if (!collection) { @@ -669,8 +660,6 @@ void rollbackCreateIndexes(OperationContext* opCtx, UUID uuid, std::set<std::str return; } - Lock::DBLock dbLock(opCtx, nss.db(), MODE_X); - // If we cannot find the index catalog, we skip over dropping the index. auto indexCatalog = collection->getIndexCatalog(); if (!indexCatalog) { @@ -696,9 +685,9 @@ void rollbackDropIndexes(OperationContext* opCtx, UUID uuid, std::map<std::string, BSONObj> indexNames) { - Collection* collection = UUIDCatalog::get(opCtx).lookupCollectionByUUID(uuid); NamespaceString nss = UUIDCatalog::get(opCtx).lookupNSSByUUID(uuid); - + Lock::DBLock dbLock(opCtx, nss.db(), MODE_X); + Collection* collection = UUIDCatalog::get(opCtx).lookupCollectionByUUID(uuid); // If we cannot find the collection, we skip over dropping the index. if (!collection) { LOG(2) << "Cannot find the collection with uuid: " << uuid.toString() @@ -706,18 +695,18 @@ void rollbackDropIndexes(OperationContext* opCtx, return; } - Lock::DBLock dbLock(opCtx, nss.db(), MODE_X); - for (auto itIndex = indexNames.begin(); itIndex != indexNames.end(); itIndex++) { + const string indexName = itIndex->first; BSONObj indexSpec = itIndex->second; - // We replace the namespace field because it is possible that a // renameCollection command has occurred, changing the namespace of the // collection from what it initially was during the creation of this index. BSONObjBuilder updatedNss; updatedNss.append("ns", nss.ns()); - indexSpec.addField(updatedNss.obj().firstElement()); + + BSONObj updatedNssObj = updatedNss.obj(); + indexSpec.addField(updatedNssObj.firstElement()); createIndexForApplyOps(opCtx, indexSpec, nss, {}); @@ -871,6 +860,9 @@ void rollbackRenameCollection(OperationContext* opCtx, UUID uuid, RenameCollecti severe() << "Unable to roll back renameCollection command: " << status.toString(); throw RSFatalException("Unable to rollback renameCollection command"); } + + log() << "Renamed collection from " << info.renameFrom.ns() << "to " << info.renameTo.ns() + << " with uuid: " << uuid; } void syncFixUp(OperationContext* opCtx, @@ -880,8 +872,9 @@ void syncFixUp(OperationContext* opCtx, ReplicationProcess* replicationProcess) { unsigned long long totalSize = 0; - // namespace -> doc id -> doc - map<string, map<DocID, BSONObj>> goodVersions; + // UUID -> doc id -> doc + stdx::unordered_map<UUID, std::map<DocID, BSONObj>, UUID::Hash> goodVersions; + auto& catalog = UUIDCatalog::get(opCtx); // Fetches all the goodVersions of each document from the current sync source. unsigned long long numFetched = 0; @@ -891,11 +884,15 @@ void syncFixUp(OperationContext* opCtx, for (auto&& doc : fixUpInfo.docsToRefetch) { invariant(!doc._id.eoo()); // This is checked when we insert to the set. + UUID uuid = doc.uuid; + NamespaceString nss = catalog.lookupNSSByUUID(uuid); + try { - LOG(2) << "Refetching document, namespace: " << doc.ns << ", _id: " << redact(doc._id); + LOG(2) << "Refetching document, namespace: " << nss.toString() + << ", _id: " << redact(doc._id); // TODO : Slow. Lots of round trips. numFetched++; - BSONObj good = rollbackSource.findOne(NamespaceString(doc.ns), doc._id.wrap()); + BSONObj good = rollbackSource.findOneByUUID(nss.db().toString(), uuid, doc._id.wrap()); totalSize += good.objsize(); // Checks that the total amount of data that needs to be refetched is at most @@ -906,7 +903,8 @@ void syncFixUp(OperationContext* opCtx, } // Note good might be empty, indicating we should delete it. - goodVersions[doc.ns][doc] = good; + goodVersions[uuid].insert(std::pair<DocID, BSONObj>(doc, good)); + } catch (const DBException& ex) { // If the collection turned into a view, we might get an error trying to // refetch documents, but these errors should be ignored, as we'll be creating @@ -914,7 +912,7 @@ void syncFixUp(OperationContext* opCtx, if (ex.getCode() == ErrorCodes::CommandNotSupportedOnView) continue; - log() << "Rollback couldn't re-get from ns: " << doc.ns << " _id: " << redact(doc._id) + log() << "Rollback couldn't re-fetch from uuid: " << uuid << " _id: " << redact(doc._id) << ' ' << numFetched << '/' << fixUpInfo.docsToRefetch.size() << ": " << redact(ex); throw; @@ -937,16 +935,20 @@ void syncFixUp(OperationContext* opCtx, // when undoing renameCollection operations. for (auto uuid : fixUpInfo.collectionsToDrop) { - // TODO: This invariant will be uncommented once the rollback via refetch for non-WT - // project is complete. See SERVER-30171. - // invariant(!fixUpInfo.indexesToDrop.count(uuid)); + // Checks that if the collection is going to be dropped, all commands that + // were done on the collection to be dropped were removed during the function + // call to removeRedundantOperations(). + invariant(!fixUpInfo.indexesToDrop.count(uuid)); + invariant(!fixUpInfo.indexesToCreate.count(uuid)); + invariant(!fixUpInfo.collectionsToRename.count(uuid)); + invariant(!fixUpInfo.collectionsToResyncMetadata.count(uuid)); - Collection* collection = UUIDCatalog::get(opCtx).lookupCollectionByUUID(uuid); NamespaceString nss = UUIDCatalog::get(opCtx).lookupNSSByUUID(uuid); - AutoGetDb dbLock(opCtx, nss.db(), MODE_X); + Database* db = dbLock.getDb(); if (db) { + Collection* collection = UUIDCatalog::get(opCtx).lookupCollectionByUUID(uuid); dropCollection(opCtx, nss, collection, db); log() << "Dropped collection with UUID: " << uuid << " and nss: " << nss; } @@ -977,32 +979,7 @@ void syncFixUp(OperationContext* opCtx, } // Full collection data and metadata resync. - if (!fixUpInfo.collectionsToResyncData.empty() || - !fixUpInfo.collectionsToResyncMetadata.empty()) { - - // Reloads the collection data from the sync source in order - // to roll back a drop/dropIndexes/renameCollection operation. - for (const string& ns : fixUpInfo.collectionsToResyncData) { - log() << "Resyncing collection, namespace: " << ns; - - // TODO: This invariant will be uncommented once the - // rollback via refetch for non-WT project is complete. See SERVER-30171. - // invariant(!fixUpInfo.indexesToDrop.count(ns)); - // invariant(!fixUpInfo.collectionsToResyncMetadata.count(ns)); - - const NamespaceString nss(ns); - - { - Lock::DBLock dbLock(opCtx, nss.db(), MODE_X); - Database* db = dbHolder().openDb(opCtx, nss.db().toString()); - invariant(db); - WriteUnitOfWork wunit(opCtx); - fassertStatusOK(40505, db->dropCollectionEvenIfSystem(opCtx, nss)); - wunit.commit(); - } - - rollbackSource.copyCollectionFromRemote(opCtx, nss); - } + if (!fixUpInfo.collectionsToResyncMetadata.empty()) { // Retrieves collections from the sync source in order to obtain the collection // flags needed to roll back collMod operations. We roll back collMod operations @@ -1013,14 +990,16 @@ void syncFixUp(OperationContext* opCtx, for (auto uuid : fixUpInfo.collectionsToResyncMetadata) { log() << "Resyncing collection metadata, uuid: " << uuid; - Collection* collection = UUIDCatalog::get(opCtx).lookupCollectionByUUID(uuid); - invariant(collection); - NamespaceString nss = collection->ns(); + NamespaceString nss = UUIDCatalog::get(opCtx).lookupNSSByUUID(uuid); Lock::DBLock dbLock(opCtx, nss.db(), MODE_X); + auto db = dbHolder().openDb(opCtx, nss.db().toString()); invariant(db); + Collection* collection = UUIDCatalog::get(opCtx).lookupCollectionByUUID(uuid); + invariant(collection); + auto cce = collection->getCatalogEntry(); auto infoResult = rollbackSource.getCollectionInfoByUUID(nss.db().toString(), uuid); @@ -1115,7 +1094,7 @@ void syncFixUp(OperationContext* opCtx, for (auto it = fixUpInfo.indexesToDrop.begin(); it != fixUpInfo.indexesToDrop.end(); it++) { UUID uuid = it->first; - auto indexNames = it->second; + std::set<std::string> indexNames = it->second; rollbackCreateIndexes(opCtx, uuid, indexNames); } @@ -1125,7 +1104,7 @@ void syncFixUp(OperationContext* opCtx, for (auto it = fixUpInfo.indexesToCreate.begin(); it != fixUpInfo.indexesToCreate.end(); it++) { UUID uuid = it->first; - auto indexNames = it->second; + std::map<std::string, BSONObj> indexNames = it->second; rollbackDropIndexes(opCtx, uuid, indexNames); } @@ -1140,13 +1119,14 @@ void syncFixUp(OperationContext* opCtx, // Keeps an archive of items rolled back if the collection has not been dropped // while rolling back createCollection operations. - const auto& ns = nsAndGoodVersionsByDocID.first; + + const auto& uuid = nsAndGoodVersionsByDocID.first; unique_ptr<Helpers::RemoveSaver> removeSaver; + invariant(!fixUpInfo.collectionsToDrop.count(uuid)); - // TODO: This invariant will be uncommented once the - // rollback via refetch for non-WT project is complete. See SERVER-30171 - // invariant(!fixUpInfo.collectionsToDrop.count(ns)); - removeSaver.reset(new Helpers::RemoveSaver("rollback", "", ns)); + NamespaceString nss = catalog.lookupNSSByUUID(uuid); + + removeSaver.reset(new Helpers::RemoveSaver("rollback", "", nss.ns())); const auto& goodVersionsByDocID = nsAndGoodVersionsByDocID.second; for (const auto& idAndDoc : goodVersionsByDocID) { @@ -1160,15 +1140,12 @@ void syncFixUp(OperationContext* opCtx, const DocID& doc = idAndDoc.first; BSONObj pattern = doc._id.wrap(); // { _id : ... } try { - verify(doc.ns && *doc.ns); - invariant(!fixUpInfo.collectionsToResyncData.count(doc.ns)); // TODO: Lots of overhead in context. This can be faster. const NamespaceString docNss(doc.ns); Lock::DBLock docDbLock(opCtx, docNss.db(), MODE_X); - OldClientContext ctx(opCtx, doc.ns); - - Collection* collection = ctx.db()->getCollection(opCtx, docNss); + OldClientContext ctx(opCtx, doc.ns.toString()); + Collection* collection = catalog.lookupCollectionByUUID(uuid); // Adds the doc to our rollback file if the collection was not dropped while // rolling back createCollection operations. Does not log an error when @@ -1182,16 +1159,16 @@ void syncFixUp(OperationContext* opCtx, if (found) { auto status = removeSaver->goingToDelete(obj); if (!status.isOK()) { - severe() << "Rollback cannot write document in namespace " << doc.ns + severe() << "Rollback cannot write document in namespace " << nss.ns() << " to archive file: " << redact(status); throw RSFatalException(str::stream() << "Rollback cannot write document in namespace " - << doc.ns + << nss.ns() << " to archive file."); } } else { error() << "Rollback cannot find object: " << pattern << " in namespace " - << doc.ns; + << nss.ns(); } } @@ -1215,7 +1192,7 @@ void syncFixUp(OperationContext* opCtx, const auto findOneStart = clock->now(); RecordId loc = Helpers::findOne(opCtx, collection, pattern, false); if (clock->now() - findOneStart > Milliseconds(200)) - warning() << "Roll back slow no _id index for " << doc.ns + warning() << "Roll back slow no _id index for " << nss.ns() << " perhaps?"; // Would be faster but requires index: // RecordId loc = Helpers::findById(nsd, pattern); @@ -1242,7 +1219,7 @@ void syncFixUp(OperationContext* opCtx, // eventually. warning() << "Ignoring failure to roll back change to capped " - << "collection " << doc.ns << " with _id " + << "collection " << nss.ns() << " with _id " << redact(idAndDoc.first._id.toString( /*includeFieldName*/ false)) << ": " << redact(e); @@ -1250,7 +1227,7 @@ void syncFixUp(OperationContext* opCtx, } else { deleteObjects(opCtx, collection, - docNss, + nss, pattern, true, // justOne true); // god @@ -1260,19 +1237,19 @@ void syncFixUp(OperationContext* opCtx, // TODO faster... updates++; - UpdateRequest request(docNss); + UpdateRequest request(nss); request.setQuery(pattern); request.setUpdates(idAndDoc.second); request.setGod(); request.setUpsert(); - UpdateLifecycleImpl updateLifecycle(docNss); + UpdateLifecycleImpl updateLifecycle(nss); request.setLifecycle(&updateLifecycle); update(opCtx, ctx.db(), request); } } catch (const DBException& e) { - log() << "Exception in rollback ns:" << doc.ns << ' ' << pattern.toString() << ' ' + log() << "Exception in rollback ns:" << nss.ns() << ' ' << pattern.toString() << ' ' << redact(e) << " ndeletes:" << deletes; throw; } diff --git a/src/mongo/db/repl/rs_rollback.h b/src/mongo/db/repl/rs_rollback.h index 73a9ba91fb4..b4659313608 100644 --- a/src/mongo/db/repl/rs_rollback.h +++ b/src/mongo/db/repl/rs_rollback.h @@ -215,19 +215,24 @@ namespace rollback_internal { struct DocID { BSONObj ownedObj; - const char* ns; + StringData ns; BSONElement _id; + UUID uuid; + + DocID(BSONObj obj, BSONElement id, UUID ui) + : ownedObj(obj), ns(obj.getStringField("ns")), _id(id), uuid(ui) {} + bool operator<(const DocID& other) const; bool operator==(const DocID& other) const; - static DocID minFor(const char* ns) { + static DocID minFor(UUID uuid) { auto obj = BSON("" << MINKEY); - return {obj, ns, obj.firstElement()}; + return DocID(obj, obj.firstElement(), uuid); } - static DocID maxFor(const char* ns) { + static DocID maxFor(UUID uuid) { auto obj = BSON("" << MAXKEY); - return {obj, ns, obj.firstElement()}; + return DocID(obj, obj.firstElement(), uuid); } }; @@ -245,9 +250,6 @@ struct FixUpInfo { // we only need to refetch it once. std::set<DocID> docsToRefetch; - // Namespaces of collections that need to be resynced from the sync source. - std::set<std::string> collectionsToResyncData; - // UUID of collections that need to be dropped. stdx::unordered_set<UUID, UUID::Hash> collectionsToDrop; @@ -293,7 +295,7 @@ struct FixUpInfo { * Removes all documents in the docsToRefetch set that are in * the collection passed into the function. */ - void removeAllDocsToRefetchFor(const std::string& collection); + void removeAllDocsToRefetchFor(UUID uuid); /** * Removes any redundant operations that may have happened during diff --git a/src/mongo/db/repl/rs_rollback_no_uuid_test.cpp b/src/mongo/db/repl/rs_rollback_no_uuid_test.cpp index f20292dd957..57e2ca10c9f 100644 --- a/src/mongo/db/repl/rs_rollback_no_uuid_test.cpp +++ b/src/mongo/db/repl/rs_rollback_no_uuid_test.cpp @@ -72,6 +72,7 @@ public: const HostAndPort& getSource() const override; BSONObj getLastOperation() const override; BSONObj findOne(const NamespaceString& nss, const BSONObj& filter) const override; + BSONObj findOneByUUID(const std::string& db, UUID uuid, const BSONObj& filter) const override; void copyCollectionFromRemote(OperationContext* opCtx, const NamespaceString& nss) const override; StatusWith<BSONObj> getCollectionInfoByUUID(const std::string& db, @@ -109,12 +110,18 @@ BSONObj RollbackSourceMock::findOne(const NamespaceString& nss, const BSONObj& f return BSONObj(); } +BSONObj RollbackSourceMock::findOneByUUID(const std::string& db, + UUID uuid, + const BSONObj& filter) const { + return BSONObj(); +} + void RollbackSourceMock::copyCollectionFromRemote(OperationContext* opCtx, const NamespaceString& nss) const {} StatusWith<BSONObj> RollbackSourceMock::getCollectionInfoByUUID(const std::string& db, const UUID& uuid) const { - return BSON("info" << BSON("uuid" << uuid) << "options" << BSONObj()); + return BSON("options" << BSONObj() << "info" << BSON("uuid" << uuid)); } StatusWith<BSONObj> RollbackSourceMock::getCollectionInfo(const NamespaceString& nss) const { diff --git a/src/mongo/db/repl/rs_rollback_test.cpp b/src/mongo/db/repl/rs_rollback_test.cpp index 30422f1456b..c2a67fe5c4a 100644 --- a/src/mongo/db/repl/rs_rollback_test.cpp +++ b/src/mongo/db/repl/rs_rollback_test.cpp @@ -74,6 +74,9 @@ public: const HostAndPort& getSource() const override; BSONObj getLastOperation() const override; BSONObj findOne(const NamespaceString& nss, const BSONObj& filter) const override; + + BSONObj findOneByUUID(const std::string& db, UUID uuid, const BSONObj& filter) const override; + void copyCollectionFromRemote(OperationContext* opCtx, const NamespaceString& nss) const override; StatusWith<BSONObj> getCollectionInfoByUUID(const std::string& db, @@ -111,6 +114,12 @@ BSONObj RollbackSourceMock::findOne(const NamespaceString& nss, const BSONObj& f return BSONObj(); } +BSONObj RollbackSourceMock::findOneByUUID(const std::string& db, + UUID uuid, + const BSONObj& filter) const { + return BSONObj(); +} + void RollbackSourceMock::copyCollectionFromRemote(OperationContext* opCtx, const NamespaceString& nss) const {} @@ -120,7 +129,7 @@ StatusWith<BSONObj> RollbackSourceMock::getCollectionInfo(const NamespaceString& StatusWith<BSONObj> RollbackSourceMock::getCollectionInfoByUUID(const std::string& db, const UUID& uuid) const { - return BSON("options" << BSON("uuid" << uuid)); + return BSON("options" << BSONObj() << "info" << BSON("uuid" << uuid)); } @@ -387,7 +396,7 @@ int _testRollbackDelete(OperationContext* opCtx, : RollbackSourceMock(std::move(oplog)), called(false), _documentAtSource(documentAtSource) {} - BSONObj findOne(const NamespaceString& nss, const BSONObj& filter) const { + BSONObj findOneByUUID(const std::string& db, UUID uuid, const BSONObj& filter) const { called = true; return _documentAtSource; } @@ -982,7 +991,7 @@ TEST_F(RSRollbackTest, RollbackRenameCollectionInSameDatabaseCommand) { // StatusWith<BSONObj> getCollectionInfoByUUID(const std::string& db, const UUID& uuid) const // { // getCollectionInfoCalled = true; -// return BSON("options" << BSON("uuid" << uuid << "temp" << true)); +// return BSON("info" << BSON("uuid" << uuid) << "options" << BSON("temp" << true))); // } // mutable bool getCollectionInfoCalled = false; // }; @@ -1634,7 +1643,9 @@ TEST_F(RSRollbackTest, RollbackApplyOpsCommand) { RollbackSourceLocal(std::unique_ptr<OplogInterface> oplog) : RollbackSourceMock(std::move(oplog)) {} - BSONObj findOne(const NamespaceString& nss, const BSONObj& filter) const override { + BSONObj findOneByUUID(const std::string& db, + UUID uuid, + const BSONObj& filter) const override { int numFields = 0; for (const auto element : filter) { ++numFields; @@ -1964,49 +1975,53 @@ DEATH_TEST_F(RSRollbackTest, LocalEntryWithTxnNumberWithoutStmtIdIsFatal, "invar RSFatalException); } -TEST(RSRollbackTest, LocalEntryWithTxnNumberAddsTransactionTableDocToBeRefetched) { - FixUpInfo fui; - auto entryWithoutTxnNumber = - BSON("ts" << Timestamp(Seconds(1), 0) << "t" << 1LL << "h" << 1LL << "op" - << "i" - << "ui" - << UUID::gen() - << "ns" - << "test.t2" - << "o" - << BSON("_id" << 2 << "a" << 2)); - ASSERT_OK(updateFixUpInfoFromLocalOplogEntry(fui, entryWithoutTxnNumber)); - - // With no txnNumber present, no extra documents need to be refetched. - ASSERT_EQ(fui.docsToRefetch.size(), 1U); - - auto entryWithTxnNumber = - BSON("ts" << Timestamp(Seconds(1), 0) << "t" << 1LL << "h" << 1LL << "op" - << "i" - << "ui" - << UUID::gen() - << "ns" - << "test.t" - << "o" - << BSON("_id" << 1 << "a" << 1) - << "txnNumber" - << 1LL - << "stmtId" - << 1 - << "lsid" - << makeLogicalSessionIdForTest().toBSON()); - ASSERT_OK(updateFixUpInfoFromLocalOplogEntry(fui, entryWithTxnNumber)); - - // If txnNumber is present, the session transactions table document corresponding to the oplog - // entry's sessionId also needs to be refetched. - ASSERT_EQ(fui.docsToRefetch.size(), 3U); - - DocID expectedTxnDoc; - expectedTxnDoc.ownedObj = BSON("_id" << entryWithTxnNumber["lsid"]); - expectedTxnDoc._id = expectedTxnDoc.ownedObj.firstElement(); - expectedTxnDoc.ns = NamespaceString::kSessionTransactionsTableNamespace.ns().c_str(); - ASSERT_TRUE(fui.docsToRefetch.find(expectedTxnDoc) != fui.docsToRefetch.end()); -} +// TODO: Uncomment this test once transactions have been updated to work with the proper +// uuid. See SERVER-30076. +// TEST(RSRollbackTest, LocalEntryWithTxnNumberAddsTransactionTableDocToBeRefetched) { +// FixUpInfo fui; +// auto entryWithoutTxnNumber = +// BSON("ts" << Timestamp(Seconds(1), 0) << "t" << 1LL << "h" << 1LL << "op" +// << "i" +// << "ui" +// << UUID::gen() +// << "ns" +// << "test.t2" +// << "o" +// << BSON("_id" << 2 << "a" << 2)); +// ASSERT_OK(updateFixUpInfoFromLocalOplogEntry(fui, entryWithoutTxnNumber)); +// +// // With no txnNumber present, no extra documents need to be refetched. +// ASSERT_EQ(fui.docsToRefetch.size(), 1U); +// +// UUID uuid = UUID::gen(); +// auto entryWithTxnNumber = +// BSON("ts" << Timestamp(Seconds(1), 0) << "t" << 1LL << "h" << 1LL << "op" +// << "i" +// << "ui" +// << uuid +// << "ns" +// << "test.t" +// << "o" +// << BSON("_id" << 1 << "a" << 1) +// << "txnNumber" +// << 1LL +// << "stmtId" +// << 1 +// << "lsid" +// << makeLogicalSessionIdForTest().toBSON()); +// ASSERT_OK(updateFixUpInfoFromLocalOplogEntry(fui, entryWithTxnNumber)); +// +// // If txnNumber is present, the session transactions table document corresponding to the oplog +// // entry's sessionId also needs to be refetched. +// ASSERT_EQ(fui.docsToRefetch.size(), 3U); +// +// DocID expectedTxnDoc; +// expectedTxnDoc.ownedObj = BSON("_id" << entryWithTxnNumber["lsid"]); +// expectedTxnDoc._id = expectedTxnDoc.ownedObj.firstElement(); +// expectedTxnDoc.ns = NamespaceString::kSessionTransactionsTableNamespace.ns().c_str(); +// expectedTxnDoc.uuid = uuid; +// ASSERT_TRUE(fui.docsToRefetch.find(expectedTxnDoc) != fui.docsToRefetch.end()); +//} TEST_F(RSRollbackTest, RollbackReturnsImmediatelyOnFailureToTransitionToRollback) { // On failing to transition to ROLLBACK, rollback() should return immediately and not call @@ -2116,51 +2131,55 @@ TEST(FixUpInfoTest, RemoveAllDocsToRefetchForWorks) { const auto normalHolder = BSON("" << OID::gen()); const auto normalKey = normalHolder.firstElement(); + UUID uuid1 = UUID::gen(); + UUID uuid2 = UUID::gen(); + UUID uuid3 = UUID::gen(); + // Can't use ASSERT_EQ with this since it isn't ostream-able. Failures will at least give you // the size. If that isn't enough, use GDB. using DocSet = std::set<DocID>; FixUpInfo fui; fui.docsToRefetch = { - DocID::minFor("a"), - DocID{{}, "a", normalKey}, - DocID::maxFor("a"), + DocID::minFor(uuid1), + DocID{{}, normalKey, uuid1}, + DocID::maxFor(uuid1), - DocID::minFor("b"), - DocID{{}, "b", normalKey}, - DocID::maxFor("b"), + DocID::minFor(uuid2), + DocID{{}, normalKey, uuid2}, + DocID::maxFor(uuid2), - DocID::minFor("c"), - DocID{{}, "c", normalKey}, - DocID::maxFor("c"), + DocID::minFor(uuid3), + DocID{{}, normalKey, uuid3}, + DocID::maxFor(uuid3), }; // Remove from the middle. - fui.removeAllDocsToRefetchFor("b"); + fui.removeAllDocsToRefetchFor(uuid2); ASSERT((fui.docsToRefetch == DocSet{ - DocID::minFor("a"), - DocID{{}, "a", normalKey}, - DocID::maxFor("a"), + DocID::minFor(uuid1), + DocID{{}, normalKey, uuid1}, + DocID::maxFor(uuid1), - DocID::minFor("c"), - DocID{{}, "c", normalKey}, - DocID::maxFor("c"), + DocID::minFor(uuid3), + DocID{{}, normalKey, uuid3}, + DocID::maxFor(uuid3), })) << "remaining docs: " << fui.docsToRefetch.size(); // Remove from the end. - fui.removeAllDocsToRefetchFor("c"); + fui.removeAllDocsToRefetchFor(uuid3); ASSERT((fui.docsToRefetch == DocSet{ - DocID::minFor("a"), // This comment helps clang-format. - DocID{{}, "a", normalKey}, - DocID::maxFor("a"), + DocID::minFor(uuid1), // This comment helps clang-format. + DocID{{}, normalKey, uuid1}, + DocID::maxFor(uuid1), })) << "remaining docs: " << fui.docsToRefetch.size(); // Everything else. - fui.removeAllDocsToRefetchFor("a"); + fui.removeAllDocsToRefetchFor(uuid1); ASSERT((fui.docsToRefetch == DocSet{})) << "remaining docs: " << fui.docsToRefetch.size(); } |