diff --git a/.github/.metadata.json b/.github/.metadata.json index d0377fa..46baa33 100644 --- a/.github/.metadata.json +++ b/.github/.metadata.json @@ -1,14 +1,14 @@ { - "templateVersion": "0.1", + "templateVersion": "0.2", "product": { "name": "Magento Cloud Patches", "description": "The Magento Cloud Patches package is a set of patches, previously used within the ece-tools package to improve the integration of all Magento versions with Cloud environments and to deliver critical fixes quickly" }, "contacts": { "team": { - "name": "Magic Mountain", - "DL": "Grp-magento-cloud-all", - "slackChannel": "magic_mountain" + "name": "Mystic Mountain", + "DL": "Grp-Mystic-Mountain", + "slackChannel": "#mystic-mountain-team" } }, "ticketTracker": { @@ -17,10 +17,8 @@ }, "securityJiraQueue": { "projectKey": "MAGREQ", - "component": "Magento Cloud Engineering" + "component": "MAGREQ/Magento Cloud Engineering" } }, - "staticScan": { - "enable": false - } + "productionCodeBranches": ["1.0"] } diff --git a/composer.json b/composer.json index 9dcd12f..6f277b0 100644 --- a/composer.json +++ b/composer.json @@ -2,7 +2,7 @@ "name": "magento/magento-cloud-patches", "description": "Provides critical fixes for Magento 2 Enterprise Edition", "type": "magento2-component", - "version": "1.0.24", + "version": "1.0.25", "license": "OSL-3.0", "repositories": { "repo.magento.com": { diff --git a/patches.json b/patches.json index 76969c6..ea81ffb 100644 --- a/patches.json +++ b/patches.json @@ -274,6 +274,12 @@ }, "Reduced the number of times the same deployment configurations load": { ">=2.4.6 <2.4.6-p2": "MCLOUD-10604__performance_degradation_around_deployment_configuration__2.4.6.patch" + }, + "Fixes the issue where missed jobs unnecessarily wait for cron job locks.": { + ">=2.4.4 <2.4.7": "MCLOUD-11329__missed_jobs_unnecessarily_wait_for_cron_job_locks__2.4.6.patch" + }, + "Enhanced Layout Cache Efficiency (memory usage reduced)": { + ">=2.4.4 <2.4.7": "MCLOUD-11514__enhanced_layout_cache_efficiency__2.4.6-p3.patch" } }, "magento/module-paypal": { diff --git a/patches/MCLOUD-11329__missed_jobs_unnecessarily_wait_for_cron_job_locks__2.4.6.patch b/patches/MCLOUD-11329__missed_jobs_unnecessarily_wait_for_cron_job_locks__2.4.6.patch new file mode 100644 index 0000000..7e07ad0 --- /dev/null +++ b/patches/MCLOUD-11329__missed_jobs_unnecessarily_wait_for_cron_job_locks__2.4.6.patch @@ -0,0 +1,95 @@ +diff --git a/vendor/magento/module-cron/Observer/ProcessCronQueueObserver.php b/vendor/magento/module-cron/Observer/ProcessCronQueueObserver.php +index a4a11156956d..d58e60ba2cab 100644 +--- a/vendor/magento/module-cron/Observer/ProcessCronQueueObserver.php ++++ b/vendor/magento/module-cron/Observer/ProcessCronQueueObserver.php +@@ -320,17 +320,11 @@ private function lockGroup(string $groupId, callable $callback): void + * + * @return void + * @throws Exception|Throwable ++ * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + protected function _runJob($scheduledTime, $currentTime, $jobConfig, $schedule, $groupId) + { + $jobCode = $schedule->getJobCode(); +- $scheduleLifetime = $this->getCronGroupConfigurationValue($groupId, self::XML_PATH_SCHEDULE_LIFETIME); +- $scheduleLifetime = $scheduleLifetime * self::SECONDS_IN_MINUTE; +- if ($scheduledTime < $currentTime - $scheduleLifetime) { +- $schedule->setStatus(Schedule::STATUS_MISSED); +- // phpcs:ignore Magento2.Exceptions.DirectThrow +- throw new Exception(sprintf('Cron Job %s is missed at %s', $jobCode, $schedule->getScheduledAt())); +- } + + if (!isset($jobConfig['instance'], $jobConfig['method'])) { + $schedule->setStatus(Schedule::STATUS_ERROR); +@@ -832,7 +826,7 @@ private function processPendingJobs(string $groupId, array $jobsRoot, int $curre + } + + $scheduledTime = strtotime($schedule->getScheduledAt()); +- if ($scheduledTime > $currentTime) { ++ if (!$this->shouldRunJob($schedule, $groupId, $currentTime, (int) $scheduledTime)) { + continue; + } + +@@ -929,4 +923,62 @@ function () use ($scheduleResource, $where) { + $scheduleResource->getConnection() + ); + } ++ ++ /** ++ * Mark job as missed ++ * ++ * @param Schedule $schedule ++ * @return void ++ */ ++ private function markJobAsMissed(Schedule $schedule): void ++ { ++ $jobCode = $schedule->getJobCode(); ++ $scheduleId = $schedule->getId(); ++ $resource = $schedule->getResource(); ++ $connection = $resource->getConnection(); ++ $message = sprintf('Cron Job %s is missed at %s', $jobCode, $schedule->getScheduledAt()); ++ $result = $this->retrier->execute( ++ function () use ($resource, $connection, $scheduleId, $message) { ++ return $connection->update( ++ $resource->getTable('cron_schedule'), ++ ['status' => Schedule::STATUS_MISSED, 'messages' => $message], ++ ['schedule_id = ?' => $scheduleId, 'status = ?' => Schedule::STATUS_PENDING] ++ ); ++ }, ++ $connection ++ ); ++ if ($result == 1) { ++ $schedule->setStatus(Schedule::STATUS_MISSED); ++ $schedule->setMessages($message); ++ if ($this->state->getMode() === State::MODE_DEVELOPER) { ++ $this->logger->info($message); ++ } ++ } ++ } ++ ++ /** ++ * Check if job should be run ++ * ++ * @param Schedule $schedule ++ * @param string $groupId ++ * @param int $currentTime ++ * @param int $scheduledTime ++ * @return bool ++ */ ++ private function shouldRunJob(Schedule $schedule, string $groupId, int $currentTime, int $scheduledTime): bool ++ { ++ if ($scheduledTime > $currentTime) { ++ return false; ++ } ++ ++ $scheduleLifetime = $this->getCronGroupConfigurationValue($groupId, self::XML_PATH_SCHEDULE_LIFETIME); ++ $scheduleLifetime = $scheduleLifetime * self::SECONDS_IN_MINUTE; ++ ++ if ($scheduledTime < $currentTime - $scheduleLifetime) { ++ $this->markJobAsMissed($schedule); ++ return false; ++ } ++ ++ return true; ++ } + } diff --git a/patches/MCLOUD-11514__enhanced_layout_cache_efficiency__2.4.6-p3.patch b/patches/MCLOUD-11514__enhanced_layout_cache_efficiency__2.4.6-p3.patch new file mode 100644 index 0000000..9f99458 --- /dev/null +++ b/patches/MCLOUD-11514__enhanced_layout_cache_efficiency__2.4.6-p3.patch @@ -0,0 +1,92 @@ +diff --git a/vendor/magento/framework/App/Cache/Type/Layout.php b/vendor/magento/framework/App/Cache/Type/Layout.php +index 2ea069a..57b1cb4 100644 +--- a/vendor/magento/framework/App/Cache/Type/Layout.php ++++ b/vendor/magento/framework/App/Cache/Type/Layout.php +@@ -3,6 +3,8 @@ + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ ++declare(strict_types=1); ++ + namespace Magento\Framework\App\Cache\Type; + + /** +@@ -11,14 +13,29 @@ namespace Magento\Framework\App\Cache\Type; + class Layout extends \Magento\Framework\Cache\Frontend\Decorator\TagScope + { + /** ++ * Prefix for hash kay and hash data ++ */ ++ public const HASH_PREFIX = 'l:'; ++ ++ /** ++ * Hash type, not used for security, only for uniqueness ++ */ ++ public const HASH_TYPE = 'xxh3'; ++ ++ /** ++ * Data lifetime in milliseconds ++ */ ++ public const DATA_LIFETIME = 86_400_000; // "1 day" milliseconds ++ ++ /** + * Cache type code unique among all cache types + */ +- const TYPE_IDENTIFIER = 'layout'; ++ public const TYPE_IDENTIFIER = 'layout'; + + /** + * Cache tag used to distinguish the cache type from all other cache + */ +- const CACHE_TAG = 'LAYOUT_GENERAL_CACHE_TAG'; ++ public const CACHE_TAG = 'LAYOUT_GENERAL_CACHE_TAG'; + + /** + * @param FrontendPool $cacheFrontendPool +@@ -27,4 +44,33 @@ class Layout extends \Magento\Framework\Cache\Frontend\Decorator\TagScope + { + parent::__construct($cacheFrontendPool->get(self::TYPE_IDENTIFIER), self::CACHE_TAG); + } ++ ++ /** ++ * @inheritDoc ++ */ ++ public function save($data, $identifier, array $tags = [], $lifeTime = null) ++ { ++ $dataHash = hash(self::HASH_TYPE, $data); ++ $identifierForHash = self::HASH_PREFIX . $dataHash; ++ return parent::save($data, $identifierForHash, $tags, self::DATA_LIFETIME) // key is hash of data hash ++ && parent::save(self::HASH_PREFIX . $dataHash, $identifier, $tags, $lifeTime); // store hash of data ++ } ++ ++ /** ++ * @inheritDoc ++ */ ++ public function load($identifier) ++ { ++ $data = parent::load($identifier); ++ if ($data === false || $data === null) { ++ return $data; ++ } ++ ++ if (str_starts_with($data, self::HASH_PREFIX)) { ++ // so data stored in other place ++ return parent::load($data); ++ } else { ++ return $data; ++ } ++ } + } +diff --git a/vendor/magento/framework/Cache/Backend/Redis.php b/vendor/magento/framework/Cache/Backend/Redis.php +index 565777d..9527ebc 100644 +--- a/vendor/magento/framework/Cache/Backend/Redis.php ++++ b/vendor/magento/framework/Cache/Backend/Redis.php +@@ -70,7 +70,7 @@ class Redis extends \Cm_Cache_Backend_Redis + * @param bool $specificLifetime + * @return bool + */ +- public function save($data, $id, $tags = [], $specificLifetime = false) ++ public function save($data, $id, $tags = [], $specificLifetime = 86_400_000) + { + try { + $result = parent::save($data, $id, $tags, $specificLifetime);