// prefetch for an oplog operation void prefetchPagesForReplicatedOp(OperationContext* opCtx, Database* db, const OplogEntry& oplogEntry) { invariant(db); const ReplSettings::IndexPrefetchConfig prefetchConfig = ReplicationCoordinator::get(opCtx)->getIndexPrefetchConfig(); // Prefetch ignores non-CRUD operations. if (!oplogEntry.isCrudOpType()) { return; } // This will have to change for engines other than MMAP V1, because they might not have // means for directly prefetching pages from the collection. For this purpose, acquire S // lock on the database, instead of optimizing with IS. const auto& nss = oplogEntry.getNamespace(); Lock::CollectionLock collLock(opCtx->lockState(), nss.ns(), MODE_S); Collection* collection = db->getCollection(opCtx, nss); if (!collection) { return; } auto opType = oplogEntry.getOpType(); LOG(4) << "index prefetch for op " << OpType_serializer(opType); // should we prefetch index pages on updates? if the update is in-place and doesn't change // indexed values, it is actually slower - a lot slower if there are a dozen indexes or // lots of multikeys. possible variations (not all mutually exclusive): // 1) current behavior: full prefetch // 2) don't do it for updates // 3) don't do multikey indexes for updates // 4) don't prefetchIndexPages on some heuristic; e.g., if it's an $inc. // 5) if not prefetching index pages (#2), we should do it if we are upsertings and it // will be an insert. to do that we could do the prefetchRecordPage first and if DNE // then we do #1. // // note that on deletes 'obj' does not have all the keys we would want to prefetch on. // a way to achieve that would be to prefetch the record first, and then afterwards do // this part. // auto obj = oplogEntry.getOperationToApply(); invariant(!obj.isEmpty()); prefetchIndexPages(opCtx, collection, prefetchConfig, obj); // do not prefetch the data for inserts; it doesn't exist yet // // we should consider doing the record prefetch for the delete op case as we hit the record // when we delete. note if done we only want to touch the first page. // // update: do record prefetch. if ((opType == OpTypeEnum::kUpdate) && // do not prefetch the data for capped collections because // they typically do not have an _id index for findById() to use. !collection->isCapped()) { prefetchRecordPages(opCtx, db, nss.ns().c_str(), obj); } }
StatusWith<std::set<NamespaceString>> RollbackImpl::_namespacesForOp(const OplogEntry& oplogEntry) { NamespaceString opNss = oplogEntry.getNamespace(); OpTypeEnum opType = oplogEntry.getOpType(); std::set<NamespaceString> namespaces; // No namespaces for a no-op. if (opType == OpTypeEnum::kNoop) { return std::set<NamespaceString>(); } // CRUD ops have the proper namespace in the operation 'ns' field. if (opType == OpTypeEnum::kInsert || opType == OpTypeEnum::kUpdate || opType == OpTypeEnum::kDelete) { return std::set<NamespaceString>({opNss}); } // If the operation is a command, then we need to extract the appropriate namespaces from the // command object, as opposed to just using the 'ns' field of the oplog entry itself. if (opType == OpTypeEnum::kCommand) { auto obj = oplogEntry.getObject(); auto firstElem = obj.firstElement(); // Does not handle 'applyOps' entries. invariant(oplogEntry.getCommandType() != OplogEntry::CommandType::kApplyOps, "_namespacesForOp does not handle 'applyOps' oplog entries."); switch (oplogEntry.getCommandType()) { case OplogEntry::CommandType::kRenameCollection: { // Add both the 'from' and 'to' namespaces. namespaces.insert(NamespaceString(firstElem.valuestrsafe())); namespaces.insert(NamespaceString(obj.getStringField("to"))); break; } case OplogEntry::CommandType::kDropDatabase: { // There is no specific namespace to save for a drop database operation. break; } case OplogEntry::CommandType::kDbCheck: case OplogEntry::CommandType::kConvertToCapped: case OplogEntry::CommandType::kEmptyCapped: { // These commands do not need to be supported by rollback. 'convertToCapped' should // always be converted to lower level DDL operations, and 'emptycapped' is a // testing-only command. std::string message = str::stream() << "Encountered unsupported command type '" << firstElem.fieldName() << "' during rollback."; return Status(ErrorCodes::UnrecoverableRollbackError, message); } case OplogEntry::CommandType::kCreate: case OplogEntry::CommandType::kDrop: case OplogEntry::CommandType::kCreateIndexes: case OplogEntry::CommandType::kDropIndexes: case OplogEntry::CommandType::kCollMod: { // For all other command types, we should be able to parse the collection name from // the first command argument. try { auto cmdNss = CommandHelpers::parseNsCollectionRequired(opNss.db(), obj); namespaces.insert(cmdNss); } catch (const DBException& ex) { return ex.toStatus(); } break; } case OplogEntry::CommandType::kApplyOps: default: // Every possible command type should be handled above. MONGO_UNREACHABLE } }