@@ -324,14 +324,24 @@ Status _applyPrepareTransaction(OperationContext* opCtx,
324324 // This will prevent hybrid index builds from corrupting an index on secondary nodes if a
325325 // prepared transaction becomes prepared during a build but commits after the index build
326326 // commits.
327- for (const auto & op : ops) {
328- auto ns = op.getNss ();
329- auto uuid = *op.getUuid ();
330- if (BackgroundOperation::inProgForNs (ns)) {
331- warning () << " blocking replication until index builds are finished on "
332- << redact (ns.toString ()) << " , due to prepared transaction" ;
333- BackgroundOperation::awaitNoBgOpInProgForNs (ns);
334- IndexBuildsCoordinator::get (opCtx)->awaitNoIndexBuildInProgressForCollection (uuid);
327+ // When two-phase index builds are in use, this is both unnecessary and unsafe. Due to locking,
328+ // we can guarantee that a transaction prepared on a primary during an index build will always
329+ // commit before that index build completes. Because two-phase index builds replicate start and
330+ // commit oplog entries, it will never be possible to replicate a prepared transaction, commit
331+ // an index build, then commit the transaction, the bug described above.
332+ // This blocking behavior can also introduce a deadlock with two-phase index builds on
333+ // a secondary if a prepared transaction blocks on an index build, but the index build can't
334+ // re-acquire its X lock because of the transaction.
335+ if (!IndexBuildsCoordinator::get (opCtx)->supportsTwoPhaseIndexBuild ()) {
336+ for (const auto & op : ops) {
337+ auto ns = op.getNss ();
338+ auto uuid = *op.getUuid ();
339+ if (BackgroundOperation::inProgForNs (ns)) {
340+ warning () << " blocking replication until index builds are finished on "
341+ << redact (ns.toString ()) << " , due to prepared transaction" ;
342+ BackgroundOperation::awaitNoBgOpInProgForNs (ns);
343+ IndexBuildsCoordinator::get (opCtx)->awaitNoIndexBuildInProgressForCollection (uuid);
344+ }
335345 }
336346 }
337347
0 commit comments