Status applyCommitTransaction(OperationContext* opCtx, const OplogEntry& entry, repl::OplogApplication::Mode mode) { // Return error if run via applyOps command. uassert(50987, "commitTransaction is only used internally by secondaries.", mode != repl::OplogApplication::Mode::kApplyOpsCmd); IDLParserErrorContext ctx("commitTransaction"); auto commitOplogEntryOpTime = entry.getOpTime(); auto commitCommand = CommitTransactionOplogObject::parse(ctx, entry.getObject()); const bool prepared = !commitCommand.getPrepared() || *commitCommand.getPrepared(); if (!prepared) return Status::OK(); invariant(commitCommand.getCommitTimestamp()); if (mode == repl::OplogApplication::Mode::kRecovering || mode == repl::OplogApplication::Mode::kInitialSync) { return _applyTransactionFromOplogChain(opCtx, entry, mode, *commitCommand.getCommitTimestamp(), commitOplogEntryOpTime.getTimestamp()); } invariant(mode == repl::OplogApplication::Mode::kSecondary); // Transaction operations are in its own batch, so we can modify their opCtx. invariant(entry.getSessionId()); invariant(entry.getTxnNumber()); opCtx->setLogicalSessionId(*entry.getSessionId()); opCtx->setTxnNumber(*entry.getTxnNumber()); // The write on transaction table may be applied concurrently, so refreshing state // from disk may read that write, causing starting a new transaction on an existing // txnNumber. Thus, we start a new transaction without refreshing state from disk. MongoDOperationContextSessionWithoutRefresh sessionCheckout(opCtx); auto transaction = TransactionParticipant::get(opCtx); invariant(transaction); transaction.unstashTransactionResources(opCtx, "commitTransaction"); transaction.commitPreparedTransaction( opCtx, *commitCommand.getCommitTimestamp(), commitOplogEntryOpTime); return Status::OK(); }
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 } }