diff --git a/src/mongo/db/s/balancer/balancer.cpp b/src/mongo/db/s/balancer/balancer.cpp
index abb04df7cc5..60ff5ac5650 100644
--- a/src/mongo/db/s/balancer/balancer.cpp
+++ b/src/mongo/db/s/balancer/balancer.cpp
@@ -617,7 +617,7 @@ void Balancer::_consumeActionStreamLoop() {
                                 [this,
                                  stream,
                                  &applyActionResponseTo,
-                                 action = mergeAllChunksAction](
+                                 action = std::move(mergeAllChunksAction)](
                                     const StatusWith<NumMergedChunks>& swNumMergedChunks) {
                                     applyActionResponseTo(action, swNumMergedChunks, stream);
                                 });
diff --git a/src/mongo/db/s/balancer/balancer_defragmentation_policy_impl.cpp b/src/mongo/db/s/balancer/balancer_defragmentation_policy_impl.cpp
index e7130f99e9b..dbb3712d76e 100644
--- a/src/mongo/db/s/balancer/balancer_defragmentation_policy_impl.cpp
+++ b/src/mongo/db/s/balancer/balancer_defragmentation_policy_impl.cpp
@@ -168,42 +168,20 @@ class MergeAndMeasureChunksPhase : public DefragmentationPhase {
 public:
     static std::unique_ptr<MergeAndMeasureChunksPhase> build(OperationContext* opCtx,
                                                              const CollectionType& coll) {
-        auto collectionChunks = getCollectionChunks(opCtx, coll);
-        const auto collectionZones = getCollectionZones(opCtx, coll);
+        // TODO optimize
+        auto shardIds = Grid::get(opCtx)->shardRegistry()->getAllShardIds(opCtx);
 
         // Calculate small chunk threshold to limit dataSize commands
         const auto maxChunkSizeBytes = getCollectionMaxChunkSizeBytes(opCtx, coll);
         const int64_t smallChunkSizeThreshold =
             (maxChunkSizeBytes / 100) * kSmallChunkSizeThresholdPctg;
 
-        stdx::unordered_map<ShardId, PendingActions> pendingActionsByShards;
-        // Find ranges of chunks; for single-chunk ranges, request DataSize; for multi-range, issue
-        // merge
-        while (!collectionChunks.empty()) {
-            auto upperRangeBound = std::prev(collectionChunks.cend());
-            auto lowerRangeBound = upperRangeBound;
-            while (lowerRangeBound != collectionChunks.cbegin() &&
-                   areMergeable(*std::prev(lowerRangeBound), *lowerRangeBound, collectionZones)) {
-                --lowerRangeBound;
-            }
-            if (lowerRangeBound != upperRangeBound) {
-                pendingActionsByShards[upperRangeBound->getShard()].rangesToMerge.emplace_back(
-                    lowerRangeBound->getMin(), upperRangeBound->getMax());
-            } else {
-                if (!upperRangeBound->getEstimatedSizeBytes().has_value()) {
-                    pendingActionsByShards[upperRangeBound->getShard()]
-                        .rangesWithoutDataSize.emplace_back(upperRangeBound->getMin(),
-                                                            upperRangeBound->getMax());
-                }
-            }
-            collectionChunks.erase(lowerRangeBound, std::next(upperRangeBound));
-        }
         return std::unique_ptr<MergeAndMeasureChunksPhase>(
             new MergeAndMeasureChunksPhase(coll.getNss(),
                                            coll.getUuid(),
                                            coll.getKeyPattern().toBSON(),
                                            smallChunkSizeThreshold,
-                                           std::move(pendingActionsByShards)));
+                                           std::move(shardIds)));
     }
 
     DefragmentationPhaseEnum getType() const override {
@@ -216,50 +194,50 @@ public:
 
     boost::optional<BalancerStreamAction> popNextStreamableAction(
         OperationContext* opCtx) override {
-        boost::optional<BalancerStreamAction> nextAction = boost::none;
-        if (!_pendingActionsByShards.empty()) {
-            auto it = _shardToProcess ? _pendingActionsByShards.find(*_shardToProcess)
-                                      : _pendingActionsByShards.begin();
+        if (_shardsRequiringMergeForNss.size() > 0) {
+            ++_outstandingActions;
+            auto shardId = *_shardsRequiringMergeForNss.begin();
+            _shardsRequiringMergeForNss.erase(_shardsRequiringMergeForNss.begin());
+            auto act = MergeAllChunksOnShardInfo(shardId, _nss);
+            act.uuid = _uuid;
+            return boost::optional<BalancerStreamAction>(std::move(act));
+        }
+
+        if (_currentMode == MergeChunks) {
+            if (_outstandingActions > 0) {
+                return boost::none;
+            }
 
-            invariant(it != _pendingActionsByShards.end());
+            auto coll = Grid::get(opCtx)->catalogClient()->getCollection(opCtx, _nss);
+            auto collectionChunks = getCollectionChunks(opCtx, coll);
+            for (size_t i = 0; i < collectionChunks.size(); i++) {
+                auto& chunk = collectionChunks.at(i);
+                _rangesWithoutDataSize.push_back({chunk.getRange(), chunk.getShard()});
+            }
+            _currentMode = CalculateDataSize;
+        }
 
-            auto& [shardId, pendingActions] = *it;
+        if (_rangesWithoutDataSize.size() > 0) {
+            ++_outstandingActions;
+            const auto [rangeToMeasure, shardId] = _rangesWithoutDataSize.back();
             auto shardVersion = getShardVersion(opCtx, shardId, _nss);
+            _rangesWithoutDataSize.pop_back();
+            return boost::optional<BalancerStreamAction>(
+                DataSizeInfo(shardId,
+                             _nss,
+                             _uuid,
+                             rangeToMeasure,
+                             shardVersion,
+                             _shardKey,
+                             true /* estimate */,
+                             _smallChunkSizeThresholdBytes /* maxSize */));
+        }
 
-            if (pendingActions.rangesWithoutDataSize.size() > pendingActions.rangesToMerge.size()) {
-                const auto& rangeToMeasure = pendingActions.rangesWithoutDataSize.back();
-                nextAction = boost::optional<BalancerStreamAction>(
-                    DataSizeInfo(shardId,
-                                 _nss,
-                                 _uuid,
-                                 rangeToMeasure,
-                                 shardVersion,
-                                 _shardKey,
-                                 true /* estimate */,
-                                 _smallChunkSizeThresholdBytes /* maxSize */));
-                pendingActions.rangesWithoutDataSize.pop_back();
-            } else if (!pendingActions.rangesToMerge.empty()) {
-                const auto& rangeToMerge = pendingActions.rangesToMerge.back();
-                nextAction = boost::optional<BalancerStreamAction>(
-                    MergeInfo(shardId, _nss, _uuid, shardVersion.placementVersion(), rangeToMerge));
-                pendingActions.rangesToMerge.pop_back();
-            }
-            if (nextAction.has_value()) {
-                ++_outstandingActions;
-                if (pendingActions.rangesToMerge.empty() &&
-                    pendingActions.rangesWithoutDataSize.empty()) {
-                    it = _pendingActionsByShards.erase(it, std::next(it));
-                } else {
-                    ++it;
-                }
-            }
-            if (it != _pendingActionsByShards.end()) {
-                _shardToProcess = it->first;
-            } else {
-                _shardToProcess = boost::none;
-            }
+        if (_outstandingActions == 0) {
+            _currentMode = Done;
         }
-        return nextAction;
+
+        return boost::none;
     }
 
     boost::optional<MigrateInfo> popNextMigration(
@@ -274,74 +252,80 @@ public:
         if (_aborted) {
             return;
         }
-        stdx::visit(OverloadedVisitor{
-                        [&](const MergeInfo& mergeAction) {
-                            auto& mergeResponse = stdx::get<Status>(response);
-                            auto& shardingPendingActions =
-                                _pendingActionsByShards[mergeAction.shardId];
-                            handleActionResult(
-                                opCtx,
-                                _nss,
-                                _uuid,
-                                getType(),
-                                mergeResponse,
-                                [&]() {
-                                    shardingPendingActions.rangesWithoutDataSize.emplace_back(
-                                        mergeAction.chunkRange);
-                                },
-                                [&]() {
-                                    shardingPendingActions.rangesToMerge.emplace_back(
-                                        mergeAction.chunkRange);
-                                },
-                                [&]() { _abort(getType()); });
-                        },
-                        [&](const DataSizeInfo& dataSizeAction) {
-                            auto& dataSizeResponse =
-                                stdx::get<StatusWith<DataSizeResponse>>(response);
-                            handleActionResult(
+
+        stdx::visit(
+            OverloadedVisitor{
+                [](const MergeInfo& mergeAction) {
+                    uasserted(ErrorCodes::BadValue, "Unexpected action type");
+                },
+                [&](const DataSizeInfo& dataSizeAction) {
+                    auto& dataSizeResponse = stdx::get<StatusWith<DataSizeResponse>>(response);
+                    handleActionResult(
+                        opCtx,
+                        _nss,
+                        _uuid,
+                        getType(),
+                        dataSizeResponse.getStatus(),
+                        [&]() {
+                            ChunkType chunk(dataSizeAction.uuid,
+                                            dataSizeAction.chunkRange,
+                                            dataSizeAction.version.placementVersion(),
+                                            dataSizeAction.shardId);
+                            auto catalogManager = ShardingCatalogManager::get(opCtx);
+                            // Max out the chunk size if it has has been estimated as bigger
+                            // than _smallChunkSizeThresholdBytes; this will exlude the
+                            // chunk from the list of candidates considered by
+                            // MoveAndMergeChunksPhase
+                            auto estimatedSize = dataSizeResponse.getValue().maxSizeReached
+                                ? kBigChunkMarker
+                                : dataSizeResponse.getValue().sizeBytes;
+                            catalogManager->setChunkEstimatedSize(
                                 opCtx,
-                                _nss,
-                                _uuid,
-                                getType(),
-                                dataSizeResponse.getStatus(),
-                                [&]() {
-                                    ChunkType chunk(dataSizeAction.uuid,
-                                                    dataSizeAction.chunkRange,
-                                                    dataSizeAction.version.placementVersion(),
-                                                    dataSizeAction.shardId);
-                                    auto catalogManager = ShardingCatalogManager::get(opCtx);
-                                    // Max out the chunk size if it has has been estimated as bigger
-                                    // than _smallChunkSizeThresholdBytes; this will exlude the
-                                    // chunk from the list of candidates considered by
-                                    // MoveAndMergeChunksPhase
-                                    auto estimatedSize = dataSizeResponse.getValue().maxSizeReached
-                                        ? kBigChunkMarker
-                                        : dataSizeResponse.getValue().sizeBytes;
-                                    catalogManager->setChunkEstimatedSize(
-                                        opCtx,
-                                        chunk,
-                                        estimatedSize,
-                                        ShardingCatalogClient::kMajorityWriteConcern);
-                                },
-                                [&]() {
-                                    auto& shardingPendingActions =
-                                        _pendingActionsByShards[dataSizeAction.shardId];
-                                    shardingPendingActions.rangesWithoutDataSize.emplace_back(
-                                        dataSizeAction.chunkRange);
-                                },
-                                [&]() { _abort(getType()); });
+                                chunk,
+                                estimatedSize,
+                                ShardingCatalogClient::kMajorityWriteConcern);
                         },
-                        [](const MigrateInfo& _) {
-                            uasserted(ErrorCodes::BadValue, "Unexpected action type");
+                        [&]() {
+                            _rangesWithoutDataSize.push_back(
+                                {dataSizeAction.chunkRange, dataSizeAction.shardId});
                         },
-                        [](const MergeAllChunksOnShardInfo& _) {
-                            uasserted(ErrorCodes::BadValue, "Unexpected action type");
-                        }},
-                    action);
+                        [&]() { _abort(getType()); });
+                },
+                [](const MigrateInfo& _) {
+                    uasserted(ErrorCodes::BadValue, "Unexpected action type");
+                },
+                [&](const MergeAllChunksOnShardInfo& mergeAction) {
+                    try {
+                        const auto& swResponse = stdx::get<StatusWith<NumMergedChunks>>(response);
+                        // swResponse must be ok, otherwise it would not have been possible to parse
+                        // it as a StatusWith<NumMergedChunks>
+                        invariant(swResponse.isOK());
+                        auto numMergedChunks = swResponse.getValue();
+                        if (numMergedChunks == 0) {
+                            return;
+                        }
+                    } catch (std::exception&) {
+                        // Parsing of StatusWith<NumMergedChunks> failed, meaning we need to parse a
+                        // status
+                        const auto& status = stdx::get<Status>(response);
+                        if (status.code() != ErrorCodes::ConflictingOperationInProgress) {
+                            LOGV2_DEBUG(7474500,
+                                        1,
+                                        "Hit unexpected error while automerging chunks during "
+                                        "defragmentation",
+                                        "shard"_attr = mergeAction.shardId,
+                                        "nss"_attr = mergeAction.nss,
+                                        "error"_attr = redact(status));
+                        }
+                    }
+
+                    _shardsRequiringMergeForNss.push_back(mergeAction.shardId);
+                }},
+            action);
     }
 
     bool isComplete() const override {
-        return _pendingActionsByShards.empty() && _outstandingActions == 0;
+        return _currentMode == Done;
     }
 
     void userAbort() override {
@@ -349,49 +333,53 @@ public:
     }
 
     BSONObj reportProgress() const override {
-
-        size_t rangesToMerge = 0, rangesWithoutDataSize = 0;
-        for (const auto& [_, pendingActions] : _pendingActionsByShards) {
-            rangesToMerge += pendingActions.rangesToMerge.size();
-            rangesWithoutDataSize += pendingActions.rangesWithoutDataSize.size();
-        }
-        auto remainingChunksToProcess = static_cast<long long>(_outstandingActions) +
-            static_cast<long long>(rangesToMerge) + static_cast<long long>(rangesWithoutDataSize);
-
-        return BSON(kRemainingChunksToProcess << remainingChunksToProcess);
+        // TODO adapt
+
+        // size_t rangesToMerge = 0, rangesWithoutDataSize = 0;
+        // for (const auto& [_, pendingActions] : _pendingActionsByShards) {
+        //     rangesToMerge += pendingActions.rangesToMerge.size();
+        //     rangesWithoutDataSize += pendingActions.rangesWithoutDataSize.size();
+        // }
+        // auto remainingChunksToProcess = static_cast<long long>(_outstandingActions) +
+        //     static_cast<long long>(rangesToMerge) + static_cast<long
+        //     long>(rangesWithoutDataSize);
+
+        return BSON(kRemainingChunksToProcess << 0);
     }
 
 private:
-    struct PendingActions {
-        std::vector<ChunkRange> rangesToMerge;
-        std::vector<ChunkRange> rangesWithoutDataSize;
-    };
-    MergeAndMeasureChunksPhase(
-        const NamespaceString& nss,
-        const UUID& uuid,
-        const BSONObj& shardKey,
-        const int64_t smallChunkSizeThresholdBytes,
-        stdx::unordered_map<ShardId, PendingActions>&& pendingActionsByShards)
+    MergeAndMeasureChunksPhase(const NamespaceString& nss,
+                               const UUID& uuid,
+                               const BSONObj& shardKey,
+                               const int64_t smallChunkSizeThresholdBytes,
+                               std::vector<ShardId>&& shardsOwningChunksForNss)
         : _nss(nss),
           _uuid(uuid),
           _shardKey(shardKey),
           _smallChunkSizeThresholdBytes(smallChunkSizeThresholdBytes),
-          _pendingActionsByShards(std::move(pendingActionsByShards)) {}
+          _shardsRequiringMergeForNss(std::move(shardsOwningChunksForNss)) {}
 
     void _abort(const DefragmentationPhaseEnum nextPhase) {
         _aborted = true;
         _nextPhase = nextPhase;
-        _pendingActionsByShards.clear();
     }
 
     const NamespaceString _nss;
     const UUID _uuid;
     const BSONObj _shardKey;
     const int64_t _smallChunkSizeThresholdBytes;
-    stdx::unordered_map<ShardId, PendingActions> _pendingActionsByShards;
-    boost::optional<ShardId> _shardToProcess;
+
+    enum Mode { MergeChunks, CalculateDataSize, Done };
+
+    Mode _currentMode{MergeChunks};
+
+    std::vector<ShardId> _shardsRequiringMergeForNss;
+
+    std::vector<std::pair<ChunkRange, ShardId>> _rangesWithoutDataSize;
+
     size_t _outstandingActions{0};
     bool _aborted{false};
+
     DefragmentationPhaseEnum _nextPhase{DefragmentationPhaseEnum::kMoveAndMergeChunks};
 };
 
@@ -1433,8 +1421,11 @@ void BalancerDefragmentationPolicyImpl::applyActionResult(
                                       targetState = _defragmentationStates.at(act.uuid).get();
                                   }
                               },
-                              [](const MergeAllChunksOnShardInfo& _) {
-                                  uasserted(ErrorCodes::BadValue, "Unexpected action type");
+                              [&](const MergeAllChunksOnShardInfo& act) {
+                                  if (act.uuid.has_value() &&
+                                      _defragmentationStates.contains(*(act.uuid))) {
+                                      targetState = _defragmentationStates.at(*(act.uuid)).get();
+                                  }
                               }},
             action);
 
diff --git a/src/mongo/db/s/balancer/balancer_policy.h b/src/mongo/db/s/balancer/balancer_policy.h
index e4ca9e76903..cc4fe8c8793 100644
--- a/src/mongo/db/s/balancer/balancer_policy.h
+++ b/src/mongo/db/s/balancer/balancer_policy.h
@@ -151,6 +151,9 @@ struct MergeAllChunksOnShardInfo {
     ShardId shardId;
     NamespaceString nss;
 
+    // Collection uuid, optional because only needed for defragmentation
+    boost::optional<UUID> uuid;
+
     bool applyThrottling{false};
 };
 
