/** * Write oplog entry(ies) for the delete operation. */ OpTimeBundle replLogDelete(OperationContext* opCtx, const NamespaceString& nss, OptionalCollectionUUID uuid, StmtId stmtId, bool fromMigrate, const boost::optional<BSONObj>& deletedDoc) { OperationSessionInfo sessionInfo; repl::OplogLink oplogLink; const auto txnParticipant = TransactionParticipant::get(opCtx); if (txnParticipant) { sessionInfo.setSessionId(*opCtx->getLogicalSessionId()); sessionInfo.setTxnNumber(*opCtx->getTxnNumber()); oplogLink.prevOpTime = txnParticipant->getLastWriteOpTime(*opCtx->getTxnNumber()); } OpTimeBundle opTimes; opTimes.wallClockTime = getWallClockTimeForOpLog(opCtx); if (deletedDoc && opCtx->getTxnNumber()) { auto noteOplog = logOperation(opCtx, "n", nss, uuid, deletedDoc.get(), nullptr, false, opTimes.wallClockTime, sessionInfo, stmtId, {}, false /* prepare */, OplogSlot()); opTimes.prePostImageOpTime = noteOplog; oplogLink.preImageOpTime = noteOplog; } auto& documentKey = documentKeyDecoration(opCtx); opTimes.writeOpTime = logOperation(opCtx, "d", nss, uuid, documentKey, nullptr, fromMigrate, opTimes.wallClockTime, sessionInfo, stmtId, oplogLink, false /* prepare */, OplogSlot()); return opTimes; }
SessionCatalogMigrationSource::SessionCatalogMigrationSource(OperationContext* opCtx, NamespaceString ns) : _ns(std::move(ns)), _rollbackIdAtInit(repl::ReplicationProcess::get(opCtx)->getRollbackID()) { // Exclude entries for transaction. Query query; // Sort is not needed for correctness. This is just for making it easier to write deterministic // tests. query.sort(BSON("_id" << 1)); DBDirectClient client(opCtx); auto cursor = client.query(NamespaceString::kSessionTransactionsTableNamespace, query); while (cursor->more()) { auto nextSession = SessionTxnRecord::parse( IDLParserErrorContext("Session migration cloning"), cursor->next()); if (!nextSession.getLastWriteOpTime().isNull()) { _sessionOplogIterators.push_back( stdx::make_unique<SessionOplogIterator>(std::move(nextSession), _rollbackIdAtInit)); } } { AutoGetCollection autoColl(opCtx, NamespaceString::kRsOplogNamespace, MODE_IX); writeConflictRetry( opCtx, "session migration initialization majority commit barrier", NamespaceString::kRsOplogNamespace.ns(), [&] { const auto message = BSON("sessionMigrateCloneStart" << _ns.ns()); WriteUnitOfWork wuow(opCtx); opCtx->getClient()->getServiceContext()->getOpObserver()->onInternalOpMessage( opCtx, _ns, {}, {}, message); wuow.commit(); }); } auto opTimeToWait = repl::ReplClientInfo::forClient(opCtx->getClient()).getLastOp(); WriteConcernResult result; WriteConcernOptions majority( WriteConcernOptions::kMajority, WriteConcernOptions::SyncMode::UNSET, 0); uassertStatusOK(waitForWriteConcern(opCtx, opTimeToWait, majority, &result)); }
void ReplicationRecoveryImpl::_reconstructPreparedTransactions(OperationContext* opCtx) { DBDirectClient client(opCtx); const auto cursor = client.query(NamespaceString::kSessionTransactionsTableNamespace, {BSON("state" << "prepared")}); // Iterate over each entry in the transactions table that has a prepared transaction. while (cursor->more()) { const auto txnRecordObj = cursor->next(); const auto txnRecord = SessionTxnRecord::parse( IDLParserErrorContext("recovering prepared transaction"), txnRecordObj); invariant(txnRecord.getState() == DurableTxnStateEnum::kPrepared); // Get the prepareTransaction oplog entry corresponding to this transactions table entry. invariant(!opCtx->recoveryUnit()->getPointInTimeReadTimestamp()); const auto prepareOpTime = txnRecord.getLastWriteOpTime(); invariant(!prepareOpTime.isNull()); TransactionHistoryIterator iter(prepareOpTime); invariant(iter.hasNext()); const auto prepareOplogEntry = iter.next(opCtx); { // Make a new opCtx so that we can set the lsid when applying the prepare transaction // oplog entry. auto newClient = opCtx->getServiceContext()->makeClient("reconstruct-prepared-transactions"); AlternativeClientRegion acr(newClient); const auto newOpCtx = cc().makeOperationContext(); repl::UnreplicatedWritesBlock uwb(newOpCtx.get()); // Snapshot transaction can never conflict with the PBWM lock. newOpCtx->lockState()->setShouldConflictWithSecondaryBatchApplication(false); // TODO: SERVER-40177 This should be removed once it is guaranteed operations applied on // recovering nodes cannot encounter unnecessary prepare conflicts. newOpCtx->recoveryUnit()->setIgnorePrepared(true); // Checks out the session, applies the operations and prepares the transactions. uassertStatusOK(applyRecoveredPrepareTransaction(newOpCtx.get(), prepareOplogEntry)); } } }
/** * Write oplog entry(ies) for the update operation. */ OpTimeBundle replLogUpdate(OperationContext* opCtx, const OplogUpdateEntryArgs& args) { BSONObj storeObj; if (args.updateArgs.storeDocOption == CollectionUpdateArgs::StoreDocOption::PreImage) { invariant(args.updateArgs.preImageDoc); storeObj = *args.updateArgs.preImageDoc; } else if (args.updateArgs.storeDocOption == CollectionUpdateArgs::StoreDocOption::PostImage) { storeObj = args.updateArgs.updatedDoc; } OperationSessionInfo sessionInfo; repl::OplogLink oplogLink; const auto txnParticipant = TransactionParticipant::get(opCtx); if (txnParticipant) { sessionInfo.setSessionId(*opCtx->getLogicalSessionId()); sessionInfo.setTxnNumber(*opCtx->getTxnNumber()); oplogLink.prevOpTime = txnParticipant->getLastWriteOpTime(*opCtx->getTxnNumber()); } OpTimeBundle opTimes; opTimes.wallClockTime = getWallClockTimeForOpLog(opCtx); if (!storeObj.isEmpty() && opCtx->getTxnNumber()) { auto noteUpdateOpTime = logOperation(opCtx, "n", args.nss, args.uuid, storeObj, nullptr, false, opTimes.wallClockTime, sessionInfo, args.updateArgs.stmtId, {}, false /* prepare */, OplogSlot()); opTimes.prePostImageOpTime = noteUpdateOpTime; if (args.updateArgs.storeDocOption == CollectionUpdateArgs::StoreDocOption::PreImage) { oplogLink.preImageOpTime = noteUpdateOpTime; } else if (args.updateArgs.storeDocOption == CollectionUpdateArgs::StoreDocOption::PostImage) { oplogLink.postImageOpTime = noteUpdateOpTime; } } opTimes.writeOpTime = logOperation(opCtx, "u", args.nss, args.uuid, args.updateArgs.update, &args.updateArgs.criteria, args.updateArgs.fromMigrate, opTimes.wallClockTime, sessionInfo, args.updateArgs.stmtId, oplogLink, false /* prepare */, OplogSlot()); return opTimes; }