void OpObserverShardingImpl::shardObserveTransactionPrepareOrUnpreparedCommit( OperationContext* opCtx, const std::vector<repl::ReplOperation>& stmts) { for (const auto stmt : stmts) { auto const nss = stmt.getNss(); AutoGetCollection autoColl(opCtx, nss, MODE_IS); auto csr = CollectionShardingRuntime::get(opCtx, nss); auto csrLock = CollectionShardingRuntime::CSRLock::lock(opCtx, csr); auto msm = MigrationSourceManager::get(csr, csrLock); if (!msm) { continue; } auto const opType = stmt.getOpType(); // We pass an empty opTime to observers because retryable write history doesn't care about // writes in transactions. if (opType == repl::OpTypeEnum::kInsert) { msm->getCloner()->onInsertOp(opCtx, stmt.getObject(), {}); } else if (opType == repl::OpTypeEnum::kUpdate) { if (auto updateDoc = stmt.getObject2()) { msm->getCloner()->onUpdateOp( opCtx, stmt.getPreImageDocumentKey(), *updateDoc, {}, {}); } } else if (opType == repl::OpTypeEnum::kDelete) { if (isMigratingWithCSRLock(csr, csrLock, stmt.getObject())) { msm->getCloner()->onDeleteOp( opCtx, getDocumentKey(opCtx, nss, stmt.getObject()), {}, {}); } } } }
void OpObserverShardingImpl::shardObserveInsertOp(OperationContext* opCtx, const NamespaceString nss, const BSONObj& insertedDoc, const repl::OpTime& opTime, const bool fromMigrate, const bool inMultiDocumentTransaction) { auto* const csr = (nss == NamespaceString::kSessionTransactionsTableNamespace || fromMigrate) ? nullptr : CollectionShardingRuntime::get(opCtx, nss); if (!csr) { return; } csr->checkShardVersionOrThrow(opCtx); if (inMultiDocumentTransaction) { assertIntersectingChunkHasNotMoved(opCtx, csr, insertedDoc); return; } auto csrLock = CollectionShardingRuntime::CSRLock::lock(opCtx, csr); auto msm = MigrationSourceManager::get(csr, csrLock); if (msm) { msm->getCloner()->onInsertOp(opCtx, insertedDoc, opTime); } }
bool OpObserverShardingImpl::isMigrating(OperationContext* opCtx, NamespaceString const& nss, BSONObj const& docToDelete) { auto css = CollectionShardingRuntime::get(opCtx, nss); auto msm = MigrationSourceManager::get(css); return msm && msm->getCloner()->isDocumentInMigratingChunk(docToDelete); }
void OpObserverShardingImpl::shardObserveUpdateOp(OperationContext* opCtx, const NamespaceString nss, const BSONObj& updatedDoc, const repl::OpTime& opTime, const repl::OpTime& prePostImageOpTime, const bool inMultiDocumentTransaction) { auto* const css = CollectionShardingRuntime::get(opCtx, nss); css->checkShardVersionOrThrow(opCtx); auto msm = MigrationSourceManager::get(css); if (msm) { msm->getCloner()->onUpdateOp(opCtx, updatedDoc, opTime, prePostImageOpTime); } if (inMultiDocumentTransaction && repl::ReadConcernArgs::get(opCtx).getArgsAtClusterTime()) { assertIntersectingChunkHasNotMoved(opCtx, css, updatedDoc); } }
void OpObserverShardingImpl::shardObserveDeleteOp(OperationContext* opCtx, const NamespaceString nss, const BSONObj& documentKey, const repl::OpTime& opTime, const repl::OpTime& preImageOpTime, const bool inMultiDocumentTransaction) { auto* const csr = CollectionShardingRuntime::get(opCtx, nss); csr->checkShardVersionOrThrow(opCtx); if (inMultiDocumentTransaction) { assertIntersectingChunkHasNotMoved(opCtx, csr, documentKey); return; } auto csrLock = CollectionShardingRuntime::CSRLock::lock(opCtx, csr); auto msm = MigrationSourceManager::get(csr, csrLock); if (msm && getIsMigrating(opCtx)) { msm->getCloner()->onDeleteOp(opCtx, documentKey, opTime, preImageOpTime); } }
void OpObserverShardingImpl::shardObserveInsertOp(OperationContext* opCtx, const NamespaceString nss, const BSONObj& insertedDoc, const repl::OpTime& opTime, const bool fromMigrate, const bool inMultiDocumentTransaction) { auto* const css = (nss == NamespaceString::kSessionTransactionsTableNamespace || fromMigrate) ? nullptr : CollectionShardingRuntime::get(opCtx, nss); if (css) { css->checkShardVersionOrThrow(opCtx); auto msm = MigrationSourceManager::get(css); if (msm) { msm->getCloner()->onInsertOp(opCtx, insertedDoc, opTime); } if (inMultiDocumentTransaction && repl::ReadConcernArgs::get(opCtx).getArgsAtClusterTime()) { assertIntersectingChunkHasNotMoved(opCtx, css, insertedDoc); } } }
void BaseClonerTest::testLifeCycle() { // GetDiagnosticString ASSERT_FALSE(getCloner()->getDiagnosticString().empty()); // IsActiveAfterStart ASSERT_FALSE(getCloner()->isActive()); ASSERT_OK(getCloner()->start()); ASSERT_TRUE(getCloner()->isActive()); tearDown(); // StartWhenActive setUp(); ASSERT_OK(getCloner()->start()); ASSERT_TRUE(getCloner()->isActive()); ASSERT_NOT_OK(getCloner()->start()); ASSERT_TRUE(getCloner()->isActive()); tearDown(); // CancelWithoutStart setUp(); ASSERT_FALSE(getCloner()->isActive()); getCloner()->cancel(); ASSERT_FALSE(getCloner()->isActive()); tearDown(); // WaitWithoutStart setUp(); ASSERT_FALSE(getCloner()->isActive()); getCloner()->wait(); ASSERT_FALSE(getCloner()->isActive()); tearDown(); // ShutdownBeforeStart setUp(); getExecutor().shutdown(); ASSERT_NOT_OK(getCloner()->start()); ASSERT_FALSE(getCloner()->isActive()); tearDown(); // StartAndCancel setUp(); ASSERT_OK(getCloner()->start()); scheduleNetworkResponse(BSON("ok" << 1)); getCloner()->cancel(); finishProcessingNetworkResponse(); ASSERT_EQUALS(ErrorCodes::CallbackCanceled, getStatus().code()); ASSERT_FALSE(getCloner()->isActive()); tearDown(); // StartButShutdown setUp(); ASSERT_OK(getCloner()->start()); scheduleNetworkResponse(BSON("ok" << 1)); getExecutor().shutdown(); // Network interface should not deliver mock response to callback. finishProcessingNetworkResponse(); ASSERT_EQUALS(ErrorCodes::CallbackCanceled, getStatus().code()); ASSERT_FALSE(getCloner()->isActive()); }