diff --git a/vendor/magento/module-catalog/Model/ResourceModel/Product/GetCategories.php b/vendor/magento/module-catalog/Model/ResourceModel/Product/GetCategories.php
new file mode 100644
index 0000000000000..24b71fcfb3475
--- /dev/null
+++ b/vendor/magento/module-catalog/Model/ResourceModel/Product/GetCategories.php
@@ -0,0 +1,62 @@
+<?php
+/************************************************************************
+ *
+ * Copyright 2024 Adobe
+ * All Rights Reserved.
+ *
+ * NOTICE: All information contained herein is, and remains
+ * the property of Adobe and its suppliers, if any. The intellectual
+ * and technical concepts contained herein are proprietary to Adobe
+ * and its suppliers and are protected by all applicable intellectual
+ * property laws, including trade secret and copyright laws.
+ * Dissemination of this information or reproduction of this material
+ * is strictly forbidden unless prior written permission is obtained
+ * from Adobe.
+ * ************************************************************************
+ */
+declare(strict_types=1);
+
+namespace Magento\Catalog\Model\ResourceModel\Product;
+
+use Magento\Catalog\Model\Indexer\Category\Product\TableMaintainer as CategoryProductTableMaintainer;
+use Magento\Framework\App\ResourceConnection;
+use Magento\Store\Model\StoreManagerInterface;
+
+class GetCategories
+{
+    /**
+     * @param ResourceConnection $resource
+     * @param CategoryProductTableMaintainer $categoryProductTableMaintainer
+     * @param StoreManagerInterface $storeManager
+     */
+    public function __construct(
+        private readonly ResourceConnection $resource,
+        private readonly CategoryProductTableMaintainer $categoryProductTableMaintainer,
+        private readonly StoreManagerInterface $storeManager
+    ) {
+    }
+
+    /**
+     * Returns list of categories ids for provided products
+     *
+     * @param int[] $productList
+     * @return int[]
+     */
+    public function execute(array $productList): array
+    {
+        $connection = $this->resource->getConnection();
+        $categories = [];
+        foreach ($this->storeManager->getStores() as $store) {
+            $select = $connection->select()->from(
+                ['category_product_index' => $this->categoryProductTableMaintainer->getMainTable((int)$store->getId())],
+                ['category_product_index.category_id']
+            );
+            $select->where('category_product_index.product_id IN (?)', $productList, \Zend_Db::INT_TYPE);
+            $select->distinct(true);
+
+            $categories += array_fill_keys($connection->fetchCol($select), true);
+        }
+
+        return array_keys($categories);
+    }
+}
diff --git a/vendor/magento/module-indexer/etc/di.xml b/vendor/magento/module-indexer/etc/di.xml
index 482ca591811b7..e609f9eace9bb 100644
--- a/vendor/magento/module-indexer/etc/di.xml
+++ b/vendor/magento/module-indexer/etc/di.xml
@@ -37,6 +37,9 @@
     <type name="Magento\Framework\Mview\View\Subscription">
         <arguments>
             <argument name="viewCollection" xsi:type="object" shared="false">Magento\Framework\Mview\View\CollectionInterface</argument>
+            <argument name="ignoredUpdateColumns" xsi:type="array">
+                <item name="updated_at" xsi:type="string">updated_at</item>
+            </argument>
         </arguments>
     </type>
     <type name="Magento\Indexer\Model\Processor">
diff --git a/app/etc/di.xml b/app/etc/di.xml
index 6056a54c66468..ea3da96fe0afd 100644
--- a/app/etc/di.xml
+++ b/app/etc/di.xml
@@ -212,6 +212,7 @@
     <preference for="Magento\Framework\HTTP\ClientInterface" type="Magento\Framework\HTTP\Client\Curl" />
     <preference for="Magento\Framework\Interception\ConfigLoaderInterface" type="Magento\Framework\Interception\PluginListGenerator" />
     <preference for="Magento\Framework\Interception\ConfigWriterInterface" type="Magento\Framework\Interception\PluginListGenerator" />
+    <preference for="Magento\Framework\Mview\View\SubscriptionStatementPostprocessorInterface" type="Magento\Framework\Mview\View\CompositeSubscriptionStatementPostprocessor" />
     <type name="Magento\Framework\Model\ResourceModel\Db\TransactionManager" shared="false" />
     <type name="Magento\Framework\Acl\Data\Cache">
         <arguments>
diff --git a/vendor/magento/framework/Mview/View/CompositeSubscriptionStatementPostprocessor.php b/vendor/magento/framework/Mview/View/CompositeSubscriptionStatementPostprocessor.php
new file mode 100644
index 0000000000000..ba6fdbdd2b220
--- /dev/null
+++ b/vendor/magento/framework/Mview/View/CompositeSubscriptionStatementPostprocessor.php
@@ -0,0 +1,36 @@
+<?php
+/**
+ * Copyright © Magento, Inc. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+declare(strict_types=1);
+
+namespace Magento\Framework\Mview\View;
+
+class CompositeSubscriptionStatementPostprocessor implements SubscriptionStatementPostprocessorInterface
+{
+    /**
+     * @var SubscriptionStatementPostprocessorInterface[]
+     */
+    private $postprocessors;
+
+    /**
+     * @param SubscriptionStatementPostprocessorInterface[] $postprocessors
+     */
+    public function __construct(array $postprocessors = [])
+    {
+        $this->postprocessors = $postprocessors;
+    }
+
+    /**
+     * @inheritdoc
+     */
+    public function process(string $tableName, string $event, string $statement): string
+    {
+        foreach ($this->postprocessors as $postprocessor) {
+            $statement = $postprocessor->process($tableName, $event, $statement);
+        }
+
+        return $statement;
+    }
+}
diff --git a/vendor/magento/framework/Mview/View/Subscription.php b/vendor/magento/framework/Mview/View/Subscription.php
index 933d075b35f75..4c96495aba767 100644
--- a/vendor/magento/framework/Mview/View/Subscription.php
+++ b/vendor/magento/framework/Mview/View/Subscription.php
@@ -87,6 +87,11 @@ class Subscription implements SubscriptionInterface, SubscriptionTriggersInterfa
      */
     private $mviewConfig;
 
+    /**
+     * @var SubscriptionStatementPostprocessorInterface
+     */
+    private $statementPostprocessor;
+
     /**
      * @var Trigger[]
      */
@@ -102,6 +107,8 @@ class Subscription implements SubscriptionInterface, SubscriptionTriggersInterfa
      * @param array $ignoredUpdateColumns
      * @param array $ignoredUpdateColumnsBySubscription
      * @param Config|null $mviewConfig
+     * @param SubscriptionStatementPostprocessorInterface|null $statementPostprocessor
+     * @SuppressWarnings(PHPMD.ExcessiveParameterList)
      */
     public function __construct(
         ResourceConnection $resource,
@@ -112,7 +119,8 @@ public function __construct(
         $columnName,
         $ignoredUpdateColumns = [],
         $ignoredUpdateColumnsBySubscription = [],
-        Config $mviewConfig = null
+        ?Config $mviewConfig = null,
+        ?SubscriptionStatementPostprocessorInterface $statementPostprocessor = null
     ) {
         $this->connection = $resource->getConnection();
         $this->triggerFactory = $triggerFactory;
@@ -124,6 +132,8 @@ public function __construct(
         $this->ignoredUpdateColumns = $ignoredUpdateColumns;
         $this->ignoredUpdateColumnsBySubscription = $ignoredUpdateColumnsBySubscription;
         $this->mviewConfig = $mviewConfig ?? ObjectManager::getInstance()->get(Config::class);
+        $this->statementPostprocessor = $statementPostprocessor
+            ?? ObjectManager::getInstance()->get(SubscriptionStatementPostprocessorInterface::class);
     }
 
     /**
@@ -324,13 +334,16 @@ protected function buildStatement(string $event, ViewInterface $view): string
         }
         $columns = $this->prepareColumns($view, $event);
 
-        return sprintf(
+        $statement = sprintf(
             $trigger,
             $this->getProcessor()->getPreStatements(),
             $this->connection->quoteIdentifier($this->resource->getTableName($changelog->getName())),
             implode(', ', $columns['column_names']),
             implode(', ', $columns['column_values'])
         );
+        $statement = $this->statementPostprocessor->process($this->getTableName(), $event, $statement);
+
+        return $statement;
     }
 
     /**
diff --git a/vendor/magento/framework/Mview/View/SubscriptionStatementPostprocessorInterface.php b/vendor/magento/framework/Mview/View/SubscriptionStatementPostprocessorInterface.php
new file mode 100644
index 0000000000000..288eb16f0222d
--- /dev/null
+++ b/vendor/magento/framework/Mview/View/SubscriptionStatementPostprocessorInterface.php
@@ -0,0 +1,21 @@
+<?php
+/**
+ * Copyright © Magento, Inc. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+declare(strict_types=1);
+
+namespace Magento\Framework\Mview\View;
+
+interface SubscriptionStatementPostprocessorInterface
+{
+    /**
+     * Postprocess subscription statement.
+     *
+     * @param string $tableName
+     * @param string $event
+     * @param string $statement
+     * @return string
+     */
+    public function process(string $tableName, string $event, string $statement): string;
+}
