TEST(OperationContextTest, SessionIdAndTransactionNumber) { auto serviceCtx = ServiceContext::make(); auto client = serviceCtx->makeClient("OperationContextTest"); auto opCtx = client->makeOperationContext(); const auto lsid = makeLogicalSessionIdForTest(); opCtx->setLogicalSessionId(lsid); opCtx->setTxnNumber(5); ASSERT(opCtx->getTxnNumber()); ASSERT_EQUALS(5, *opCtx->getTxnNumber()); }
BSONObj TransactionRouter::Participant::attachTxnFieldsIfNeeded( BSONObj cmd, bool isFirstStatementInThisParticipant) const { bool hasStartTxn = false; bool hasAutoCommit = false; bool hasTxnNum = false; BSONObjIterator iter(cmd); while (iter.more()) { auto elem = iter.next(); if (OperationSessionInfoFromClient::kStartTransactionFieldName == elem.fieldNameStringData()) { hasStartTxn = true; } else if (OperationSessionInfoFromClient::kAutocommitFieldName == elem.fieldNameStringData()) { hasAutoCommit = true; } else if (OperationSessionInfo::kTxnNumberFieldName == elem.fieldNameStringData()) { hasTxnNum = true; } } // TODO: SERVER-37045 assert when attaching startTransaction to killCursors command. // The first command sent to a participant must start a transaction, unless it is a transaction // command, which don't support the options that start transactions, i.e. startTransaction and // readConcern. Otherwise the command must not have a read concern. bool mustStartTransaction = isFirstStatementInThisParticipant && !isTransactionCommand(cmd); if (!mustStartTransaction) { dassert(!cmd.hasField(repl::ReadConcernArgs::kReadConcernFieldName)); } BSONObjBuilder newCmd = mustStartTransaction ? appendFieldsForStartTransaction(std::move(cmd), sharedOptions.readConcernArgs, sharedOptions.atClusterTime, !hasStartTxn) : BSONObjBuilder(std::move(cmd)); if (isCoordinator) { newCmd.append(kCoordinatorField, true); } if (!hasAutoCommit) { newCmd.append(OperationSessionInfoFromClient::kAutocommitFieldName, false); } if (!hasTxnNum) { newCmd.append(OperationSessionInfo::kTxnNumberFieldName, sharedOptions.txnNumber); } else { auto osi = OperationSessionInfoFromClient::parse("OperationSessionInfo"_sd, newCmd.asTempObj()); invariant(sharedOptions.txnNumber == *osi.getTxnNumber()); } return newCmd.obj(); }
bool SessionCatalogMigrationSource::_fetchNextNewWriteOplog(OperationContext* opCtx) { repl::OpTime nextOpTimeToFetch; EntryAtOpTimeType entryAtOpTimeType; { stdx::lock_guard<stdx::mutex> lk(_newOplogMutex); if (_newWriteOpTimeList.empty()) { _lastFetchedNewWriteOplog.reset(); return false; } std::tie(nextOpTimeToFetch, entryAtOpTimeType) = _newWriteOpTimeList.front(); } DBDirectClient client(opCtx); const auto& newWriteOplogDoc = client.findOne(NamespaceString::kRsOplogNamespace.ns(), nextOpTimeToFetch.asQuery(), nullptr, QueryOption_OplogReplay); uassert(40620, str::stream() << "Unable to fetch oplog entry with opTime: " << nextOpTimeToFetch.toBSON(), !newWriteOplogDoc.isEmpty()); auto newWriteOplogEntry = uassertStatusOK(repl::OplogEntry::parse(newWriteOplogDoc)); // If this oplog entry corresponds to transaction prepare/commit, replace it with a sentinel // entry. if (entryAtOpTimeType == EntryAtOpTimeType::kTransaction) { newWriteOplogEntry = makeSentinelOplogEntry(*newWriteOplogEntry.getSessionId(), *newWriteOplogEntry.getTxnNumber(), opCtx->getServiceContext()->getFastClockSource()->now()); } { stdx::lock_guard<stdx::mutex> lk(_newOplogMutex); _lastFetchedNewWriteOplog = newWriteOplogEntry; _newWriteOpTimeList.pop_front(); } return true; }