diff -Nuar a/vendor/magento/module-catalog-search/Model/Indexer/Fulltext.php b/vendor/magento/module-catalog-search/Model/Indexer/Fulltext.php
index af3625f6dcc..d02f33e51f3 100644
--- a/vendor/magento/module-catalog-search/Model/Indexer/Fulltext.php
+++ b/vendor/magento/module-catalog-search/Model/Indexer/Fulltext.php
@@ -12,6 +12,7 @@ use Magento\CatalogSearch\Model\Indexer\Scope\StateFactory;
 use Magento\CatalogSearch\Model\ResourceModel\Fulltext as FulltextResource;
 use Magento\Framework\App\ObjectManager;
 use Magento\Framework\Indexer\DimensionProviderInterface;
+use Magento\Framework\Indexer\SaveHandler\IndexerInterface;
 use Magento\Store\Model\StoreDimensionProvider;
 use Magento\Indexer\Model\ProcessManager;
 
@@ -34,6 +35,11 @@ class Fulltext implements
     const INDEXER_ID = 'catalogsearch_fulltext';
 
     /**
+     * Default batch size
+     */
+    private const BATCH_SIZE = 100;
+
+    /**
      * @var array index structure
      */
     protected $data;
@@ -74,6 +80,11 @@ class Fulltext implements
     private $processManager;
 
     /**
+     * @var int
+     */
+    private $batchSize;
+
+    /**
      * @param FullFactory $fullActionFactory
      * @param IndexerHandlerFactory $indexerHandlerFactory
      * @param FulltextResource $fulltextResource
@@ -82,6 +93,7 @@ class Fulltext implements
      * @param DimensionProviderInterface $dimensionProvider
      * @param array $data
      * @param ProcessManager $processManager
+     * @param int|null $batchSize
      * @SuppressWarnings(PHPMD.UnusedFormalParameter)
      */
     public function __construct(
@@ -92,7 +104,8 @@ class Fulltext implements
         StateFactory $indexScopeStateFactory,
         DimensionProviderInterface $dimensionProvider,
         array $data,
-        ProcessManager $processManager = null
+        ProcessManager $processManager = null,
+        ?int $batchSize = null
     ) {
         $this->fullAction = $fullActionFactory->create(['data' => $data]);
         $this->indexerHandlerFactory = $indexerHandlerFactory;
@@ -102,6 +115,7 @@ class Fulltext implements
         $this->indexScopeState = ObjectManager::getInstance()->get(State::class);
         $this->dimensionProvider = $dimensionProvider;
         $this->processManager = $processManager ?: ObjectManager::getInstance()->get(ProcessManager::class);
+        $this->batchSize = $batchSize ?? self::BATCH_SIZE;
     }
 
     /**
@@ -148,13 +162,42 @@ class Fulltext implements
         } else {
             // internal implementation works only with array
             $entityIds = iterator_to_array($entityIds);
-            $productIds = array_unique(
-                array_merge($entityIds, $this->fulltextResource->getRelationsByChild($entityIds))
-            );
-            if ($saveHandler->isAvailable($dimensions)) {
-                $saveHandler->deleteIndex($dimensions, new \ArrayIterator($productIds));
-                $saveHandler->saveIndex($dimensions, $this->fullAction->rebuildStoreIndex($storeId, $productIds));
+            $currentBatch = [];
+            $i = 0;
+
+            foreach ($entityIds as $entityId) {
+                $currentBatch[] = $entityId;
+                if (++$i === $this->batchSize) {
+                    $this->processBatch($saveHandler, $dimensions, $currentBatch);
+                    $i = 0;
+                    $currentBatch = [];
+                }
             }
+            if (!empty($currentBatch)) {
+                $this->processBatch($saveHandler, $dimensions, $currentBatch);
+            }
+        }
+    }
+
+    /**
+     * Process batch
+     *
+     * @param IndexerInterface $saveHandler
+     * @param array $dimensions
+     * @param array $entityIds
+     */
+    private function processBatch(
+        IndexerInterface $saveHandler,
+        array $dimensions,
+        array $entityIds
+    ) : void {
+        $storeId = $dimensions[StoreDimensionProvider::DIMENSION_NAME]->getValue();
+        $productIds = array_unique(
+            array_merge($entityIds, $this->fulltextResource->getRelationsByChild($entityIds))
+        );
+        if ($saveHandler->isAvailable($dimensions)) {
+            $saveHandler->deleteIndex($dimensions, new \ArrayIterator($productIds));
+            $saveHandler->saveIndex($dimensions, $this->fullAction->rebuildStoreIndex($storeId, $productIds));
         }
     }

diff -Nuar a/vendor/magento/module-catalog/Model/Indexer/Product/Price/Action/Rows.php b/vendor/magento/module-catalog/Model/Indexer/Product/Price/Action/Rows.php
index 27b50eea883..ce2f1ff75ad 100644
--- a/vendor/magento/module-catalog/Model/Indexer/Product/Price/Action/Rows.php
+++ b/vendor/magento/module-catalog/Model/Indexer/Product/Price/Action/Rows.php
@@ -5,12 +5,82 @@
  */
 namespace Magento\Catalog\Model\Indexer\Product\Price\Action;

+use Magento\Directory\Model\CurrencyFactory;
+use Magento\Catalog\Model\Indexer\Product\Price\DimensionCollectionFactory;
+use Magento\Catalog\Model\Indexer\Product\Price\TableMaintainer;
+use Magento\Catalog\Model\Product\Type;
+use Magento\Catalog\Model\ResourceModel\Product\Indexer\Price\DefaultPrice;
+use Magento\Catalog\Model\ResourceModel\Product\Indexer\Price\Factory;
+use Magento\Catalog\Model\ResourceModel\Product\Indexer\Price\TierPrice;
+use Magento\Framework\App\Config\ScopeConfigInterface;
+use Magento\Framework\App\ObjectManager;
+use Magento\Framework\Stdlib\DateTime;
+use Magento\Framework\Stdlib\DateTime\TimezoneInterface;
+use Magento\Store\Model\StoreManagerInterface;
+
 /**
  * Class Rows reindex action for mass actions
  *
  */
 class Rows extends \Magento\Catalog\Model\Indexer\Product\Price\AbstractAction
 {
+    /**
+     * Default batch size
+     */
+    private const BATCH_SIZE = 100;
+
+    /**
+     * @var int
+     */
+    private $batchSize;
+
+    /**
+     * @param ScopeConfigInterface $config
+     * @param StoreManagerInterface $storeManager
+     * @param CurrencyFactory $currencyFactory
+     * @param TimezoneInterface $localeDate
+     * @param DateTime $dateTime
+     * @param Type $catalogProductType
+     * @param Factory $indexerPriceFactory
+     * @param DefaultPrice $defaultIndexerResource
+     * @param TierPrice|null $tierPriceIndexResource
+     * @param DimensionCollectionFactory|null $dimensionCollectionFactory
+     * @param TableMaintainer|null $tableMaintainer
+     * @param int|null $batchSize
+     * @SuppressWarnings(PHPMD.NPathComplexity) Added to backward compatibility with abstract class
+     * @SuppressWarnings(PHPMD.CyclomaticComplexity) Added to backward compatibility with abstract class
+     * @SuppressWarnings(PHPMD.ExcessiveParameterList) Added to backward compatibility with abstract class
+     */
+    public function __construct(
+        ScopeConfigInterface $config,
+        StoreManagerInterface $storeManager,
+        CurrencyFactory $currencyFactory,
+        TimezoneInterface $localeDate,
+        DateTime $dateTime,
+        Type $catalogProductType,
+        Factory $indexerPriceFactory,
+        DefaultPrice $defaultIndexerResource,
+        TierPrice $tierPriceIndexResource = null,
+        DimensionCollectionFactory $dimensionCollectionFactory = null,
+        TableMaintainer $tableMaintainer = null,
+        ?int $batchSize = null
+    ) {
+        parent::__construct(
+            $config,
+            $storeManager,
+            $currencyFactory,
+            $localeDate,
+            $dateTime,
+            $catalogProductType,
+            $indexerPriceFactory,
+            $defaultIndexerResource,
+            $tierPriceIndexResource,
+            $dimensionCollectionFactory,
+            $tableMaintainer
+        );
+        $this->batchSize = $batchSize ?? self::BATCH_SIZE;
+    }
+
     /**
      * Execute Rows reindex
      *
@@ -24,10 +94,28 @@ class Rows extends \Magento\Catalog\Model\Indexer\Product\Price\AbstractAction
         if (empty($ids)) {
             throw new \Magento\Framework\Exception\InputException(__('Bad value was supplied.'));
         }
-        try {
-            $this->_reindexRows($ids);
-        } catch (\Exception $e) {
-            throw new \Magento\Framework\Exception\LocalizedException(__($e->getMessage()), $e);
+        $currentBatch = [];
+        $i = 0;
+
+        foreach ($ids as $id) {
+            $currentBatch[] = $id;
+            if (++$i === $this->batchSize) {
+                try {
+                    $this->_reindexRows($currentBatch);
+                } catch (\Exception $e) {
+                    throw new \Magento\Framework\Exception\LocalizedException(__($e->getMessage()), $e);
+                }
+                $i = 0;
+                $currentBatch = [];
+            }
+        }
+
+        if (!empty($currentBatch)) {
+            try {
+                $this->_reindexRows($currentBatch);
+            } catch (\Exception $e) {
+                throw new \Magento\Framework\Exception\LocalizedException(__($e->getMessage()), $e);
+            }
         }
     }
 }
diff -Nuar a/vendor/magento/module-indexer/Model/Indexer.php b/vendor/magento/module-indexer/Model/Indexer.php
index 2821a46f294..47a0b3205bc 100644
--- a/vendor/magento/module-indexer/Model/Indexer.php
+++ b/vendor/magento/module-indexer/Model/Indexer.php
@@ -322,7 +322,10 @@ class Indexer extends \Magento\Framework\DataObject implements IndexerInterface
      */
     public function isWorking()
     {
-        return $this->getState()->getStatus() == StateInterface::STATUS_WORKING;
+        //retrieve actual state, not cached one
+        $state = $this->stateFactory->create();
+        $state->loadByIndexer($this->getId());
+        return $state->getStatus() == StateInterface::STATUS_WORKING;
     }

     /**
diff -Nuar a/vendor/magento/module-indexer/Model/Processor.php b/vendor/magento/module-indexer/Model/Processor.php
index 29a9f3a1f41..b7b939fadc2 100644
--- a/vendor/magento/module-indexer/Model/Processor.php
+++ b/vendor/magento/module-indexer/Model/Processor.php
@@ -71,12 +71,14 @@ class Processor
                 if (!in_array($indexerConfig['shared_index'], $sharedIndexesComplete)) {
                     $indexer->reindexAll();
                 } else {
-                    /** @var \Magento\Indexer\Model\Indexer\State $state */
-                    $state = $indexer->getState();
-                    $state->setStatus(StateInterface::STATUS_WORKING);
-                    $state->save();
-                    $state->setStatus(StateInterface::STATUS_VALID);
-                    $state->save();
+                    if (!$indexer->isWorking()) {
+                        /** @var \Magento\Indexer\Model\Indexer\State $state */
+                        $state = $indexer->getState();
+                        $state->setStatus(StateInterface::STATUS_WORKING);
+                        $state->save();
+                        $state->setStatus(StateInterface::STATUS_VALID);
+                        $state->save();
+                    }
                 }
                 if ($indexerConfig['shared_index']) {
                     $sharedIndexesComplete[] = $indexerConfig['shared_index'];

