diff --git a/patches.json b/patches.json
index 7ec4466..a8d786d 100644
--- a/patches.json
+++ b/patches.json
@@ -70,7 +70,9 @@
},
"Add Zookeeper and flock locks": {
"2.2.5 - 2.2.8": "MAGECLOUD-3054__add_zookeeper_and_flock_locks__2.2.5.patch",
- "2.3.0 - 2.3.1": "MAGECLOUD-3054__add_zookeeper_and_flock_locks__2.3.0.patch"
+ "2.3.0": "MAGECLOUD-3054__add_zookeeper_and_flock_locks__2.3.0.patch",
+ "2.3.1": "MAGECLOUD-3054__add_zookeeper_and_flock_locks__2.3.1.patch",
+ ">=2.3.2 <2.3.5": "MCLOUD_6139__improvement_flock_locks__2.3.2.patch"
},
"Reduce memory usage when importing customers and addresses": {
"2.1.4 - 2.1.10": "SET-36__fix_oom_during_customer_import__2.1.4.patch",
@@ -245,13 +247,17 @@
"Fix load balancer issue": {
">=2.3.4 <2.3.6": "MCLOUD-5837__fix_filesystem_load_balancer_issue__2.3.4.patch"
},
- "Cache Locking performance issue": {
- "2.3.3": "MDVA-27538__fix_performance_issue_in_cache_locking_mechanism__2.3.3.patch",
- ">=2.3.3-p1 <2.3.4": "MDVA-27538__fix_performance_issue_in_cache_locking_mechanism__2.3.3-p1.patch",
- ">=2.3.4 <2.3.5": "MDVA-26795__fix_performance_issue_in_cache_locking_mechanism__2.3.4.patch"
- },
"Large amount of Keys Block Cache": {
">=2.3.1 <2.3.5": "MDVA-22950__large_amount_of_keys_block_cache__2.3.1.patch"
+ },
+ "Redis improvements": {
+ "2.3.0": "MCLOUD-6139_MCLOUD-6211__redis_improvement_patches__2.3.0.patch",
+ "2.3.1": "MCLOUD-6139_MCLOUD-6211__redis_improvement_patches__2.3.1.patch",
+ ">=2.3.2 <2.3.3": "MCLOUD-6139_MCLOUD-6211__redis_improvement_patches__2.3.2.patch",
+ "2.3.3": "MCLOUD-6139_MCLOUD-6211__redis_improvement_patches__2.3.3.patch",
+ ">=2.3.3-p1 <2.3.4": "MCLOUD-6139_MCLOUD-6211__redis_improvement_patches__2.3.3-p1.patch",
+ ">=2.3.4 <2.3.5": "MCLOUD-6139_MCLOUD-6211__redis_improvement_patches__2.3.4.patch",
+ ">=2.3.5 <2.3.6": "MCLOUD-6211__redis_improvement_patches__2.3.5.patch"
}
},
"magento/module-paypal": {
diff --git a/patches/MAGECLOUD-3054__add_zookeeper_and_flock_locks__2.3.0.patch b/patches/MAGECLOUD-3054__add_zookeeper_and_flock_locks__2.3.0.patch
index f364ba3..3ee0c70 100644
--- a/patches/MAGECLOUD-3054__add_zookeeper_and_flock_locks__2.3.0.patch
+++ b/patches/MAGECLOUD-3054__add_zookeeper_and_flock_locks__2.3.0.patch
@@ -10,10 +10,11 @@ diff -Naur a/app/etc/di.xml b/app/etc/di.xml
-diff -Naur a/vendor/magento/framework/Lock/Backend/FileLock.php b/vendor/magento/framework/Lock/Backend/FileLock.php
+diff --git a/vendor/magento/framework/Lock/Backend/FileLock.php b/vendor/magento/framework/Lock/Backend/FileLock.php
+new file mode 100644
--- /dev/null
+++ b/vendor/magento/framework/Lock/Backend/FileLock.php
-@@ -0,0 +1,194 @@
+@@ -0,0 +1,196 @@
+tryToLock($fileResource)) {
+ if (!$skipDeadline && $deadline <= microtime(true)) {
++ $this->tryToUnlock($fileResource);
+ $this->fileDriver->fileClose($fileResource);
+ return false;
+ }
@@ -140,6 +142,7 @@ diff -Naur a/vendor/magento/framework/Lock/Backend/FileLock.php b/vendor/magento
+ } else {
+ $result = true;
+ }
++ $this->tryToUnlock($fileResource);
+ $this->fileDriver->fileClose($fileResource);
+ }
+ } catch (FileSystemException $exception) {
@@ -208,6 +211,7 @@ diff -Naur a/vendor/magento/framework/Lock/Backend/FileLock.php b/vendor/magento
+ }
+ }
+}
+
diff -Naur a/vendor/magento/framework/Lock/Backend/Zookeeper.php b/vendor/magento/framework/Lock/Backend/Zookeeper.php
--- /dev/null
+++ b/vendor/magento/framework/Lock/Backend/Zookeeper.php
@@ -1053,3 +1057,142 @@ diff -Naur a/setup/src/Magento/Setup/Model/ConfigOptionsList/Lock.php b/setup/sr
+ }
+ }
+}
+diff -Nuar a/vendor/magento/framework/Lock/Backend/Cache.php b/vendor/magento/framework/Lock/Backend/Cache.php
+new file mode 100644
+--- /dev/null
++++ b/vendor/magento/framework/Lock/Backend/Cache.php
+@@ -0,0 +1,134 @@
++cache = $cache;
++ $this->lockSign = $this->generateLockSign();
++ }
++
++ /**
++ * @inheritdoc
++ */
++ public function lock(string $name, int $timeout = -1): bool
++ {
++ if (empty($this->lockSign)) {
++ $this->lockSign = $this->generateLockSign();
++ }
++
++ $data = $this->cache->load($this->getIdentifier($name));
++
++ if (false !== $data) {
++ return false;
++ }
++
++ $timeout = $timeout <= 0 ? null : $timeout;
++ $this->cache->save($this->lockSign, $this->getIdentifier($name), [], $timeout);
++
++ $data = $this->cache->load($this->getIdentifier($name));
++
++ if ($data === $this->lockSign) {
++ return true;
++ }
++
++ return false;
++ }
++
++ /**
++ * @inheritdoc
++ */
++ public function unlock(string $name): bool
++ {
++ if (empty($this->lockSign)) {
++ return false;
++ }
++
++ $data = $this->cache->load($this->getIdentifier($name));
++
++ if (false === $data) {
++ return false;
++ }
++
++ $removeResult = false;
++ if ($data === $this->lockSign) {
++ $removeResult = (bool)$this->cache->remove($this->getIdentifier($name));
++ }
++
++ return $removeResult;
++ }
++
++ /**
++ * @inheritdoc
++ */
++ public function isLocked(string $name): bool
++ {
++ return (bool)$this->cache->test($this->getIdentifier($name));
++ }
++
++ /**
++ * Get cache locked identifier based on cache identifier.
++ *
++ * @param string $cacheIdentifier
++ * @return string
++ */
++ private function getIdentifier(string $cacheIdentifier): string
++ {
++ return self::LOCK_PREFIX . $cacheIdentifier;
++ }
++
++ /**
++ * Function that generates lock sign that helps to avoid removing a lock that was created by another client.
++ *
++ * @return string
++ */
++ private function generateLockSign()
++ {
++ $sign = implode(
++ '-',
++ [
++ \getmypid(), \crc32(\gethostname())
++ ]
++ );
++
++ try {
++ $sign .= '-' . \bin2hex(\random_bytes(4));
++ } catch (\Exception $e) {
++ $sign .= '-' . \uniqid('-uniqid-');
++ }
++
++ return $sign;
++ }
++}
diff --git a/patches/MAGECLOUD-3054__add_zookeeper_and_flock_locks__2.3.1.patch b/patches/MAGECLOUD-3054__add_zookeeper_and_flock_locks__2.3.1.patch
new file mode 100644
index 0000000..2ba83d8
--- /dev/null
+++ b/patches/MAGECLOUD-3054__add_zookeeper_and_flock_locks__2.3.1.patch
@@ -0,0 +1,1059 @@
+diff -Naur a/app/etc/di.xml b/app/etc/di.xml
+--- a/app/etc/di.xml
++++ b/app/etc/di.xml
+@@ -38,7 +38,7 @@
+
+
+
+-
++
+
+
+
+diff --git a/vendor/magento/framework/Lock/Backend/FileLock.php b/vendor/magento/framework/Lock/Backend/FileLock.php
+new file mode 100644
+--- /dev/null
++++ b/vendor/magento/framework/Lock/Backend/FileLock.php
+@@ -0,0 +1,196 @@
++fileDriver = $fileDriver;
++ $this->path = rtrim($path, '/') . '/';
++
++ try {
++ if (!$this->fileDriver->isExists($this->path)) {
++ $this->fileDriver->createDirectory($this->path);
++ }
++ } catch (FileSystemException $exception) {
++ throw new RuntimeException(
++ new Phrase('Cannot create the directory for locks: %1', [$this->path]),
++ $exception
++ );
++ }
++ }
++
++ /**
++ * Acquires a lock by name
++ *
++ * @param string $name The lock name
++ * @param int $timeout Timeout in seconds. A negative timeout value means infinite timeout
++ * @return bool Returns true if the lock is acquired, otherwise returns false
++ * @throws RuntimeException Throws RuntimeException if cannot acquires the lock because FS problems
++ */
++ public function lock(string $name, int $timeout = -1): bool
++ {
++ try {
++ $lockFile = $this->getLockPath($name);
++ $fileResource = $this->fileDriver->fileOpen($lockFile, 'w+');
++ $skipDeadline = $timeout < 0;
++ $deadline = microtime(true) + $timeout;
++
++ while (!$this->tryToLock($fileResource)) {
++ if (!$skipDeadline && $deadline <= microtime(true)) {
++ $this->tryToUnlock($fileResource);
++ $this->fileDriver->fileClose($fileResource);
++ return false;
++ }
++ usleep($this->sleepCycle);
++ }
++ } catch (FileSystemException $exception) {
++ throw new RuntimeException(new Phrase('Cannot acquire a lock.'), $exception);
++ }
++
++ $this->locks[$lockFile] = $fileResource;
++ return true;
++ }
++
++ /**
++ * Checks if a lock exists by name
++ *
++ * @param string $name The lock name
++ * @return bool Returns true if the lock exists, otherwise returns false
++ * @throws RuntimeException Throws RuntimeException if cannot check that the lock exists
++ */
++ public function isLocked(string $name): bool
++ {
++ $lockFile = $this->getLockPath($name);
++ $result = false;
++
++ try {
++ if ($this->fileDriver->isExists($lockFile)) {
++ $fileResource = $this->fileDriver->fileOpen($lockFile, 'w+');
++ if ($this->tryToLock($fileResource)) {
++ $result = false;
++ } else {
++ $result = true;
++ }
++ $this->tryToUnlock($fileResource);
++ $this->fileDriver->fileClose($fileResource);
++ }
++ } catch (FileSystemException $exception) {
++ throw new RuntimeException(new Phrase('Cannot verify that the lock exists.'), $exception);
++ }
++
++ return $result;
++ }
++
++ /**
++ * Remove the lock by name
++ *
++ * @param string $name The lock name
++ * @return bool If the lock is removed returns true, otherwise returns false
++ */
++ public function unlock(string $name): bool
++ {
++ $lockFile = $this->getLockPath($name);
++
++ if (isset($this->locks[$lockFile]) && $this->tryToUnlock($this->locks[$lockFile])) {
++ unset($this->locks[$lockFile]);
++ return true;
++ }
++
++ return false;
++ }
++
++ /**
++ * Returns the full path to the lock file by name
++ *
++ * @param string $name The lock name
++ * @return string The path to the lock file
++ */
++ private function getLockPath(string $name): string
++ {
++ return $this->path . $name;
++ }
++
++ /**
++ * Tries to lock a file resource
++ *
++ * @param resource $resource The file resource
++ * @return bool If the lock is acquired returns true, otherwise returns false
++ */
++ private function tryToLock($resource): bool
++ {
++ try {
++ return $this->fileDriver->fileLock($resource, LOCK_EX | LOCK_NB);
++ } catch (FileSystemException $exception) {
++ return false;
++ }
++ }
++
++ /**
++ * Tries to unlock a file resource
++ *
++ * @param resource $resource The file resource
++ * @return bool If the lock is removed returns true, otherwise returns false
++ */
++ private function tryToUnlock($resource): bool
++ {
++ try {
++ return $this->fileDriver->fileLock($resource, LOCK_UN | LOCK_NB);
++ } catch (FileSystemException $exception) {
++ return false;
++ }
++ }
++}
+
+diff -Naur a/vendor/magento/framework/Lock/Backend/Zookeeper.php b/vendor/magento/framework/Lock/Backend/Zookeeper.php
+--- /dev/null
++++ b/vendor/magento/framework/Lock/Backend/Zookeeper.php
+@@ -0,0 +1,280 @@
++\Zookeeper::PERM_ALL, 'scheme' => 'world', 'id' => 'anyone']];
++
++ /**
++ * The mapping list of the lock name with the full lock path
++ *
++ * @var array
++ */
++ private $locks = [];
++
++ /**
++ * The default path to storage locks
++ */
++ const DEFAULT_PATH = '/magento/locks';
++
++ /**
++ * @param string $host The host to connect to Zookeeper
++ * @param string $path The base path to locks in Zookeeper
++ * @throws RuntimeException
++ */
++ public function __construct(string $host, string $path = self::DEFAULT_PATH)
++ {
++ if (!$path) {
++ throw new RuntimeException(
++ new Phrase('The path needs to be a non-empty string.')
++ );
++ }
++
++ if (!$host) {
++ throw new RuntimeException(
++ new Phrase('The host needs to be a non-empty string.')
++ );
++ }
++
++ $this->host = $host;
++ $this->path = rtrim($path, '/') . '/';
++ }
++
++ /**
++ * @inheritdoc
++ *
++ * You can see the lock algorithm by the link
++ * @link https://zookeeper.apache.org/doc/r3.1.2/recipes.html#sc_recipes_Locks
++ *
++ * @throws RuntimeException
++ */
++ public function lock(string $name, int $timeout = -1): bool
++ {
++ $skipDeadline = $timeout < 0;
++ $lockPath = $this->getFullPathToLock($name);
++ $deadline = microtime(true) + $timeout;
++
++ if (!$this->checkAndCreateParentNode($lockPath)) {
++ throw new RuntimeException(new Phrase('Failed creating the path %1', [$lockPath]));
++ }
++
++ $lockKey = $this->getProvider()
++ ->create($lockPath, '1', $this->acl, \Zookeeper::EPHEMERAL | \Zookeeper::SEQUENCE);
++
++ if (!$lockKey) {
++ throw new RuntimeException(new Phrase('Failed creating lock %1', [$lockPath]));
++ }
++
++ while ($this->isAnyLock($lockKey, $this->getIndex($lockKey))) {
++ if (!$skipDeadline && $deadline <= microtime(true)) {
++ $this->getProvider()->delete($lockKey);
++ return false;
++ }
++
++ usleep($this->sleepCycle);
++ }
++
++ $this->locks[$name] = $lockKey;
++
++ return true;
++ }
++
++ /**
++ * @inheritdoc
++ *
++ * @throws RuntimeException
++ */
++ public function unlock(string $name): bool
++ {
++ if (!isset($this->locks[$name])) {
++ return false;
++ }
++
++ return $this->getProvider()->delete($this->locks[$name]);
++ }
++
++ /**
++ * @inheritdoc
++ *
++ * @throws RuntimeException
++ */
++ public function isLocked(string $name): bool
++ {
++ return $this->isAnyLock($this->getFullPathToLock($name));
++ }
++
++ /**
++ * Gets full path to lock by its name
++ *
++ * @param string $name
++ * @return string
++ */
++ private function getFullPathToLock(string $name): string
++ {
++ return $this->path . $name . '/' . $this->lockName;
++ }
++
++ /**
++ * Initiolizes and returns Zookeeper provider
++ *
++ * @return \Zookeeper
++ * @throws RuntimeException
++ */
++ private function getProvider(): \Zookeeper
++ {
++ if (!$this->zookeeper) {
++ $this->zookeeper = new \Zookeeper($this->host);
++ }
++
++ $deadline = microtime(true) + $this->connectionTimeout;
++ while ($this->zookeeper->getState() != \Zookeeper::CONNECTED_STATE) {
++ if ($deadline <= microtime(true)) {
++ throw new RuntimeException(new Phrase('Zookeeper connection timed out!'));
++ }
++ usleep($this->sleepCycle);
++ }
++
++ return $this->zookeeper;
++ }
++
++ /**
++ * Checks and creates base path recursively
++ *
++ * @param string $path
++ * @return bool
++ * @throws RuntimeException
++ */
++ private function checkAndCreateParentNode(string $path): bool
++ {
++ $path = dirname($path);
++ if ($this->getProvider()->exists($path)) {
++ return true;
++ }
++
++ if (!$this->checkAndCreateParentNode($path)) {
++ return false;
++ }
++
++ if ($this->getProvider()->create($path, '1', $this->acl)) {
++ return true;
++ }
++
++ return $this->getProvider()->exists($path);
++ }
++
++ /**
++ * Gets int increment of lock key
++ *
++ * @param string $key
++ * @return int|null
++ */
++ private function getIndex(string $key)
++ {
++ if (!preg_match('/' . $this->lockName . '([0-9]+)$/', $key, $matches)) {
++ return null;
++ }
++
++ return intval($matches[1]);
++ }
++
++ /**
++ * Checks if there is any sequence node under parent of $fullKey.
++ *
++ * At first checks that the $fullKey node is present, if not - returns false.
++ * If $indexKey is non-null and there is a smaller index than $indexKey then returns true,
++ * otherwise returns false.
++ *
++ * @param string $fullKey The full path without any sequence info
++ * @param int|null $indexKey The index to compare
++ * @return bool
++ * @throws RuntimeException
++ */
++ private function isAnyLock(string $fullKey, int $indexKey = null): bool
++ {
++ $parent = dirname($fullKey);
++
++ if (!$this->getProvider()->exists($parent)) {
++ return false;
++ }
++
++ $children = $this->getProvider()->getChildren($parent);
++
++ if (null === $indexKey && !empty($children)) {
++ return true;
++ }
++
++ foreach ($children as $childKey) {
++ $childIndex = $this->getIndex($childKey);
++
++ if (null === $childIndex) {
++ continue;
++ }
++
++ if ($childIndex < $indexKey) {
++ return true;
++ }
++ }
++
++ return false;
++ }
++}
+diff -Naur a/vendor/magento/framework/Lock/LockBackendFactory.php b/vendor/magento/framework/Lock/LockBackendFactory.php
+--- /dev/null
++++ b/vendor/magento/framework/Lock/LockBackendFactory.php
+@@ -0,0 +1,111 @@
++ DatabaseLock::class,
++ self::LOCK_ZOOKEEPER => ZookeeperLock::class,
++ self::LOCK_CACHE => CacheLock::class,
++ self::LOCK_FILE => FileLock::class,
++ ];
++
++ /**
++ * @param ObjectManagerInterface $objectManager The Object Manager instance
++ * @param DeploymentConfig $deploymentConfig The Application deployment configuration
++ */
++ public function __construct(
++ ObjectManagerInterface $objectManager,
++ DeploymentConfig $deploymentConfig
++ ) {
++ $this->objectManager = $objectManager;
++ $this->deploymentConfig = $deploymentConfig;
++ }
++
++ /**
++ * Creates an instance of LockManagerInterface using information from deployment config
++ *
++ * @return LockManagerInterface
++ * @throws RuntimeException
++ */
++ public function create(): LockManagerInterface
++ {
++ $provider = $this->deploymentConfig->get('lock/provider', self::LOCK_DB);
++ $config = $this->deploymentConfig->get('lock/config', []);
++
++ if (!isset($this->lockers[$provider])) {
++ throw new RuntimeException(new Phrase('Unknown locks provider: %1', [$provider]));
++ }
++
++ if (self::LOCK_ZOOKEEPER === $provider && !extension_loaded(self::LOCK_ZOOKEEPER)) {
++ throw new RuntimeException(new Phrase('php extension Zookeeper is not installed.'));
++ }
++
++ return $this->objectManager->create($this->lockers[$provider], $config);
++ }
++}
+diff -Naur a/vendor/magento/framework/Lock/Proxy.php b/vendor/magento/framework/Lock/Proxy.php
+--- /dev/null
++++ b/vendor/magento/framework/Lock/Proxy.php
+@@ -0,0 +1,83 @@
++factory = $factory;
++ }
++
++ /**
++ * @inheritdoc
++ *
++ * @throws RuntimeException
++ */
++ public function isLocked(string $name): bool
++ {
++ return $this->getLocker()->isLocked($name);
++ }
++
++ /**
++ * @inheritdoc
++ *
++ * @throws RuntimeException
++ */
++ public function lock(string $name, int $timeout = -1): bool
++ {
++ return $this->getLocker()->lock($name, $timeout);
++ }
++
++ /**
++ * @inheritdoc
++ *
++ * @throws RuntimeException
++ */
++ public function unlock(string $name): bool
++ {
++ return $this->getLocker()->unlock($name);
++ }
++
++ /**
++ * Gets LockManagerInterface implementation using Factory
++ *
++ * @return LockManagerInterface
++ * @throws RuntimeException
++ */
++ private function getLocker(): LockManagerInterface
++ {
++ if (!$this->locker) {
++ $this->locker = $this->factory->create();
++ }
++
++ return $this->locker;
++ }
++}
+diff -Naur a/setup/src/Magento/Setup/Model/ConfigOptionsList.php b/setup/src/Magento/Setup/Model/ConfigOptionsList.php
+--- a/setup/src/Magento/Setup/Model/ConfigOptionsList.php
++++ b/setup/src/Magento/Setup/Model/ConfigOptionsList.php
+@@ -50,7 +50,8 @@ class ConfigOptionsList implements ConfigOptionsListInterface
+ private $configOptionsListClasses = [
+ \Magento\Setup\Model\ConfigOptionsList\Session::class,
+ \Magento\Setup\Model\ConfigOptionsList\Cache::class,
+- \Magento\Setup\Model\ConfigOptionsList\PageCache::class
++ \Magento\Setup\Model\ConfigOptionsList\PageCache::class,
++ \Magento\Setup\Model\ConfigOptionsList\Lock::class,
+ ];
+
+ /**
+diff -Naur a/setup/src/Magento/Setup/Model/ConfigOptionsList/Lock.php b/setup/src/Magento/Setup/Model/ConfigOptionsList/Lock.php
+--- /dev/null
++++ b/setup/src/Magento/Setup/Model/ConfigOptionsList/Lock.php
+@@ -0,0 +1,342 @@
++ [
++ self::INPUT_KEY_LOCK_PROVIDER => self::CONFIG_PATH_LOCK_PROVIDER,
++ self::INPUT_KEY_LOCK_DB_PREFIX => self::CONFIG_PATH_LOCK_DB_PREFIX,
++ ],
++ LockBackendFactory::LOCK_ZOOKEEPER => [
++ self::INPUT_KEY_LOCK_PROVIDER => self::CONFIG_PATH_LOCK_PROVIDER,
++ self::INPUT_KEY_LOCK_ZOOKEEPER_HOST => self::CONFIG_PATH_LOCK_ZOOKEEPER_HOST,
++ self::INPUT_KEY_LOCK_ZOOKEEPER_PATH => self::CONFIG_PATH_LOCK_ZOOKEEPER_PATH,
++ ],
++ LockBackendFactory::LOCK_CACHE => [
++ self::INPUT_KEY_LOCK_PROVIDER => self::CONFIG_PATH_LOCK_PROVIDER,
++ ],
++ LockBackendFactory::LOCK_FILE => [
++ self::INPUT_KEY_LOCK_PROVIDER => self::CONFIG_PATH_LOCK_PROVIDER,
++ self::INPUT_KEY_LOCK_FILE_PATH => self::CONFIG_PATH_LOCK_FILE_PATH,
++ ],
++ ];
++
++ /**
++ * The list of default values
++ *
++ * @var array
++ */
++ private $defaultConfigValues = [
++ self::INPUT_KEY_LOCK_PROVIDER => LockBackendFactory::LOCK_DB,
++ self::INPUT_KEY_LOCK_DB_PREFIX => null,
++ self::INPUT_KEY_LOCK_ZOOKEEPER_PATH => ZookeeperLock::DEFAULT_PATH,
++ ];
++
++ /**
++ * @inheritdoc
++ */
++ public function getOptions()
++ {
++ return [
++ new SelectConfigOption(
++ self::INPUT_KEY_LOCK_PROVIDER,
++ SelectConfigOption::FRONTEND_WIZARD_SELECT,
++ $this->validLockProviders,
++ self::CONFIG_PATH_LOCK_PROVIDER,
++ 'Lock provider name',
++ LockBackendFactory::LOCK_DB
++ ),
++ new TextConfigOption(
++ self::INPUT_KEY_LOCK_DB_PREFIX,
++ TextConfigOption::FRONTEND_WIZARD_TEXT,
++ self::CONFIG_PATH_LOCK_DB_PREFIX,
++ 'Installation specific lock prefix to avoid lock conflicts'
++ ),
++ new TextConfigOption(
++ self::INPUT_KEY_LOCK_ZOOKEEPER_HOST,
++ TextConfigOption::FRONTEND_WIZARD_TEXT,
++ self::CONFIG_PATH_LOCK_ZOOKEEPER_HOST,
++ 'Host and port to connect to Zookeeper cluster. For example: 127.0.0.1:2181'
++ ),
++ new TextConfigOption(
++ self::INPUT_KEY_LOCK_ZOOKEEPER_PATH,
++ TextConfigOption::FRONTEND_WIZARD_TEXT,
++ self::CONFIG_PATH_LOCK_ZOOKEEPER_PATH,
++ 'The path where Zookeeper will save locks. The default path is: ' . ZookeeperLock::DEFAULT_PATH
++ ),
++ new TextConfigOption(
++ self::INPUT_KEY_LOCK_FILE_PATH,
++ TextConfigOption::FRONTEND_WIZARD_TEXT,
++ self::CONFIG_PATH_LOCK_FILE_PATH,
++ 'The path where file locks will be saved.'
++ ),
++ ];
++ }
++
++ /**
++ * @inheritdoc
++ */
++ public function createConfig(array $options, DeploymentConfig $deploymentConfig)
++ {
++ $configData = new ConfigData(ConfigFilePool::APP_ENV);
++ $configData->setOverrideWhenSave(true);
++ $lockProvider = $this->getLockProvider($options, $deploymentConfig);
++
++ $this->setDefaultConfiguration($configData, $deploymentConfig, $lockProvider);
++
++ foreach ($this->mappingInputKeyToConfigPath[$lockProvider] as $input => $path) {
++ if (isset($options[$input])) {
++ $configData->set($path, $options[$input]);
++ }
++ }
++
++ return $configData;
++ }
++
++ /**
++ * @inheritdoc
++ */
++ public function validate(array $options, DeploymentConfig $deploymentConfig)
++ {
++ $lockProvider = $this->getLockProvider($options, $deploymentConfig);
++ switch ($lockProvider) {
++ case LockBackendFactory::LOCK_ZOOKEEPER:
++ $errors = $this->validateZookeeperConfig($options, $deploymentConfig);
++ break;
++ case LockBackendFactory::LOCK_FILE:
++ $errors = $this->validateFileConfig($options, $deploymentConfig);
++ break;
++ case LockBackendFactory::LOCK_CACHE:
++ case LockBackendFactory::LOCK_DB:
++ $errors = [];
++ break;
++ default:
++ $errors[] = 'The lock provider ' . $lockProvider . ' does not exist.';
++ }
++
++ return $errors;
++ }
++
++ /**
++ * Validates File locks configuration
++ *
++ * @param array $options
++ * @param DeploymentConfig $deploymentConfig
++ * @return array
++ */
++ private function validateFileConfig(array $options, DeploymentConfig $deploymentConfig): array
++ {
++ $errors = [];
++
++ $path = $options[self::INPUT_KEY_LOCK_FILE_PATH]
++ ?? $deploymentConfig->get(
++ self::CONFIG_PATH_LOCK_FILE_PATH,
++ $this->getDefaultValue(self::INPUT_KEY_LOCK_FILE_PATH)
++ );
++
++ if (!$path) {
++ $errors[] = 'The path needs to be a non-empty string.';
++ }
++
++ return $errors;
++ }
++
++ /**
++ * Validates Zookeeper configuration
++ *
++ * @param array $options
++ * @param DeploymentConfig $deploymentConfig
++ * @return array
++ */
++ private function validateZookeeperConfig(array $options, DeploymentConfig $deploymentConfig): array
++ {
++ $errors = [];
++
++ if (!extension_loaded(LockBackendFactory::LOCK_ZOOKEEPER)) {
++ $errors[] = 'php extension Zookeeper is not installed.';
++ }
++
++ $host = $options[self::INPUT_KEY_LOCK_ZOOKEEPER_HOST]
++ ?? $deploymentConfig->get(
++ self::CONFIG_PATH_LOCK_ZOOKEEPER_HOST,
++ $this->getDefaultValue(self::INPUT_KEY_LOCK_ZOOKEEPER_HOST)
++ );
++ $path = $options[self::INPUT_KEY_LOCK_ZOOKEEPER_PATH]
++ ?? $deploymentConfig->get(
++ self::CONFIG_PATH_LOCK_ZOOKEEPER_PATH,
++ $this->getDefaultValue(self::INPUT_KEY_LOCK_ZOOKEEPER_PATH)
++ );
++
++ if (!$path) {
++ $errors[] = 'Zookeeper path needs to be a non-empty string.';
++ }
++
++ if (!$host) {
++ $errors[] = 'Zookeeper host is should be set.';
++ }
++
++ return $errors;
++ }
++
++ /**
++ * Returns the name of lock provider
++ *
++ * @param array $options
++ * @param DeploymentConfig $deploymentConfig
++ * @return string
++ */
++ private function getLockProvider(array $options, DeploymentConfig $deploymentConfig): string
++ {
++ if (!isset($options[self::INPUT_KEY_LOCK_PROVIDER])) {
++ return (string) $deploymentConfig->get(
++ self::CONFIG_PATH_LOCK_PROVIDER,
++ $this->getDefaultValue(self::INPUT_KEY_LOCK_PROVIDER)
++ );
++ }
++
++ return (string) $options[self::INPUT_KEY_LOCK_PROVIDER];
++ }
++
++ /**
++ * Sets default configuration for locks
++ *
++ * @param ConfigData $configData
++ * @param DeploymentConfig $deploymentConfig
++ * @param string $lockProvider
++ * @return ConfigData
++ */
++ private function setDefaultConfiguration(
++ ConfigData $configData,
++ DeploymentConfig $deploymentConfig,
++ string $lockProvider
++ ) {
++ foreach ($this->mappingInputKeyToConfigPath[$lockProvider] as $input => $path) {
++ $configData->set($path, $deploymentConfig->get($path, $this->getDefaultValue($input)));
++ }
++
++ return $configData;
++ }
++
++ /**
++ * Returns default value by input key
++ *
++ * If default value is not set returns null
++ *
++ * @param string $inputKey
++ * @return mixed|null
++ */
++ private function getDefaultValue(string $inputKey)
++ {
++ if (isset($this->defaultConfigValues[$inputKey])) {
++ return $this->defaultConfigValues[$inputKey];
++ } else {
++ return null;
++ }
++ }
++}
diff --git a/patches/MCLOUD-6139_MCLOUD-6211__redis_improvement_patches__2.3.0.patch b/patches/MCLOUD-6139_MCLOUD-6211__redis_improvement_patches__2.3.0.patch
new file mode 100644
index 0000000..b742ebf
--- /dev/null
+++ b/patches/MCLOUD-6139_MCLOUD-6211__redis_improvement_patches__2.3.0.patch
@@ -0,0 +1,2441 @@
+diff -Nuar a/vendor/magento/module-config/App/Config/Type/System.php b/vendor/magento/module-config/App/Config/Type/System.php
+--- a/vendor/magento/module-config/App/Config/Type/System.php
++++ b/vendor/magento/module-config/App/Config/Type/System.php
+@@ -3,6 +3,7 @@
+ * Copyright © Magento, Inc. All rights reserved.
+ * See COPYING.txt for license details.
+ */
++
+ namespace Magento\Config\App\Config\Type;
+
+ use Magento\Framework\App\Config\ConfigSourceInterface;
+@@ -13,10 +14,14 @@ use Magento\Framework\App\ObjectManager;
+ use Magento\Config\App\Config\Type\System\Reader;
+ use Magento\Framework\App\ScopeInterface;
+ use Magento\Framework\Cache\FrontendInterface;
++use Magento\Framework\Cache\LockGuardedCacheLoader;
++use Magento\Framework\Lock\LockManagerInterface;
+ use Magento\Framework\Serialize\SerializerInterface;
+ use Magento\Store\Model\Config\Processor\Fallback;
+-use Magento\Store\Model\ScopeInterface as StoreScope;
+ use Magento\Framework\Encryption\Encryptor;
++use Magento\Store\Model\ScopeInterface as StoreScope;
++use Magento\Framework\App\Cache\StateInterface;
++use Magento\Framework\App\Cache\Type\Config;
+
+ /**
+ * System configuration type
+@@ -24,12 +29,25 @@ use Magento\Framework\Encryption\Encryptor;
+ * @api
+ * @since 100.1.2
+ * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
++ * @SuppressWarnings(PHPMD.UnusedPrivateMethod)
+ */
+ class System implements ConfigTypeInterface
+ {
++ /**
++ * Config cache tag.
++ */
+ const CACHE_TAG = 'config_scopes';
++
++ /**
++ * System config type.
++ */
+ const CONFIG_TYPE = 'system';
+
++ /**
++ * @var string
++ */
++ private static $lockName = 'SYSTEM_CONFIG';
++
+ /**
+ * @var array
+ */
+@@ -77,6 +95,17 @@ class System implements ConfigTypeInterface
+ private $encryptor;
+
+ /**
++ * @var LockGuardedCacheLoader
++ */
++ private $lockQuery;
++
++ /**
++ * @var StateInterface
++ */
++ private $cacheState;
++
++ /**
++ * System constructor.
+ * @param ConfigSourceInterface $source
+ * @param PostProcessorInterface $postProcessor
+ * @param Fallback $fallback
+@@ -87,7 +116,9 @@ class System implements ConfigTypeInterface
+ * @param string $configType
+ * @param Reader|null $reader
+ * @param Encryptor|null $encryptor
+- *
++ * @param LockManagerInterface|null $locker
++ * @param LockGuardedCacheLoader|null $lockQuery
++ * @param StateInterface|null $cacheState
+ * @SuppressWarnings(PHPMD.UnusedFormalParameter)
+ * @SuppressWarnings(PHPMD.ExcessiveParameterList)
+ */
+@@ -101,14 +132,22 @@ class System implements ConfigTypeInterface
+ $cachingNestedLevel = 1,
+ $configType = self::CONFIG_TYPE,
+ Reader $reader = null,
+- Encryptor $encryptor = null
++ Encryptor $encryptor = null,
++ LockManagerInterface $locker = null,
++ LockGuardedCacheLoader $lockQuery = null,
++ StateInterface $cacheState = null
+ ) {
+ $this->postProcessor = $postProcessor;
+ $this->cache = $cache;
+ $this->serializer = $serializer;
+ $this->configType = $configType;
+ $this->reader = $reader ?: ObjectManager::getInstance()->get(Reader::class);
+- $this->encryptor = $encryptor ?: ObjectManager::getInstance()->get(\Magento\Framework\Encryption\Encryptor::class);
++ $this->encryptor = $encryptor
++ ?: ObjectManager::getInstance()->get(Encryptor::class);
++ $this->lockQuery = $lockQuery
++ ?: ObjectManager::getInstance()->get(LockGuardedCacheLoader::class);
++ $this->cacheState = $cacheState
++ ?: ObjectManager::getInstance()->get(StateInterface::class);
+ }
+
+ /**
+@@ -187,45 +226,64 @@ class System implements ConfigTypeInterface
+ }
+
+ /**
+- * Load configuration data for all scopes
++ * Load configuration data for all scopes.
+ *
+ * @return array
+ */
+ private function loadAllData()
+ {
+- $cachedData = $this->cache->load($this->configType);
+-
+- if ($cachedData === false) {
+- $data = $this->readData();
+- } else {
+- $data = $this->serializer->unserialize($this->encryptor->decrypt($cachedData));
++ if (!$this->cacheState->isEnabled(Config::TYPE_IDENTIFIER)) {
++ return $this->readData();
+ }
+
+- return $data;
++ $loadAction = function () {
++ $cachedData = $this->cache->load($this->configType);
++ $data = false;
++ if ($cachedData !== false) {
++ $data = $this->serializer->unserialize($this->encryptor->decrypt($cachedData));
++ }
++ return $data;
++ };
++
++ return $this->lockQuery->lockedLoadData(
++ self::$lockName,
++ $loadAction,
++ \Closure::fromCallable([$this, 'readData']),
++ \Closure::fromCallable([$this, 'cacheData'])
++ );
+ }
+
+ /**
+- * Load configuration data for default scope
++ * Load configuration data for default scope.
+ *
+ * @param string $scopeType
+ * @return array
+ */
+ private function loadDefaultScopeData($scopeType)
+ {
+- $cachedData = $this->cache->load($this->configType . '_' . $scopeType);
+-
+- if ($cachedData === false) {
+- $data = $this->readData();
+- $this->cacheData($data);
+- } else {
+- $data = [$scopeType => $this->serializer->unserialize($this->encryptor->decrypt($cachedData))];
++ if (!$this->cacheState->isEnabled(Config::TYPE_IDENTIFIER)) {
++ return $this->readData();
+ }
+
+- return $data;
++ $loadAction = function () use ($scopeType) {
++ $cachedData = $this->cache->load($this->configType . '_' . $scopeType);
++ $scopeData = false;
++ if ($cachedData !== false) {
++ $scopeData = [$scopeType => $this->serializer->unserialize($this->encryptor->decrypt($cachedData))];
++ }
++ return $scopeData;
++ };
++
++ return $this->lockQuery->lockedLoadData(
++ self::$lockName,
++ $loadAction,
++ \Closure::fromCallable([$this, 'readData']),
++ \Closure::fromCallable([$this, 'cacheData'])
++ );
+ }
+
+ /**
+- * Load configuration data for a specified scope
++ * Load configuration data for a specified scope.
+ *
+ * @param string $scopeType
+ * @param string $scopeId
+@@ -233,31 +291,42 @@ class System implements ConfigTypeInterface
+ */
+ private function loadScopeData($scopeType, $scopeId)
+ {
+- $cachedData = $this->cache->load($this->configType . '_' . $scopeType . '_' . $scopeId);
+-
+- if ($cachedData === false) {
+- if ($this->availableDataScopes === null) {
+- $cachedScopeData = $this->cache->load($this->configType . '_scopes');
+- if ($cachedScopeData !== false) {
+- $serializedCachedData = $this->encryptor->decrypt($cachedScopeData);
+- $this->availableDataScopes = $this->serializer->unserialize($serializedCachedData);
++ if (!$this->cacheState->isEnabled(Config::TYPE_IDENTIFIER)) {
++ return $this->readData();
++ }
++
++ $loadAction = function () use ($scopeType, $scopeId) {
++ $cachedData = $this->cache->load($this->configType . '_' . $scopeType . '_' . $scopeId);
++ $scopeData = false;
++ if ($cachedData === false) {
++ if ($this->availableDataScopes === null) {
++ $cachedScopeData = $this->cache->load($this->configType . '_scopes');
++ if ($cachedScopeData !== false) {
++ $serializedCachedData = $this->encryptor->decrypt($cachedScopeData);
++ $this->availableDataScopes = $this->serializer->unserialize($serializedCachedData);
++ }
+ }
++ if (is_array($this->availableDataScopes) && !isset($this->availableDataScopes[$scopeType][$scopeId])) {
++ $scopeData = [$scopeType => [$scopeId => []]];
++ }
++ } else {
++ $serializedCachedData = $this->encryptor->decrypt($cachedData);
++ $scopeData = [$scopeType => [$scopeId => $this->serializer->unserialize($serializedCachedData)]];
+ }
+- if (is_array($this->availableDataScopes) && !isset($this->availableDataScopes[$scopeType][$scopeId])) {
+- return [$scopeType => [$scopeId => []]];
+- }
+- $data = $this->readData();
+- $this->cacheData($data);
+- } else {
+- $serializedCachedData = $this->encryptor->decrypt($cachedData);
+- $data = [$scopeType => [$scopeId => $this->serializer->unserialize($serializedCachedData)]];
+- }
+
+- return $data;
++ return $scopeData;
++ };
++
++ return $this->lockQuery->lockedLoadData(
++ self::$lockName,
++ $loadAction,
++ \Closure::fromCallable([$this, 'readData']),
++ \Closure::fromCallable([$this, 'cacheData'])
++ );
+ }
+
+ /**
+- * Cache configuration data
++ * Cache configuration data.
+ *
+ * Caches data per scope to avoid reading data for all scopes on every request
+ *
+@@ -295,7 +364,7 @@ class System implements ConfigTypeInterface
+ }
+
+ /**
+- * Walk nested hash map by keys from $pathParts
++ * Walk nested hash map by keys from $pathParts.
+ *
+ * @param array $data to walk in
+ * @param array $pathParts keys path
+@@ -332,7 +401,7 @@ class System implements ConfigTypeInterface
+ }
+
+ /**
+- * Clean cache and global variables cache
++ * Clean cache and global variables cache.
+ *
+ * Next items cleared:
+ * - Internal property intended to store already loaded configuration data
+@@ -344,6 +413,17 @@ class System implements ConfigTypeInterface
+ public function clean()
+ {
+ $this->data = [];
+- $this->cache->clean(\Zend_Cache::CLEANING_MODE_MATCHING_TAG, [self::CACHE_TAG]);
++ $cleanAction = function () {
++ $this->cache->clean(\Zend_Cache::CLEANING_MODE_MATCHING_TAG, [self::CACHE_TAG]);
++ };
++
++ if (!$this->cacheState->isEnabled(Config::TYPE_IDENTIFIER)) {
++ return $cleanAction();
++ }
++
++ $this->lockQuery->lockedCleanData(
++ self::$lockName,
++ $cleanAction
++ );
+ }
+ }
+diff -Nuar a/vendor/magento/module-config/etc/di.xml b/vendor/magento/module-config/etc/di.xml
+--- a/vendor/magento/module-config/etc/di.xml
++++ b/vendor/magento/module-config/etc/di.xml
+@@ -77,6 +77,11 @@
+
+
+
++
++
++ Magento\Framework\App\Cache\Type\Config
++
++
+
+
+ systemConfigSourceAggregatedProxy
+@@ -85,8 +90,16 @@
+ Magento\Framework\App\Config\PreProcessorComposite
+ Magento\Framework\Serialize\Serializer\Serialize
+ Magento\Config\App\Config\Type\System\Reader\Proxy
++ systemConfigQueryLocker
+
+
++
++
++
++ Magento\Framework\Lock\Backend\Cache
++
++
++
+
+
+ systemConfigSourceAggregated
+diff -Nuar a/vendor/magento/framework/Interception/Config/Config.php b/vendor/magento/framework/Interception/Config/Config.php
+--- a/vendor/magento/framework/Interception/Config/Config.php
++++ b/vendor/magento/framework/Interception/Config/Config.php
+@@ -1,15 +1,17 @@
+ _omConfig = $omConfig;
+ $this->_relations = $relations;
+@@ -107,13 +113,13 @@ class Config implements \Magento\Framework\Interception\ConfigInterface
+ $this->_cacheId = $cacheId;
+ $this->_reader = $reader;
+ $this->_scopeList = $scopeList;
+- $this->serializer = $serializer ?: \Magento\Framework\App\ObjectManager::getInstance()
+- ->get(Serialize::class);
+- $intercepted = $this->_cache->load($this->_cacheId);
+- if ($intercepted !== false) {
+- $this->_intercepted = $this->serializer->unserialize($intercepted);
++ $this->cacheManager =
++ $cacheManager ?? \Magento\Framework\App\ObjectManager::getInstance()->get(CacheManager::class);
++ $intercepted = $this->cacheManager->load($cacheId);
++ if ($intercepted !== null) {
++ $this->_intercepted = $intercepted;
+ } else {
+- $this->initialize($this->_classDefinitions->getClasses());
++ $this->initializeUncompiled($this->_classDefinitions->getClasses());
+ }
+ }
+
+@@ -125,24 +131,9 @@ class Config implements \Magento\Framework\Interception\ConfigInterface
+ */
+ public function initialize($classDefinitions = [])
+ {
+- $this->_cache->clean(\Zend_Cache::CLEANING_MODE_MATCHING_TAG, [$this->_cacheId]);
+- $config = [];
+- foreach ($this->_scopeList->getAllScopes() as $scope) {
+- $config = array_replace_recursive($config, $this->_reader->read($scope));
+- }
+- unset($config['preferences']);
+- foreach ($config as $typeName => $typeConfig) {
+- if (!empty($typeConfig['plugins'])) {
+- $this->_intercepted[ltrim($typeName, '\\')] = true;
+- }
+- }
+- foreach ($config as $typeName => $typeConfig) {
+- $this->hasPlugins($typeName);
+- }
+- foreach ($classDefinitions as $class) {
+- $this->hasPlugins($class);
+- }
+- $this->_cache->save($this->serializer->serialize($this->_intercepted), $this->_cacheId);
++ $this->generateIntercepted($classDefinitions);
++
++ $this->cacheManager->saveCompiled($this->_cacheId, $this->_intercepted);
+ }
+
+ /**
+@@ -179,7 +170,7 @@ class Config implements \Magento\Framework\Interception\ConfigInterface
+ }
+
+ /**
+- * {@inheritdoc}
++ * @inheritdoc
+ */
+ public function hasPlugins($type)
+ {
+@@ -188,4 +179,41 @@ class Config implements \Magento\Framework\Interception\ConfigInterface
+ }
+ return $this->_inheritInterception($type);
+ }
++
++ /**
++ * Write interception config to cache
++ *
++ * @param array $classDefinitions
++ */
++ private function initializeUncompiled($classDefinitions = [])
++ {
++ $this->generateIntercepted($classDefinitions);
++
++ $this->cacheManager->save($this->_cacheId, $this->_intercepted);
++ }
++
++ /**
++ * Generate intercepted array to store in compiled metadata or frontend cache
++ *
++ * @param array $classDefinitions
++ */
++ private function generateIntercepted($classDefinitions)
++ {
++ $config = [];
++ foreach ($this->_scopeList->getAllScopes() as $scope) {
++ $config = array_replace_recursive($config, $this->_reader->read($scope));
++ }
++ unset($config['preferences']);
++ foreach ($config as $typeName => $typeConfig) {
++ if (!empty($typeConfig['plugins'])) {
++ $this->_intercepted[ltrim($typeName, '\\')] = true;
++ }
++ }
++ foreach ($config as $typeName => $typeConfig) {
++ $this->hasPlugins($typeName);
++ }
++ foreach ($classDefinitions as $class) {
++ $this->hasPlugins($class);
++ }
++ }
+ }
+diff -Nuar a/app/etc/di.xml b/app/etc/di.xml
+--- a/app/etc/di.xml
++++ b/app/etc/di.xml
+@@ -1742,4 +1742,25 @@
+
+
+
++
++
++ Magento\Framework\Lock\Backend\Cache
++
++
++
++
++ block_html
++
++
++
++
++ configured_block_cache
++
++
++
++
++
++ Magento\Framework\App\Cache\Type\Config
++
++
+
+diff -Nuar a/vendor/magento/framework/Cache/LockGuardedCacheLoader.php b/vendor/magento/framework/Cache/LockGuardedCacheLoader.php
+new file mode 100644
+--- /dev/null
++++ b/vendor/magento/framework/Cache/LockGuardedCacheLoader.php
+@@ -0,0 +1,147 @@
++locker = $locker;
++ $this->lockTimeout = $lockTimeout;
++ $this->delayTimeout = $delayTimeout;
++ $this->loadTimeout = $loadTimeout;
++ $this->minimalDelayTimeout = $minimalDelayTimeout;
++ }
++
++ /**
++ * Load data.
++ *
++ * @param string $lockName
++ * @param callable $dataLoader
++ * @param callable $dataCollector
++ * @param callable $dataSaver
++ * @return mixed
++ */
++ public function lockedLoadData(
++ string $lockName,
++ callable $dataLoader,
++ callable $dataCollector,
++ callable $dataSaver
++ ) {
++ $cachedData = $dataLoader(); //optimistic read
++ $deadline = microtime(true) + $this->loadTimeout / 100;
++
++ while ($cachedData === false) {
++ if ($deadline <= microtime(true)) {
++ return $dataCollector();
++ }
++
++ if ($this->locker->lock($lockName, $this->lockTimeout / 1000)) {
++ try {
++ $data = $dataCollector();
++ $dataSaver($data);
++ $cachedData = $data;
++ } finally {
++ $this->locker->unlock($lockName);
++ }
++ }
++
++ if ($cachedData === false) {
++ usleep($this->getLookupTimeout() * 1000);
++ $cachedData = $dataLoader();
++ }
++ }
++
++ return $cachedData;
++ }
++
++ /**
++ * Clean data.
++ *
++ * @param string $lockName
++ * @param callable $dataCleaner
++ * @return void
++ */
++ public function lockedCleanData(string $lockName, callable $dataCleaner)
++ {
++ while ($this->locker->isLocked($lockName)) {
++ usleep($this->getLookupTimeout() * 1000);
++ }
++
++ $dataCleaner();
++ }
++
++ /**
++ * Delay will be applied as rand($minimalDelayTimeout, $delayTimeout).
++ * This helps to desynchronize multiple clients trying
++ * to acquire the lock for the same resource at the same time
++ *
++ * @return int
++ */
++ private function getLookupTimeout()
++ {
++ return rand($this->minimalDelayTimeout, $this->delayTimeout);
++ }
++}
+diff -Nuar a/vendor/magento/framework/View/Element/AbstractBlock.php b/vendor/magento/framework/View/Element/AbstractBlock.php
+--- a/vendor/magento/framework/View/Element/AbstractBlock.php
++++ b/vendor/magento/framework/View/Element/AbstractBlock.php
+@@ -3,8 +3,10 @@
+ * Copyright © Magento, Inc. All rights reserved.
+ * See COPYING.txt for license details.
+ */
++
+ namespace Magento\Framework\View\Element;
+
++use Magento\Framework\Cache\LockGuardedCacheLoader;
+ use Magento\Framework\DataObject\IdentityInterface;
+
+ /**
+@@ -14,6 +16,7 @@ use Magento\Framework\DataObject\IdentityInterface;
+ *
+ * Marked as public API because it is actively used now.
+ *
++ * phpcs:disable Magento2.Classes.AbstractApi
+ * @api
+ * @SuppressWarnings(PHPMD.ExcessivePublicCount)
+ * @SuppressWarnings(PHPMD.ExcessiveClassComplexity)
+@@ -52,6 +55,7 @@ abstract class AbstractBlock extends \Magento\Framework\DataObject implements Bl
+ * SID Resolver
+ *
+ * @var \Magento\Framework\Session\SidResolverInterface
++ * @deprecated 102.0.5 Not used anymore.
+ */
+ protected $_sidResolver;
+
+@@ -176,14 +180,21 @@ abstract class AbstractBlock extends \Magento\Framework\DataObject implements Bl
+ */
+ protected $_cache;
+
++ /**
++ * @var LockGuardedCacheLoader
++ */
++ private $lockQuery;
++
+ /**
+ * Constructor
+ *
+ * @param \Magento\Framework\View\Element\Context $context
+ * @param array $data
+ */
+- public function __construct(\Magento\Framework\View\Element\Context $context, array $data = [])
+- {
++ public function __construct(
++ \Magento\Framework\View\Element\Context $context,
++ array $data = []
++ ) {
+ $this->_request = $context->getRequest();
+ $this->_layout = $context->getLayout();
+ $this->_eventManager = $context->getEventManager();
+@@ -201,6 +212,7 @@ abstract class AbstractBlock extends \Magento\Framework\DataObject implements Bl
+ $this->filterManager = $context->getFilterManager();
+ $this->_localeDate = $context->getLocaleDate();
+ $this->inlineTranslation = $context->getInlineTranslation();
++ $this->lockQuery = $context->getLockGuardedCacheLoader();
+ if (isset($data['jsLayout'])) {
+ $this->jsLayout = $data['jsLayout'];
+ unset($data['jsLayout']);
+@@ -235,6 +247,7 @@ abstract class AbstractBlock extends \Magento\Framework\DataObject implements Bl
+ * Please override this one instead of overriding real __construct constructor
+ *
+ * @return void
++ * phpcs:disable Magento2.CodeAnalysis.EmptyBlock
+ */
+ protected function _construct()
+ {
+@@ -430,9 +443,9 @@ abstract class AbstractBlock extends \Magento\Framework\DataObject implements Bl
+ */
+ public function unsetCallChild($alias, $callback, $result, $params)
+ {
++ $args = func_get_args();
+ $child = $this->getChildBlock($alias);
+ if ($child) {
+- $args = func_get_args();
+ $alias = array_shift($args);
+ $callback = array_shift($args);
+ $result = (string)array_shift($args);
+@@ -659,19 +672,6 @@ abstract class AbstractBlock extends \Magento\Framework\DataObject implements Bl
+ }
+
+ $html = $this->_loadCache();
+- if ($html === false) {
+- if ($this->hasData('translate_inline')) {
+- $this->inlineTranslation->suspend($this->getData('translate_inline'));
+- }
+-
+- $this->_beforeToHtml();
+- $html = $this->_toHtml();
+- $this->_saveCache($html);
+-
+- if ($this->hasData('translate_inline')) {
+- $this->inlineTranslation->resume();
+- }
+- }
+ $html = $this->_afterToHtml($html);
+
+ /** @var \Magento\Framework\DataObject */
+@@ -680,10 +680,13 @@ abstract class AbstractBlock extends \Magento\Framework\DataObject implements Bl
+ 'html' => $html,
+ ]
+ );
+- $this->_eventManager->dispatch('view_block_abstract_to_html_after', [
+- 'block' => $this,
+- 'transport' => $transportObject
+- ]);
++ $this->_eventManager->dispatch(
++ 'view_block_abstract_to_html_after',
++ [
++ 'block' => $this,
++ 'transport' => $transportObject
++ ]
++ );
+ $html = $transportObject->getHtml();
+
+ return $html;
+@@ -726,7 +729,7 @@ abstract class AbstractBlock extends \Magento\Framework\DataObject implements Bl
+ */
+ public function getUiId($arg1 = null, $arg2 = null, $arg3 = null, $arg4 = null, $arg5 = null)
+ {
+- return ' data-ui-id="' . $this->getJsId($arg1, $arg2, $arg3, $arg4, $arg5) . '" ';
++ return ' data-ui-id="' . $this->escapeHtmlAttr($this->getJsId($arg1, $arg2, $arg3, $arg4, $arg5)) . '" ';
+ }
+
+ /**
+@@ -875,7 +878,7 @@ abstract class AbstractBlock extends \Magento\Framework\DataObject implements Bl
+ $namespace = substr(
+ $className,
+ 0,
+- strpos($className, '\\' . 'Block')
++ strpos($className, '\\' . 'Block' . '\\')
+ );
+ return str_replace('\\', '_', $namespace);
+ }
+@@ -973,8 +976,8 @@ abstract class AbstractBlock extends \Magento\Framework\DataObject implements Bl
+ *
+ * Use $addSlashes = false for escaping js that inside html attribute (onClick, onSubmit etc)
+ *
+- * @param string $data
+- * @param bool $addSlashes
++ * @param string $data
++ * @param bool $addSlashes
+ * @return string
+ * @deprecated 101.0.0
+ */
+@@ -1084,23 +1087,43 @@ abstract class AbstractBlock extends \Magento\Framework\DataObject implements Bl
+ /**
+ * Load block html from cache storage
+ *
+- * @return string|false
++ * @return string
+ */
+ protected function _loadCache()
+ {
++ $collectAction = function () {
++ if ($this->hasData('translate_inline')) {
++ $this->inlineTranslation->suspend($this->getData('translate_inline'));
++ }
++
++ $this->_beforeToHtml();
++ return $this->_toHtml();
++ };
++
+ if ($this->getCacheLifetime() === null || !$this->_cacheState->isEnabled(self::CACHE_GROUP)) {
+- return false;
+- }
+- $cacheKey = $this->getCacheKey();
+- $cacheData = $this->_cache->load($cacheKey);
+- if ($cacheData) {
+- $cacheData = str_replace(
+- $this->_getSidPlaceholder($cacheKey),
+- $this->_sidResolver->getSessionIdQueryParam($this->_session) . '=' . $this->_session->getSessionId(),
+- $cacheData
+- );
++ $html = $collectAction();
++ if ($this->hasData('translate_inline')) {
++ $this->inlineTranslation->resume();
++ }
++ return $html;
+ }
+- return $cacheData;
++ $loadAction = function () {
++ return $this->_cache->load($this->getCacheKey());
++ };
++
++ $saveAction = function ($data) {
++ $this->_saveCache($data);
++ if ($this->hasData('translate_inline')) {
++ $this->inlineTranslation->resume();
++ }
++ };
++
++ return (string)$this->lockQuery->lockedLoadData(
++ $this->getCacheKey(),
++ $loadAction,
++ $collectAction,
++ $saveAction
++ );
+ }
+
+ /**
+@@ -1115,11 +1138,6 @@ abstract class AbstractBlock extends \Magento\Framework\DataObject implements Bl
+ return false;
+ }
+ $cacheKey = $this->getCacheKey();
+- $data = str_replace(
+- $this->_sidResolver->getSessionIdQueryParam($this->_session) . '=' . $this->_session->getSessionId(),
+- $this->_getSidPlaceholder($cacheKey),
+- $data
+- );
+
+ $this->_cache->save($data, $cacheKey, array_unique($this->getCacheTags()), $this->getCacheLifetime());
+ return $this;
+diff -Nuar a/vendor/magento/framework/View/Element/Context.php b/vendor/magento/framework/View/Element/Context.php
+--- a/vendor/magento/framework/View/Element/Context.php
++++ b/vendor/magento/framework/View/Element/Context.php
+@@ -5,6 +5,9 @@
+ */
+ namespace Magento\Framework\View\Element;
+
++use Magento\Framework\Cache\LockGuardedCacheLoader;
++use Magento\Framework\App\ObjectManager;
++
+ /**
+ * Constructor modification point for Magento\Framework\View\Element\AbstractBlock.
+ *
+@@ -16,8 +19,7 @@ namespace Magento\Framework\View\Element;
+ * As Magento moves from inheritance-based APIs all such classes will be deprecated together with
+ * the classes they were introduced for.
+ *
+- * @SuppressWarnings(PHPMD.TooManyFields)
+- * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
++ * @SuppressWarnings(PHPMD)
+ *
+ * @api
+ * @since 100.0.2
+@@ -137,12 +139,16 @@ class Context implements \Magento\Framework\ObjectManager\ContextInterface
+ */
+ protected $inlineTranslation;
+
++ /**
++ * @var LockGuardedCacheLoader
++ */
++ private $lockQuery;
++
+ /**
+ * @param \Magento\Framework\App\RequestInterface $request
+ * @param \Magento\Framework\View\LayoutInterface $layout
+ * @param \Magento\Framework\Event\ManagerInterface $eventManager
+ * @param \Magento\Framework\UrlInterface $urlBuilder
+- * @param \Magento\Framework\TranslateInterface $translator
+ * @param \Magento\Framework\App\CacheInterface $cache
+ * @param \Magento\Framework\View\DesignInterface $design
+ * @param \Magento\Framework\Session\SessionManagerInterface $session
+@@ -156,6 +162,7 @@ class Context implements \Magento\Framework\ObjectManager\ContextInterface
+ * @param \Magento\Framework\Filter\FilterManager $filterManager
+ * @param \Magento\Framework\Stdlib\DateTime\TimezoneInterface $localeDate
+ * @param \Magento\Framework\Translate\Inline\StateInterface $inlineTranslation
++ * @param LockGuardedCacheLoader $lockQuery
+ *
+ * @SuppressWarnings(PHPMD.ExcessiveParameterList)
+ */
+@@ -176,7 +183,8 @@ class Context implements \Magento\Framework\ObjectManager\ContextInterface
+ \Magento\Framework\Escaper $escaper,
+ \Magento\Framework\Filter\FilterManager $filterManager,
+ \Magento\Framework\Stdlib\DateTime\TimezoneInterface $localeDate,
+- \Magento\Framework\Translate\Inline\StateInterface $inlineTranslation
++ \Magento\Framework\Translate\Inline\StateInterface $inlineTranslation,
++ LockGuardedCacheLoader $lockQuery = null
+ ) {
+ $this->_request = $request;
+ $this->_layout = $layout;
+@@ -195,6 +203,7 @@ class Context implements \Magento\Framework\ObjectManager\ContextInterface
+ $this->_filterManager = $filterManager;
+ $this->_localeDate = $localeDate;
+ $this->inlineTranslation = $inlineTranslation;
++ $this->lockQuery = $lockQuery ?: ObjectManager::getInstance()->get(LockGuardedCacheLoader::class);
+ }
+
+ /**
+@@ -358,10 +367,23 @@ class Context implements \Magento\Framework\ObjectManager\ContextInterface
+ }
+
+ /**
++ * Get locale date.
++ *
+ * @return \Magento\Framework\Stdlib\DateTime\TimezoneInterface
+ */
+ public function getLocaleDate()
+ {
+ return $this->_localeDate;
+ }
++
++ /**
++ * Lock guarded cache loader.
++ *
++ * @return LockGuardedCacheLoader
++ * @since 102.0.2
++ */
++ public function getLockGuardedCacheLoader()
++ {
++ return $this->lockQuery;
++ }
+ }
+
+diff -Nuar a/vendor/magento/module-eav/Model/Config.php b/vendor/magento/module-eav/Model/Config.php
+--- a/vendor/magento/module-eav/Model/Config.php
++++ b/vendor/magento/module-eav/Model/Config.php
+@@ -7,12 +7,20 @@ namespace Magento\Eav\Model;
+
+ use Magento\Eav\Model\Entity\Attribute\AbstractAttribute;
+ use Magento\Eav\Model\Entity\Type;
++use Magento\Eav\Model\ResourceModel\Attribute\DefaultEntityAttributes\ProviderInterface;
++use Magento\Framework\App\Config\ScopeConfigInterface;
+ use Magento\Framework\App\ObjectManager;
++use Magento\Framework\Exception\LocalizedException;
++use Magento\Framework\Model\AbstractModel;
+ use Magento\Framework\Serialize\SerializerInterface;
+
+ /**
++ * EAV config model.
++ *
+ * @api
++ * @SuppressWarnings(PHPMD.TooManyFields)
+ * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
++ * @SuppressWarnings(PHPMD.ExcessiveClassComplexity)
+ * @since 100.0.2
+ */
+ class Config
+@@ -25,6 +33,11 @@ class Config
+ const ATTRIBUTES_CODES_CACHE_ID = 'EAV_ENTITY_ATTRIBUTES_CODES';
+ /**#@-*/
+
++ /**
++ * Xml path to caching user defined eav attributes configuration.
++ */
++ private const XML_PATH_CACHE_USER_DEFINED_ATTRIBUTES = 'dev/caching/cache_user_defined_attributes';
++
+ /**#@-*/
+ protected $_entityTypeData;
+
+@@ -116,6 +129,11 @@ class Config
+ */
+ private $serializer;
+
++ /**
++ * @var ScopeConfigInterface
++ */
++ private $scopeConfig;
++
+ /**
+ * Cache of attributes per set
+ *
+@@ -123,13 +141,29 @@ class Config
+ */
+ private $attributesPerSet = [];
+
++ /**
++ * Is system attributes loaded flag.
++ *
++ * @var array
++ */
++ private $isSystemAttributesLoaded = [];
++
++ /**
++ * List of predefined system attributes for preload.
++ *
++ * @var array
++ */
++ private $attributesForPreload;
++
+ /**
+ * @param \Magento\Framework\App\CacheInterface $cache
+- * @param \Magento\Eav\Model\Entity\TypeFactory $entityTypeFactory
+- * @param \Magento\Eav\Model\ResourceModel\Entity\Type\CollectionFactory $entityTypeCollectionFactory
++ * @param Entity\TypeFactory $entityTypeFactory
++ * @param ResourceModel\Entity\Type\CollectionFactory $entityTypeCollectionFactory
+ * @param \Magento\Framework\App\Cache\StateInterface $cacheState
+ * @param \Magento\Framework\Validator\UniversalFactory $universalFactory
+- * @param SerializerInterface $serializer
++ * @param SerializerInterface|null $serializer
++ * @param ScopeConfigInterface|null $scopeConfig
++ * @param array $attributesForPreload
+ * @codeCoverageIgnore
+ */
+ public function __construct(
+@@ -138,7 +172,9 @@ class Config
+ \Magento\Eav\Model\ResourceModel\Entity\Type\CollectionFactory $entityTypeCollectionFactory,
+ \Magento\Framework\App\Cache\StateInterface $cacheState,
+ \Magento\Framework\Validator\UniversalFactory $universalFactory,
+- SerializerInterface $serializer = null
++ SerializerInterface $serializer = null,
++ ScopeConfigInterface $scopeConfig = null,
++ $attributesForPreload = []
+ ) {
+ $this->_cache = $cache;
+ $this->_entityTypeFactory = $entityTypeFactory;
+@@ -146,6 +182,8 @@ class Config
+ $this->_cacheState = $cacheState;
+ $this->_universalFactory = $universalFactory;
+ $this->serializer = $serializer ?: ObjectManager::getInstance()->get(SerializerInterface::class);
++ $this->scopeConfig = $scopeConfig ?: ObjectManager::getInstance()->get(ScopeConfigInterface::class);
++ $this->attributesForPreload = $attributesForPreload;
+ }
+
+ /**
+@@ -207,8 +245,8 @@ class Config
+ /**
+ * Associate object with identifier
+ *
+- * @param mixed $obj
+- * @param mixed $id
++ * @param mixed $obj
++ * @param mixed $id
+ * @return void
+ * @codeCoverageIgnore
+ */
+@@ -233,8 +271,8 @@ class Config
+ /**
+ * Specify reference for entity type id
+ *
+- * @param int $id
+- * @param string $code
++ * @param int $id
++ * @param string $code
+ * @return $this
+ * @codeCoverageIgnore
+ */
+@@ -258,9 +296,9 @@ class Config
+ /**
+ * Specify reference between entity attribute id and attribute code
+ *
+- * @param int $id
+- * @param string $code
+- * @param string $entityTypeCode
++ * @param int $id
++ * @param string $code
++ * @param string $entityTypeCode
+ * @return $this
+ */
+ protected function _addAttributeReference($id, $code, $entityTypeCode)
+@@ -336,7 +374,9 @@ class Config
+ }
+ \Magento\Framework\Profiler::start('EAV: ' . __METHOD__, ['group' => 'EAV', 'method' => __METHOD__]);
+
+- if ($this->isCacheEnabled() && ($cache = $this->_cache->load(self::ENTITIES_CACHE_ID))) {
++ if ($this->isCacheEnabled() &&
++ ($cache = $this->_cache->load(self::ENTITIES_CACHE_ID))
++ ) {
+ $this->_entityTypeData = $this->serializer->unserialize($cache);
+ foreach ($this->_entityTypeData as $typeCode => $data) {
+ $typeId = $data['entity_type_id'];
+@@ -484,10 +524,10 @@ class Config
+ /**
+ * Get attribute by code for entity type
+ *
+- * @param mixed $entityType
+- * @param mixed $code
+- * @return AbstractAttribute
+- * @throws \Magento\Framework\Exception\LocalizedException
++ * @param mixed $entityType
++ * @param mixed $code
++ * @return AbstractAttribute
++ * @throws LocalizedException
+ */
+ public function getAttribute($entityType, $code)
+ {
+@@ -507,8 +547,152 @@ class Config
+ return $this->attributes[$entityTypeCode][$code];
+ }
+
++ if (array_key_exists($entityTypeCode, $this->attributesForPreload)
++ && array_key_exists($code, $this->attributesForPreload[$entityTypeCode])
++ ) {
++ $this->initSystemAttributes($entityType, $this->attributesForPreload[$entityTypeCode]);
++ }
++ if (isset($this->attributes[$entityTypeCode][$code])) {
++ \Magento\Framework\Profiler::stop('EAV: ' . __METHOD__);
++ return $this->attributes[$entityTypeCode][$code];
++ }
++
++ if ($this->scopeConfig->getValue(self::XML_PATH_CACHE_USER_DEFINED_ATTRIBUTES)) {
++ $attribute = $this->cacheUserDefinedAttribute($entityType, $entityTypeCode, $code);
++ } else {
++ $attribute = $this->initUserDefinedAttribute($entityType, $entityTypeCode, $code);
++ }
++
++ \Magento\Framework\Profiler::stop('EAV: ' . __METHOD__);
++ return $attribute;
++ }
++
++ /**
++ * Initialize predefined system attributes for preload.
++ *
++ * @param string $entityType
++ * @param array $systemAttributes
++ * @return $this|bool|void
++ * @throws LocalizedException
++ */
++ private function initSystemAttributes($entityType, $systemAttributes)
++ {
++ $entityType = $this->getEntityType($entityType);
++ $entityTypeCode = $entityType->getEntityTypeCode();
++ if (!empty($this->isSystemAttributesLoaded[$entityTypeCode])) {
++ return;
++ }
++
++ $cacheKey = self::ATTRIBUTES_CACHE_ID . '-' . $entityTypeCode . '-preload';
++ if ($this->isCacheEnabled() && ($attributes = $this->_cache->load($cacheKey))) {
++ $attributes = $this->serializer->unserialize($attributes);
++ if ($attributes) {
++ foreach ($attributes as $attribute) {
++ $attributeObject = $this->_createAttribute($entityType, $attribute);
++ $this->saveAttribute($attributeObject, $entityTypeCode, $attributeObject->getAttributeCode());
++ }
++ return true;
++ }
++ }
++
++ \Magento\Framework\Profiler::start('EAV: ' . __METHOD__, ['group' => 'EAV', 'method' => __METHOD__]);
++
++ /** @var \Magento\Eav\Model\ResourceModel\Entity\Attribute\Collection $attributes */
++ $attributes = $this->_universalFactory->create(
++ $entityType->getEntityAttributeCollection()
++ )->setEntityTypeFilter(
++ $entityType
++ )->addFieldToFilter(
++ 'attribute_code',
++ ['in' => array_keys($systemAttributes)]
++ )->getData();
++
++ $attributeData = [];
++ foreach ($attributes as $attribute) {
++ if (empty($attribute['attribute_model'])) {
++ $attribute['attribute_model'] = $entityType->getAttributeModel();
++ }
++ $attributeObject = $this->_createAttribute($entityType, $attribute);
++ $this->saveAttribute($attributeObject, $entityTypeCode, $attributeObject->getAttributeCode());
++ $attributeData[$attribute['attribute_code']] = $attributeObject->toArray();
++ }
++ if ($this->isCacheEnabled()) {
++ $this->_cache->save(
++ $this->serializer->serialize($attributeData),
++ $cacheKey,
++ [
++ \Magento\Eav\Model\Cache\Type::CACHE_TAG,
++ \Magento\Eav\Model\Entity\Attribute::CACHE_TAG
++ ]
++ );
++ }
++
++ \Magento\Framework\Profiler::stop('EAV: ' . __METHOD__);
++ $this->isSystemAttributesLoaded[$entityTypeCode] = true;
++
++ return $this;
++ }
++
++ /**
++ * Initialize user defined attribute from cache or cache it.
++ *
++ * @param string $entityType
++ * @param mixed $entityTypeCode
++ * @param string $code
++ * @return AbstractAttribute
++ * @throws LocalizedException
++ */
++ private function cacheUserDefinedAttribute($entityType, $entityTypeCode, $code): AbstractAttribute
++ {
++ $cacheKey = self::ATTRIBUTES_CACHE_ID . '-attribute-' . $entityTypeCode . '-' . $code;
++ $attributeData = $this->isCacheEnabled() && ($attribute = $this->_cache->load($cacheKey))
++ ? $this->serializer->unserialize($attribute)
++ : null;
++ if ($attributeData) {
++ if (isset($attributeData['attribute_id'])) {
++ $attribute = $this->_createAttribute($entityType, $attributeData);
++ } else {
++ $entityType = $this->getEntityType($entityType);
++ $attribute = $this->createAttribute($entityType->getAttributeModel());
++ $attribute->setAttributeCode($code);
++ $attribute = $this->setAttributeData($attribute, $entityType);
++ }
++ } else {
++ $attribute = $this->createAttributeByAttributeCode($entityType, $code);
++ $this->_addAttributeReference(
++ $attribute->getAttributeId(),
++ $attribute->getAttributeCode(),
++ $entityTypeCode
++ );
++ $this->saveAttribute($attribute, $entityTypeCode, $attribute->getAttributeCode());
++ if ($this->isCacheEnabled()) {
++ $this->_cache->save(
++ $this->serializer->serialize($attribute->getData()),
++ $cacheKey,
++ [
++ \Magento\Eav\Model\Cache\Type::CACHE_TAG,
++ \Magento\Eav\Model\Entity\Attribute::CACHE_TAG
++ ]
++ );
++ }
++ }
++
++ return $attribute;
++ }
++
++ /**
++ * Initialize user defined attribute and save it to memory cache.
++ *
++ * @param mixed $entityType
++ * @param string $entityTypeCode
++ * @param string $code
++ * @return AbstractAttribute|null
++ * @throws LocalizedException
++ */
++ private function initUserDefinedAttribute($entityType, $entityTypeCode, $code): ?AbstractAttribute
++ {
+ $attributes = $this->loadAttributes($entityTypeCode);
+- $attribute = isset($attributes[$code]) ? $attributes[$code] : null;
++ $attribute = $attributes[$code] ?? null;
+ if (!$attribute) {
+ $attribute = $this->createAttributeByAttributeCode($entityType, $code);
+ $this->_addAttributeReference(
+@@ -518,7 +702,7 @@ class Config
+ );
+ $this->saveAttribute($attribute, $entityTypeCode, $attribute->getAttributeCode());
+ }
+- \Magento\Framework\Profiler::stop('EAV: ' . __METHOD__);
++
+ return $attribute;
+ }
+
+@@ -555,8 +739,8 @@ class Config
+ /**
+ * Get all entity type attributes
+ *
+- * @param int|string|Type $entityType
+- * @param \Magento\Framework\DataObject|null $object
++ * @param int|string|Type $entityType
++ * @param \Magento\Framework\DataObject|null $object
+ * @return AbstractAttribute[]
+ *
+ * @SuppressWarnings(PHPMD.CyclomaticComplexity)
+@@ -639,7 +823,11 @@ class Config
+ $existsFullAttribute = $attribute->hasIsRequired();
+ $fullAttributeData = array_key_exists('is_required', $attributeData);
+
+- if ($existsFullAttribute || !$existsFullAttribute && !$fullAttributeData) {
++ if ($existsFullAttribute || (!$existsFullAttribute && !$fullAttributeData)) {
++ $scopeIsRequired = $attributeData['scope_is_required'] ?? null;
++ if ($scopeIsRequired !== null) {
++ $attribute->setData('scope_is_required', $scopeIsRequired);
++ }
+ return $attribute;
+ }
+ }
+@@ -708,6 +896,7 @@ class Config
+ * @param string $entityType
+ * @param string $attributeCode
+ * @return AbstractAttribute
++ * @throws LocalizedException
+ */
+ private function createAttributeByAttributeCode($entityType, $attributeCode)
+ {
+@@ -723,13 +912,28 @@ class Config
+ $attribute->setAttributeCode($attributeCode);
+ }
+
++ $attribute = $this->setAttributeData($attribute, $entityType);
++
++ return $attribute;
++ }
++
++ /**
++ * Set entity type id, backend type, is global to attribute.
++ *
++ * @param AbstractAttribute $attribute
++ * @param AbstractModel $entityType
++ * @return AbstractAttribute
++ */
++ private function setAttributeData($attribute, $entityType): AbstractAttribute
++ {
+ $entity = $entityType->getEntity();
+- if ($entity instanceof \Magento\Eav\Model\ResourceModel\Attribute\DefaultEntityAttributes\ProviderInterface
++ if ($entity instanceof ProviderInterface
+ && in_array($attribute->getAttributeCode(), $entity->getDefaultAttributes(), true)
+ ) {
+ $attribute->setBackendType(AbstractAttribute::TYPE_STATIC)->setIsGlobal(1);
+ }
+ $attribute->setEntityType($entityType)->setEntityTypeId($entityType->getId());
++
+ return $attribute;
+ }
+
+diff -Nuar a/vendor/magento/module-eav/Model/ResourceModel/Entity/Attribute/Set.php b/vendor/magento/module-eav/Model/ResourceModel/Entity/Attribute/Set.php
+--- a/vendor/magento/module-eav/Model/ResourceModel/Entity/Attribute/Set.php
++++ b/vendor/magento/module-eav/Model/ResourceModel/Entity/Attribute/Set.php
+@@ -6,6 +6,9 @@
+
+ namespace Magento\Eav\Model\ResourceModel\Entity\Attribute;
+
++/**
++ * Basic implementation for attribute sets
++ */
+ class Set extends \Magento\Framework\Model\ResourceModel\Db\AbstractDb
+ {
+ /**
+@@ -24,8 +27,6 @@ class Set extends \Magento\Framework\Model\ResourceModel\Db\AbstractDb
+ protected $eavConfig;
+
+ /**
+- * Constructor
+- *
+ * @param \Magento\Framework\Model\ResourceModel\Db\Context $context
+ * @param GroupFactory $attrGroupFactory
+ * @param \Magento\Eav\Model\Config $eavConfig
+@@ -54,7 +55,7 @@ class Set extends \Magento\Framework\Model\ResourceModel\Db\AbstractDb
+ }
+
+ /**
+- * Perform actions after object save
++ * Perform actions after object save.
+ *
+ * @param \Magento\Framework\Model\AbstractModel $object
+ * @return $this
+diff -Nuar a/vendor/magento/module-eav/etc/di.xml b/vendor/magento/module-eav/etc/di.xml
+--- a/vendor/magento/module-eav/etc/di.xml
++++ b/vendor/magento/module-eav/etc/di.xml
+@@ -209,4 +209,14 @@
+
+
+
++
++
++ eav
++
++
++
++
++ configured_eav_cache
++
++
+
+diff -Nuar a/vendor/magento/module-theme/etc/di.xml b/vendor/magento/module-theme/etc/di.xml
+--- a/vendor/magento/module-theme/etc/di.xml
++++ b/vendor/magento/module-theme/etc/di.xml
+@@ -285,4 +285,24 @@
+ theme_id
+
+
++
++
++ layout
++
++
++
++
++ configured_design_cache
++
++
++
++
++ design_context
++
++
++
++
++ configured_design_cache
++
++
+
+diff -Nuar a/vendor/magento/framework/App/Cache.php b/vendor/magento/framework/App/Cache.php
+--- a/vendor/magento/framework/App/Cache.php
++++ b/vendor/magento/framework/App/Cache.php
+@@ -4,12 +4,11 @@
+ * See COPYING.txt for license details.
+ */
+
+-/**
+- * System cache model
+- * support id and tags prefix support,
+- */
+ namespace Magento\Framework\App;
+
++/**
++ * System cache model support id and tags prefix support.
++ */
+ class Cache implements CacheInterface
+ {
+ /**
+@@ -30,12 +29,13 @@ class Cache implements CacheInterface
+ protected $_frontend;
+
+ /**
+- * @param \Magento\Framework\App\Cache\Frontend\Pool $frontendPool
++ * @param Cache\Frontend\Pool $frontendPool
++ * @param string|null $cacheIdentifier
+ */
+- public function __construct(\Magento\Framework\App\Cache\Frontend\Pool $frontendPool)
++ public function __construct(\Magento\Framework\App\Cache\Frontend\Pool $frontendPool, $cacheIdentifier = null)
+ {
+ $this->_frontendPool = $frontendPool;
+- $this->_frontend = $frontendPool->get($this->_frontendIdentifier);
++ $this->_frontend = $frontendPool->get($cacheIdentifier ?? $this->_frontendIdentifier);
+ }
+
+ /**
+diff -Nuar a/vendor/magento/framework/App/Cache/Frontend/Pool.php b/vendor/magento/framework/App/Cache/Frontend/Pool.php
+--- a/vendor/magento/framework/App/Cache/Frontend/Pool.php
++++ b/vendor/magento/framework/App/Cache/Frontend/Pool.php
+@@ -152,6 +152,15 @@ class Pool implements \Iterator
+ if (isset($this->_instances[$identifier])) {
+ return $this->_instances[$identifier];
+ }
+- throw new \InvalidArgumentException("Cache frontend '{$identifier}' is not recognized.");
++
++ if (!isset($this->_instances[self::DEFAULT_FRONTEND_ID])) {
++ throw new \InvalidArgumentException(
++ "Cache frontend '{$identifier}' is not recognized. As well as " .
++ self::DEFAULT_FRONTEND_ID .
++ "cache is not configured"
++ );
++ }
++
++ return $this->_instances[self::DEFAULT_FRONTEND_ID];
+ }
+ }
+diff -Nuar a/vendor/magento/framework/App/Router/ActionList.php b/vendor/magento/framework/App/Router/ActionList.php
+--- a/vendor/magento/framework/App/Router/ActionList.php
++++ b/vendor/magento/framework/App/Router/ActionList.php
+@@ -5,6 +5,8 @@
+ */
+ namespace Magento\Framework\App\Router;
+
++use Magento\Framework\App\Filesystem\DirectoryList;
++use Magento\Framework\App\State;
+ use Magento\Framework\Serialize\SerializerInterface;
+ use Magento\Framework\Serialize\Serializer\Serialize;
+ use Magento\Framework\Module\Dir\Reader as ModuleReader;
+@@ -70,12 +72,26 @@ class ActionList
+ $this->reservedWords = array_merge($reservedWords, $this->reservedWords);
+ $this->actionInterface = $actionInterface;
+ $this->serializer = $serializer ?: \Magento\Framework\App\ObjectManager::getInstance()->get(Serialize::class);
+- $data = $cache->load($cacheKey);
+- if (!$data) {
+- $this->actions = $moduleReader->getActionFiles();
+- $cache->save($this->serializer->serialize($this->actions), $cacheKey);
++ $objectManager = \Magento\Framework\App\ObjectManager::getInstance();
++ $state = $objectManager->get(State::class);
++
++ if ($state->getMode() === State::MODE_PRODUCTION) {
++ $directoryList = $objectManager->get(DirectoryList::class);
++ $file = $directoryList->getPath(DirectoryList::GENERATED_METADATA) . '/' . $cacheKey . '.' . 'php';
++
++ if (file_exists($file)) {
++ $this->actions = (include $file) ?? $moduleReader->getActionFiles();
++ } else {
++ $this->actions = $moduleReader->getActionFiles();
++ }
+ } else {
+- $this->actions = $this->serializer->unserialize($data);
++ $data = $cache->load($cacheKey);
++ if (!$data) {
++ $this->actions = $moduleReader->getActionFiles();
++ $cache->save($this->serializer->serialize($this->actions), $cacheKey);
++ } else {
++ $this->actions = $this->serializer->unserialize($data);
++ }
+ }
+ }
+
+diff -Nuar a/2.3.5/vendor/magento/framework/Cache/Backend/Redis.php b/vendor/magento/framework/Cache/Backend/Redis.php
+new file mode 100644
+--- /dev/null
++++ b/vendor/magento/framework/Cache/Backend/Redis.php
+@@ -0,0 +1,83 @@
++preloadKeys = $options['preload_keys'] ?? [];
++ parent::__construct($options);
++ }
++
++ /**
++ * Load value with given id from cache
++ *
++ * @param string $id Cache id
++ * @param boolean $doNotTestCacheValidity If set to true, the cache validity won't be tested
++ * @return bool|string
++ */
++ public function load($id, $doNotTestCacheValidity = false)
++ {
++ if (!empty($this->preloadKeys) && empty($this->preloadedData)) {
++ $redis = $this->_slave ?? $this->_redis;
++ $redis = $redis->pipeline();
++
++ foreach ($this->preloadKeys as $key) {
++ $redis->hGet(self::PREFIX_KEY . $key, self::FIELD_DATA);
++ }
++
++ $this->preloadedData = array_filter(array_combine($this->preloadKeys, $redis->exec()));
++ }
++
++ if (isset($this->preloadedData[$id])) {
++ return $this->_decodeData($this->preloadedData[$id]);
++ }
++
++ return parent::load($id, $doNotTestCacheValidity);
++ }
++
++ /**
++ * Cover errors on save operations, which may occurs when Redis cannot evict keys, which is expected in some cases.
++ *
++ * @param string $data
++ * @param string $id
++ * @param array $tags
++ * @param bool $specificLifetime
++ * @return bool
++ */
++ public function save($data, $id, $tags = [], $specificLifetime = false)
++ {
++ try {
++ parent::save($data, $id, $tags, $specificLifetime);
++ } catch (\Throwable $exception) {
++ return false;
++ }
++
++ return true;
++ }
++}
+diff -Nuar a/vendor/magento/framework/Cache/Backend/RemoteSynchronizedCache.php b/vendor/magento/framework/Cache/Backend/RemoteSynchronizedCache.php
+--- a/vendor/magento/framework/Cache/Backend/RemoteSynchronizedCache.php
++++ b/vendor/magento/framework/Cache/Backend/RemoteSynchronizedCache.php
+@@ -9,8 +9,10 @@ namespace Magento\Framework\Cache\Backend;
+ /**
+ * Remote synchronized cache
+ *
+- * This class created for correct work local caches with multiple web nodes,
+- * that will be check cache status from remote cache
++ * This class created for correct work witch local caches and multiple web nodes,
++ * in order to be sure that we always have up to date local version of cache.
++ * This class will be check cache version from remote cache and in case it newer
++ * than local one, it will update local one from remote cache a.k.a two level cache.
+ */
+ class RemoteSynchronizedCache extends \Zend_Cache_Backend implements \Zend_Cache_Backend_ExtendedInterface
+ {
+@@ -36,11 +38,15 @@ class RemoteSynchronizedCache extends \Zend_Cache_Backend implements \Zend_Cache
+ protected $cacheInvalidationTime;
+
+ /**
+- * {@inheritdoc}
++ * Suffix for hash to compare data version in cache storage.
++ */
++ private const HASH_SUFFIX = ':hash';
++
++ /**
++ * @inheritdoc
+ */
+ protected $_options = [
+ 'remote_backend' => '',
+- 'remote_backend_invalidation_time_id' => 'default_remote_backend_invalidation_time',
+ 'remote_backend_custom_naming' => true,
+ 'remote_backend_autoload' => true,
+ 'remote_backend_options' => [],
+@@ -52,6 +58,7 @@ class RemoteSynchronizedCache extends \Zend_Cache_Backend implements \Zend_Cache
+
+ /**
+ * @param array $options
++ * @throws \Zend_Cache_Exception
+ */
+ public function __construct(array $options = [])
+ {
+@@ -97,76 +104,137 @@ class RemoteSynchronizedCache extends \Zend_Cache_Backend implements \Zend_Cache
+ }
+
+ /**
+- * Update remote cache status info
++ * @inheritdoc
++ */
++ public function setDirectives($directives)
++ {
++ return $this->local->setDirectives($directives);
++ }
++
++ /**
++ * Return hash sign of the data.
+ *
+- * @return void
++ * @param string $data
++ * @return string
+ */
+- private function updateRemoteCacheStatusInfo()
++ private function getDataVersion(string $data)
+ {
+- $this->remote->save(time(), $this->_options['remote_backend_invalidation_time_id'], [], null);
+- $this->cacheInvalidationTime = null;
++ return \hash('sha256', $data);
+ }
+
+ /**
+- * {@inheritdoc}
++ * Load data version by id from remote.
++ *
++ * @param string $id
++ * @return false|string
+ */
+- public function setDirectives($directives)
++ private function loadRemoteDataVersion(string $id)
+ {
+- return $this->local->setDirectives($directives);
++ return $this->remote->load(
++ $id . self::HASH_SUFFIX
++ );
+ }
+
+ /**
+- * {@inheritdoc}
++ * Save new data version to remote.
++ *
++ * @param string $data
++ * @param string $id
++ * @param array $tags
++ * @param mixed $specificLifetime
++ * @return bool
++ */
++ private function saveRemoteDataVersion(string $data, string $id, array $tags, $specificLifetime = false)
++ {
++ return $this->remote->save($this->getDataVersion($data), $id . self::HASH_SUFFIX, $tags, $specificLifetime);
++ }
++
++ /**
++ * Remove remote data version.
++ *
++ * @param string $id
++ * @return bool
++ */
++ private function removeRemoteDataVersion($id)
++ {
++ return $this->remote->remove($id . self::HASH_SUFFIX);
++ }
++
++ /**
++ * @inheritdoc
+ */
+ public function load($id, $doNotTestCacheValidity = false)
+ {
+- $dataModificationTime = $this->local->test($id);
+- if ($this->cacheInvalidationTime === null) {
+- $this->cacheInvalidationTime = $this->remote->load($this->_options['remote_backend_invalidation_time_id']);
+- }
+- if ($dataModificationTime >= $this->cacheInvalidationTime) {
+- return $this->local->load($id, $doNotTestCacheValidity);
++ $localData = $this->local->load($id);
++ $remoteData = false;
++
++ if (false === $localData) {
++ $remoteData = $this->remote->load($id);
++
++ if (false === $remoteData) {
++ return false;
++ }
+ } else {
+- return false;
++ if ($this->getDataVersion($localData) !== $this->loadRemoteDataVersion($id)) {
++ $localData = false;
++ $remoteData = $this->remote->load($id);
++ }
+ }
++
++ if ($remoteData !== false) {
++ $this->local->save($remoteData, $id);
++ $localData = $remoteData;
++ }
++
++ return $localData;
+ }
+
+ /**
+- * {@inheritdoc}
++ * @inheritdoc
+ */
+ public function test($id)
+ {
+- return $this->local->test($id);
++ return $this->local->test($id) ?? $this->remote->test($id);
+ }
+
+ /**
+- * {@inheritdoc}
++ * @inheritdoc
+ */
+ public function save($data, $id, $tags = [], $specificLifetime = false)
+ {
+- return $this->local->save($data, $id, $tags, $specificLifetime);
++ $dataToSave = $data;
++ $remHash = $this->loadRemoteDataVersion($id);
++
++ if ($remHash !== false) {
++ $dataToSave = $this->remote->load($id);
++ } else {
++ $this->remote->save($data, $id, $tags, $specificLifetime);
++ $this->saveRemoteDataVersion($data, $id, $tags, $specificLifetime);
++ }
++
++ return $this->local->save($dataToSave, $id, [], $specificLifetime);
+ }
+
+ /**
+- * {@inheritdoc}
++ * @inheritdoc
+ */
+ public function remove($id)
+ {
+- $this->updateRemoteCacheStatusInfo();
+- return $this->local->remove($id);
++ return $this->removeRemoteDataVersion($id) &&
++ $this->remote->remove($id) &&
++ $this->local->remove($id);
+ }
+
+ /**
+- * {@inheritdoc}
++ * @inheritdoc
+ */
+ public function clean($mode = \Zend_Cache::CLEANING_MODE_ALL, $tags = [])
+ {
+- $this->updateRemoteCacheStatusInfo();
+- return $this->local->clean($mode, $tags);
++ return $this->remote->clean($mode, $tags);
+ }
+
+ /**
+- * {@inheritdoc}
++ * @inheritdoc
+ */
+ public function getIds()
+ {
+@@ -174,7 +242,7 @@ class RemoteSynchronizedCache extends \Zend_Cache_Backend implements \Zend_Cache
+ }
+
+ /**
+- * {@inheritdoc}
++ * @inheritdoc
+ */
+ public function getTags()
+ {
+@@ -182,7 +250,7 @@ class RemoteSynchronizedCache extends \Zend_Cache_Backend implements \Zend_Cache
+ }
+
+ /**
+- * {@inheritdoc}
++ * @inheritdoc
+ */
+ public function getIdsMatchingTags($tags = [])
+ {
+@@ -190,7 +258,7 @@ class RemoteSynchronizedCache extends \Zend_Cache_Backend implements \Zend_Cache
+ }
+
+ /**
+- * {@inheritdoc}
++ * @inheritdoc
+ */
+ public function getIdsNotMatchingTags($tags = [])
+ {
+@@ -198,7 +266,7 @@ class RemoteSynchronizedCache extends \Zend_Cache_Backend implements \Zend_Cache
+ }
+
+ /**
+- * {@inheritdoc}
++ * @inheritdoc
+ */
+ public function getIdsMatchingAnyTags($tags = [])
+ {
+@@ -206,7 +274,7 @@ class RemoteSynchronizedCache extends \Zend_Cache_Backend implements \Zend_Cache
+ }
+
+ /**
+- * {@inheritdoc}
++ * @inheritdoc
+ */
+ public function getFillingPercentage()
+ {
+@@ -214,7 +282,7 @@ class RemoteSynchronizedCache extends \Zend_Cache_Backend implements \Zend_Cache
+ }
+
+ /**
+- * {@inheritdoc}
++ * @inheritdoc
+ */
+ public function getMetadatas($id)
+ {
+@@ -222,7 +290,7 @@ class RemoteSynchronizedCache extends \Zend_Cache_Backend implements \Zend_Cache
+ }
+
+ /**
+- * {@inheritdoc}
++ * @inheritdoc
+ */
+ public function touch($id, $extraLifetime)
+ {
+@@ -230,7 +298,7 @@ class RemoteSynchronizedCache extends \Zend_Cache_Backend implements \Zend_Cache
+ }
+
+ /**
+- * {@inheritdoc}
++ * @inheritdoc
+ */
+ public function getCapabilities()
+ {
+diff -Nuar a/setup/src/Magento/Setup/Console/Command/DiCompileCommand.php b/setup/src/Magento/Setup/Console/Command/DiCompileCommand.php
+--- a/setup/src/Magento/Setup/Console/Command/DiCompileCommand.php
++++ b/setup/src/Magento/Setup/Console/Command/DiCompileCommand.php
+@@ -260,9 +260,12 @@ class DiCompileCommand extends Command
+ */
+ private function getExcludedLibraryPaths(array $libraryPaths)
+ {
+- $libraryPaths = array_map(function ($libraryPath) {
+- return preg_quote($libraryPath, '#');
+- }, $libraryPaths);
++ $libraryPaths = array_map(
++ function ($libraryPath) {
++ return preg_quote($libraryPath, '#');
++ },
++ $libraryPaths
++ );
+
+ $excludedLibraryPaths = [
+ '#^(?:' . join('|', $libraryPaths) . ')/([\\w]+/)?Test#',
+@@ -395,7 +398,8 @@ class DiCompileCommand extends Command
+ $compiledPathsList['application'],
+ $compiledPathsList['library'],
+ $compiledPathsList['generated_helpers'],
+- ]
++ ],
++ OperationFactory::APPLICATION_ACTION_LIST_GENERATOR => [],
+ ];
+
+ return $operations;
+diff -Nuar a/2.3.5/setup/src/Magento/Setup/Module/Di/App/Task/Operation/AppActionListGenerator.php b/setup/src/Magento/Setup/Module/Di/App/Task/Operation/AppActionListGenerator.php
+new file mode 100644
+--- /dev/null
++++ b/setup/src/Magento/Setup/Module/Di/App/Task/Operation/AppActionListGenerator.php
+@@ -0,0 +1,58 @@
++moduleReader = $moduleReader;
++ $this->configWriter = $configWriter;
++ }
++
++ /**
++ * @inheritDoc
++ */
++ public function doOperation()
++ {
++ $actionList = $this->moduleReader->getActionFiles();
++ $this->configWriter->write(
++ 'app_action_list',
++ $actionList
++ );
++ }
++
++ /**
++ * @inheritDoc
++ */
++ public function getName()
++ {
++ return 'App action list generation';
++ }
++}
+diff -Nuar a/setup/src/Magento/Setup/Module/Di/App/Task/OperationFactory.php b/setup/src/Magento/Setup/Module/Di/App/Task/OperationFactory.php
+--- a/setup/src/Magento/Setup/Module/Di/App/Task/OperationFactory.php
++++ b/setup/src/Magento/Setup/Module/Di/App/Task/OperationFactory.php
+@@ -5,6 +5,12 @@
+ */
+ namespace Magento\Setup\Module\Di\App\Task;
+
++use Magento\Setup\Module\Di\App\Task\Operation\AppActionListGenerator;
++use Magento\Setup\Module\Di\App\Task\Operation\PluginListGenerator;
++
++/**
++ * Factory that creates list of OperationInterface classes
++ */
+ class OperationFactory
+ {
+ /**
+@@ -47,6 +53,11 @@ class OperationFactory
+ */
+ const APPLICATION_CODE_GENERATOR = 'application_code_generator';
+
++ /**
++ * Application action list generator
++ */
++ const APPLICATION_ACTION_LIST_GENERATOR = 'application_action_list_generator';
++
+ /**
+ * Operations definitions
+ *
+@@ -61,6 +72,7 @@ class OperationFactory
+ self::INTERCEPTION_CACHE => \Magento\Setup\Module\Di\App\Task\Operation\InterceptionCache::class,
+ self::REPOSITORY_GENERATOR => \Magento\Setup\Module\Di\App\Task\Operation\RepositoryGenerator::class,
+ self::PROXY_GENERATOR => \Magento\Setup\Module\Di\App\Task\Operation\ProxyGenerator::class,
++ self::APPLICATION_ACTION_LIST_GENERATOR => AppActionListGenerator::class,
+ ];
+
+ /**
+
+diff --git a/vendor/magento/framework/Interception/Config/CacheManager.php b/vendor/magento/framework/Interception/Config/CacheManager.php
+new file mode 100644
+--- /dev/null
++++ b/vendor/magento/framework/Interception/Config/CacheManager.php
+@@ -0,0 +1,114 @@
++cache = $cache;
++ $this->serializer = $serializer;
++ $this->configWriter = $configWriter;
++ $this->compiledLoader = $compiledLoader;
++ }
++
++ /**
++ * Load the interception config from cache
++ *
++ * @param string $key
++ * @return array|null
++ */
++ public function load(string $key): ?array
++ {
++ if ($this->isCompiled($key)) {
++ return $this->compiledLoader->load($key);
++ }
++
++ $intercepted = $this->cache->load($key);
++ return $intercepted ? $this->serializer->unserialize($intercepted) : null;
++ }
++
++ /**
++ * Save config to cache backend
++ *
++ * @param string $key
++ * @param array $data
++ */
++ public function save(string $key, array $data)
++ {
++ $this->cache->save($this->serializer->serialize($data), $key);
++ }
++
++ /**
++ * Save config to filesystem
++ *
++ * @param string $key
++ * @param array $data
++ */
++ public function saveCompiled(string $key, array $data)
++ {
++ $this->configWriter->write($key, $data);
++ }
++
++ /**
++ * Purge interception cache
++ *
++ * @param string $key
++ */
++ public function clean(string $key)
++ {
++ $this->cache->remove($key);
++ }
++
++ /**
++ * Check for the compiled config with the generated metadata
++ *
++ * @param string $key
++ * @return bool
++ */
++ private function isCompiled(string $key): bool
++ {
++ return file_exists(\Magento\Framework\App\ObjectManager\ConfigLoader\Compiled::getFilePath($key));
++ }
++}
+
+diff --git a/vendor/magento/framework/App/ObjectManager/ConfigWriterInterface.php b/vendor/magento/framework/App/ObjectManager/ConfigWriterInterface.php
+new file mode 100644
+--- /dev/null
++++ b/vendor/magento/framework/App/ObjectManager/ConfigWriterInterface.php
+@@ -0,0 +1,24 @@
++directoryList = $directoryList;
++ }
++
++ /**
++ * Writes config in storage
++ *
++ * @param string $key
++ * @param array $config
++ * @return void
++ */
++ public function write(string $key, array $config)
++ {
++ $this->initialize();
++ $configuration = sprintf('directoryList->getPath(DirectoryList::GENERATED_METADATA) . '/' . $key . '.php',
++ $configuration
++ );
++ }
++
++ /**
++ * Initializes writer
++ *
++ * @return void
++ */
++ private function initialize()
++ {
++ if (!file_exists($this->directoryList->getPath(DirectoryList::GENERATED_METADATA))) {
++ mkdir($this->directoryList->getPath(DirectoryList::GENERATED_METADATA));
++ }
++ }
++}
+diff -Nuar a/vendor/magento/module-catalog/Model/Product.php b/vendor/magento/module-catalog/Model/Product.php
+--- a/vendor/magento/module-catalog/Model/Product.php
++++ b/vendor/magento/module-catalog/Model/Product.php
+@@ -72,9 +72,9 @@ class Product extends \Magento\Catalog\Model\AbstractModel implements
+ const STORE_ID = 'store_id';
+
+ /**
+- * @var string
++ * @var string|bool
+ */
+- protected $_cacheTag = self::CACHE_TAG;
++ protected $_cacheTag = false;
+
+ /**
+ * @var string
+@@ -865,7 +865,6 @@ class Product extends \Magento\Catalog\Model\AbstractModel implements
+ */
+ public function beforeSave()
+ {
+- $this->cleanCache();
+ $this->setTypeHasOptions(false);
+ $this->setTypeHasRequiredOptions(false);
+ $this->setHasOptions(false);
+diff -Nuar a/setup/src/Magento/Setup/Model/ConfigOptionsList/Cache.php b/setup/src/Magento/Setup/Model/ConfigOptionsList/Cache.php
+--- a/setup/src/Magento/Setup/Model/ConfigOptionsList/Cache.php
++++ b/setup/src/Magento/Setup/Model/ConfigOptionsList/Cache.php
+@@ -20,19 +20,21 @@ use Magento\Setup\Validator\RedisConnectionValidator;
+ class Cache implements ConfigOptionsListInterface
+ {
+ const INPUT_VALUE_CACHE_REDIS = 'redis';
+- const CONFIG_VALUE_CACHE_REDIS = 'Cm_Cache_Backend_Redis';
++ const CONFIG_VALUE_CACHE_REDIS = \Magento\Framework\Cache\Backend\Redis::class;
+
+ const INPUT_KEY_CACHE_BACKEND = 'cache-backend';
+ const INPUT_KEY_CACHE_BACKEND_REDIS_SERVER = 'cache-backend-redis-server';
+ const INPUT_KEY_CACHE_BACKEND_REDIS_DATABASE = 'cache-backend-redis-db';
+ const INPUT_KEY_CACHE_BACKEND_REDIS_PORT = 'cache-backend-redis-port';
+ const INPUT_KEY_CACHE_BACKEND_REDIS_PASSWORD = 'cache-backend-redis-password';
++ const INPUT_KEY_CACHE_ID_PREFIX = 'cache-id-prefix';
+
+ const CONFIG_PATH_CACHE_BACKEND = 'cache/frontend/default/backend';
+ const CONFIG_PATH_CACHE_BACKEND_SERVER = 'cache/frontend/default/backend_options/server';
+ const CONFIG_PATH_CACHE_BACKEND_DATABASE = 'cache/frontend/default/backend_options/database';
+ const CONFIG_PATH_CACHE_BACKEND_PORT = 'cache/frontend/default/backend_options/port';
+ const CONFIG_PATH_CACHE_BACKEND_PASSWORD = 'cache/frontend/default/backend_options/password';
++ const CONFIG_PATH_CACHE_ID_PREFIX = 'cache/frontend/default/id_prefix';
+
+ /**
+ * @var array
+@@ -77,7 +79,7 @@ class Cache implements ConfigOptionsListInterface
+ }
+
+ /**
+- * {@inheritdoc}
++ * @inheritdoc
+ */
+ public function getOptions()
+ {
+@@ -112,6 +114,12 @@ class Cache implements ConfigOptionsListInterface
+ TextConfigOption::FRONTEND_WIZARD_TEXT,
+ self::CONFIG_PATH_CACHE_BACKEND_PASSWORD,
+ 'Redis server password'
++ ),
++ new TextConfigOption(
++ self::INPUT_KEY_CACHE_ID_PREFIX,
++ TextConfigOption::FRONTEND_WIZARD_TEXT,
++ self::CONFIG_PATH_CACHE_ID_PREFIX,
++ 'ID prefix for cache keys'
+ )
+ ];
+ }
+@@ -122,6 +130,11 @@ class Cache implements ConfigOptionsListInterface
+ public function createConfig(array $options, DeploymentConfig $deploymentConfig)
+ {
+ $configData = new ConfigData(ConfigFilePool::APP_ENV);
++ if (isset($options[self::INPUT_KEY_CACHE_ID_PREFIX])) {
++ $configData->set(self::CONFIG_PATH_CACHE_ID_PREFIX, $options[self::INPUT_KEY_CACHE_ID_PREFIX]);
++ } else {
++ $configData->set(self::CONFIG_PATH_CACHE_ID_PREFIX, $this->generateCachePrefix());
++ }
+
+ if (isset($options[self::INPUT_KEY_CACHE_BACKEND])) {
+ if ($options[self::INPUT_KEY_CACHE_BACKEND] == self::INPUT_VALUE_CACHE_REDIS) {
+@@ -241,4 +254,14 @@ class Cache implements ConfigOptionsListInterface
+ return '';
+ }
+ }
++
++ /**
++ * Generate default cache ID prefix based on installation dir
++ *
++ * @return string
++ */
++ private function generateCachePrefix(): string
++ {
++ return substr(\hash('sha256', dirname(__DIR__, 6)), 0, 3) . '_';
++ }
+ }
+diff -Nuar a/setup/src/Magento/Setup/Model/ConfigOptionsList/PageCache.php b/setup/src/Magento/Setup/Model/ConfigOptionsList/PageCache.php
+--- a/setup/src/Magento/Setup/Model/ConfigOptionsList/PageCache.php
++++ b/setup/src/Magento/Setup/Model/ConfigOptionsList/PageCache.php
+@@ -20,7 +20,7 @@ use Magento\Setup\Validator\RedisConnectionValidator;
+ class PageCache implements ConfigOptionsListInterface
+ {
+ const INPUT_VALUE_PAGE_CACHE_REDIS = 'redis';
+- const CONFIG_VALUE_PAGE_CACHE_REDIS = 'Cm_Cache_Backend_Redis';
++ const CONFIG_VALUE_PAGE_CACHE_REDIS = \Magento\Framework\Cache\Backend\Redis::class;
+
+ const INPUT_KEY_PAGE_CACHE_BACKEND = 'page-cache';
+ const INPUT_KEY_PAGE_CACHE_BACKEND_REDIS_SERVER = 'page-cache-redis-server';
+@@ -28,6 +28,7 @@ class PageCache implements ConfigOptionsListInterface
+ const INPUT_KEY_PAGE_CACHE_BACKEND_REDIS_PORT = 'page-cache-redis-port';
+ const INPUT_KEY_PAGE_CACHE_BACKEND_REDIS_COMPRESS_DATA = 'page-cache-redis-compress-data';
+ const INPUT_KEY_PAGE_CACHE_BACKEND_REDIS_PASSWORD = 'page-cache-redis-password';
++ const INPUT_KEY_PAGE_CACHE_ID_PREFIX = 'page-cache-id-prefix';
+
+ const CONFIG_PATH_PAGE_CACHE_BACKEND = 'cache/frontend/page_cache/backend';
+ const CONFIG_PATH_PAGE_CACHE_BACKEND_SERVER = 'cache/frontend/page_cache/backend_options/server';
+@@ -35,6 +36,7 @@ class PageCache implements ConfigOptionsListInterface
+ const CONFIG_PATH_PAGE_CACHE_BACKEND_PORT = 'cache/frontend/page_cache/backend_options/port';
+ const CONFIG_PATH_PAGE_CACHE_BACKEND_COMPRESS_DATA = 'cache/frontend/page_cache/backend_options/compress_data';
+ const CONFIG_PATH_PAGE_CACHE_BACKEND_PASSWORD = 'cache/frontend/page_cache/backend_options/password';
++ const CONFIG_PATH_PAGE_CACHE_ID_PREFIX = 'cache/frontend/page_cache/id_prefix';
+
+ /**
+ * @var array
+@@ -81,7 +83,7 @@ class PageCache implements ConfigOptionsListInterface
+ }
+
+ /**
+- * {@inheritdoc}
++ * @inheritdoc
+ */
+ public function getOptions()
+ {
+@@ -122,6 +124,12 @@ class PageCache implements ConfigOptionsListInterface
+ TextConfigOption::FRONTEND_WIZARD_TEXT,
+ self::CONFIG_PATH_PAGE_CACHE_BACKEND_PASSWORD,
+ 'Redis server password'
++ ),
++ new TextConfigOption(
++ self::INPUT_KEY_PAGE_CACHE_ID_PREFIX,
++ TextConfigOption::FRONTEND_WIZARD_TEXT,
++ self::CONFIG_PATH_PAGE_CACHE_ID_PREFIX,
++ 'ID prefix for cache keys'
+ )
+ ];
+ }
+@@ -132,6 +140,11 @@ class PageCache implements ConfigOptionsListInterface
+ public function createConfig(array $options, DeploymentConfig $deploymentConfig)
+ {
+ $configData = new ConfigData(ConfigFilePool::APP_ENV);
++ if (isset($options[self::INPUT_KEY_PAGE_CACHE_ID_PREFIX])) {
++ $configData->set(self::CONFIG_PATH_PAGE_CACHE_ID_PREFIX, $options[self::INPUT_KEY_PAGE_CACHE_ID_PREFIX]);
++ } else {
++ $configData->set(self::CONFIG_PATH_PAGE_CACHE_ID_PREFIX, $this->generateCachePrefix());
++ }
+
+ if (isset($options[self::INPUT_KEY_PAGE_CACHE_BACKEND])) {
+ if ($options[self::INPUT_KEY_PAGE_CACHE_BACKEND] == self::INPUT_VALUE_PAGE_CACHE_REDIS) {
+@@ -241,7 +254,7 @@ class PageCache implements ConfigOptionsListInterface
+ /**
+ * Get the default value for input key
+ *
+- * @param string $inputKey
++ * @param string $inputKeyz
+ * @return string
+ */
+ private function getDefaultConfigValue($inputKey)
+@@ -252,4 +265,14 @@ class PageCache implements ConfigOptionsListInterface
+ return '';
+ }
+ }
++
++ /**
++ * Generate default cache ID prefix based on installation dir
++ *
++ * @return string
++ */
++ private function generateCachePrefix(): string
++ {
++ return substr(\hash('sha256', dirname(__DIR__, 6)), 0, 3) . '_';
++ }
+ }
diff --git a/patches/MCLOUD-6139_MCLOUD-6211__redis_improvement_patches__2.3.1.patch b/patches/MCLOUD-6139_MCLOUD-6211__redis_improvement_patches__2.3.1.patch
new file mode 100644
index 0000000..46a0073
--- /dev/null
+++ b/patches/MCLOUD-6139_MCLOUD-6211__redis_improvement_patches__2.3.1.patch
@@ -0,0 +1,2060 @@
+diff -Nuar a/vendor/magento/module-config/App/Config/Type/System.php b/vendor/magento/module-config/App/Config/Type/System.php
+--- a/vendor/magento/module-config/App/Config/Type/System.php
++++ b/vendor/magento/module-config/App/Config/Type/System.php
+@@ -3,6 +3,7 @@
+ * Copyright © Magento, Inc. All rights reserved.
+ * See COPYING.txt for license details.
+ */
++
+ namespace Magento\Config\App\Config\Type;
+
+ use Magento\Framework\App\Config\ConfigSourceInterface;
+@@ -13,11 +14,14 @@ use Magento\Framework\App\ObjectManager;
+ use Magento\Config\App\Config\Type\System\Reader;
+ use Magento\Framework\App\ScopeInterface;
+ use Magento\Framework\Cache\FrontendInterface;
++use Magento\Framework\Cache\LockGuardedCacheLoader;
+ use Magento\Framework\Lock\LockManagerInterface;
+ use Magento\Framework\Serialize\SerializerInterface;
+ use Magento\Store\Model\Config\Processor\Fallback;
+-use Magento\Store\Model\ScopeInterface as StoreScope;
+ use Magento\Framework\Encryption\Encryptor;
++use Magento\Store\Model\ScopeInterface as StoreScope;
++use Magento\Framework\App\Cache\StateInterface;
++use Magento\Framework\App\Cache\Type\Config;
+
+ /**
+ * System configuration type
+@@ -25,6 +29,7 @@ use Magento\Framework\Encryption\Encryptor;
+ * @api
+ * @since 100.1.2
+ * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
++ * @SuppressWarnings(PHPMD.UnusedPrivateMethod)
+ */
+ class System implements ConfigTypeInterface
+ {
+@@ -42,22 +47,6 @@ class System implements ConfigTypeInterface
+ * @var string
+ */
+ private static $lockName = 'SYSTEM_CONFIG';
+- /**
+- * Timeout between retrieves to load the configuration from the cache.
+- *
+- * Value of the variable in microseconds.
+- *
+- * @var int
+- */
+- private static $delayTimeout = 100000;
+- /**
+- * Lifetime of the lock for write in cache.
+- *
+- * Value of the variable in seconds.
+- *
+- * @var int
+- */
+- private static $lockTimeout = 42;
+
+ /**
+ * @var array
+@@ -106,11 +95,17 @@ class System implements ConfigTypeInterface
+ private $encryptor;
+
+ /**
+- * @var LockManagerInterface
++ * @var LockGuardedCacheLoader
+ */
+- private $locker;
++ private $lockQuery;
+
+ /**
++ * @var StateInterface
++ */
++ private $cacheState;
++
++ /**
++ * System constructor.
+ * @param ConfigSourceInterface $source
+ * @param PostProcessorInterface $postProcessor
+ * @param Fallback $fallback
+@@ -122,6 +117,8 @@ class System implements ConfigTypeInterface
+ * @param Reader|null $reader
+ * @param Encryptor|null $encryptor
+ * @param LockManagerInterface|null $locker
++ * @param LockGuardedCacheLoader|null $lockQuery
++ * @param StateInterface|null $cacheState
+ * @SuppressWarnings(PHPMD.UnusedFormalParameter)
+ * @SuppressWarnings(PHPMD.ExcessiveParameterList)
+ */
+@@ -136,7 +133,9 @@ class System implements ConfigTypeInterface
+ $configType = self::CONFIG_TYPE,
+ Reader $reader = null,
+ Encryptor $encryptor = null,
+- LockManagerInterface $locker = null
++ LockManagerInterface $locker = null,
++ LockGuardedCacheLoader $lockQuery = null,
++ StateInterface $cacheState = null
+ ) {
+ $this->postProcessor = $postProcessor;
+ $this->cache = $cache;
+@@ -145,8 +144,10 @@ class System implements ConfigTypeInterface
+ $this->reader = $reader ?: ObjectManager::getInstance()->get(Reader::class);
+ $this->encryptor = $encryptor
+ ?: ObjectManager::getInstance()->get(Encryptor::class);
+- $this->locker = $locker
+- ?: ObjectManager::getInstance()->get(LockManagerInterface::class);
++ $this->lockQuery = $lockQuery
++ ?: ObjectManager::getInstance()->get(LockGuardedCacheLoader::class);
++ $this->cacheState = $cacheState
++ ?: ObjectManager::getInstance()->get(StateInterface::class);
+ }
+
+ /**
+@@ -225,83 +226,64 @@ class System implements ConfigTypeInterface
+ }
+
+ /**
+- * Make lock on data load.
++ * Load configuration data for all scopes.
+ *
+- * @param callable $dataLoader
+- * @param bool $flush
+ * @return array
+ */
+- private function lockedLoadData(callable $dataLoader, bool $flush = false): array
++ private function loadAllData()
+ {
+- $cachedData = $dataLoader(); //optimistic read
+-
+- while ($cachedData === false && $this->locker->isLocked(self::$lockName)) {
+- usleep(self::$delayTimeout);
+- $cachedData = $dataLoader();
++ if (!$this->cacheState->isEnabled(Config::TYPE_IDENTIFIER)) {
++ return $this->readData();
+ }
+
+- while ($cachedData === false) {
+- try {
+- if ($this->locker->lock(self::$lockName, self::$lockTimeout)) {
+- if (!$flush) {
+- $data = $this->readData();
+- $this->cacheData($data);
+- $cachedData = $data;
+- } else {
+- $this->cache->clean(\Zend_Cache::CLEANING_MODE_MATCHING_TAG, [self::CACHE_TAG]);
+- $cachedData = [];
+- }
+- }
+- } finally {
+- $this->locker->unlock(self::$lockName);
+- }
+-
+- if ($cachedData === false) {
+- usleep(self::$delayTimeout);
+- $cachedData = $dataLoader();
+- }
+- }
+-
+- return $cachedData;
+- }
+-
+- /**
+- * Load configuration data for all scopes
+- *
+- * @return array
+- */
+- private function loadAllData()
+- {
+- return $this->lockedLoadData(function () {
++ $loadAction = function () {
+ $cachedData = $this->cache->load($this->configType);
+ $data = false;
+ if ($cachedData !== false) {
+ $data = $this->serializer->unserialize($this->encryptor->decrypt($cachedData));
+ }
+ return $data;
+- });
++ };
++
++ return $this->lockQuery->lockedLoadData(
++ self::$lockName,
++ $loadAction,
++ \Closure::fromCallable([$this, 'readData']),
++ \Closure::fromCallable([$this, 'cacheData'])
++ );
+ }
+
+ /**
+- * Load configuration data for default scope
++ * Load configuration data for default scope.
+ *
+ * @param string $scopeType
+ * @return array
+ */
+ private function loadDefaultScopeData($scopeType)
+ {
+- return $this->lockedLoadData(function () use ($scopeType) {
++ if (!$this->cacheState->isEnabled(Config::TYPE_IDENTIFIER)) {
++ return $this->readData();
++ }
++
++ $loadAction = function () use ($scopeType) {
+ $cachedData = $this->cache->load($this->configType . '_' . $scopeType);
+ $scopeData = false;
+ if ($cachedData !== false) {
+ $scopeData = [$scopeType => $this->serializer->unserialize($this->encryptor->decrypt($cachedData))];
+ }
+ return $scopeData;
+- });
++ };
++
++ return $this->lockQuery->lockedLoadData(
++ self::$lockName,
++ $loadAction,
++ \Closure::fromCallable([$this, 'readData']),
++ \Closure::fromCallable([$this, 'cacheData'])
++ );
+ }
+
+ /**
+- * Load configuration data for a specified scope
++ * Load configuration data for a specified scope.
+ *
+ * @param string $scopeType
+ * @param string $scopeId
+@@ -309,7 +291,11 @@ class System implements ConfigTypeInterface
+ */
+ private function loadScopeData($scopeType, $scopeId)
+ {
+- return $this->lockedLoadData(function () use ($scopeType, $scopeId) {
++ if (!$this->cacheState->isEnabled(Config::TYPE_IDENTIFIER)) {
++ return $this->readData();
++ }
++
++ $loadAction = function () use ($scopeType, $scopeId) {
+ $cachedData = $this->cache->load($this->configType . '_' . $scopeType . '_' . $scopeId);
+ $scopeData = false;
+ if ($cachedData === false) {
+@@ -329,7 +315,14 @@ class System implements ConfigTypeInterface
+ }
+
+ return $scopeData;
+- });
++ };
++
++ return $this->lockQuery->lockedLoadData(
++ self::$lockName,
++ $loadAction,
++ \Closure::fromCallable([$this, 'readData']),
++ \Closure::fromCallable([$this, 'cacheData'])
++ );
+ }
+
+ /**
+@@ -371,7 +364,7 @@ class System implements ConfigTypeInterface
+ }
+
+ /**
+- * Walk nested hash map by keys from $pathParts
++ * Walk nested hash map by keys from $pathParts.
+ *
+ * @param array $data to walk in
+ * @param array $pathParts keys path
+@@ -408,7 +401,7 @@ class System implements ConfigTypeInterface
+ }
+
+ /**
+- * Clean cache and global variables cache
++ * Clean cache and global variables cache.
+ *
+ * Next items cleared:
+ * - Internal property intended to store already loaded configuration data
+@@ -420,11 +413,17 @@ class System implements ConfigTypeInterface
+ public function clean()
+ {
+ $this->data = [];
+- $this->lockedLoadData(
+- function () {
+- return false;
+- },
+- true
++ $cleanAction = function () {
++ $this->cache->clean(\Zend_Cache::CLEANING_MODE_MATCHING_TAG, [self::CACHE_TAG]);
++ };
++
++ if (!$this->cacheState->isEnabled(Config::TYPE_IDENTIFIER)) {
++ return $cleanAction();
++ }
++
++ $this->lockQuery->lockedCleanData(
++ self::$lockName,
++ $cleanAction
+ );
+ }
+ }
+diff -Nuar a/vendor/magento/module-config/etc/di.xml b/vendor/magento/module-config/etc/di.xml
+--- a/vendor/magento/module-config/etc/di.xml
++++ b/vendor/magento/module-config/etc/di.xml
+@@ -90,9 +90,16 @@
+ Magento\Framework\App\Config\PreProcessorComposite
+ Magento\Framework\Serialize\Serializer\Serialize
+ Magento\Config\App\Config\Type\System\Reader\Proxy
+- Magento\Framework\Lock\Backend\Cache
++ systemConfigQueryLocker
+
+
++
++
++
++ Magento\Framework\Lock\Backend\Cache
++
++
++
+
+
+ systemConfigSourceAggregated
+diff -Nuar a/app/etc/di.xml b/app/etc/di.xml
+--- a/app/etc/di.xml
++++ b/app/etc/di.xml
+@@ -1756,4 +1756,19 @@
+
+
+
++
++
++ Magento\Framework\Lock\Backend\Cache
++
++
++
++
++ block_html
++
++
++
++
++ configured_block_cache
++
++
+
+diff -Nuar a/vendor/magento/framework/Cache/LockGuardedCacheLoader.php b/vendor/magento/framework/Cache/LockGuardedCacheLoader.php
+new file mode 100644
+--- /dev/null
++++ b/vendor/magento/framework/Cache/LockGuardedCacheLoader.php
+@@ -0,0 +1,147 @@
++locker = $locker;
++ $this->lockTimeout = $lockTimeout;
++ $this->delayTimeout = $delayTimeout;
++ $this->loadTimeout = $loadTimeout;
++ $this->minimalDelayTimeout = $minimalDelayTimeout;
++ }
++
++ /**
++ * Load data.
++ *
++ * @param string $lockName
++ * @param callable $dataLoader
++ * @param callable $dataCollector
++ * @param callable $dataSaver
++ * @return mixed
++ */
++ public function lockedLoadData(
++ string $lockName,
++ callable $dataLoader,
++ callable $dataCollector,
++ callable $dataSaver
++ ) {
++ $cachedData = $dataLoader(); //optimistic read
++ $deadline = microtime(true) + $this->loadTimeout / 100;
++
++ while ($cachedData === false) {
++ if ($deadline <= microtime(true)) {
++ return $dataCollector();
++ }
++
++ if ($this->locker->lock($lockName, $this->lockTimeout / 1000)) {
++ try {
++ $data = $dataCollector();
++ $dataSaver($data);
++ $cachedData = $data;
++ } finally {
++ $this->locker->unlock($lockName);
++ }
++ }
++
++ if ($cachedData === false) {
++ usleep($this->getLookupTimeout() * 1000);
++ $cachedData = $dataLoader();
++ }
++ }
++
++ return $cachedData;
++ }
++
++ /**
++ * Clean data.
++ *
++ * @param string $lockName
++ * @param callable $dataCleaner
++ * @return void
++ */
++ public function lockedCleanData(string $lockName, callable $dataCleaner)
++ {
++ while ($this->locker->isLocked($lockName)) {
++ usleep($this->getLookupTimeout() * 1000);
++ }
++
++ $dataCleaner();
++ }
++
++ /**
++ * Delay will be applied as rand($minimalDelayTimeout, $delayTimeout).
++ * This helps to desynchronize multiple clients trying
++ * to acquire the lock for the same resource at the same time
++ *
++ * @return int
++ */
++ private function getLookupTimeout()
++ {
++ return rand($this->minimalDelayTimeout, $this->delayTimeout);
++ }
++}
+diff -Nuar a/vendor/magento/framework/Lock/Backend/Cache.php b/vendor/magento/framework/Lock/Backend/Cache.php
+--- a/vendor/magento/framework/Lock/Backend/Cache.php
++++ b/vendor/magento/framework/Lock/Backend/Cache.php
+@@ -14,24 +14,57 @@ use Magento\Framework\Cache\FrontendInterface;
+ */
+ class Cache implements \Magento\Framework\Lock\LockManagerInterface
+ {
++ /**
++ * Prefix for marking that key is locked or not.
++ */
++ const LOCK_PREFIX = 'LOCKED_RECORD_INFO_';
++
+ /**
+ * @var FrontendInterface
+ */
+ private $cache;
+
++ /**
++ * Sign for locks, helps to avoid removing a lock that was created by another client
++ *
++ * @string
++ */
++ private $lockSign;
++
+ /**
+ * @param FrontendInterface $cache
+ */
+ public function __construct(FrontendInterface $cache)
+ {
+ $this->cache = $cache;
++ $this->lockSign = $this->generateLockSign();
+ }
++
+ /**
+ * @inheritdoc
+ */
+ public function lock(string $name, int $timeout = -1): bool
+ {
+- return $this->cache->save('1', $name, [], $timeout);
++ if (empty($this->lockSign)) {
++ $this->lockSign = $this->generateLockSign();
++ }
++
++ $data = $this->cache->load($this->getIdentifier($name));
++
++ if (false !== $data) {
++ return false;
++ }
++
++ $timeout = $timeout <= 0 ? null : $timeout;
++ $this->cache->save($this->lockSign, $this->getIdentifier($name), [], $timeout);
++
++ $data = $this->cache->load($this->getIdentifier($name));
++
++ if ($data === $this->lockSign) {
++ return true;
++ }
++
++ return false;
+ }
+
+ /**
+@@ -39,7 +72,22 @@ class Cache implements \Magento\Framework\Lock\LockManagerInterface
+ */
+ public function unlock(string $name): bool
+ {
+- return $this->cache->remove($name);
++ if (empty($this->lockSign)) {
++ return false;
++ }
++
++ $data = $this->cache->load($this->getIdentifier($name));
++
++ if (false === $data) {
++ return false;
++ }
++
++ $removeResult = false;
++ if ($data === $this->lockSign) {
++ $removeResult = (bool)$this->cache->remove($this->getIdentifier($name));
++ }
++
++ return $removeResult;
+ }
+
+ /**
+@@ -47,6 +95,40 @@ class Cache implements \Magento\Framework\Lock\LockManagerInterface
+ */
+ public function isLocked(string $name): bool
+ {
+- return (bool)$this->cache->test($name);
++ return (bool)$this->cache->test($this->getIdentifier($name));
++ }
++
++ /**
++ * Get cache locked identifier based on cache identifier.
++ *
++ * @param string $cacheIdentifier
++ * @return string
++ */
++ private function getIdentifier(string $cacheIdentifier): string
++ {
++ return self::LOCK_PREFIX . $cacheIdentifier;
++ }
++
++ /**
++ * Function that generates lock sign that helps to avoid removing a lock that was created by another client.
++ *
++ * @return string
++ */
++ private function generateLockSign()
++ {
++ $sign = implode(
++ '-',
++ [
++ \getmypid(), \crc32(\gethostname())
++ ]
++ );
++
++ try {
++ $sign .= '-' . \bin2hex(\random_bytes(4));
++ } catch (\Exception $e) {
++ $sign .= '-' . \uniqid('-uniqid-');
++ }
++
++ return $sign;
+ }
+ }
+diff -Nuar a/vendor/magento/framework/View/Element/AbstractBlock.php b/vendor/magento/framework/View/Element/AbstractBlock.php
+--- a/vendor/magento/framework/View/Element/AbstractBlock.php
++++ b/vendor/magento/framework/View/Element/AbstractBlock.php
+@@ -3,8 +3,10 @@
+ * Copyright © Magento, Inc. All rights reserved.
+ * See COPYING.txt for license details.
+ */
++
+ namespace Magento\Framework\View\Element;
+
++use Magento\Framework\Cache\LockGuardedCacheLoader;
+ use Magento\Framework\DataObject\IdentityInterface;
+
+ /**
+@@ -14,6 +16,7 @@ use Magento\Framework\DataObject\IdentityInterface;
+ *
+ * Marked as public API because it is actively used now.
+ *
++ * phpcs:disable Magento2.Classes.AbstractApi
+ * @api
+ * @SuppressWarnings(PHPMD.ExcessivePublicCount)
+ * @SuppressWarnings(PHPMD.ExcessiveClassComplexity)
+@@ -52,6 +55,7 @@ abstract class AbstractBlock extends \Magento\Framework\DataObject implements Bl
+ * SID Resolver
+ *
+ * @var \Magento\Framework\Session\SidResolverInterface
++ * @deprecated 102.0.5 Not used anymore.
+ */
+ protected $_sidResolver;
+
+@@ -176,14 +180,21 @@ abstract class AbstractBlock extends \Magento\Framework\DataObject implements Bl
+ */
+ protected $_cache;
+
++ /**
++ * @var LockGuardedCacheLoader
++ */
++ private $lockQuery;
++
+ /**
+ * Constructor
+ *
+ * @param \Magento\Framework\View\Element\Context $context
+ * @param array $data
+ */
+- public function __construct(\Magento\Framework\View\Element\Context $context, array $data = [])
+- {
++ public function __construct(
++ \Magento\Framework\View\Element\Context $context,
++ array $data = []
++ ) {
+ $this->_request = $context->getRequest();
+ $this->_layout = $context->getLayout();
+ $this->_eventManager = $context->getEventManager();
+@@ -201,6 +212,7 @@ abstract class AbstractBlock extends \Magento\Framework\DataObject implements Bl
+ $this->filterManager = $context->getFilterManager();
+ $this->_localeDate = $context->getLocaleDate();
+ $this->inlineTranslation = $context->getInlineTranslation();
++ $this->lockQuery = $context->getLockGuardedCacheLoader();
+ if (isset($data['jsLayout'])) {
+ $this->jsLayout = $data['jsLayout'];
+ unset($data['jsLayout']);
+@@ -235,6 +247,7 @@ abstract class AbstractBlock extends \Magento\Framework\DataObject implements Bl
+ * Please override this one instead of overriding real __construct constructor
+ *
+ * @return void
++ * phpcs:disable Magento2.CodeAnalysis.EmptyBlock
+ */
+ protected function _construct()
+ {
+@@ -430,9 +443,9 @@ abstract class AbstractBlock extends \Magento\Framework\DataObject implements Bl
+ */
+ public function unsetCallChild($alias, $callback, $result, $params)
+ {
++ $args = func_get_args();
+ $child = $this->getChildBlock($alias);
+ if ($child) {
+- $args = func_get_args();
+ $alias = array_shift($args);
+ $callback = array_shift($args);
+ $result = (string)array_shift($args);
+@@ -659,19 +672,6 @@ abstract class AbstractBlock extends \Magento\Framework\DataObject implements Bl
+ }
+
+ $html = $this->_loadCache();
+- if ($html === false) {
+- if ($this->hasData('translate_inline')) {
+- $this->inlineTranslation->suspend($this->getData('translate_inline'));
+- }
+-
+- $this->_beforeToHtml();
+- $html = $this->_toHtml();
+- $this->_saveCache($html);
+-
+- if ($this->hasData('translate_inline')) {
+- $this->inlineTranslation->resume();
+- }
+- }
+ $html = $this->_afterToHtml($html);
+
+ /** @var \Magento\Framework\DataObject */
+@@ -680,10 +680,13 @@ abstract class AbstractBlock extends \Magento\Framework\DataObject implements Bl
+ 'html' => $html,
+ ]
+ );
+- $this->_eventManager->dispatch('view_block_abstract_to_html_after', [
+- 'block' => $this,
+- 'transport' => $transportObject
+- ]);
++ $this->_eventManager->dispatch(
++ 'view_block_abstract_to_html_after',
++ [
++ 'block' => $this,
++ 'transport' => $transportObject
++ ]
++ );
+ $html = $transportObject->getHtml();
+
+ return $html;
+@@ -726,7 +729,7 @@ abstract class AbstractBlock extends \Magento\Framework\DataObject implements Bl
+ */
+ public function getUiId($arg1 = null, $arg2 = null, $arg3 = null, $arg4 = null, $arg5 = null)
+ {
+- return ' data-ui-id="' . $this->getJsId($arg1, $arg2, $arg3, $arg4, $arg5) . '" ';
++ return ' data-ui-id="' . $this->escapeHtmlAttr($this->getJsId($arg1, $arg2, $arg3, $arg4, $arg5)) . '" ';
+ }
+
+ /**
+@@ -875,7 +878,7 @@ abstract class AbstractBlock extends \Magento\Framework\DataObject implements Bl
+ $namespace = substr(
+ $className,
+ 0,
+- strpos($className, '\\' . 'Block')
++ strpos($className, '\\' . 'Block' . '\\')
+ );
+ return str_replace('\\', '_', $namespace);
+ }
+@@ -973,8 +976,8 @@ abstract class AbstractBlock extends \Magento\Framework\DataObject implements Bl
+ *
+ * Use $addSlashes = false for escaping js that inside html attribute (onClick, onSubmit etc)
+ *
+- * @param string $data
+- * @param bool $addSlashes
++ * @param string $data
++ * @param bool $addSlashes
+ * @return string
+ * @deprecated 101.0.0
+ */
+@@ -1084,23 +1087,43 @@ abstract class AbstractBlock extends \Magento\Framework\DataObject implements Bl
+ /**
+ * Load block html from cache storage
+ *
+- * @return string|false
++ * @return string
+ */
+ protected function _loadCache()
+ {
++ $collectAction = function () {
++ if ($this->hasData('translate_inline')) {
++ $this->inlineTranslation->suspend($this->getData('translate_inline'));
++ }
++
++ $this->_beforeToHtml();
++ return $this->_toHtml();
++ };
++
+ if ($this->getCacheLifetime() === null || !$this->_cacheState->isEnabled(self::CACHE_GROUP)) {
+- return false;
+- }
+- $cacheKey = $this->getCacheKey();
+- $cacheData = $this->_cache->load($cacheKey);
+- if ($cacheData) {
+- $cacheData = str_replace(
+- $this->_getSidPlaceholder($cacheKey),
+- $this->_sidResolver->getSessionIdQueryParam($this->_session) . '=' . $this->_session->getSessionId(),
+- $cacheData
+- );
++ $html = $collectAction();
++ if ($this->hasData('translate_inline')) {
++ $this->inlineTranslation->resume();
++ }
++ return $html;
+ }
+- return $cacheData;
++ $loadAction = function () {
++ return $this->_cache->load($this->getCacheKey());
++ };
++
++ $saveAction = function ($data) {
++ $this->_saveCache($data);
++ if ($this->hasData('translate_inline')) {
++ $this->inlineTranslation->resume();
++ }
++ };
++
++ return (string)$this->lockQuery->lockedLoadData(
++ $this->getCacheKey(),
++ $loadAction,
++ $collectAction,
++ $saveAction
++ );
+ }
+
+ /**
+@@ -1115,11 +1138,6 @@ abstract class AbstractBlock extends \Magento\Framework\DataObject implements Bl
+ return false;
+ }
+ $cacheKey = $this->getCacheKey();
+- $data = str_replace(
+- $this->_sidResolver->getSessionIdQueryParam($this->_session) . '=' . $this->_session->getSessionId(),
+- $this->_getSidPlaceholder($cacheKey),
+- $data
+- );
+
+ $this->_cache->save($data, $cacheKey, array_unique($this->getCacheTags()), $this->getCacheLifetime());
+ return $this;
+diff -Nuar a/vendor/magento/framework/View/Element/Context.php b/vendor/magento/framework/View/Element/Context.php
+--- a/vendor/magento/framework/View/Element/Context.php
++++ b/vendor/magento/framework/View/Element/Context.php
+@@ -5,6 +5,9 @@
+ */
+ namespace Magento\Framework\View\Element;
+
++use Magento\Framework\Cache\LockGuardedCacheLoader;
++use Magento\Framework\App\ObjectManager;
++
+ /**
+ * Constructor modification point for Magento\Framework\View\Element\AbstractBlock.
+ *
+@@ -16,8 +19,7 @@ namespace Magento\Framework\View\Element;
+ * As Magento moves from inheritance-based APIs all such classes will be deprecated together with
+ * the classes they were introduced for.
+ *
+- * @SuppressWarnings(PHPMD.TooManyFields)
+- * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
++ * @SuppressWarnings(PHPMD)
+ *
+ * @api
+ * @since 100.0.2
+@@ -137,12 +139,16 @@ class Context implements \Magento\Framework\ObjectManager\ContextInterface
+ */
+ protected $inlineTranslation;
+
++ /**
++ * @var LockGuardedCacheLoader
++ */
++ private $lockQuery;
++
+ /**
+ * @param \Magento\Framework\App\RequestInterface $request
+ * @param \Magento\Framework\View\LayoutInterface $layout
+ * @param \Magento\Framework\Event\ManagerInterface $eventManager
+ * @param \Magento\Framework\UrlInterface $urlBuilder
+- * @param \Magento\Framework\TranslateInterface $translator
+ * @param \Magento\Framework\App\CacheInterface $cache
+ * @param \Magento\Framework\View\DesignInterface $design
+ * @param \Magento\Framework\Session\SessionManagerInterface $session
+@@ -156,6 +162,7 @@ class Context implements \Magento\Framework\ObjectManager\ContextInterface
+ * @param \Magento\Framework\Filter\FilterManager $filterManager
+ * @param \Magento\Framework\Stdlib\DateTime\TimezoneInterface $localeDate
+ * @param \Magento\Framework\Translate\Inline\StateInterface $inlineTranslation
++ * @param LockGuardedCacheLoader $lockQuery
+ *
+ * @SuppressWarnings(PHPMD.ExcessiveParameterList)
+ */
+@@ -176,7 +183,8 @@ class Context implements \Magento\Framework\ObjectManager\ContextInterface
+ \Magento\Framework\Escaper $escaper,
+ \Magento\Framework\Filter\FilterManager $filterManager,
+ \Magento\Framework\Stdlib\DateTime\TimezoneInterface $localeDate,
+- \Magento\Framework\Translate\Inline\StateInterface $inlineTranslation
++ \Magento\Framework\Translate\Inline\StateInterface $inlineTranslation,
++ LockGuardedCacheLoader $lockQuery = null
+ ) {
+ $this->_request = $request;
+ $this->_layout = $layout;
+@@ -195,6 +203,7 @@ class Context implements \Magento\Framework\ObjectManager\ContextInterface
+ $this->_filterManager = $filterManager;
+ $this->_localeDate = $localeDate;
+ $this->inlineTranslation = $inlineTranslation;
++ $this->lockQuery = $lockQuery ?: ObjectManager::getInstance()->get(LockGuardedCacheLoader::class);
+ }
+
+ /**
+@@ -358,10 +367,23 @@ class Context implements \Magento\Framework\ObjectManager\ContextInterface
+ }
+
+ /**
++ * Get locale date.
++ *
+ * @return \Magento\Framework\Stdlib\DateTime\TimezoneInterface
+ */
+ public function getLocaleDate()
+ {
+ return $this->_localeDate;
+ }
++
++ /**
++ * Lock guarded cache loader.
++ *
++ * @return LockGuardedCacheLoader
++ * @since 102.0.2
++ */
++ public function getLockGuardedCacheLoader()
++ {
++ return $this->lockQuery;
++ }
+ }
+
+diff -Nuar a/vendor/magento/module-eav/Model/Config.php b/vendor/magento/module-eav/Model/Config.php
+--- a/vendor/magento/module-eav/Model/Config.php
++++ b/vendor/magento/module-eav/Model/Config.php
+@@ -7,12 +7,20 @@ namespace Magento\Eav\Model;
+
+ use Magento\Eav\Model\Entity\Attribute\AbstractAttribute;
+ use Magento\Eav\Model\Entity\Type;
++use Magento\Eav\Model\ResourceModel\Attribute\DefaultEntityAttributes\ProviderInterface;
++use Magento\Framework\App\Config\ScopeConfigInterface;
+ use Magento\Framework\App\ObjectManager;
++use Magento\Framework\Exception\LocalizedException;
++use Magento\Framework\Model\AbstractModel;
+ use Magento\Framework\Serialize\SerializerInterface;
+
+ /**
++ * EAV config model.
++ *
+ * @api
++ * @SuppressWarnings(PHPMD.TooManyFields)
+ * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
++ * @SuppressWarnings(PHPMD.ExcessiveClassComplexity)
+ * @since 100.0.2
+ */
+ class Config
+@@ -25,6 +33,11 @@ class Config
+ const ATTRIBUTES_CODES_CACHE_ID = 'EAV_ENTITY_ATTRIBUTES_CODES';
+ /**#@-*/
+
++ /**
++ * Xml path to caching user defined eav attributes configuration.
++ */
++ private const XML_PATH_CACHE_USER_DEFINED_ATTRIBUTES = 'dev/caching/cache_user_defined_attributes';
++
+ /**#@-*/
+ protected $_entityTypeData;
+
+@@ -116,6 +129,11 @@ class Config
+ */
+ private $serializer;
+
++ /**
++ * @var ScopeConfigInterface
++ */
++ private $scopeConfig;
++
+ /**
+ * Cache of attributes per set
+ *
+@@ -123,13 +141,29 @@ class Config
+ */
+ private $attributesPerSet = [];
+
++ /**
++ * Is system attributes loaded flag.
++ *
++ * @var array
++ */
++ private $isSystemAttributesLoaded = [];
++
++ /**
++ * List of predefined system attributes for preload.
++ *
++ * @var array
++ */
++ private $attributesForPreload;
++
+ /**
+ * @param \Magento\Framework\App\CacheInterface $cache
+- * @param \Magento\Eav\Model\Entity\TypeFactory $entityTypeFactory
+- * @param \Magento\Eav\Model\ResourceModel\Entity\Type\CollectionFactory $entityTypeCollectionFactory
++ * @param Entity\TypeFactory $entityTypeFactory
++ * @param ResourceModel\Entity\Type\CollectionFactory $entityTypeCollectionFactory
+ * @param \Magento\Framework\App\Cache\StateInterface $cacheState
+ * @param \Magento\Framework\Validator\UniversalFactory $universalFactory
+- * @param SerializerInterface $serializer
++ * @param SerializerInterface|null $serializer
++ * @param ScopeConfigInterface|null $scopeConfig
++ * @param array $attributesForPreload
+ * @codeCoverageIgnore
+ */
+ public function __construct(
+@@ -138,7 +172,9 @@ class Config
+ \Magento\Eav\Model\ResourceModel\Entity\Type\CollectionFactory $entityTypeCollectionFactory,
+ \Magento\Framework\App\Cache\StateInterface $cacheState,
+ \Magento\Framework\Validator\UniversalFactory $universalFactory,
+- SerializerInterface $serializer = null
++ SerializerInterface $serializer = null,
++ ScopeConfigInterface $scopeConfig = null,
++ $attributesForPreload = []
+ ) {
+ $this->_cache = $cache;
+ $this->_entityTypeFactory = $entityTypeFactory;
+@@ -146,6 +182,8 @@ class Config
+ $this->_cacheState = $cacheState;
+ $this->_universalFactory = $universalFactory;
+ $this->serializer = $serializer ?: ObjectManager::getInstance()->get(SerializerInterface::class);
++ $this->scopeConfig = $scopeConfig ?: ObjectManager::getInstance()->get(ScopeConfigInterface::class);
++ $this->attributesForPreload = $attributesForPreload;
+ }
+
+ /**
+@@ -207,8 +245,8 @@ class Config
+ /**
+ * Associate object with identifier
+ *
+- * @param mixed $obj
+- * @param mixed $id
++ * @param mixed $obj
++ * @param mixed $id
+ * @return void
+ * @codeCoverageIgnore
+ */
+@@ -233,8 +271,8 @@ class Config
+ /**
+ * Specify reference for entity type id
+ *
+- * @param int $id
+- * @param string $code
++ * @param int $id
++ * @param string $code
+ * @return $this
+ * @codeCoverageIgnore
+ */
+@@ -258,9 +296,9 @@ class Config
+ /**
+ * Specify reference between entity attribute id and attribute code
+ *
+- * @param int $id
+- * @param string $code
+- * @param string $entityTypeCode
++ * @param int $id
++ * @param string $code
++ * @param string $entityTypeCode
+ * @return $this
+ */
+ protected function _addAttributeReference($id, $code, $entityTypeCode)
+@@ -336,7 +374,9 @@ class Config
+ }
+ \Magento\Framework\Profiler::start('EAV: ' . __METHOD__, ['group' => 'EAV', 'method' => __METHOD__]);
+
+- if ($this->isCacheEnabled() && ($cache = $this->_cache->load(self::ENTITIES_CACHE_ID))) {
++ if ($this->isCacheEnabled() &&
++ ($cache = $this->_cache->load(self::ENTITIES_CACHE_ID))
++ ) {
+ $this->_entityTypeData = $this->serializer->unserialize($cache);
+ foreach ($this->_entityTypeData as $typeCode => $data) {
+ $typeId = $data['entity_type_id'];
+@@ -484,10 +524,10 @@ class Config
+ /**
+ * Get attribute by code for entity type
+ *
+- * @param mixed $entityType
+- * @param mixed $code
+- * @return AbstractAttribute
+- * @throws \Magento\Framework\Exception\LocalizedException
++ * @param mixed $entityType
++ * @param mixed $code
++ * @return AbstractAttribute
++ * @throws LocalizedException
+ */
+ public function getAttribute($entityType, $code)
+ {
+@@ -507,8 +547,152 @@ class Config
+ return $this->attributes[$entityTypeCode][$code];
+ }
+
++ if (array_key_exists($entityTypeCode, $this->attributesForPreload)
++ && array_key_exists($code, $this->attributesForPreload[$entityTypeCode])
++ ) {
++ $this->initSystemAttributes($entityType, $this->attributesForPreload[$entityTypeCode]);
++ }
++ if (isset($this->attributes[$entityTypeCode][$code])) {
++ \Magento\Framework\Profiler::stop('EAV: ' . __METHOD__);
++ return $this->attributes[$entityTypeCode][$code];
++ }
++
++ if ($this->scopeConfig->getValue(self::XML_PATH_CACHE_USER_DEFINED_ATTRIBUTES)) {
++ $attribute = $this->cacheUserDefinedAttribute($entityType, $entityTypeCode, $code);
++ } else {
++ $attribute = $this->initUserDefinedAttribute($entityType, $entityTypeCode, $code);
++ }
++
++ \Magento\Framework\Profiler::stop('EAV: ' . __METHOD__);
++ return $attribute;
++ }
++
++ /**
++ * Initialize predefined system attributes for preload.
++ *
++ * @param string $entityType
++ * @param array $systemAttributes
++ * @return $this|bool|void
++ * @throws LocalizedException
++ */
++ private function initSystemAttributes($entityType, $systemAttributes)
++ {
++ $entityType = $this->getEntityType($entityType);
++ $entityTypeCode = $entityType->getEntityTypeCode();
++ if (!empty($this->isSystemAttributesLoaded[$entityTypeCode])) {
++ return;
++ }
++
++ $cacheKey = self::ATTRIBUTES_CACHE_ID . '-' . $entityTypeCode . '-preload';
++ if ($this->isCacheEnabled() && ($attributes = $this->_cache->load($cacheKey))) {
++ $attributes = $this->serializer->unserialize($attributes);
++ if ($attributes) {
++ foreach ($attributes as $attribute) {
++ $attributeObject = $this->_createAttribute($entityType, $attribute);
++ $this->saveAttribute($attributeObject, $entityTypeCode, $attributeObject->getAttributeCode());
++ }
++ return true;
++ }
++ }
++
++ \Magento\Framework\Profiler::start('EAV: ' . __METHOD__, ['group' => 'EAV', 'method' => __METHOD__]);
++
++ /** @var \Magento\Eav\Model\ResourceModel\Entity\Attribute\Collection $attributes */
++ $attributes = $this->_universalFactory->create(
++ $entityType->getEntityAttributeCollection()
++ )->setEntityTypeFilter(
++ $entityType
++ )->addFieldToFilter(
++ 'attribute_code',
++ ['in' => array_keys($systemAttributes)]
++ )->getData();
++
++ $attributeData = [];
++ foreach ($attributes as $attribute) {
++ if (empty($attribute['attribute_model'])) {
++ $attribute['attribute_model'] = $entityType->getAttributeModel();
++ }
++ $attributeObject = $this->_createAttribute($entityType, $attribute);
++ $this->saveAttribute($attributeObject, $entityTypeCode, $attributeObject->getAttributeCode());
++ $attributeData[$attribute['attribute_code']] = $attributeObject->toArray();
++ }
++ if ($this->isCacheEnabled()) {
++ $this->_cache->save(
++ $this->serializer->serialize($attributeData),
++ $cacheKey,
++ [
++ \Magento\Eav\Model\Cache\Type::CACHE_TAG,
++ \Magento\Eav\Model\Entity\Attribute::CACHE_TAG
++ ]
++ );
++ }
++
++ \Magento\Framework\Profiler::stop('EAV: ' . __METHOD__);
++ $this->isSystemAttributesLoaded[$entityTypeCode] = true;
++
++ return $this;
++ }
++
++ /**
++ * Initialize user defined attribute from cache or cache it.
++ *
++ * @param string $entityType
++ * @param mixed $entityTypeCode
++ * @param string $code
++ * @return AbstractAttribute
++ * @throws LocalizedException
++ */
++ private function cacheUserDefinedAttribute($entityType, $entityTypeCode, $code): AbstractAttribute
++ {
++ $cacheKey = self::ATTRIBUTES_CACHE_ID . '-attribute-' . $entityTypeCode . '-' . $code;
++ $attributeData = $this->isCacheEnabled() && ($attribute = $this->_cache->load($cacheKey))
++ ? $this->serializer->unserialize($attribute)
++ : null;
++ if ($attributeData) {
++ if (isset($attributeData['attribute_id'])) {
++ $attribute = $this->_createAttribute($entityType, $attributeData);
++ } else {
++ $entityType = $this->getEntityType($entityType);
++ $attribute = $this->createAttribute($entityType->getAttributeModel());
++ $attribute->setAttributeCode($code);
++ $attribute = $this->setAttributeData($attribute, $entityType);
++ }
++ } else {
++ $attribute = $this->createAttributeByAttributeCode($entityType, $code);
++ $this->_addAttributeReference(
++ $attribute->getAttributeId(),
++ $attribute->getAttributeCode(),
++ $entityTypeCode
++ );
++ $this->saveAttribute($attribute, $entityTypeCode, $attribute->getAttributeCode());
++ if ($this->isCacheEnabled()) {
++ $this->_cache->save(
++ $this->serializer->serialize($attribute->getData()),
++ $cacheKey,
++ [
++ \Magento\Eav\Model\Cache\Type::CACHE_TAG,
++ \Magento\Eav\Model\Entity\Attribute::CACHE_TAG
++ ]
++ );
++ }
++ }
++
++ return $attribute;
++ }
++
++ /**
++ * Initialize user defined attribute and save it to memory cache.
++ *
++ * @param mixed $entityType
++ * @param string $entityTypeCode
++ * @param string $code
++ * @return AbstractAttribute|null
++ * @throws LocalizedException
++ */
++ private function initUserDefinedAttribute($entityType, $entityTypeCode, $code): ?AbstractAttribute
++ {
+ $attributes = $this->loadAttributes($entityTypeCode);
+- $attribute = isset($attributes[$code]) ? $attributes[$code] : null;
++ $attribute = $attributes[$code] ?? null;
+ if (!$attribute) {
+ $attribute = $this->createAttributeByAttributeCode($entityType, $code);
+ $this->_addAttributeReference(
+@@ -518,7 +702,7 @@ class Config
+ );
+ $this->saveAttribute($attribute, $entityTypeCode, $attribute->getAttributeCode());
+ }
+- \Magento\Framework\Profiler::stop('EAV: ' . __METHOD__);
++
+ return $attribute;
+ }
+
+@@ -555,8 +739,8 @@ class Config
+ /**
+ * Get all entity type attributes
+ *
+- * @param int|string|Type $entityType
+- * @param \Magento\Framework\DataObject|null $object
++ * @param int|string|Type $entityType
++ * @param \Magento\Framework\DataObject|null $object
+ * @return AbstractAttribute[]
+ *
+ * @SuppressWarnings(PHPMD.CyclomaticComplexity)
+@@ -639,7 +823,11 @@ class Config
+ $existsFullAttribute = $attribute->hasIsRequired();
+ $fullAttributeData = array_key_exists('is_required', $attributeData);
+
+- if ($existsFullAttribute || !$existsFullAttribute && !$fullAttributeData) {
++ if ($existsFullAttribute || (!$existsFullAttribute && !$fullAttributeData)) {
++ $scopeIsRequired = $attributeData['scope_is_required'] ?? null;
++ if ($scopeIsRequired !== null) {
++ $attribute->setData('scope_is_required', $scopeIsRequired);
++ }
+ return $attribute;
+ }
+ }
+@@ -708,6 +896,7 @@ class Config
+ * @param string $entityType
+ * @param string $attributeCode
+ * @return AbstractAttribute
++ * @throws LocalizedException
+ */
+ private function createAttributeByAttributeCode($entityType, $attributeCode)
+ {
+@@ -723,13 +912,28 @@ class Config
+ $attribute->setAttributeCode($attributeCode);
+ }
+
++ $attribute = $this->setAttributeData($attribute, $entityType);
++
++ return $attribute;
++ }
++
++ /**
++ * Set entity type id, backend type, is global to attribute.
++ *
++ * @param AbstractAttribute $attribute
++ * @param AbstractModel $entityType
++ * @return AbstractAttribute
++ */
++ private function setAttributeData($attribute, $entityType): AbstractAttribute
++ {
+ $entity = $entityType->getEntity();
+- if ($entity instanceof \Magento\Eav\Model\ResourceModel\Attribute\DefaultEntityAttributes\ProviderInterface
++ if ($entity instanceof ProviderInterface
+ && in_array($attribute->getAttributeCode(), $entity->getDefaultAttributes(), true)
+ ) {
+ $attribute->setBackendType(AbstractAttribute::TYPE_STATIC)->setIsGlobal(1);
+ }
+ $attribute->setEntityType($entityType)->setEntityTypeId($entityType->getId());
++
+ return $attribute;
+ }
+
+diff -Nuar a/vendor/magento/module-eav/Model/ResourceModel/Entity/Attribute/Set.php b/vendor/magento/module-eav/Model/ResourceModel/Entity/Attribute/Set.php
+--- a/vendor/magento/module-eav/Model/ResourceModel/Entity/Attribute/Set.php
++++ b/vendor/magento/module-eav/Model/ResourceModel/Entity/Attribute/Set.php
+@@ -6,6 +6,9 @@
+
+ namespace Magento\Eav\Model\ResourceModel\Entity\Attribute;
+
++/**
++ * Basic implementation for attribute sets
++ */
+ class Set extends \Magento\Framework\Model\ResourceModel\Db\AbstractDb
+ {
+ /**
+@@ -24,8 +27,6 @@ class Set extends \Magento\Framework\Model\ResourceModel\Db\AbstractDb
+ protected $eavConfig;
+
+ /**
+- * Constructor
+- *
+ * @param \Magento\Framework\Model\ResourceModel\Db\Context $context
+ * @param GroupFactory $attrGroupFactory
+ * @param \Magento\Eav\Model\Config $eavConfig
+@@ -54,7 +55,7 @@ class Set extends \Magento\Framework\Model\ResourceModel\Db\AbstractDb
+ }
+
+ /**
+- * Perform actions after object save
++ * Perform actions after object save.
+ *
+ * @param \Magento\Framework\Model\AbstractModel $object
+ * @return $this
+diff -Nuar a/vendor/magento/module-eav/etc/di.xml b/vendor/magento/module-eav/etc/di.xml
+--- a/vendor/magento/module-eav/etc/di.xml
++++ b/vendor/magento/module-eav/etc/di.xml
+@@ -209,4 +209,14 @@
+
+
+
++
++
++ eav
++
++
++
++
++ configured_eav_cache
++
++
+
+diff -Nuar a/vendor/magento/module-theme/etc/di.xml b/vendor/magento/module-theme/etc/di.xml
+--- a/vendor/magento/module-theme/etc/di.xml
++++ b/vendor/magento/module-theme/etc/di.xml
+@@ -285,4 +285,24 @@
+ theme_id
+
+
++
++
++ layout
++
++
++
++
++ configured_design_cache
++
++
++
++
++ design_context
++
++
++
++
++ configured_design_cache
++
++
+
+diff -Nuar a/vendor/magento/framework/App/Cache.php b/vendor/magento/framework/App/Cache.php
+--- a/vendor/magento/framework/App/Cache.php
++++ b/vendor/magento/framework/App/Cache.php
+@@ -4,12 +4,11 @@
+ * See COPYING.txt for license details.
+ */
+
+-/**
+- * System cache model
+- * support id and tags prefix support,
+- */
+ namespace Magento\Framework\App;
+
++/**
++ * System cache model support id and tags prefix support.
++ */
+ class Cache implements CacheInterface
+ {
+ /**
+@@ -30,12 +29,13 @@ class Cache implements CacheInterface
+ protected $_frontend;
+
+ /**
+- * @param \Magento\Framework\App\Cache\Frontend\Pool $frontendPool
++ * @param Cache\Frontend\Pool $frontendPool
++ * @param string|null $cacheIdentifier
+ */
+- public function __construct(\Magento\Framework\App\Cache\Frontend\Pool $frontendPool)
++ public function __construct(\Magento\Framework\App\Cache\Frontend\Pool $frontendPool, $cacheIdentifier = null)
+ {
+ $this->_frontendPool = $frontendPool;
+- $this->_frontend = $frontendPool->get($this->_frontendIdentifier);
++ $this->_frontend = $frontendPool->get($cacheIdentifier ?? $this->_frontendIdentifier);
+ }
+
+ /**
+diff -Nuar a/vendor/magento/framework/App/Cache/Frontend/Pool.php b/vendor/magento/framework/App/Cache/Frontend/Pool.php
+--- a/vendor/magento/framework/App/Cache/Frontend/Pool.php
++++ b/vendor/magento/framework/App/Cache/Frontend/Pool.php
+@@ -152,6 +152,15 @@ class Pool implements \Iterator
+ if (isset($this->_instances[$identifier])) {
+ return $this->_instances[$identifier];
+ }
+- throw new \InvalidArgumentException("Cache frontend '{$identifier}' is not recognized.");
++
++ if (!isset($this->_instances[self::DEFAULT_FRONTEND_ID])) {
++ throw new \InvalidArgumentException(
++ "Cache frontend '{$identifier}' is not recognized. As well as " .
++ self::DEFAULT_FRONTEND_ID .
++ "cache is not configured"
++ );
++ }
++
++ return $this->_instances[self::DEFAULT_FRONTEND_ID];
+ }
+ }
+diff -Nuar a/vendor/magento/framework/App/Router/ActionList.php b/vendor/magento/framework/App/Router/ActionList.php
+--- a/vendor/magento/framework/App/Router/ActionList.php
++++ b/vendor/magento/framework/App/Router/ActionList.php
+@@ -5,6 +5,8 @@
+ */
+ namespace Magento\Framework\App\Router;
+
++use Magento\Framework\App\Filesystem\DirectoryList;
++use Magento\Framework\App\State;
+ use Magento\Framework\Serialize\SerializerInterface;
+ use Magento\Framework\Serialize\Serializer\Serialize;
+ use Magento\Framework\Module\Dir\Reader as ModuleReader;
+@@ -70,12 +72,26 @@ class ActionList
+ $this->reservedWords = array_merge($reservedWords, $this->reservedWords);
+ $this->actionInterface = $actionInterface;
+ $this->serializer = $serializer ?: \Magento\Framework\App\ObjectManager::getInstance()->get(Serialize::class);
+- $data = $cache->load($cacheKey);
+- if (!$data) {
+- $this->actions = $moduleReader->getActionFiles();
+- $cache->save($this->serializer->serialize($this->actions), $cacheKey);
++ $objectManager = \Magento\Framework\App\ObjectManager::getInstance();
++ $state = $objectManager->get(State::class);
++
++ if ($state->getMode() === State::MODE_PRODUCTION) {
++ $directoryList = $objectManager->get(DirectoryList::class);
++ $file = $directoryList->getPath(DirectoryList::GENERATED_METADATA) . '/' . $cacheKey . '.' . 'php';
++
++ if (file_exists($file)) {
++ $this->actions = (include $file) ?? $moduleReader->getActionFiles();
++ } else {
++ $this->actions = $moduleReader->getActionFiles();
++ }
+ } else {
+- $this->actions = $this->serializer->unserialize($data);
++ $data = $cache->load($cacheKey);
++ if (!$data) {
++ $this->actions = $moduleReader->getActionFiles();
++ $cache->save($this->serializer->serialize($this->actions), $cacheKey);
++ } else {
++ $this->actions = $this->serializer->unserialize($data);
++ }
+ }
+ }
+
+diff -Nuar a/2.3.5/vendor/magento/framework/Cache/Backend/Redis.php b/vendor/magento/framework/Cache/Backend/Redis.php
+new file mode 100644
+--- /dev/null
++++ b/vendor/magento/framework/Cache/Backend/Redis.php
+@@ -0,0 +1,83 @@
++preloadKeys = $options['preload_keys'] ?? [];
++ parent::__construct($options);
++ }
++
++ /**
++ * Load value with given id from cache
++ *
++ * @param string $id Cache id
++ * @param boolean $doNotTestCacheValidity If set to true, the cache validity won't be tested
++ * @return bool|string
++ */
++ public function load($id, $doNotTestCacheValidity = false)
++ {
++ if (!empty($this->preloadKeys) && empty($this->preloadedData)) {
++ $redis = $this->_slave ?? $this->_redis;
++ $redis = $redis->pipeline();
++
++ foreach ($this->preloadKeys as $key) {
++ $redis->hGet(self::PREFIX_KEY . $key, self::FIELD_DATA);
++ }
++
++ $this->preloadedData = array_filter(array_combine($this->preloadKeys, $redis->exec()));
++ }
++
++ if (isset($this->preloadedData[$id])) {
++ return $this->_decodeData($this->preloadedData[$id]);
++ }
++
++ return parent::load($id, $doNotTestCacheValidity);
++ }
++
++ /**
++ * Cover errors on save operations, which may occurs when Redis cannot evict keys, which is expected in some cases.
++ *
++ * @param string $data
++ * @param string $id
++ * @param array $tags
++ * @param bool $specificLifetime
++ * @return bool
++ */
++ public function save($data, $id, $tags = [], $specificLifetime = false)
++ {
++ try {
++ parent::save($data, $id, $tags, $specificLifetime);
++ } catch (\Throwable $exception) {
++ return false;
++ }
++
++ return true;
++ }
++}
+diff -Nuar a/vendor/magento/framework/Cache/Backend/RemoteSynchronizedCache.php b/vendor/magento/framework/Cache/Backend/RemoteSynchronizedCache.php
+--- a/vendor/magento/framework/Cache/Backend/RemoteSynchronizedCache.php
++++ b/vendor/magento/framework/Cache/Backend/RemoteSynchronizedCache.php
+@@ -9,8 +9,10 @@ namespace Magento\Framework\Cache\Backend;
+ /**
+ * Remote synchronized cache
+ *
+- * This class created for correct work local caches with multiple web nodes,
+- * that will be check cache status from remote cache
++ * This class created for correct work witch local caches and multiple web nodes,
++ * in order to be sure that we always have up to date local version of cache.
++ * This class will be check cache version from remote cache and in case it newer
++ * than local one, it will update local one from remote cache a.k.a two level cache.
+ */
+ class RemoteSynchronizedCache extends \Zend_Cache_Backend implements \Zend_Cache_Backend_ExtendedInterface
+ {
+@@ -36,11 +38,15 @@ class RemoteSynchronizedCache extends \Zend_Cache_Backend implements \Zend_Cache
+ protected $cacheInvalidationTime;
+
+ /**
+- * {@inheritdoc}
++ * Suffix for hash to compare data version in cache storage.
++ */
++ private const HASH_SUFFIX = ':hash';
++
++ /**
++ * @inheritdoc
+ */
+ protected $_options = [
+ 'remote_backend' => '',
+- 'remote_backend_invalidation_time_id' => 'default_remote_backend_invalidation_time',
+ 'remote_backend_custom_naming' => true,
+ 'remote_backend_autoload' => true,
+ 'remote_backend_options' => [],
+@@ -52,6 +58,7 @@ class RemoteSynchronizedCache extends \Zend_Cache_Backend implements \Zend_Cache
+
+ /**
+ * @param array $options
++ * @throws \Zend_Cache_Exception
+ */
+ public function __construct(array $options = [])
+ {
+@@ -97,76 +104,137 @@ class RemoteSynchronizedCache extends \Zend_Cache_Backend implements \Zend_Cache
+ }
+
+ /**
+- * Update remote cache status info
++ * @inheritdoc
++ */
++ public function setDirectives($directives)
++ {
++ return $this->local->setDirectives($directives);
++ }
++
++ /**
++ * Return hash sign of the data.
+ *
+- * @return void
++ * @param string $data
++ * @return string
+ */
+- private function updateRemoteCacheStatusInfo()
++ private function getDataVersion(string $data)
+ {
+- $this->remote->save(time(), $this->_options['remote_backend_invalidation_time_id'], [], null);
+- $this->cacheInvalidationTime = null;
++ return \hash('sha256', $data);
+ }
+
+ /**
+- * {@inheritdoc}
++ * Load data version by id from remote.
++ *
++ * @param string $id
++ * @return false|string
+ */
+- public function setDirectives($directives)
++ private function loadRemoteDataVersion(string $id)
+ {
+- return $this->local->setDirectives($directives);
++ return $this->remote->load(
++ $id . self::HASH_SUFFIX
++ );
+ }
+
+ /**
+- * {@inheritdoc}
++ * Save new data version to remote.
++ *
++ * @param string $data
++ * @param string $id
++ * @param array $tags
++ * @param mixed $specificLifetime
++ * @return bool
++ */
++ private function saveRemoteDataVersion(string $data, string $id, array $tags, $specificLifetime = false)
++ {
++ return $this->remote->save($this->getDataVersion($data), $id . self::HASH_SUFFIX, $tags, $specificLifetime);
++ }
++
++ /**
++ * Remove remote data version.
++ *
++ * @param string $id
++ * @return bool
++ */
++ private function removeRemoteDataVersion($id)
++ {
++ return $this->remote->remove($id . self::HASH_SUFFIX);
++ }
++
++ /**
++ * @inheritdoc
+ */
+ public function load($id, $doNotTestCacheValidity = false)
+ {
+- $dataModificationTime = $this->local->test($id);
+- if ($this->cacheInvalidationTime === null) {
+- $this->cacheInvalidationTime = $this->remote->load($this->_options['remote_backend_invalidation_time_id']);
+- }
+- if ($dataModificationTime >= $this->cacheInvalidationTime) {
+- return $this->local->load($id, $doNotTestCacheValidity);
++ $localData = $this->local->load($id);
++ $remoteData = false;
++
++ if (false === $localData) {
++ $remoteData = $this->remote->load($id);
++
++ if (false === $remoteData) {
++ return false;
++ }
+ } else {
+- return false;
++ if ($this->getDataVersion($localData) !== $this->loadRemoteDataVersion($id)) {
++ $localData = false;
++ $remoteData = $this->remote->load($id);
++ }
+ }
++
++ if ($remoteData !== false) {
++ $this->local->save($remoteData, $id);
++ $localData = $remoteData;
++ }
++
++ return $localData;
+ }
+
+ /**
+- * {@inheritdoc}
++ * @inheritdoc
+ */
+ public function test($id)
+ {
+- return $this->local->test($id);
++ return $this->local->test($id) ?? $this->remote->test($id);
+ }
+
+ /**
+- * {@inheritdoc}
++ * @inheritdoc
+ */
+ public function save($data, $id, $tags = [], $specificLifetime = false)
+ {
+- return $this->local->save($data, $id, $tags, $specificLifetime);
++ $dataToSave = $data;
++ $remHash = $this->loadRemoteDataVersion($id);
++
++ if ($remHash !== false) {
++ $dataToSave = $this->remote->load($id);
++ } else {
++ $this->remote->save($data, $id, $tags, $specificLifetime);
++ $this->saveRemoteDataVersion($data, $id, $tags, $specificLifetime);
++ }
++
++ return $this->local->save($dataToSave, $id, [], $specificLifetime);
+ }
+
+ /**
+- * {@inheritdoc}
++ * @inheritdoc
+ */
+ public function remove($id)
+ {
+- $this->updateRemoteCacheStatusInfo();
+- return $this->local->remove($id);
++ return $this->removeRemoteDataVersion($id) &&
++ $this->remote->remove($id) &&
++ $this->local->remove($id);
+ }
+
+ /**
+- * {@inheritdoc}
++ * @inheritdoc
+ */
+ public function clean($mode = \Zend_Cache::CLEANING_MODE_ALL, $tags = [])
+ {
+- $this->updateRemoteCacheStatusInfo();
+- return $this->local->clean($mode, $tags);
++ return $this->remote->clean($mode, $tags);
+ }
+
+ /**
+- * {@inheritdoc}
++ * @inheritdoc
+ */
+ public function getIds()
+ {
+@@ -174,7 +242,7 @@ class RemoteSynchronizedCache extends \Zend_Cache_Backend implements \Zend_Cache
+ }
+
+ /**
+- * {@inheritdoc}
++ * @inheritdoc
+ */
+ public function getTags()
+ {
+@@ -182,7 +250,7 @@ class RemoteSynchronizedCache extends \Zend_Cache_Backend implements \Zend_Cache
+ }
+
+ /**
+- * {@inheritdoc}
++ * @inheritdoc
+ */
+ public function getIdsMatchingTags($tags = [])
+ {
+@@ -190,7 +258,7 @@ class RemoteSynchronizedCache extends \Zend_Cache_Backend implements \Zend_Cache
+ }
+
+ /**
+- * {@inheritdoc}
++ * @inheritdoc
+ */
+ public function getIdsNotMatchingTags($tags = [])
+ {
+@@ -198,7 +266,7 @@ class RemoteSynchronizedCache extends \Zend_Cache_Backend implements \Zend_Cache
+ }
+
+ /**
+- * {@inheritdoc}
++ * @inheritdoc
+ */
+ public function getIdsMatchingAnyTags($tags = [])
+ {
+@@ -206,7 +274,7 @@ class RemoteSynchronizedCache extends \Zend_Cache_Backend implements \Zend_Cache
+ }
+
+ /**
+- * {@inheritdoc}
++ * @inheritdoc
+ */
+ public function getFillingPercentage()
+ {
+@@ -214,7 +282,7 @@ class RemoteSynchronizedCache extends \Zend_Cache_Backend implements \Zend_Cache
+ }
+
+ /**
+- * {@inheritdoc}
++ * @inheritdoc
+ */
+ public function getMetadatas($id)
+ {
+@@ -222,7 +290,7 @@ class RemoteSynchronizedCache extends \Zend_Cache_Backend implements \Zend_Cache
+ }
+
+ /**
+- * {@inheritdoc}
++ * @inheritdoc
+ */
+ public function touch($id, $extraLifetime)
+ {
+@@ -230,7 +298,7 @@ class RemoteSynchronizedCache extends \Zend_Cache_Backend implements \Zend_Cache
+ }
+
+ /**
+- * {@inheritdoc}
++ * @inheritdoc
+ */
+ public function getCapabilities()
+ {
+diff -Nuar a/vendor/magento/framework/Interception/Config/Config.php b/vendor/magento/framework/Interception/Config/Config.php
+--- a/vendor/magento/framework/Interception/Config/Config.php
++++ b/vendor/magento/framework/Interception/Config/Config.php
+@@ -187,8 +187,6 @@ class Config implements \Magento\Framework\Interception\ConfigInterface
+ */
+ private function initializeUncompiled($classDefinitions = [])
+ {
+- $this->cacheManager->clean($this->_cacheId);
+-
+ $this->generateIntercepted($classDefinitions);
+
+ $this->cacheManager->save($this->_cacheId, $this->_intercepted);
+diff -Nuar a/setup/src/Magento/Setup/Console/Command/DiCompileCommand.php b/setup/src/Magento/Setup/Console/Command/DiCompileCommand.php
+--- a/setup/src/Magento/Setup/Console/Command/DiCompileCommand.php
++++ b/setup/src/Magento/Setup/Console/Command/DiCompileCommand.php
+@@ -260,9 +260,12 @@ class DiCompileCommand extends Command
+ */
+ private function getExcludedLibraryPaths(array $libraryPaths)
+ {
+- $libraryPaths = array_map(function ($libraryPath) {
+- return preg_quote($libraryPath, '#');
+- }, $libraryPaths);
++ $libraryPaths = array_map(
++ function ($libraryPath) {
++ return preg_quote($libraryPath, '#');
++ },
++ $libraryPaths
++ );
+
+ $excludedLibraryPaths = [
+ '#^(?:' . join('|', $libraryPaths) . ')/([\\w]+/)?Test#',
+@@ -395,7 +398,8 @@ class DiCompileCommand extends Command
+ $compiledPathsList['application'],
+ $compiledPathsList['library'],
+ $compiledPathsList['generated_helpers'],
+- ]
++ ],
++ OperationFactory::APPLICATION_ACTION_LIST_GENERATOR => [],
+ ];
+
+ return $operations;
+diff -Nuar a/2.3.5/setup/src/Magento/Setup/Module/Di/App/Task/Operation/AppActionListGenerator.php b/setup/src/Magento/Setup/Module/Di/App/Task/Operation/AppActionListGenerator.php
+new file mode 100644
+--- /dev/null
++++ b/setup/src/Magento/Setup/Module/Di/App/Task/Operation/AppActionListGenerator.php
+@@ -0,0 +1,58 @@
++moduleReader = $moduleReader;
++ $this->configWriter = $configWriter;
++ }
++
++ /**
++ * @inheritDoc
++ */
++ public function doOperation()
++ {
++ $actionList = $this->moduleReader->getActionFiles();
++ $this->configWriter->write(
++ 'app_action_list',
++ $actionList
++ );
++ }
++
++ /**
++ * @inheritDoc
++ */
++ public function getName()
++ {
++ return 'App action list generation';
++ }
++}
+diff -Nuar a/setup/src/Magento/Setup/Module/Di/App/Task/OperationFactory.php b/setup/src/Magento/Setup/Module/Di/App/Task/OperationFactory.php
+--- a/setup/src/Magento/Setup/Module/Di/App/Task/OperationFactory.php
++++ b/setup/src/Magento/Setup/Module/Di/App/Task/OperationFactory.php
+@@ -5,6 +5,12 @@
+ */
+ namespace Magento\Setup\Module\Di\App\Task;
+
++use Magento\Setup\Module\Di\App\Task\Operation\AppActionListGenerator;
++use Magento\Setup\Module\Di\App\Task\Operation\PluginListGenerator;
++
++/**
++ * Factory that creates list of OperationInterface classes
++ */
+ class OperationFactory
+ {
+ /**
+@@ -47,6 +53,11 @@ class OperationFactory
+ */
+ const APPLICATION_CODE_GENERATOR = 'application_code_generator';
+
++ /**
++ * Application action list generator
++ */
++ const APPLICATION_ACTION_LIST_GENERATOR = 'application_action_list_generator';
++
+ /**
+ * Operations definitions
+ *
+@@ -61,6 +72,7 @@ class OperationFactory
+ self::INTERCEPTION_CACHE => \Magento\Setup\Module\Di\App\Task\Operation\InterceptionCache::class,
+ self::REPOSITORY_GENERATOR => \Magento\Setup\Module\Di\App\Task\Operation\RepositoryGenerator::class,
+ self::PROXY_GENERATOR => \Magento\Setup\Module\Di\App\Task\Operation\ProxyGenerator::class,
++ self::APPLICATION_ACTION_LIST_GENERATOR => AppActionListGenerator::class,
+ ];
+
+ /**
+
+diff -Nuar a/vendor/magento/module-catalog/Model/Product.php b/vendor/magento/module-catalog/Model/Product.php
+--- a/vendor/magento/module-catalog/Model/Product.php
++++ b/vendor/magento/module-catalog/Model/Product.php
+@@ -72,9 +72,9 @@ class Product extends \Magento\Catalog\Model\AbstractModel implements
+ const STORE_ID = 'store_id';
+
+ /**
+- * @var string
++ * @var string|bool
+ */
+- protected $_cacheTag = self::CACHE_TAG;
++ protected $_cacheTag = false;
+
+ /**
+ * @var string
+@@ -868,7 +868,6 @@ class Product extends \Magento\Catalog\Model\AbstractModel implements
+ */
+ public function beforeSave()
+ {
+- $this->cleanCache();
+ $this->setTypeHasOptions(false);
+ $this->setTypeHasRequiredOptions(false);
+ $this->setHasOptions(false);
+diff -Nuar a/setup/src/Magento/Setup/Model/ConfigOptionsList/PageCache.php b/setup/src/Magento/Setup/Model/ConfigOptionsList/PageCache.php
+--- a/setup/src/Magento/Setup/Model/ConfigOptionsList/PageCache.php
++++ b/setup/src/Magento/Setup/Model/ConfigOptionsList/PageCache.php
+@@ -20,7 +20,7 @@ use Magento\Setup\Validator\RedisConnectionValidator;
+ class PageCache implements ConfigOptionsListInterface
+ {
+ const INPUT_VALUE_PAGE_CACHE_REDIS = 'redis';
+- const CONFIG_VALUE_PAGE_CACHE_REDIS = 'Cm_Cache_Backend_Redis';
++ const CONFIG_VALUE_PAGE_CACHE_REDIS = \Magento\Framework\Cache\Backend\Redis::class;
+
+ const INPUT_KEY_PAGE_CACHE_BACKEND = 'page-cache';
+ const INPUT_KEY_PAGE_CACHE_BACKEND_REDIS_SERVER = 'page-cache-redis-server';
+@@ -273,6 +273,6 @@ class PageCache implements ConfigOptionsListInterface
+ */
+ private function generateCachePrefix(): string
+ {
+- return substr(\md5(dirname(__DIR__, 6)), 0, 3) . '_';
++ return substr(\hash('sha256', dirname(__DIR__, 6)), 0, 3) . '_';
+ }
+ }
+diff -Nuar a/setup/src/Magento/Setup/Model/ConfigOptionsList/Cache.php b/setup/src/Magento/Setup/Model/ConfigOptionsList/Cache.php
+--- a/setup/src/Magento/Setup/Model/ConfigOptionsList/Cache.php
++++ b/setup/src/Magento/Setup/Model/ConfigOptionsList/Cache.php
+@@ -20,7 +20,7 @@ use Magento\Setup\Validator\RedisConnectionValidator;
+ class Cache implements ConfigOptionsListInterface
+ {
+ const INPUT_VALUE_CACHE_REDIS = 'redis';
+- const CONFIG_VALUE_CACHE_REDIS = 'Cm_Cache_Backend_Redis';
++ const CONFIG_VALUE_CACHE_REDIS = \Magento\Framework\Cache\Backend\Redis::class;
+
+ const INPUT_KEY_CACHE_BACKEND = 'cache-backend';
+ const INPUT_KEY_CACHE_BACKEND_REDIS_SERVER = 'cache-backend-redis-server';
+@@ -262,6 +262,6 @@ class Cache implements ConfigOptionsListInterface
+ */
+ private function generateCachePrefix(): string
+ {
+- return substr(\md5(dirname(__DIR__, 6)), 0, 3) . '_';
++ return substr(\hash('sha256', dirname(__DIR__, 6)), 0, 3) . '_';
+ }
+ }
diff --git a/patches/MCLOUD-6139_MCLOUD-6211__redis_improvement_patches__2.3.2.patch b/patches/MCLOUD-6139_MCLOUD-6211__redis_improvement_patches__2.3.2.patch
new file mode 100644
index 0000000..cafc156
--- /dev/null
+++ b/patches/MCLOUD-6139_MCLOUD-6211__redis_improvement_patches__2.3.2.patch
@@ -0,0 +1,1486 @@
+diff -Nuar a/vendor/magento/module-eav/Model/Config.php b/vendor/magento/module-eav/Model/Config.php
+--- a/vendor/magento/module-eav/Model/Config.php
++++ b/vendor/magento/module-eav/Model/Config.php
+@@ -7,12 +7,20 @@ namespace Magento\Eav\Model;
+
+ use Magento\Eav\Model\Entity\Attribute\AbstractAttribute;
+ use Magento\Eav\Model\Entity\Type;
++use Magento\Eav\Model\ResourceModel\Attribute\DefaultEntityAttributes\ProviderInterface;
++use Magento\Framework\App\Config\ScopeConfigInterface;
+ use Magento\Framework\App\ObjectManager;
++use Magento\Framework\Exception\LocalizedException;
++use Magento\Framework\Model\AbstractModel;
+ use Magento\Framework\Serialize\SerializerInterface;
+
+ /**
++ * EAV config model.
++ *
+ * @api
++ * @SuppressWarnings(PHPMD.TooManyFields)
+ * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
++ * @SuppressWarnings(PHPMD.ExcessiveClassComplexity)
+ * @since 100.0.2
+ */
+ class Config
+@@ -25,6 +33,11 @@ class Config
+ const ATTRIBUTES_CODES_CACHE_ID = 'EAV_ENTITY_ATTRIBUTES_CODES';
+ /**#@-*/
+
++ /**
++ * Xml path to caching user defined eav attributes configuration.
++ */
++ private const XML_PATH_CACHE_USER_DEFINED_ATTRIBUTES = 'dev/caching/cache_user_defined_attributes';
++
+ /**#@-*/
+ protected $_entityTypeData;
+
+@@ -116,6 +129,11 @@ class Config
+ */
+ private $serializer;
+
++ /**
++ * @var ScopeConfigInterface
++ */
++ private $scopeConfig;
++
+ /**
+ * Cache of attributes per set
+ *
+@@ -123,13 +141,29 @@ class Config
+ */
+ private $attributesPerSet = [];
+
++ /**
++ * Is system attributes loaded flag.
++ *
++ * @var array
++ */
++ private $isSystemAttributesLoaded = [];
++
++ /**
++ * List of predefined system attributes for preload.
++ *
++ * @var array
++ */
++ private $attributesForPreload;
++
+ /**
+ * @param \Magento\Framework\App\CacheInterface $cache
+- * @param \Magento\Eav\Model\Entity\TypeFactory $entityTypeFactory
+- * @param \Magento\Eav\Model\ResourceModel\Entity\Type\CollectionFactory $entityTypeCollectionFactory
++ * @param Entity\TypeFactory $entityTypeFactory
++ * @param ResourceModel\Entity\Type\CollectionFactory $entityTypeCollectionFactory
+ * @param \Magento\Framework\App\Cache\StateInterface $cacheState
+ * @param \Magento\Framework\Validator\UniversalFactory $universalFactory
+- * @param SerializerInterface $serializer
++ * @param SerializerInterface|null $serializer
++ * @param ScopeConfigInterface|null $scopeConfig
++ * @param array $attributesForPreload
+ * @codeCoverageIgnore
+ */
+ public function __construct(
+@@ -138,7 +172,9 @@ class Config
+ \Magento\Eav\Model\ResourceModel\Entity\Type\CollectionFactory $entityTypeCollectionFactory,
+ \Magento\Framework\App\Cache\StateInterface $cacheState,
+ \Magento\Framework\Validator\UniversalFactory $universalFactory,
+- SerializerInterface $serializer = null
++ SerializerInterface $serializer = null,
++ ScopeConfigInterface $scopeConfig = null,
++ $attributesForPreload = []
+ ) {
+ $this->_cache = $cache;
+ $this->_entityTypeFactory = $entityTypeFactory;
+@@ -146,6 +182,8 @@ class Config
+ $this->_cacheState = $cacheState;
+ $this->_universalFactory = $universalFactory;
+ $this->serializer = $serializer ?: ObjectManager::getInstance()->get(SerializerInterface::class);
++ $this->scopeConfig = $scopeConfig ?: ObjectManager::getInstance()->get(ScopeConfigInterface::class);
++ $this->attributesForPreload = $attributesForPreload;
+ }
+
+ /**
+@@ -207,8 +245,8 @@ class Config
+ /**
+ * Associate object with identifier
+ *
+- * @param mixed $obj
+- * @param mixed $id
++ * @param mixed $obj
++ * @param mixed $id
+ * @return void
+ * @codeCoverageIgnore
+ */
+@@ -233,8 +271,8 @@ class Config
+ /**
+ * Specify reference for entity type id
+ *
+- * @param int $id
+- * @param string $code
++ * @param int $id
++ * @param string $code
+ * @return $this
+ * @codeCoverageIgnore
+ */
+@@ -258,9 +296,9 @@ class Config
+ /**
+ * Specify reference between entity attribute id and attribute code
+ *
+- * @param int $id
+- * @param string $code
+- * @param string $entityTypeCode
++ * @param int $id
++ * @param string $code
++ * @param string $entityTypeCode
+ * @return $this
+ */
+ protected function _addAttributeReference($id, $code, $entityTypeCode)
+@@ -336,7 +374,9 @@ class Config
+ }
+ \Magento\Framework\Profiler::start('EAV: ' . __METHOD__, ['group' => 'EAV', 'method' => __METHOD__]);
+
+- if ($this->isCacheEnabled() && ($cache = $this->_cache->load(self::ENTITIES_CACHE_ID))) {
++ if ($this->isCacheEnabled() &&
++ ($cache = $this->_cache->load(self::ENTITIES_CACHE_ID))
++ ) {
+ $this->_entityTypeData = $this->serializer->unserialize($cache);
+ foreach ($this->_entityTypeData as $typeCode => $data) {
+ $typeId = $data['entity_type_id'];
+@@ -484,10 +524,10 @@ class Config
+ /**
+ * Get attribute by code for entity type
+ *
+- * @param mixed $entityType
+- * @param mixed $code
+- * @return AbstractAttribute
+- * @throws \Magento\Framework\Exception\LocalizedException
++ * @param mixed $entityType
++ * @param mixed $code
++ * @return AbstractAttribute
++ * @throws LocalizedException
+ */
+ public function getAttribute($entityType, $code)
+ {
+@@ -507,8 +547,152 @@ class Config
+ return $this->attributes[$entityTypeCode][$code];
+ }
+
++ if (array_key_exists($entityTypeCode, $this->attributesForPreload)
++ && array_key_exists($code, $this->attributesForPreload[$entityTypeCode])
++ ) {
++ $this->initSystemAttributes($entityType, $this->attributesForPreload[$entityTypeCode]);
++ }
++ if (isset($this->attributes[$entityTypeCode][$code])) {
++ \Magento\Framework\Profiler::stop('EAV: ' . __METHOD__);
++ return $this->attributes[$entityTypeCode][$code];
++ }
++
++ if ($this->scopeConfig->getValue(self::XML_PATH_CACHE_USER_DEFINED_ATTRIBUTES)) {
++ $attribute = $this->cacheUserDefinedAttribute($entityType, $entityTypeCode, $code);
++ } else {
++ $attribute = $this->initUserDefinedAttribute($entityType, $entityTypeCode, $code);
++ }
++
++ \Magento\Framework\Profiler::stop('EAV: ' . __METHOD__);
++ return $attribute;
++ }
++
++ /**
++ * Initialize predefined system attributes for preload.
++ *
++ * @param string $entityType
++ * @param array $systemAttributes
++ * @return $this|bool|void
++ * @throws LocalizedException
++ */
++ private function initSystemAttributes($entityType, $systemAttributes)
++ {
++ $entityType = $this->getEntityType($entityType);
++ $entityTypeCode = $entityType->getEntityTypeCode();
++ if (!empty($this->isSystemAttributesLoaded[$entityTypeCode])) {
++ return;
++ }
++
++ $cacheKey = self::ATTRIBUTES_CACHE_ID . '-' . $entityTypeCode . '-preload';
++ if ($this->isCacheEnabled() && ($attributes = $this->_cache->load($cacheKey))) {
++ $attributes = $this->serializer->unserialize($attributes);
++ if ($attributes) {
++ foreach ($attributes as $attribute) {
++ $attributeObject = $this->_createAttribute($entityType, $attribute);
++ $this->saveAttribute($attributeObject, $entityTypeCode, $attributeObject->getAttributeCode());
++ }
++ return true;
++ }
++ }
++
++ \Magento\Framework\Profiler::start('EAV: ' . __METHOD__, ['group' => 'EAV', 'method' => __METHOD__]);
++
++ /** @var \Magento\Eav\Model\ResourceModel\Entity\Attribute\Collection $attributes */
++ $attributes = $this->_universalFactory->create(
++ $entityType->getEntityAttributeCollection()
++ )->setEntityTypeFilter(
++ $entityType
++ )->addFieldToFilter(
++ 'attribute_code',
++ ['in' => array_keys($systemAttributes)]
++ )->getData();
++
++ $attributeData = [];
++ foreach ($attributes as $attribute) {
++ if (empty($attribute['attribute_model'])) {
++ $attribute['attribute_model'] = $entityType->getAttributeModel();
++ }
++ $attributeObject = $this->_createAttribute($entityType, $attribute);
++ $this->saveAttribute($attributeObject, $entityTypeCode, $attributeObject->getAttributeCode());
++ $attributeData[$attribute['attribute_code']] = $attributeObject->toArray();
++ }
++ if ($this->isCacheEnabled()) {
++ $this->_cache->save(
++ $this->serializer->serialize($attributeData),
++ $cacheKey,
++ [
++ \Magento\Eav\Model\Cache\Type::CACHE_TAG,
++ \Magento\Eav\Model\Entity\Attribute::CACHE_TAG
++ ]
++ );
++ }
++
++ \Magento\Framework\Profiler::stop('EAV: ' . __METHOD__);
++ $this->isSystemAttributesLoaded[$entityTypeCode] = true;
++
++ return $this;
++ }
++
++ /**
++ * Initialize user defined attribute from cache or cache it.
++ *
++ * @param string $entityType
++ * @param mixed $entityTypeCode
++ * @param string $code
++ * @return AbstractAttribute
++ * @throws LocalizedException
++ */
++ private function cacheUserDefinedAttribute($entityType, $entityTypeCode, $code): AbstractAttribute
++ {
++ $cacheKey = self::ATTRIBUTES_CACHE_ID . '-attribute-' . $entityTypeCode . '-' . $code;
++ $attributeData = $this->isCacheEnabled() && ($attribute = $this->_cache->load($cacheKey))
++ ? $this->serializer->unserialize($attribute)
++ : null;
++ if ($attributeData) {
++ if (isset($attributeData['attribute_id'])) {
++ $attribute = $this->_createAttribute($entityType, $attributeData);
++ } else {
++ $entityType = $this->getEntityType($entityType);
++ $attribute = $this->createAttribute($entityType->getAttributeModel());
++ $attribute->setAttributeCode($code);
++ $attribute = $this->setAttributeData($attribute, $entityType);
++ }
++ } else {
++ $attribute = $this->createAttributeByAttributeCode($entityType, $code);
++ $this->_addAttributeReference(
++ $attribute->getAttributeId(),
++ $attribute->getAttributeCode(),
++ $entityTypeCode
++ );
++ $this->saveAttribute($attribute, $entityTypeCode, $attribute->getAttributeCode());
++ if ($this->isCacheEnabled()) {
++ $this->_cache->save(
++ $this->serializer->serialize($attribute->getData()),
++ $cacheKey,
++ [
++ \Magento\Eav\Model\Cache\Type::CACHE_TAG,
++ \Magento\Eav\Model\Entity\Attribute::CACHE_TAG
++ ]
++ );
++ }
++ }
++
++ return $attribute;
++ }
++
++ /**
++ * Initialize user defined attribute and save it to memory cache.
++ *
++ * @param mixed $entityType
++ * @param string $entityTypeCode
++ * @param string $code
++ * @return AbstractAttribute|null
++ * @throws LocalizedException
++ */
++ private function initUserDefinedAttribute($entityType, $entityTypeCode, $code): ?AbstractAttribute
++ {
+ $attributes = $this->loadAttributes($entityTypeCode);
+- $attribute = isset($attributes[$code]) ? $attributes[$code] : null;
++ $attribute = $attributes[$code] ?? null;
+ if (!$attribute) {
+ $attribute = $this->createAttributeByAttributeCode($entityType, $code);
+ $this->_addAttributeReference(
+@@ -518,7 +702,7 @@ class Config
+ );
+ $this->saveAttribute($attribute, $entityTypeCode, $attribute->getAttributeCode());
+ }
+- \Magento\Framework\Profiler::stop('EAV: ' . __METHOD__);
++
+ return $attribute;
+ }
+
+@@ -555,8 +739,8 @@ class Config
+ /**
+ * Get all entity type attributes
+ *
+- * @param int|string|Type $entityType
+- * @param \Magento\Framework\DataObject|null $object
++ * @param int|string|Type $entityType
++ * @param \Magento\Framework\DataObject|null $object
+ * @return AbstractAttribute[]
+ *
+ * @SuppressWarnings(PHPMD.CyclomaticComplexity)
+@@ -639,7 +823,11 @@ class Config
+ $existsFullAttribute = $attribute->hasIsRequired();
+ $fullAttributeData = array_key_exists('is_required', $attributeData);
+
+- if ($existsFullAttribute || !$existsFullAttribute && !$fullAttributeData) {
++ if ($existsFullAttribute || (!$existsFullAttribute && !$fullAttributeData)) {
++ $scopeIsRequired = $attributeData['scope_is_required'] ?? null;
++ if ($scopeIsRequired !== null) {
++ $attribute->setData('scope_is_required', $scopeIsRequired);
++ }
+ return $attribute;
+ }
+ }
+@@ -708,6 +896,7 @@ class Config
+ * @param string $entityType
+ * @param string $attributeCode
+ * @return AbstractAttribute
++ * @throws LocalizedException
+ */
+ private function createAttributeByAttributeCode($entityType, $attributeCode)
+ {
+@@ -723,13 +912,28 @@ class Config
+ $attribute->setAttributeCode($attributeCode);
+ }
+
++ $attribute = $this->setAttributeData($attribute, $entityType);
++
++ return $attribute;
++ }
++
++ /**
++ * Set entity type id, backend type, is global to attribute.
++ *
++ * @param AbstractAttribute $attribute
++ * @param AbstractModel $entityType
++ * @return AbstractAttribute
++ */
++ private function setAttributeData($attribute, $entityType): AbstractAttribute
++ {
+ $entity = $entityType->getEntity();
+- if ($entity instanceof \Magento\Eav\Model\ResourceModel\Attribute\DefaultEntityAttributes\ProviderInterface
++ if ($entity instanceof ProviderInterface
+ && in_array($attribute->getAttributeCode(), $entity->getDefaultAttributes(), true)
+ ) {
+ $attribute->setBackendType(AbstractAttribute::TYPE_STATIC)->setIsGlobal(1);
+ }
+ $attribute->setEntityType($entityType)->setEntityTypeId($entityType->getId());
++
+ return $attribute;
+ }
+
+diff -Nuar a/vendor/magento/module-eav/Model/ResourceModel/Entity/Attribute/Set.php b/vendor/magento/module-eav/Model/ResourceModel/Entity/Attribute/Set.php
+--- a/vendor/magento/module-eav/Model/ResourceModel/Entity/Attribute/Set.php
++++ b/vendor/magento/module-eav/Model/ResourceModel/Entity/Attribute/Set.php
+@@ -6,6 +6,9 @@
+
+ namespace Magento\Eav\Model\ResourceModel\Entity\Attribute;
+
++/**
++ * Basic implementation for attribute sets
++ */
+ class Set extends \Magento\Framework\Model\ResourceModel\Db\AbstractDb
+ {
+ /**
+@@ -24,8 +27,6 @@ class Set extends \Magento\Framework\Model\ResourceModel\Db\AbstractDb
+ protected $eavConfig;
+
+ /**
+- * Constructor
+- *
+ * @param \Magento\Framework\Model\ResourceModel\Db\Context $context
+ * @param GroupFactory $attrGroupFactory
+ * @param \Magento\Eav\Model\Config $eavConfig
+@@ -54,7 +55,7 @@ class Set extends \Magento\Framework\Model\ResourceModel\Db\AbstractDb
+ }
+
+ /**
+- * Perform actions after object save
++ * Perform actions after object save.
+ *
+ * @param \Magento\Framework\Model\AbstractModel $object
+ * @return $this
+diff -Nuar a/vendor/magento/module-eav/etc/di.xml b/vendor/magento/module-eav/etc/di.xml
+--- a/vendor/magento/module-eav/etc/di.xml
++++ b/vendor/magento/module-eav/etc/di.xml
+@@ -209,4 +209,14 @@
+
+
+
++
++
++ eav
++
++
++
++
++ configured_eav_cache
++
++
+
+diff -Nuar a/vendor/magento/module-theme/etc/di.xml b/vendor/magento/module-theme/etc/di.xml
+--- a/vendor/magento/module-theme/etc/di.xml
++++ b/vendor/magento/module-theme/etc/di.xml
+@@ -285,4 +285,24 @@
+ theme_id
+
+
++
++
++ layout
++
++
++
++
++ configured_design_cache
++
++
++
++
++ design_context
++
++
++
++
++ configured_design_cache
++
++
+
+diff -Nuar a/app/etc/di.xml b/app/etc/di.xml
+--- a/app/etc/di.xml
++++ b/app/etc/di.xml
+@@ -1760,12 +1760,20 @@
+
+
+ Magento\Framework\Lock\Backend\Cache
+- 10000
+- 20
+
+
+
+
+
+
++
++
++ block_html
++
++
++
++
++ configured_block_cache
++
++
+
+diff -Nuar a/vendor/magento/framework/App/Cache.php b/vendor/magento/framework/App/Cache.php
+--- a/vendor/magento/framework/App/Cache.php
++++ b/vendor/magento/framework/App/Cache.php
+@@ -4,12 +4,11 @@
+ * See COPYING.txt for license details.
+ */
+
+-/**
+- * System cache model
+- * support id and tags prefix support,
+- */
+ namespace Magento\Framework\App;
+
++/**
++ * System cache model support id and tags prefix support.
++ */
+ class Cache implements CacheInterface
+ {
+ /**
+@@ -30,12 +29,13 @@ class Cache implements CacheInterface
+ protected $_frontend;
+
+ /**
+- * @param \Magento\Framework\App\Cache\Frontend\Pool $frontendPool
++ * @param Cache\Frontend\Pool $frontendPool
++ * @param string|null $cacheIdentifier
+ */
+- public function __construct(\Magento\Framework\App\Cache\Frontend\Pool $frontendPool)
++ public function __construct(\Magento\Framework\App\Cache\Frontend\Pool $frontendPool, $cacheIdentifier = null)
+ {
+ $this->_frontendPool = $frontendPool;
+- $this->_frontend = $frontendPool->get($this->_frontendIdentifier);
++ $this->_frontend = $frontendPool->get($cacheIdentifier ?? $this->_frontendIdentifier);
+ }
+
+ /**
+diff -Nuar a/vendor/magento/framework/App/Cache/Frontend/Pool.php b/vendor/magento/framework/App/Cache/Frontend/Pool.php
+--- a/vendor/magento/framework/App/Cache/Frontend/Pool.php
++++ b/vendor/magento/framework/App/Cache/Frontend/Pool.php
+@@ -152,6 +152,15 @@ class Pool implements \Iterator
+ if (isset($this->_instances[$identifier])) {
+ return $this->_instances[$identifier];
+ }
+- throw new \InvalidArgumentException("Cache frontend '{$identifier}' is not recognized.");
++
++ if (!isset($this->_instances[self::DEFAULT_FRONTEND_ID])) {
++ throw new \InvalidArgumentException(
++ "Cache frontend '{$identifier}' is not recognized. As well as " .
++ self::DEFAULT_FRONTEND_ID .
++ "cache is not configured"
++ );
++ }
++
++ return $this->_instances[self::DEFAULT_FRONTEND_ID];
+ }
+ }
+diff -Nuar a/vendor/magento/framework/App/Router/ActionList.php b/vendor/magento/framework/App/Router/ActionList.php
+--- a/vendor/magento/framework/App/Router/ActionList.php
++++ b/vendor/magento/framework/App/Router/ActionList.php
+@@ -5,6 +5,8 @@
+ */
+ namespace Magento\Framework\App\Router;
+
++use Magento\Framework\App\Filesystem\DirectoryList;
++use Magento\Framework\App\State;
+ use Magento\Framework\Serialize\SerializerInterface;
+ use Magento\Framework\Serialize\Serializer\Serialize;
+ use Magento\Framework\Module\Dir\Reader as ModuleReader;
+@@ -70,12 +72,26 @@ class ActionList
+ $this->reservedWords = array_merge($reservedWords, $this->reservedWords);
+ $this->actionInterface = $actionInterface;
+ $this->serializer = $serializer ?: \Magento\Framework\App\ObjectManager::getInstance()->get(Serialize::class);
+- $data = $cache->load($cacheKey);
+- if (!$data) {
+- $this->actions = $moduleReader->getActionFiles();
+- $cache->save($this->serializer->serialize($this->actions), $cacheKey);
++ $objectManager = \Magento\Framework\App\ObjectManager::getInstance();
++ $state = $objectManager->get(State::class);
++
++ if ($state->getMode() === State::MODE_PRODUCTION) {
++ $directoryList = $objectManager->get(DirectoryList::class);
++ $file = $directoryList->getPath(DirectoryList::GENERATED_METADATA) . '/' . $cacheKey . '.' . 'php';
++
++ if (file_exists($file)) {
++ $this->actions = (include $file) ?? $moduleReader->getActionFiles();
++ } else {
++ $this->actions = $moduleReader->getActionFiles();
++ }
+ } else {
+- $this->actions = $this->serializer->unserialize($data);
++ $data = $cache->load($cacheKey);
++ if (!$data) {
++ $this->actions = $moduleReader->getActionFiles();
++ $cache->save($this->serializer->serialize($this->actions), $cacheKey);
++ } else {
++ $this->actions = $this->serializer->unserialize($data);
++ }
+ }
+ }
+
+diff -Nuar a/2.3.5/vendor/magento/framework/Cache/Backend/Redis.php b/vendor/magento/framework/Cache/Backend/Redis.php
+new file mode 100644
+--- /dev/null
++++ b/vendor/magento/framework/Cache/Backend/Redis.php
+@@ -0,0 +1,83 @@
++preloadKeys = $options['preload_keys'] ?? [];
++ parent::__construct($options);
++ }
++
++ /**
++ * Load value with given id from cache
++ *
++ * @param string $id Cache id
++ * @param boolean $doNotTestCacheValidity If set to true, the cache validity won't be tested
++ * @return bool|string
++ */
++ public function load($id, $doNotTestCacheValidity = false)
++ {
++ if (!empty($this->preloadKeys) && empty($this->preloadedData)) {
++ $redis = $this->_slave ?? $this->_redis;
++ $redis = $redis->pipeline();
++
++ foreach ($this->preloadKeys as $key) {
++ $redis->hGet(self::PREFIX_KEY . $key, self::FIELD_DATA);
++ }
++
++ $this->preloadedData = array_filter(array_combine($this->preloadKeys, $redis->exec()));
++ }
++
++ if (isset($this->preloadedData[$id])) {
++ return $this->_decodeData($this->preloadedData[$id]);
++ }
++
++ return parent::load($id, $doNotTestCacheValidity);
++ }
++
++ /**
++ * Cover errors on save operations, which may occurs when Redis cannot evict keys, which is expected in some cases.
++ *
++ * @param string $data
++ * @param string $id
++ * @param array $tags
++ * @param bool $specificLifetime
++ * @return bool
++ */
++ public function save($data, $id, $tags = [], $specificLifetime = false)
++ {
++ try {
++ parent::save($data, $id, $tags, $specificLifetime);
++ } catch (\Throwable $exception) {
++ return false;
++ }
++
++ return true;
++ }
++}
+diff -Nuar a/vendor/magento/framework/Cache/Backend/RemoteSynchronizedCache.php b/vendor/magento/framework/Cache/Backend/RemoteSynchronizedCache.php
+--- a/vendor/magento/framework/Cache/Backend/RemoteSynchronizedCache.php
++++ b/vendor/magento/framework/Cache/Backend/RemoteSynchronizedCache.php
+@@ -9,8 +9,10 @@ namespace Magento\Framework\Cache\Backend;
+ /**
+ * Remote synchronized cache
+ *
+- * This class created for correct work local caches with multiple web nodes,
+- * that will be check cache status from remote cache
++ * This class created for correct work witch local caches and multiple web nodes,
++ * in order to be sure that we always have up to date local version of cache.
++ * This class will be check cache version from remote cache and in case it newer
++ * than local one, it will update local one from remote cache a.k.a two level cache.
+ */
+ class RemoteSynchronizedCache extends \Zend_Cache_Backend implements \Zend_Cache_Backend_ExtendedInterface
+ {
+@@ -36,11 +38,15 @@ class RemoteSynchronizedCache extends \Zend_Cache_Backend implements \Zend_Cache
+ protected $cacheInvalidationTime;
+
+ /**
+- * {@inheritdoc}
++ * Suffix for hash to compare data version in cache storage.
++ */
++ private const HASH_SUFFIX = ':hash';
++
++ /**
++ * @inheritdoc
+ */
+ protected $_options = [
+ 'remote_backend' => '',
+- 'remote_backend_invalidation_time_id' => 'default_remote_backend_invalidation_time',
+ 'remote_backend_custom_naming' => true,
+ 'remote_backend_autoload' => true,
+ 'remote_backend_options' => [],
+@@ -52,6 +58,7 @@ class RemoteSynchronizedCache extends \Zend_Cache_Backend implements \Zend_Cache
+
+ /**
+ * @param array $options
++ * @throws \Zend_Cache_Exception
+ */
+ public function __construct(array $options = [])
+ {
+@@ -97,76 +104,137 @@ class RemoteSynchronizedCache extends \Zend_Cache_Backend implements \Zend_Cache
+ }
+
+ /**
+- * Update remote cache status info
++ * @inheritdoc
++ */
++ public function setDirectives($directives)
++ {
++ return $this->local->setDirectives($directives);
++ }
++
++ /**
++ * Return hash sign of the data.
+ *
+- * @return void
++ * @param string $data
++ * @return string
+ */
+- private function updateRemoteCacheStatusInfo()
++ private function getDataVersion(string $data)
+ {
+- $this->remote->save(time(), $this->_options['remote_backend_invalidation_time_id'], [], null);
+- $this->cacheInvalidationTime = null;
++ return \hash('sha256', $data);
+ }
+
+ /**
+- * {@inheritdoc}
++ * Load data version by id from remote.
++ *
++ * @param string $id
++ * @return false|string
+ */
+- public function setDirectives($directives)
++ private function loadRemoteDataVersion(string $id)
+ {
+- return $this->local->setDirectives($directives);
++ return $this->remote->load(
++ $id . self::HASH_SUFFIX
++ );
+ }
+
+ /**
+- * {@inheritdoc}
++ * Save new data version to remote.
++ *
++ * @param string $data
++ * @param string $id
++ * @param array $tags
++ * @param mixed $specificLifetime
++ * @return bool
++ */
++ private function saveRemoteDataVersion(string $data, string $id, array $tags, $specificLifetime = false)
++ {
++ return $this->remote->save($this->getDataVersion($data), $id . self::HASH_SUFFIX, $tags, $specificLifetime);
++ }
++
++ /**
++ * Remove remote data version.
++ *
++ * @param string $id
++ * @return bool
++ */
++ private function removeRemoteDataVersion($id)
++ {
++ return $this->remote->remove($id . self::HASH_SUFFIX);
++ }
++
++ /**
++ * @inheritdoc
+ */
+ public function load($id, $doNotTestCacheValidity = false)
+ {
+- $dataModificationTime = $this->local->test($id);
+- if ($this->cacheInvalidationTime === null) {
+- $this->cacheInvalidationTime = $this->remote->load($this->_options['remote_backend_invalidation_time_id']);
+- }
+- if ($dataModificationTime >= $this->cacheInvalidationTime) {
+- return $this->local->load($id, $doNotTestCacheValidity);
++ $localData = $this->local->load($id);
++ $remoteData = false;
++
++ if (false === $localData) {
++ $remoteData = $this->remote->load($id);
++
++ if (false === $remoteData) {
++ return false;
++ }
+ } else {
+- return false;
++ if ($this->getDataVersion($localData) !== $this->loadRemoteDataVersion($id)) {
++ $localData = false;
++ $remoteData = $this->remote->load($id);
++ }
+ }
++
++ if ($remoteData !== false) {
++ $this->local->save($remoteData, $id);
++ $localData = $remoteData;
++ }
++
++ return $localData;
+ }
+
+ /**
+- * {@inheritdoc}
++ * @inheritdoc
+ */
+ public function test($id)
+ {
+- return $this->local->test($id);
++ return $this->local->test($id) ?? $this->remote->test($id);
+ }
+
+ /**
+- * {@inheritdoc}
++ * @inheritdoc
+ */
+ public function save($data, $id, $tags = [], $specificLifetime = false)
+ {
+- return $this->local->save($data, $id, $tags, $specificLifetime);
++ $dataToSave = $data;
++ $remHash = $this->loadRemoteDataVersion($id);
++
++ if ($remHash !== false) {
++ $dataToSave = $this->remote->load($id);
++ } else {
++ $this->remote->save($data, $id, $tags, $specificLifetime);
++ $this->saveRemoteDataVersion($data, $id, $tags, $specificLifetime);
++ }
++
++ return $this->local->save($dataToSave, $id, [], $specificLifetime);
+ }
+
+ /**
+- * {@inheritdoc}
++ * @inheritdoc
+ */
+ public function remove($id)
+ {
+- $this->updateRemoteCacheStatusInfo();
+- return $this->local->remove($id);
++ return $this->removeRemoteDataVersion($id) &&
++ $this->remote->remove($id) &&
++ $this->local->remove($id);
+ }
+
+ /**
+- * {@inheritdoc}
++ * @inheritdoc
+ */
+ public function clean($mode = \Zend_Cache::CLEANING_MODE_ALL, $tags = [])
+ {
+- $this->updateRemoteCacheStatusInfo();
+- return $this->local->clean($mode, $tags);
++ return $this->remote->clean($mode, $tags);
+ }
+
+ /**
+- * {@inheritdoc}
++ * @inheritdoc
+ */
+ public function getIds()
+ {
+@@ -174,7 +242,7 @@ class RemoteSynchronizedCache extends \Zend_Cache_Backend implements \Zend_Cache
+ }
+
+ /**
+- * {@inheritdoc}
++ * @inheritdoc
+ */
+ public function getTags()
+ {
+@@ -182,7 +250,7 @@ class RemoteSynchronizedCache extends \Zend_Cache_Backend implements \Zend_Cache
+ }
+
+ /**
+- * {@inheritdoc}
++ * @inheritdoc
+ */
+ public function getIdsMatchingTags($tags = [])
+ {
+@@ -190,7 +258,7 @@ class RemoteSynchronizedCache extends \Zend_Cache_Backend implements \Zend_Cache
+ }
+
+ /**
+- * {@inheritdoc}
++ * @inheritdoc
+ */
+ public function getIdsNotMatchingTags($tags = [])
+ {
+@@ -198,7 +266,7 @@ class RemoteSynchronizedCache extends \Zend_Cache_Backend implements \Zend_Cache
+ }
+
+ /**
+- * {@inheritdoc}
++ * @inheritdoc
+ */
+ public function getIdsMatchingAnyTags($tags = [])
+ {
+@@ -206,7 +274,7 @@ class RemoteSynchronizedCache extends \Zend_Cache_Backend implements \Zend_Cache
+ }
+
+ /**
+- * {@inheritdoc}
++ * @inheritdoc
+ */
+ public function getFillingPercentage()
+ {
+@@ -214,7 +282,7 @@ class RemoteSynchronizedCache extends \Zend_Cache_Backend implements \Zend_Cache
+ }
+
+ /**
+- * {@inheritdoc}
++ * @inheritdoc
+ */
+ public function getMetadatas($id)
+ {
+@@ -222,7 +290,7 @@ class RemoteSynchronizedCache extends \Zend_Cache_Backend implements \Zend_Cache
+ }
+
+ /**
+- * {@inheritdoc}
++ * @inheritdoc
+ */
+ public function touch($id, $extraLifetime)
+ {
+@@ -230,7 +298,7 @@ class RemoteSynchronizedCache extends \Zend_Cache_Backend implements \Zend_Cache
+ }
+
+ /**
+- * {@inheritdoc}
++ * @inheritdoc
+ */
+ public function getCapabilities()
+ {
+diff -Nuar a/vendor/magento/framework/Cache/LockGuardedCacheLoader.php b/vendor/magento/framework/Cache/LockGuardedCacheLoader.php
+--- a/vendor/magento/framework/Cache/LockGuardedCacheLoader.php
++++ b/vendor/magento/framework/Cache/LockGuardedCacheLoader.php
+@@ -36,19 +36,43 @@ class LockGuardedCacheLoader
+ */
+ private $delayTimeout;
+
++ /**
++ * Timeout for information to be collected and saved.
++ * If timeout passed that means that data cannot be saved right now.
++ * And we will just return collected data.
++ *
++ * Value of the variable in milliseconds.
++ *
++ * @var int
++ */
++ private $loadTimeout;
++
++ /**
++ * Minimal delay timeout in ms.
++ *
++ * @var int
++ */
++ private $minimalDelayTimeout;
++
+ /**
+ * @param LockManagerInterface $locker
+ * @param int $lockTimeout
+ * @param int $delayTimeout
++ * @param int $loadTimeout
++ * @param int $minimalDelayTimeout
+ */
+ public function __construct(
+ LockManagerInterface $locker,
+ int $lockTimeout = 10000,
+- int $delayTimeout = 20
++ int $delayTimeout = 20,
++ int $loadTimeout = 10000,
++ int $minimalDelayTimeout = 5
+ ) {
+ $this->locker = $locker;
+ $this->lockTimeout = $lockTimeout;
+ $this->delayTimeout = $delayTimeout;
++ $this->loadTimeout = $loadTimeout;
++ $this->minimalDelayTimeout = $minimalDelayTimeout;
+ }
+
+ /**
+@@ -67,25 +91,25 @@ class LockGuardedCacheLoader
+ callable $dataSaver
+ ) {
+ $cachedData = $dataLoader(); //optimistic read
+-
+- while ($cachedData === false && $this->locker->isLocked($lockName)) {
+- usleep($this->delayTimeout * 1000);
+- $cachedData = $dataLoader();
+- }
++ $deadline = microtime(true) + $this->loadTimeout / 100;
+
+ while ($cachedData === false) {
+- try {
+- if ($this->locker->lock($lockName, $this->lockTimeout / 1000)) {
++ if ($deadline <= microtime(true)) {
++ return $dataCollector();
++ }
++
++ if ($this->locker->lock($lockName, $this->lockTimeout / 1000)) {
++ try {
+ $data = $dataCollector();
+ $dataSaver($data);
+ $cachedData = $data;
++ } finally {
++ $this->locker->unlock($lockName);
+ }
+- } finally {
+- $this->locker->unlock($lockName);
+ }
+
+ if ($cachedData === false) {
+- usleep($this->delayTimeout * 1000);
++ usleep($this->getLookupTimeout() * 1000);
+ $cachedData = $dataLoader();
+ }
+ }
+@@ -103,14 +127,21 @@ class LockGuardedCacheLoader
+ public function lockedCleanData(string $lockName, callable $dataCleaner)
+ {
+ while ($this->locker->isLocked($lockName)) {
+- usleep($this->delayTimeout * 1000);
+- }
+- try {
+- if ($this->locker->lock($lockName, $this->lockTimeout / 1000)) {
+- $dataCleaner();
+- }
+- } finally {
+- $this->locker->unlock($lockName);
++ usleep($this->getLookupTimeout() * 1000);
+ }
++
++ $dataCleaner();
++ }
++
++ /**
++ * Delay will be applied as rand($minimalDelayTimeout, $delayTimeout).
++ * This helps to desynchronize multiple clients trying
++ * to acquire the lock for the same resource at the same time
++ *
++ * @return int
++ */
++ private function getLookupTimeout()
++ {
++ return rand($this->minimalDelayTimeout, $this->delayTimeout);
+ }
+ }
+diff -Nuar a/vendor/magento/framework/Interception/Config/Config.php b/vendor/magento/framework/Interception/Config/Config.php
+--- a/vendor/magento/framework/Interception/Config/Config.php
++++ b/vendor/magento/framework/Interception/Config/Config.php
+@@ -187,8 +187,6 @@ class Config implements \Magento\Framework\Interception\ConfigInterface
+ */
+ private function initializeUncompiled($classDefinitions = [])
+ {
+- $this->cacheManager->clean($this->_cacheId);
+-
+ $this->generateIntercepted($classDefinitions);
+
+ $this->cacheManager->save($this->_cacheId, $this->_intercepted);
+diff -Nuar a/vendor/magento/framework/Lock/Backend/Cache.php b/vendor/magento/framework/Lock/Backend/Cache.php
+--- a/vendor/magento/framework/Lock/Backend/Cache.php
++++ b/vendor/magento/framework/Lock/Backend/Cache.php
+@@ -24,12 +24,20 @@ class Cache implements \Magento\Framework\Lock\LockManagerInterface
+ */
+ private $cache;
+
++ /**
++ * Sign for locks, helps to avoid removing a lock that was created by another client
++ *
++ * @string
++ */
++ private $lockSign;
++
+ /**
+ * @param FrontendInterface $cache
+ */
+ public function __construct(FrontendInterface $cache)
+ {
+ $this->cache = $cache;
++ $this->lockSign = $this->generateLockSign();
+ }
+
+ /**
+@@ -37,7 +45,26 @@ class Cache implements \Magento\Framework\Lock\LockManagerInterface
+ */
+ public function lock(string $name, int $timeout = -1): bool
+ {
+- return $this->cache->save('1', $this->getIdentifier($name), [], $timeout);
++ if (empty($this->lockSign)) {
++ $this->lockSign = $this->generateLockSign();
++ }
++
++ $data = $this->cache->load($this->getIdentifier($name));
++
++ if (false !== $data) {
++ return false;
++ }
++
++ $timeout = $timeout <= 0 ? null : $timeout;
++ $this->cache->save($this->lockSign, $this->getIdentifier($name), [], $timeout);
++
++ $data = $this->cache->load($this->getIdentifier($name));
++
++ if ($data === $this->lockSign) {
++ return true;
++ }
++
++ return false;
+ }
+
+ /**
+@@ -45,7 +72,22 @@ class Cache implements \Magento\Framework\Lock\LockManagerInterface
+ */
+ public function unlock(string $name): bool
+ {
+- return $this->cache->remove($this->getIdentifier($name));
++ if (empty($this->lockSign)) {
++ return false;
++ }
++
++ $data = $this->cache->load($this->getIdentifier($name));
++
++ if (false === $data) {
++ return false;
++ }
++
++ $removeResult = false;
++ if ($data === $this->lockSign) {
++ $removeResult = (bool)$this->cache->remove($this->getIdentifier($name));
++ }
++
++ return $removeResult;
+ }
+
+ /**
+@@ -66,4 +108,27 @@ class Cache implements \Magento\Framework\Lock\LockManagerInterface
+ {
+ return self::LOCK_PREFIX . $cacheIdentifier;
+ }
++
++ /**
++ * Function that generates lock sign that helps to avoid removing a lock that was created by another client.
++ *
++ * @return string
++ */
++ private function generateLockSign()
++ {
++ $sign = implode(
++ '-',
++ [
++ \getmypid(), \crc32(\gethostname())
++ ]
++ );
++
++ try {
++ $sign .= '-' . \bin2hex(\random_bytes(4));
++ } catch (\Exception $e) {
++ $sign .= '-' . \uniqid('-uniqid-');
++ }
++
++ return $sign;
++ }
+ }
+diff -Nuar a/setup/src/Magento/Setup/Console/Command/DiCompileCommand.php b/setup/src/Magento/Setup/Console/Command/DiCompileCommand.php
+--- a/setup/src/Magento/Setup/Console/Command/DiCompileCommand.php
++++ b/setup/src/Magento/Setup/Console/Command/DiCompileCommand.php
+@@ -260,9 +260,12 @@ class DiCompileCommand extends Command
+ */
+ private function getExcludedLibraryPaths(array $libraryPaths)
+ {
+- $libraryPaths = array_map(function ($libraryPath) {
+- return preg_quote($libraryPath, '#');
+- }, $libraryPaths);
++ $libraryPaths = array_map(
++ function ($libraryPath) {
++ return preg_quote($libraryPath, '#');
++ },
++ $libraryPaths
++ );
+
+ $excludedLibraryPaths = [
+ '#^(?:' . join('|', $libraryPaths) . ')/([\\w]+/)?Test#',
+@@ -395,7 +398,8 @@ class DiCompileCommand extends Command
+ $compiledPathsList['application'],
+ $compiledPathsList['library'],
+ $compiledPathsList['generated_helpers'],
+- ]
++ ],
++ OperationFactory::APPLICATION_ACTION_LIST_GENERATOR => [],
+ ];
+
+ return $operations;
+diff -Nuar a/2.3.5/setup/src/Magento/Setup/Module/Di/App/Task/Operation/AppActionListGenerator.php b/setup/src/Magento/Setup/Module/Di/App/Task/Operation/AppActionListGenerator.php
+new file mode 100644
+--- /dev/null
++++ b/setup/src/Magento/Setup/Module/Di/App/Task/Operation/AppActionListGenerator.php
+@@ -0,0 +1,58 @@
++moduleReader = $moduleReader;
++ $this->configWriter = $configWriter;
++ }
++
++ /**
++ * @inheritDoc
++ */
++ public function doOperation()
++ {
++ $actionList = $this->moduleReader->getActionFiles();
++ $this->configWriter->write(
++ 'app_action_list',
++ $actionList
++ );
++ }
++
++ /**
++ * @inheritDoc
++ */
++ public function getName()
++ {
++ return 'App action list generation';
++ }
++}
+diff -Nuar a/setup/src/Magento/Setup/Module/Di/App/Task/OperationFactory.php b/setup/src/Magento/Setup/Module/Di/App/Task/OperationFactory.php
+--- a/setup/src/Magento/Setup/Module/Di/App/Task/OperationFactory.php
++++ b/setup/src/Magento/Setup/Module/Di/App/Task/OperationFactory.php
+@@ -5,6 +5,12 @@
+ */
+ namespace Magento\Setup\Module\Di\App\Task;
+
++use Magento\Setup\Module\Di\App\Task\Operation\AppActionListGenerator;
++use Magento\Setup\Module\Di\App\Task\Operation\PluginListGenerator;
++
++/**
++ * Factory that creates list of OperationInterface classes
++ */
+ class OperationFactory
+ {
+ /**
+@@ -47,6 +53,11 @@ class OperationFactory
+ */
+ const APPLICATION_CODE_GENERATOR = 'application_code_generator';
+
++ /**
++ * Application action list generator
++ */
++ const APPLICATION_ACTION_LIST_GENERATOR = 'application_action_list_generator';
++
+ /**
+ * Operations definitions
+ *
+@@ -61,6 +72,7 @@ class OperationFactory
+ self::INTERCEPTION_CACHE => \Magento\Setup\Module\Di\App\Task\Operation\InterceptionCache::class,
+ self::REPOSITORY_GENERATOR => \Magento\Setup\Module\Di\App\Task\Operation\RepositoryGenerator::class,
+ self::PROXY_GENERATOR => \Magento\Setup\Module\Di\App\Task\Operation\ProxyGenerator::class,
++ self::APPLICATION_ACTION_LIST_GENERATOR => AppActionListGenerator::class,
+ ];
+
+ /**
+diff -Nuar a/setup/src/Magento/Setup/Model/ConfigOptionsList/Cache.php b/setup/src/Magento/Setup/Model/ConfigOptionsList/Cache.php
+--- a/setup/src/Magento/Setup/Model/ConfigOptionsList/Cache.php
++++ b/setup/src/Magento/Setup/Model/ConfigOptionsList/Cache.php
+@@ -20,7 +20,7 @@ use Magento\Setup\Validator\RedisConnectionValidator;
+ class Cache implements ConfigOptionsListInterface
+ {
+ const INPUT_VALUE_CACHE_REDIS = 'redis';
+- const CONFIG_VALUE_CACHE_REDIS = 'Cm_Cache_Backend_Redis';
++ const CONFIG_VALUE_CACHE_REDIS = \Magento\Framework\Cache\Backend\Redis::class;
+
+ const INPUT_KEY_CACHE_BACKEND = 'cache-backend';
+ const INPUT_KEY_CACHE_BACKEND_REDIS_SERVER = 'cache-backend-redis-server';
+@@ -282,6 +282,6 @@ class Cache implements ConfigOptionsListInterface
+ */
+ private function generateCachePrefix(): string
+ {
+- return substr(\md5(dirname(__DIR__, 6)), 0, 3) . '_';
++ return substr(\hash('sha256', dirname(__DIR__, 6)), 0, 3) . '_';
+ }
+ }
+diff -Nuar a/setup/src/Magento/Setup/Model/ConfigOptionsList/PageCache.php b/setup/src/Magento/Setup/Model/ConfigOptionsList/PageCache.php
+--- a/setup/src/Magento/Setup/Model/ConfigOptionsList/PageCache.php
++++ b/setup/src/Magento/Setup/Model/ConfigOptionsList/PageCache.php
+@@ -20,7 +20,7 @@ use Magento\Setup\Validator\RedisConnectionValidator;
+ class PageCache implements ConfigOptionsListInterface
+ {
+ const INPUT_VALUE_PAGE_CACHE_REDIS = 'redis';
+- const CONFIG_VALUE_PAGE_CACHE_REDIS = 'Cm_Cache_Backend_Redis';
++ const CONFIG_VALUE_PAGE_CACHE_REDIS = \Magento\Framework\Cache\Backend\Redis::class;
+
+ const INPUT_KEY_PAGE_CACHE_BACKEND = 'page-cache';
+ const INPUT_KEY_PAGE_CACHE_BACKEND_REDIS_SERVER = 'page-cache-redis-server';
+@@ -284,6 +284,6 @@ class PageCache implements ConfigOptionsListInterface
+ */
+ private function generateCachePrefix(): string
+ {
+- return substr(\md5(dirname(__DIR__, 6)), 0, 3) . '_';
++ return substr(\hash('sha256', dirname(__DIR__, 6)), 0, 3) . '_';
+ }
+ }
+diff -Nuar a/vendor/magento/module-catalog/Model/Product.php b/vendor/magento/module-catalog/Model/Product.php
+--- a/vendor/magento/module-catalog/Model/Product.php
++++ b/vendor/magento/module-catalog/Model/Product.php
+@@ -74,9 +74,9 @@ class Product extends \Magento\Catalog\Model\AbstractModel implements
+ const STORE_ID = 'store_id';
+
+ /**
+- * @var string
++ * @var string|bool
+ */
+- protected $_cacheTag = self::CACHE_TAG;
++ protected $_cacheTag = false;
+
+ /**
+ * @var string
+@@ -910,7 +910,6 @@ class Product extends \Magento\Catalog\Model\AbstractModel implements
+ */
+ public function beforeSave()
+ {
+- $this->cleanCache();
+ $this->setTypeHasOptions(false);
+ $this->setTypeHasRequiredOptions(false);
+ $this->setHasOptions(false);
+diff -Nuar a/vendor/magento/module-config/App/Config/Type/System.php b/vendor/magento/module-config/App/Config/Type/System.php
+--- a/vendor/magento/module-config/App/Config/Type/System.php
++++ b/vendor/magento/module-config/App/Config/Type/System.php
+@@ -20,6 +20,8 @@ use Magento\Framework\Serialize\SerializerInterface;
+ use Magento\Store\Model\Config\Processor\Fallback;
+ use Magento\Framework\Encryption\Encryptor;
+ use Magento\Store\Model\ScopeInterface as StoreScope;
++use Magento\Framework\App\Cache\StateInterface;
++use Magento\Framework\App\Cache\Type\Config;
+
+ /**
+ * System configuration type
+@@ -98,6 +100,12 @@ class System implements ConfigTypeInterface
+ private $lockQuery;
+
+ /**
++ * @var StateInterface
++ */
++ private $cacheState;
++
++ /**
++ * System constructor.
+ * @param ConfigSourceInterface $source
+ * @param PostProcessorInterface $postProcessor
+ * @param Fallback $fallback
+@@ -110,6 +118,7 @@ class System implements ConfigTypeInterface
+ * @param Encryptor|null $encryptor
+ * @param LockManagerInterface|null $locker
+ * @param LockGuardedCacheLoader|null $lockQuery
++ * @param StateInterface|null $cacheState
+ * @SuppressWarnings(PHPMD.UnusedFormalParameter)
+ * @SuppressWarnings(PHPMD.ExcessiveParameterList)
+ */
+@@ -125,7 +134,8 @@ class System implements ConfigTypeInterface
+ Reader $reader = null,
+ Encryptor $encryptor = null,
+ LockManagerInterface $locker = null,
+- LockGuardedCacheLoader $lockQuery = null
++ LockGuardedCacheLoader $lockQuery = null,
++ StateInterface $cacheState = null
+ ) {
+ $this->postProcessor = $postProcessor;
+ $this->cache = $cache;
+@@ -136,6 +146,8 @@ class System implements ConfigTypeInterface
+ ?: ObjectManager::getInstance()->get(Encryptor::class);
+ $this->lockQuery = $lockQuery
+ ?: ObjectManager::getInstance()->get(LockGuardedCacheLoader::class);
++ $this->cacheState = $cacheState
++ ?: ObjectManager::getInstance()->get(StateInterface::class);
+ }
+
+ /**
+@@ -220,6 +232,10 @@ class System implements ConfigTypeInterface
+ */
+ private function loadAllData()
+ {
++ if (!$this->cacheState->isEnabled(Config::TYPE_IDENTIFIER)) {
++ return $this->readData();
++ }
++
+ $loadAction = function () {
+ $cachedData = $this->cache->load($this->configType);
+ $data = false;
+@@ -245,6 +261,10 @@ class System implements ConfigTypeInterface
+ */
+ private function loadDefaultScopeData($scopeType)
+ {
++ if (!$this->cacheState->isEnabled(Config::TYPE_IDENTIFIER)) {
++ return $this->readData();
++ }
++
+ $loadAction = function () use ($scopeType) {
+ $cachedData = $this->cache->load($this->configType . '_' . $scopeType);
+ $scopeData = false;
+@@ -271,6 +291,10 @@ class System implements ConfigTypeInterface
+ */
+ private function loadScopeData($scopeType, $scopeId)
+ {
++ if (!$this->cacheState->isEnabled(Config::TYPE_IDENTIFIER)) {
++ return $this->readData();
++ }
++
+ $loadAction = function () use ($scopeType, $scopeId) {
+ $cachedData = $this->cache->load($this->configType . '_' . $scopeType . '_' . $scopeId);
+ $scopeData = false;
+@@ -393,6 +417,10 @@ class System implements ConfigTypeInterface
+ $this->cache->clean(\Zend_Cache::CLEANING_MODE_MATCHING_TAG, [self::CACHE_TAG]);
+ };
+
++ if (!$this->cacheState->isEnabled(Config::TYPE_IDENTIFIER)) {
++ return $cleanAction();
++ }
++
+ $this->lockQuery->lockedCleanData(
+ self::$lockName,
+ $cleanAction
+diff -Nuar a/vendor/magento/module-config/etc/di.xml b/vendor/magento/module-config/etc/di.xml
+--- a/vendor/magento/module-config/etc/di.xml
++++ b/vendor/magento/module-config/etc/di.xml
+@@ -97,8 +97,6 @@
+
+
+ Magento\Framework\Lock\Backend\Cache
+- 42000
+- 100
+
+
+
diff --git a/patches/MDVA-26795__fix_performance_issue_in_cache_locking_mechanism__2.3.4.patch b/patches/MCLOUD-6139_MCLOUD-6211__redis_improvement_patches__2.3.3-p1.patch
similarity index 50%
rename from patches/MDVA-26795__fix_performance_issue_in_cache_locking_mechanism__2.3.4.patch
rename to patches/MCLOUD-6139_MCLOUD-6211__redis_improvement_patches__2.3.3-p1.patch
index 0c1b7e0..51c2558 100644
--- a/patches/MDVA-26795__fix_performance_issue_in_cache_locking_mechanism__2.3.4.patch
+++ b/patches/MCLOUD-6139_MCLOUD-6211__redis_improvement_patches__2.3.3-p1.patch
@@ -1,8 +1,69 @@
diff -Nuar a/vendor/magento/module-eav/Model/Config.php b/vendor/magento/module-eav/Model/Config.php
--- a/vendor/magento/module-eav/Model/Config.php
+++ b/vendor/magento/module-eav/Model/Config.php
-@@ -157,12 +157,12 @@ class Config
-
+@@ -7,12 +7,20 @@ namespace Magento\Eav\Model;
+
+ use Magento\Eav\Model\Entity\Attribute\AbstractAttribute;
+ use Magento\Eav\Model\Entity\Type;
++use Magento\Eav\Model\ResourceModel\Attribute\DefaultEntityAttributes\ProviderInterface;
++use Magento\Framework\App\Config\ScopeConfigInterface;
+ use Magento\Framework\App\ObjectManager;
++use Magento\Framework\Exception\LocalizedException;
++use Magento\Framework\Model\AbstractModel;
+ use Magento\Framework\Serialize\SerializerInterface;
+
+ /**
++ * EAV config model.
++ *
+ * @api
++ * @SuppressWarnings(PHPMD.TooManyFields)
+ * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
++ * @SuppressWarnings(PHPMD.ExcessiveClassComplexity)
+ * @since 100.0.2
+ */
+ class Config
+@@ -25,6 +33,11 @@ class Config
+ const ATTRIBUTES_CODES_CACHE_ID = 'EAV_ENTITY_ATTRIBUTES_CODES';
+ /**#@-*/
+
++ /**
++ * Xml path to caching user defined eav attributes configuration.
++ */
++ private const XML_PATH_CACHE_USER_DEFINED_ATTRIBUTES = 'dev/caching/cache_user_defined_attributes';
++
+ /**#@-*/
+ protected $_entityTypeData;
+
+@@ -116,6 +129,11 @@ class Config
+ */
+ private $serializer;
+
++ /**
++ * @var ScopeConfigInterface
++ */
++ private $scopeConfig;
++
+ /**
+ * Cache of attributes per set
+ *
+@@ -123,13 +141,29 @@ class Config
+ */
+ private $attributesPerSet = [];
+
++ /**
++ * Is system attributes loaded flag.
++ *
++ * @var array
++ */
++ private $isSystemAttributesLoaded = [];
++
++ /**
++ * List of predefined system attributes for preload.
++ *
++ * @var array
++ */
++ private $attributesForPreload;
++
/**
* @param \Magento\Framework\App\CacheInterface $cache
- * @param \Magento\Eav\Model\Entity\TypeFactory $entityTypeFactory
@@ -12,16 +73,71 @@ diff -Nuar a/vendor/magento/module-eav/Model/Config.php b/vendor/magento/module-
* @param \Magento\Framework\App\Cache\StateInterface $cacheState
* @param \Magento\Framework\Validator\UniversalFactory $universalFactory
- * @param SerializerInterface $serializer
-- * @param ScopeConfigInterface $scopeConfig
+ * @param SerializerInterface|null $serializer
+ * @param ScopeConfigInterface|null $scopeConfig
- * @param array $attributesForPreload
++ * @param array $attributesForPreload
* @codeCoverageIgnore
*/
-@@ -374,7 +374,9 @@ class Config
+ public function __construct(
+@@ -138,7 +172,9 @@ class Config
+ \Magento\Eav\Model\ResourceModel\Entity\Type\CollectionFactory $entityTypeCollectionFactory,
+ \Magento\Framework\App\Cache\StateInterface $cacheState,
+ \Magento\Framework\Validator\UniversalFactory $universalFactory,
+- SerializerInterface $serializer = null
++ SerializerInterface $serializer = null,
++ ScopeConfigInterface $scopeConfig = null,
++ $attributesForPreload = []
+ ) {
+ $this->_cache = $cache;
+ $this->_entityTypeFactory = $entityTypeFactory;
+@@ -146,6 +182,8 @@ class Config
+ $this->_cacheState = $cacheState;
+ $this->_universalFactory = $universalFactory;
+ $this->serializer = $serializer ?: ObjectManager::getInstance()->get(SerializerInterface::class);
++ $this->scopeConfig = $scopeConfig ?: ObjectManager::getInstance()->get(ScopeConfigInterface::class);
++ $this->attributesForPreload = $attributesForPreload;
+ }
+
+ /**
+@@ -207,8 +245,8 @@ class Config
+ /**
+ * Associate object with identifier
+ *
+- * @param mixed $obj
+- * @param mixed $id
++ * @param mixed $obj
++ * @param mixed $id
+ * @return void
+ * @codeCoverageIgnore
+ */
+@@ -233,8 +271,8 @@ class Config
+ /**
+ * Specify reference for entity type id
+ *
+- * @param int $id
+- * @param string $code
++ * @param int $id
++ * @param string $code
+ * @return $this
+ * @codeCoverageIgnore
+ */
+@@ -258,9 +296,9 @@ class Config
+ /**
+ * Specify reference between entity attribute id and attribute code
+ *
+- * @param int $id
+- * @param string $code
+- * @param string $entityTypeCode
++ * @param int $id
++ * @param string $code
++ * @param string $entityTypeCode
+ * @return $this
+ */
+ protected function _addAttributeReference($id, $code, $entityTypeCode)
+@@ -336,7 +374,9 @@ class Config
}
\Magento\Framework\Profiler::start('EAV: ' . __METHOD__, ['group' => 'EAV', 'method' => __METHOD__]);
-
+
- if ($this->isCacheEnabled() && ($cache = $this->_cache->load(self::ENTITIES_CACHE_ID))) {
+ if ($this->isCacheEnabled() &&
+ ($cache = $this->_cache->load(self::ENTITIES_CACHE_ID))
@@ -29,13 +145,253 @@ diff -Nuar a/vendor/magento/module-eav/Model/Config.php b/vendor/magento/module-
$this->_entityTypeData = $this->serializer->unserialize($cache);
foreach ($this->_entityTypeData as $typeCode => $data) {
$typeId = $data['entity_type_id'];
+@@ -484,10 +524,10 @@ class Config
+ /**
+ * Get attribute by code for entity type
+ *
+- * @param mixed $entityType
+- * @param mixed $code
+- * @return AbstractAttribute
+- * @throws \Magento\Framework\Exception\LocalizedException
++ * @param mixed $entityType
++ * @param mixed $code
++ * @return AbstractAttribute
++ * @throws LocalizedException
+ */
+ public function getAttribute($entityType, $code)
+ {
+@@ -507,8 +547,152 @@ class Config
+ return $this->attributes[$entityTypeCode][$code];
+ }
+
++ if (array_key_exists($entityTypeCode, $this->attributesForPreload)
++ && array_key_exists($code, $this->attributesForPreload[$entityTypeCode])
++ ) {
++ $this->initSystemAttributes($entityType, $this->attributesForPreload[$entityTypeCode]);
++ }
++ if (isset($this->attributes[$entityTypeCode][$code])) {
++ \Magento\Framework\Profiler::stop('EAV: ' . __METHOD__);
++ return $this->attributes[$entityTypeCode][$code];
++ }
++
++ if ($this->scopeConfig->getValue(self::XML_PATH_CACHE_USER_DEFINED_ATTRIBUTES)) {
++ $attribute = $this->cacheUserDefinedAttribute($entityType, $entityTypeCode, $code);
++ } else {
++ $attribute = $this->initUserDefinedAttribute($entityType, $entityTypeCode, $code);
++ }
++
++ \Magento\Framework\Profiler::stop('EAV: ' . __METHOD__);
++ return $attribute;
++ }
++
++ /**
++ * Initialize predefined system attributes for preload.
++ *
++ * @param string $entityType
++ * @param array $systemAttributes
++ * @return $this|bool|void
++ * @throws LocalizedException
++ */
++ private function initSystemAttributes($entityType, $systemAttributes)
++ {
++ $entityType = $this->getEntityType($entityType);
++ $entityTypeCode = $entityType->getEntityTypeCode();
++ if (!empty($this->isSystemAttributesLoaded[$entityTypeCode])) {
++ return;
++ }
++
++ $cacheKey = self::ATTRIBUTES_CACHE_ID . '-' . $entityTypeCode . '-preload';
++ if ($this->isCacheEnabled() && ($attributes = $this->_cache->load($cacheKey))) {
++ $attributes = $this->serializer->unserialize($attributes);
++ if ($attributes) {
++ foreach ($attributes as $attribute) {
++ $attributeObject = $this->_createAttribute($entityType, $attribute);
++ $this->saveAttribute($attributeObject, $entityTypeCode, $attributeObject->getAttributeCode());
++ }
++ return true;
++ }
++ }
++
++ \Magento\Framework\Profiler::start('EAV: ' . __METHOD__, ['group' => 'EAV', 'method' => __METHOD__]);
++
++ /** @var \Magento\Eav\Model\ResourceModel\Entity\Attribute\Collection $attributes */
++ $attributes = $this->_universalFactory->create(
++ $entityType->getEntityAttributeCollection()
++ )->setEntityTypeFilter(
++ $entityType
++ )->addFieldToFilter(
++ 'attribute_code',
++ ['in' => array_keys($systemAttributes)]
++ )->getData();
++
++ $attributeData = [];
++ foreach ($attributes as $attribute) {
++ if (empty($attribute['attribute_model'])) {
++ $attribute['attribute_model'] = $entityType->getAttributeModel();
++ }
++ $attributeObject = $this->_createAttribute($entityType, $attribute);
++ $this->saveAttribute($attributeObject, $entityTypeCode, $attributeObject->getAttributeCode());
++ $attributeData[$attribute['attribute_code']] = $attributeObject->toArray();
++ }
++ if ($this->isCacheEnabled()) {
++ $this->_cache->save(
++ $this->serializer->serialize($attributeData),
++ $cacheKey,
++ [
++ \Magento\Eav\Model\Cache\Type::CACHE_TAG,
++ \Magento\Eav\Model\Entity\Attribute::CACHE_TAG
++ ]
++ );
++ }
++
++ \Magento\Framework\Profiler::stop('EAV: ' . __METHOD__);
++ $this->isSystemAttributesLoaded[$entityTypeCode] = true;
++
++ return $this;
++ }
++
++ /**
++ * Initialize user defined attribute from cache or cache it.
++ *
++ * @param string $entityType
++ * @param mixed $entityTypeCode
++ * @param string $code
++ * @return AbstractAttribute
++ * @throws LocalizedException
++ */
++ private function cacheUserDefinedAttribute($entityType, $entityTypeCode, $code): AbstractAttribute
++ {
++ $cacheKey = self::ATTRIBUTES_CACHE_ID . '-attribute-' . $entityTypeCode . '-' . $code;
++ $attributeData = $this->isCacheEnabled() && ($attribute = $this->_cache->load($cacheKey))
++ ? $this->serializer->unserialize($attribute)
++ : null;
++ if ($attributeData) {
++ if (isset($attributeData['attribute_id'])) {
++ $attribute = $this->_createAttribute($entityType, $attributeData);
++ } else {
++ $entityType = $this->getEntityType($entityType);
++ $attribute = $this->createAttribute($entityType->getAttributeModel());
++ $attribute->setAttributeCode($code);
++ $attribute = $this->setAttributeData($attribute, $entityType);
++ }
++ } else {
++ $attribute = $this->createAttributeByAttributeCode($entityType, $code);
++ $this->_addAttributeReference(
++ $attribute->getAttributeId(),
++ $attribute->getAttributeCode(),
++ $entityTypeCode
++ );
++ $this->saveAttribute($attribute, $entityTypeCode, $attribute->getAttributeCode());
++ if ($this->isCacheEnabled()) {
++ $this->_cache->save(
++ $this->serializer->serialize($attribute->getData()),
++ $cacheKey,
++ [
++ \Magento\Eav\Model\Cache\Type::CACHE_TAG,
++ \Magento\Eav\Model\Entity\Attribute::CACHE_TAG
++ ]
++ );
++ }
++ }
++
++ return $attribute;
++ }
++
++ /**
++ * Initialize user defined attribute and save it to memory cache.
++ *
++ * @param mixed $entityType
++ * @param string $entityTypeCode
++ * @param string $code
++ * @return AbstractAttribute|null
++ * @throws LocalizedException
++ */
++ private function initUserDefinedAttribute($entityType, $entityTypeCode, $code): ?AbstractAttribute
++ {
+ $attributes = $this->loadAttributes($entityTypeCode);
+- $attribute = isset($attributes[$code]) ? $attributes[$code] : null;
++ $attribute = $attributes[$code] ?? null;
+ if (!$attribute) {
+ $attribute = $this->createAttributeByAttributeCode($entityType, $code);
+ $this->_addAttributeReference(
+@@ -518,7 +702,7 @@ class Config
+ );
+ $this->saveAttribute($attribute, $entityTypeCode, $attribute->getAttributeCode());
+ }
+- \Magento\Framework\Profiler::stop('EAV: ' . __METHOD__);
++
+ return $attribute;
+ }
+
+@@ -555,8 +739,8 @@ class Config
+ /**
+ * Get all entity type attributes
+ *
+- * @param int|string|Type $entityType
+- * @param \Magento\Framework\DataObject|null $object
++ * @param int|string|Type $entityType
++ * @param \Magento\Framework\DataObject|null $object
+ * @return AbstractAttribute[]
+ *
+ * @SuppressWarnings(PHPMD.CyclomaticComplexity)
+@@ -639,7 +823,11 @@ class Config
+ $existsFullAttribute = $attribute->hasIsRequired();
+ $fullAttributeData = array_key_exists('is_required', $attributeData);
+
+- if ($existsFullAttribute || !$existsFullAttribute && !$fullAttributeData) {
++ if ($existsFullAttribute || (!$existsFullAttribute && !$fullAttributeData)) {
++ $scopeIsRequired = $attributeData['scope_is_required'] ?? null;
++ if ($scopeIsRequired !== null) {
++ $attribute->setData('scope_is_required', $scopeIsRequired);
++ }
+ return $attribute;
+ }
+ }
+@@ -708,6 +896,7 @@ class Config
+ * @param string $entityType
+ * @param string $attributeCode
+ * @return AbstractAttribute
++ * @throws LocalizedException
+ */
+ private function createAttributeByAttributeCode($entityType, $attributeCode)
+ {
+@@ -723,13 +912,28 @@ class Config
+ $attribute->setAttributeCode($attributeCode);
+ }
+
++ $attribute = $this->setAttributeData($attribute, $entityType);
++
++ return $attribute;
++ }
++
++ /**
++ * Set entity type id, backend type, is global to attribute.
++ *
++ * @param AbstractAttribute $attribute
++ * @param AbstractModel $entityType
++ * @return AbstractAttribute
++ */
++ private function setAttributeData($attribute, $entityType): AbstractAttribute
++ {
+ $entity = $entityType->getEntity();
+- if ($entity instanceof \Magento\Eav\Model\ResourceModel\Attribute\DefaultEntityAttributes\ProviderInterface
++ if ($entity instanceof ProviderInterface
+ && in_array($attribute->getAttributeCode(), $entity->getDefaultAttributes(), true)
+ ) {
+ $attribute->setBackendType(AbstractAttribute::TYPE_STATIC)->setIsGlobal(1);
+ }
+ $attribute->setEntityType($entityType)->setEntityTypeId($entityType->getId());
++
+ return $attribute;
+ }
+
diff -Nuar a/vendor/magento/module-eav/Model/ResourceModel/Entity/Attribute/Set.php b/vendor/magento/module-eav/Model/ResourceModel/Entity/Attribute/Set.php
--- a/vendor/magento/module-eav/Model/ResourceModel/Entity/Attribute/Set.php
+++ b/vendor/magento/module-eav/Model/ResourceModel/Entity/Attribute/Set.php
@@ -6,6 +6,9 @@
-
+
namespace Magento\Eav\Model\ResourceModel\Entity\Attribute;
-
+
+/**
+ * Basic implementation for attribute sets
+ */
@@ -44,7 +400,7 @@ diff -Nuar a/vendor/magento/module-eav/Model/ResourceModel/Entity/Attribute/Set.
/**
@@ -24,8 +27,6 @@ class Set extends \Magento\Framework\Model\ResourceModel\Db\AbstractDb
protected $eavConfig;
-
+
/**
- * Constructor
- *
@@ -53,7 +409,7 @@ diff -Nuar a/vendor/magento/module-eav/Model/ResourceModel/Entity/Attribute/Set.
* @param \Magento\Eav\Model\Config $eavConfig
@@ -54,7 +55,7 @@ class Set extends \Magento\Framework\Model\ResourceModel\Db\AbstractDb
}
-
+
/**
- * Perform actions after object save
+ * Perform actions after object save.
@@ -109,9 +465,18 @@ diff -Nuar a/vendor/magento/module-theme/etc/di.xml b/vendor/magento/module-them
diff -Nuar a/app/etc/di.xml b/app/etc/di.xml
--- a/app/etc/di.xml
+++ b/app/etc/di.xml
-@@ -1800,4 +1800,14 @@
+@@ -1779,8 +1779,6 @@
+
+
+ Magento\Framework\Lock\Backend\Cache
+- 10000
+- 20
+
-
+
+@@ -1795,4 +1793,14 @@
+
+
+
@@ -170,11 +535,11 @@ diff -Nuar a/vendor/magento/framework/App/Cache/Frontend/Pool.php b/vendor/magen
- throw new \InvalidArgumentException("Cache frontend '{$identifier}' is not recognized.");
+
+ if (!isset($this->_instances[self::DEFAULT_FRONTEND_ID])) {
-+ throw new \InvalidArgumentException(sprintf(
-+ 'Cache frontend \'%s\' is not recognized. As well as %s cache is not configured',
-+ $identifier,
-+ self::DEFAULT_FRONTEND_ID
-+ ));
++ throw new \InvalidArgumentException(
++ "Cache frontend '{$identifier}' is not recognized. As well as " .
++ self::DEFAULT_FRONTEND_ID .
++ "cache is not configured"
++ );
+ }
+
+ return $this->_instances[self::DEFAULT_FRONTEND_ID];
@@ -224,7 +589,7 @@ diff -Nuar a/vendor/magento/framework/App/Router/ActionList.php b/vendor/magento
}
}
-diff -Nuar a/vendor/magento/framework/Cache/Backend/Redis.php b/vendor/magento/framework/Cache/Backend/Redis.php
+diff -Nuar a/2.3.5/vendor/magento/framework/Cache/Backend/Redis.php b/vendor/magento/framework/Cache/Backend/Redis.php
new file mode 100644
--- /dev/null
+++ b/vendor/magento/framework/Cache/Backend/Redis.php
@@ -268,7 +633,7 @@ new file mode 100644
+ /**
+ * Load value with given id from cache
+ *
-+ * @param string $id Cache id
++ * @param string $id Cache id
+ * @param boolean $doNotTestCacheValidity If set to true, the cache validity won't be tested
+ * @return bool|string
+ */
@@ -418,7 +783,7 @@ diff -Nuar a/vendor/magento/framework/Cache/Backend/RemoteSynchronizedCache.php
+ * @param string $id
+ * @return bool
+ */
-+ private function removeRemoteDataVersion($id): bool
++ private function removeRemoteDataVersion($id)
+ {
+ return $this->remote->remove($id . self::HASH_SUFFIX);
+ }
@@ -594,10 +959,11 @@ diff -Nuar a/vendor/magento/framework/Cache/Backend/RemoteSynchronizedCache.php
diff -Nuar a/vendor/magento/framework/Cache/LockGuardedCacheLoader.php b/vendor/magento/framework/Cache/LockGuardedCacheLoader.php
--- a/vendor/magento/framework/Cache/LockGuardedCacheLoader.php
+++ b/vendor/magento/framework/Cache/LockGuardedCacheLoader.php
-@@ -37,18 +37,33 @@ class LockGuardedCacheLoader
+@@ -36,19 +36,43 @@ class LockGuardedCacheLoader
+ */
private $delayTimeout;
- /**
++ /**
+ * Timeout for information to be collected and saved.
+ * If timeout passed that means that data cannot be saved right now.
+ * And we will just return collected data.
@@ -609,27 +975,36 @@ diff -Nuar a/vendor/magento/framework/Cache/LockGuardedCacheLoader.php b/vendor/
+ private $loadTimeout;
+
+ /**
-+ * LockGuardedCacheLoader constructor.
++ * Minimal delay timeout in ms.
++ *
++ * @var int
++ */
++ private $minimalDelayTimeout;
++
+ /**
* @param LockManagerInterface $locker
* @param int $lockTimeout
* @param int $delayTimeout
+ * @param int $loadTimeout
++ * @param int $minimalDelayTimeout
*/
public function __construct(
LockManagerInterface $locker,
int $lockTimeout = 10000,
- int $delayTimeout = 20
+ int $delayTimeout = 20,
-+ int $loadTimeout = 10000
++ int $loadTimeout = 10000,
++ int $minimalDelayTimeout = 5
) {
$this->locker = $locker;
$this->lockTimeout = $lockTimeout;
$this->delayTimeout = $delayTimeout;
+ $this->loadTimeout = $loadTimeout;
++ $this->minimalDelayTimeout = $minimalDelayTimeout;
}
/**
-@@ -67,21 +82,21 @@ class LockGuardedCacheLoader
+@@ -67,25 +91,25 @@ class LockGuardedCacheLoader
callable $dataSaver
) {
$cachedData = $dataLoader(); //optimistic read
@@ -638,7 +1013,7 @@ diff -Nuar a/vendor/magento/framework/Cache/LockGuardedCacheLoader.php b/vendor/
- usleep($this->delayTimeout * 1000);
- $cachedData = $dataLoader();
- }
-+ $deadline = microtime(true) + $this->loadTimeout;
++ $deadline = microtime(true) + $this->loadTimeout / 100;
while ($cachedData === false) {
- try {
@@ -660,6 +1035,41 @@ diff -Nuar a/vendor/magento/framework/Cache/LockGuardedCacheLoader.php b/vendor/
}
if ($cachedData === false) {
+- usleep($this->delayTimeout * 1000);
++ usleep($this->getLookupTimeout() * 1000);
+ $cachedData = $dataLoader();
+ }
+ }
+@@ -103,14 +127,21 @@ class LockGuardedCacheLoader
+ public function lockedCleanData(string $lockName, callable $dataCleaner)
+ {
+ while ($this->locker->isLocked($lockName)) {
+- usleep($this->delayTimeout * 1000);
+- }
+- try {
+- if ($this->locker->lock($lockName, $this->lockTimeout / 1000)) {
+- $dataCleaner();
+- }
+- } finally {
+- $this->locker->unlock($lockName);
++ usleep($this->getLookupTimeout() * 1000);
+ }
++
++ $dataCleaner();
++ }
++
++ /**
++ * Delay will be applied as rand($minimalDelayTimeout, $delayTimeout).
++ * This helps to desynchronize multiple clients trying
++ * to acquire the lock for the same resource at the same time
++ *
++ * @return int
++ */
++ private function getLookupTimeout()
++ {
++ return rand($this->minimalDelayTimeout, $this->delayTimeout);
+ }
+ }
diff -Nuar a/vendor/magento/framework/Interception/Config/Config.php b/vendor/magento/framework/Interception/Config/Config.php
--- a/vendor/magento/framework/Interception/Config/Config.php
+++ b/vendor/magento/framework/Interception/Config/Config.php
@@ -675,36 +1085,107 @@ diff -Nuar a/vendor/magento/framework/Interception/Config/Config.php b/vendor/ma
diff -Nuar a/vendor/magento/framework/Lock/Backend/Cache.php b/vendor/magento/framework/Lock/Backend/Cache.php
--- a/vendor/magento/framework/Lock/Backend/Cache.php
+++ b/vendor/magento/framework/Lock/Backend/Cache.php
-@@ -37,6 +37,10 @@ class Cache implements \Magento\Framework\Lock\LockManagerInterface
+@@ -24,12 +24,20 @@ class Cache implements \Magento\Framework\Lock\LockManagerInterface
+ */
+ private $cache;
+
++ /**
++ * Sign for locks, helps to avoid removing a lock that was created by another client
++ *
++ * @string
++ */
++ private $lockSign;
++
+ /**
+ * @param FrontendInterface $cache
+ */
+ public function __construct(FrontendInterface $cache)
+ {
+ $this->cache = $cache;
++ $this->lockSign = $this->generateLockSign();
+ }
+
+ /**
+@@ -37,7 +45,26 @@ class Cache implements \Magento\Framework\Lock\LockManagerInterface
*/
public function lock(string $name, int $timeout = -1): bool
{
-+ if ((bool)$this->cache->test($this->getIdentifier($name))) {
+- return $this->cache->save('1', $this->getIdentifier($name), [], $timeout);
++ if (empty($this->lockSign)) {
++ $this->lockSign = $this->generateLockSign();
++ }
++
++ $data = $this->cache->load($this->getIdentifier($name));
++
++ if (false !== $data) {
+ return false;
+ }
+
- return $this->cache->save('1', $this->getIdentifier($name), [], $timeout);
++ $timeout = $timeout <= 0 ? null : $timeout;
++ $this->cache->save($this->lockSign, $this->getIdentifier($name), [], $timeout);
++
++ $data = $this->cache->load($this->getIdentifier($name));
++
++ if ($data === $this->lockSign) {
++ return true;
++ }
++
++ return false;
}
-diff -Nuar a/vendor/magento/framework/Lock/Backend/FileLock.php b/vendor/magento/framework/Lock/Backend/FileLock.php
---- a/vendor/magento/framework/Lock/Backend/FileLock.php
-+++ b/vendor/magento/framework/Lock/Backend/FileLock.php
-@@ -91,6 +91,7 @@ class FileLock implements LockManagerInterface
+ /**
+@@ -45,7 +72,22 @@ class Cache implements \Magento\Framework\Lock\LockManagerInterface
+ */
+ public function unlock(string $name): bool
+ {
+- return $this->cache->remove($this->getIdentifier($name));
++ if (empty($this->lockSign)) {
++ return false;
++ }
++
++ $data = $this->cache->load($this->getIdentifier($name));
++
++ if (false === $data) {
++ return false;
++ }
++
++ $removeResult = false;
++ if ($data === $this->lockSign) {
++ $removeResult = (bool)$this->cache->remove($this->getIdentifier($name));
++ }
++
++ return $removeResult;
+ }
- while (!$this->tryToLock($fileResource)) {
- if (!$skipDeadline && $deadline <= microtime(true)) {
-+ $this->tryToUnlock($fileResource);
- $this->fileDriver->fileClose($fileResource);
- return false;
- }
-@@ -124,6 +125,7 @@ class FileLock implements LockManagerInterface
- } else {
- $result = true;
- }
-+ $this->tryToUnlock($fileResource);
- $this->fileDriver->fileClose($fileResource);
- }
- } catch (FileSystemException $exception) {
+ /**
+@@ -66,4 +108,27 @@ class Cache implements \Magento\Framework\Lock\LockManagerInterface
+ {
+ return self::LOCK_PREFIX . $cacheIdentifier;
+ }
++
++ /**
++ * Function that generates lock sign that helps to avoid removing a lock that was created by another client.
++ *
++ * @return string
++ */
++ private function generateLockSign()
++ {
++ $sign = implode(
++ '-',
++ [
++ \getmypid(), \crc32(\gethostname())
++ ]
++ );
++
++ try {
++ $sign .= '-' . \bin2hex(\random_bytes(4));
++ } catch (\Exception $e) {
++ $sign .= '-' . \uniqid('-uniqid-');
++ }
++
++ return $sign;
++ }
+ }
diff -Nuar a/setup/src/Magento/Setup/Console/Command/DiCompileCommand.php b/setup/src/Magento/Setup/Console/Command/DiCompileCommand.php
--- a/setup/src/Magento/Setup/Console/Command/DiCompileCommand.php
+++ b/setup/src/Magento/Setup/Console/Command/DiCompileCommand.php
@@ -734,7 +1215,7 @@ diff -Nuar a/setup/src/Magento/Setup/Console/Command/DiCompileCommand.php b/setu
];
return $operations;
-diff -Nuar a/setup/src/Magento/Setup/Module/Di/App/Task/Operation/AppActionListGenerator.php b/setup/src/Magento/Setup/Module/Di/App/Task/Operation/AppActionListGenerator.php
+diff -Nuar a/2.3.5/setup/src/Magento/Setup/Module/Di/App/Task/Operation/AppActionListGenerator.php b/setup/src/Magento/Setup/Module/Di/App/Task/Operation/AppActionListGenerator.php
new file mode 100644
--- /dev/null
+++ b/setup/src/Magento/Setup/Module/Di/App/Task/Operation/AppActionListGenerator.php
@@ -780,7 +1261,7 @@ new file mode 100644
+ /**
+ * @inheritDoc
+ */
-+ public function doOperation(): void
++ public function doOperation()
+ {
+ $actionList = $this->moduleReader->getActionFiles();
+ $this->configWriter->write(
@@ -792,7 +1273,7 @@ new file mode 100644
+ /**
+ * @inheritDoc
+ */
-+ public function getName(): string
++ public function getName()
+ {
+ return 'App action list generation';
+ }
@@ -813,18 +1294,18 @@ diff -Nuar a/setup/src/Magento/Setup/Module/Di/App/Task/OperationFactory.php b/s
class OperationFactory
{
/**
-@@ -48,6 +54,11 @@ class OperationFactory
+@@ -47,6 +53,11 @@ class OperationFactory
+ */
const APPLICATION_CODE_GENERATOR = 'application_code_generator';
- /**
++ /**
+ * Application action list generator
+ */
+ const APPLICATION_ACTION_LIST_GENERATOR = 'application_action_list_generator';
+
-+ /**
+ /**
* Operations definitions
*
- * @var array
@@ -61,6 +72,7 @@ class OperationFactory
self::INTERCEPTION_CACHE => \Magento\Setup\Module\Di\App\Task\Operation\InterceptionCache::class,
self::REPOSITORY_GENERATOR => \Magento\Setup\Module\Di\App\Task\Operation\RepositoryGenerator::class,
@@ -833,3 +1314,175 @@ diff -Nuar a/setup/src/Magento/Setup/Module/Di/App/Task/OperationFactory.php b/s
];
/**
+
+diff -Nuar a/vendor/magento/module-catalog/Model/Product.php b/vendor/magento/module-catalog/Model/Product.php
+--- a/vendor/magento/module-catalog/Model/Product.php
++++ b/vendor/magento/module-catalog/Model/Product.php
+@@ -74,9 +74,9 @@ class Product extends \Magento\Catalog\Model\AbstractModel implements
+ const STORE_ID = 'store_id';
+
+ /**
+- * @var string
++ * @var string|bool
+ */
+- protected $_cacheTag = self::CACHE_TAG;
++ protected $_cacheTag = false;
+
+ /**
+ * @var string
+@@ -912,7 +912,6 @@ class Product extends \Magento\Catalog\Model\AbstractModel implements
+ */
+ public function beforeSave()
+ {
+- $this->cleanCache();
+ $this->setTypeHasOptions(false);
+ $this->setTypeHasRequiredOptions(false);
+ $this->setHasOptions(false);
+diff -Nuar a/vendor/magento/module-config/App/Config/Type/System.php b/vendor/magento/module-config/App/Config/Type/System.php
+--- a/vendor/magento/module-config/App/Config/Type/System.php
++++ b/vendor/magento/module-config/App/Config/Type/System.php
+@@ -20,6 +20,8 @@ use Magento\Framework\Serialize\SerializerInterface;
+ use Magento\Store\Model\Config\Processor\Fallback;
+ use Magento\Framework\Encryption\Encryptor;
+ use Magento\Store\Model\ScopeInterface as StoreScope;
++use Magento\Framework\App\Cache\StateInterface;
++use Magento\Framework\App\Cache\Type\Config;
+
+ /**
+ * System configuration type
+@@ -98,6 +100,12 @@ class System implements ConfigTypeInterface
+ private $lockQuery;
+
+ /**
++ * @var StateInterface
++ */
++ private $cacheState;
++
++ /**
++ * System constructor.
+ * @param ConfigSourceInterface $source
+ * @param PostProcessorInterface $postProcessor
+ * @param Fallback $fallback
+@@ -110,6 +118,7 @@ class System implements ConfigTypeInterface
+ * @param Encryptor|null $encryptor
+ * @param LockManagerInterface|null $locker
+ * @param LockGuardedCacheLoader|null $lockQuery
++ * @param StateInterface|null $cacheState
+ * @SuppressWarnings(PHPMD.UnusedFormalParameter)
+ * @SuppressWarnings(PHPMD.ExcessiveParameterList)
+ */
+@@ -125,7 +134,8 @@ class System implements ConfigTypeInterface
+ Reader $reader = null,
+ Encryptor $encryptor = null,
+ LockManagerInterface $locker = null,
+- LockGuardedCacheLoader $lockQuery = null
++ LockGuardedCacheLoader $lockQuery = null,
++ StateInterface $cacheState = null
+ ) {
+ $this->postProcessor = $postProcessor;
+ $this->cache = $cache;
+@@ -136,6 +146,8 @@ class System implements ConfigTypeInterface
+ ?: ObjectManager::getInstance()->get(Encryptor::class);
+ $this->lockQuery = $lockQuery
+ ?: ObjectManager::getInstance()->get(LockGuardedCacheLoader::class);
++ $this->cacheState = $cacheState
++ ?: ObjectManager::getInstance()->get(StateInterface::class);
+ }
+
+ /**
+@@ -220,6 +232,10 @@ class System implements ConfigTypeInterface
+ */
+ private function loadAllData()
+ {
++ if (!$this->cacheState->isEnabled(Config::TYPE_IDENTIFIER)) {
++ return $this->readData();
++ }
++
+ $loadAction = function () {
+ $cachedData = $this->cache->load($this->configType);
+ $data = false;
+@@ -245,6 +261,10 @@ class System implements ConfigTypeInterface
+ */
+ private function loadDefaultScopeData($scopeType)
+ {
++ if (!$this->cacheState->isEnabled(Config::TYPE_IDENTIFIER)) {
++ return $this->readData();
++ }
++
+ $loadAction = function () use ($scopeType) {
+ $cachedData = $this->cache->load($this->configType . '_' . $scopeType);
+ $scopeData = false;
+@@ -271,6 +291,10 @@ class System implements ConfigTypeInterface
+ */
+ private function loadScopeData($scopeType, $scopeId)
+ {
++ if (!$this->cacheState->isEnabled(Config::TYPE_IDENTIFIER)) {
++ return $this->readData();
++ }
++
+ $loadAction = function () use ($scopeType, $scopeId) {
+ $cachedData = $this->cache->load($this->configType . '_' . $scopeType . '_' . $scopeId);
+ $scopeData = false;
+@@ -393,6 +417,10 @@ class System implements ConfigTypeInterface
+ $this->cache->clean(\Zend_Cache::CLEANING_MODE_MATCHING_TAG, [self::CACHE_TAG]);
+ };
+
++ if (!$this->cacheState->isEnabled(Config::TYPE_IDENTIFIER)) {
++ return $cleanAction();
++ }
++
+ $this->lockQuery->lockedCleanData(
+ self::$lockName,
+ $cleanAction
+diff -Nuar a/vendor/magento/module-config/etc/di.xml b/vendor/magento/module-config/etc/di.xml
+--- a/vendor/magento/module-config/etc/di.xml
++++ b/vendor/magento/module-config/etc/di.xml
+@@ -97,8 +97,6 @@
+
+
+ Magento\Framework\Lock\Backend\Cache
+- 42000
+- 100
+
+
+
+diff -Nuar a/setup/src/Magento/Setup/Model/ConfigOptionsList/Cache.php b/setup/src/Magento/Setup/Model/ConfigOptionsList/Cache.php
+--- a/setup/src/Magento/Setup/Model/ConfigOptionsList/Cache.php
++++ b/setup/src/Magento/Setup/Model/ConfigOptionsList/Cache.php
+@@ -20,7 +20,7 @@ use Magento\Setup\Validator\RedisConnectionValidator;
+ class Cache implements ConfigOptionsListInterface
+ {
+ const INPUT_VALUE_CACHE_REDIS = 'redis';
+- const CONFIG_VALUE_CACHE_REDIS = 'Cm_Cache_Backend_Redis';
++ const CONFIG_VALUE_CACHE_REDIS = \Magento\Framework\Cache\Backend\Redis::class;
+
+ const INPUT_KEY_CACHE_BACKEND = 'cache-backend';
+ const INPUT_KEY_CACHE_BACKEND_REDIS_SERVER = 'cache-backend-redis-server';
+@@ -282,6 +282,6 @@ class Cache implements ConfigOptionsListInterface
+ */
+ private function generateCachePrefix(): string
+ {
+- return substr(\md5(dirname(__DIR__, 6)), 0, 3) . '_';
++ return substr(\hash('sha256', dirname(__DIR__, 6)), 0, 3) . '_';
+ }
+ }
+diff -Nuar a/setup/src/Magento/Setup/Model/ConfigOptionsList/PageCache.php b/setup/src/Magento/Setup/Model/ConfigOptionsList/PageCache.php
+--- a/setup/src/Magento/Setup/Model/ConfigOptionsList/PageCache.php
++++ b/setup/src/Magento/Setup/Model/ConfigOptionsList/PageCache.php
+@@ -20,7 +20,7 @@ use Magento\Setup\Validator\RedisConnectionValidator;
+ class PageCache implements ConfigOptionsListInterface
+ {
+ const INPUT_VALUE_PAGE_CACHE_REDIS = 'redis';
+- const CONFIG_VALUE_PAGE_CACHE_REDIS = 'Cm_Cache_Backend_Redis';
++ const CONFIG_VALUE_PAGE_CACHE_REDIS = \Magento\Framework\Cache\Backend\Redis::class;
+
+ const INPUT_KEY_PAGE_CACHE_BACKEND = 'page-cache';
+ const INPUT_KEY_PAGE_CACHE_BACKEND_REDIS_SERVER = 'page-cache-redis-server';
+@@ -284,6 +284,6 @@ class PageCache implements ConfigOptionsListInterface
+ */
+ private function generateCachePrefix(): string
+ {
+- return substr(\md5(dirname(__DIR__, 6)), 0, 3) . '_';
++ return substr(\hash('sha256', dirname(__DIR__, 6)), 0, 3) . '_';
+ }
+ }
diff --git a/patches/MDVA-27538__fix_performance_issue_in_cache_locking_mechanism__2.3.3.patch b/patches/MCLOUD-6139_MCLOUD-6211__redis_improvement_patches__2.3.3.patch
similarity index 69%
rename from patches/MDVA-27538__fix_performance_issue_in_cache_locking_mechanism__2.3.3.patch
rename to patches/MCLOUD-6139_MCLOUD-6211__redis_improvement_patches__2.3.3.patch
index 4a51005..90ee3df 100644
--- a/patches/MDVA-27538__fix_performance_issue_in_cache_locking_mechanism__2.3.3.patch
+++ b/patches/MCLOUD-6139_MCLOUD-6211__redis_improvement_patches__2.3.3.patch
@@ -1,139 +1,69 @@
-diff --git a/vendor/magento/module-catalog/Model/Product.php b/vendor/magento/module-catalog/Model/Product.php
---- a/vendor/magento/module-catalog/Model/Product.php
-+++ b/vendor/magento/module-catalog/Model/Product.php
-@@ -74,9 +74,9 @@ class Product extends \Magento\Catalog\Model\AbstractModel implements
- const STORE_ID = 'store_id';
-
- /**
-- * @var string
-+ * @var string|bool
- */
-- protected $_cacheTag = self::CACHE_TAG;
-+ protected $_cacheTag = false;
-
- /**
- * @var string
-@@ -912,7 +912,6 @@ class Product extends \Magento\Catalog\Model\AbstractModel implements
- */
- public function beforeSave()
- {
-- $this->cleanCache();
- $this->setTypeHasOptions(false);
- $this->setTypeHasRequiredOptions(false);
- $this->setHasOptions(false);
-diff --git a/vendor/magento/module-config/App/Config/Type/System.php b/vendor/magento/module-config/App/Config/Type/System.php
---- a/vendor/magento/module-config/App/Config/Type/System.php
-+++ b/vendor/magento/module-config/App/Config/Type/System.php
-@@ -20,6 +20,8 @@ use Magento\Framework\Serialize\SerializerInterface;
- use Magento\Store\Model\Config\Processor\Fallback;
- use Magento\Framework\Encryption\Encryptor;
- use Magento\Store\Model\ScopeInterface as StoreScope;
-+use Magento\Framework\App\Cache\StateInterface;
-+use Magento\Framework\App\Cache\Type\Config;
-
+diff -Nuar a/vendor/magento/module-eav/Model/Config.php b/vendor/magento/module-eav/Model/Config.php
+--- a/vendor/magento/module-eav/Model/Config.php
++++ b/vendor/magento/module-eav/Model/Config.php
+@@ -7,12 +7,20 @@ namespace Magento\Eav\Model;
+
+ use Magento\Eav\Model\Entity\Attribute\AbstractAttribute;
+ use Magento\Eav\Model\Entity\Type;
++use Magento\Eav\Model\ResourceModel\Attribute\DefaultEntityAttributes\ProviderInterface;
++use Magento\Framework\App\Config\ScopeConfigInterface;
+ use Magento\Framework\App\ObjectManager;
++use Magento\Framework\Exception\LocalizedException;
++use Magento\Framework\Model\AbstractModel;
+ use Magento\Framework\Serialize\SerializerInterface;
+
/**
- * System configuration type
-@@ -98,6 +100,12 @@ class System implements ConfigTypeInterface
- private $lockQuery;
-
- /**
-+ * @var StateInterface
-+ */
-+ private $cacheState;
-+
++ * EAV config model.
++ *
+ * @api
++ * @SuppressWarnings(PHPMD.TooManyFields)
+ * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
++ * @SuppressWarnings(PHPMD.ExcessiveClassComplexity)
+ * @since 100.0.2
+ */
+ class Config
+@@ -25,6 +33,11 @@ class Config
+ const ATTRIBUTES_CODES_CACHE_ID = 'EAV_ENTITY_ATTRIBUTES_CODES';
+ /**#@-*/
+
+ /**
-+ * System constructor.
- * @param ConfigSourceInterface $source
- * @param PostProcessorInterface $postProcessor
- * @param Fallback $fallback
-@@ -110,6 +118,7 @@ class System implements ConfigTypeInterface
- * @param Encryptor|null $encryptor
- * @param LockManagerInterface|null $locker
- * @param LockGuardedCacheLoader|null $lockQuery
-+ * @param StateInterface|null $cacheState
- * @SuppressWarnings(PHPMD.UnusedFormalParameter)
- * @SuppressWarnings(PHPMD.ExcessiveParameterList)
- */
-@@ -125,7 +134,8 @@ class System implements ConfigTypeInterface
- Reader $reader = null,
- Encryptor $encryptor = null,
- LockManagerInterface $locker = null,
-- LockGuardedCacheLoader $lockQuery = null
-+ LockGuardedCacheLoader $lockQuery = null,
-+ StateInterface $cacheState = null
- ) {
- $this->postProcessor = $postProcessor;
- $this->cache = $cache;
-@@ -136,6 +146,8 @@ class System implements ConfigTypeInterface
- ?: ObjectManager::getInstance()->get(Encryptor::class);
- $this->lockQuery = $lockQuery
- ?: ObjectManager::getInstance()->get(LockGuardedCacheLoader::class);
-+ $this->cacheState = $cacheState
-+ ?: ObjectManager::getInstance()->get(StateInterface::class);
- }
-
- /**
-@@ -220,6 +232,10 @@ class System implements ConfigTypeInterface
- */
- private function loadAllData()
- {
-+ if (!$this->cacheState->isEnabled(Config::TYPE_IDENTIFIER)) {
-+ return $this->readData();
-+ }
++ * Xml path to caching user defined eav attributes configuration.
++ */
++ private const XML_PATH_CACHE_USER_DEFINED_ATTRIBUTES = 'dev/caching/cache_user_defined_attributes';
+
- $loadAction = function () {
- $cachedData = $this->cache->load($this->configType);
- $data = false;
-@@ -245,6 +261,10 @@ class System implements ConfigTypeInterface
+ /**#@-*/
+ protected $_entityTypeData;
+
+@@ -116,6 +129,11 @@ class Config
*/
- private function loadDefaultScopeData($scopeType)
- {
-+ if (!$this->cacheState->isEnabled(Config::TYPE_IDENTIFIER)) {
-+ return $this->readData();
-+ }
+ private $serializer;
+
++ /**
++ * @var ScopeConfigInterface
++ */
++ private $scopeConfig;
+
- $loadAction = function () use ($scopeType) {
- $cachedData = $this->cache->load($this->configType . '_' . $scopeType);
- $scopeData = false;
-@@ -271,6 +291,10 @@ class System implements ConfigTypeInterface
+ /**
+ * Cache of attributes per set
+ *
+@@ -123,13 +141,29 @@ class Config
*/
- private function loadScopeData($scopeType, $scopeId)
- {
-+ if (!$this->cacheState->isEnabled(Config::TYPE_IDENTIFIER)) {
-+ return $this->readData();
-+ }
+ private $attributesPerSet = [];
+
++ /**
++ * Is system attributes loaded flag.
++ *
++ * @var array
++ */
++ private $isSystemAttributesLoaded = [];
+
- $loadAction = function () use ($scopeType, $scopeId) {
- $cachedData = $this->cache->load($this->configType . '_' . $scopeType . '_' . $scopeId);
- $scopeData = false;
-@@ -393,6 +417,10 @@ class System implements ConfigTypeInterface
- $this->cache->clean(\Zend_Cache::CLEANING_MODE_MATCHING_TAG, [self::CACHE_TAG]);
- };
-
-+ if (!$this->cacheState->isEnabled(Config::TYPE_IDENTIFIER)) {
-+ return $cleanAction();
-+ }
++ /**
++ * List of predefined system attributes for preload.
++ *
++ * @var array
++ */
++ private $attributesForPreload;
+
- $this->lockQuery->lockedCleanData(
- self::$lockName,
- $cleanAction
-diff --git a/vendor/magento/module-config/etc/di.xml b/vendor/magento/module-config/etc/di.xml
---- a/vendor/magento/module-config/etc/di.xml
-+++ b/vendor/magento/module-config/etc/di.xml
-@@ -97,8 +97,6 @@
-
-
- Magento\Framework\Lock\Backend\Cache
-- 42000
-- 100
-
-
-
-diff --git a/vendor/magento/module-eav/Model/Config.php b/vendor/magento/module-eav/Model/Config.php
---- a/vendor/magento/module-eav/Model/Config.php
-+++ b/vendor/magento/module-eav/Model/Config.php
-@@ -125,11 +125,11 @@ class Config
-
/**
* @param \Magento\Framework\App\CacheInterface $cache
- * @param \Magento\Eav\Model\Entity\TypeFactory $entityTypeFactory
@@ -144,13 +74,70 @@ diff --git a/vendor/magento/module-eav/Model/Config.php b/vendor/magento/module-
* @param \Magento\Framework\Validator\UniversalFactory $universalFactory
- * @param SerializerInterface $serializer
+ * @param SerializerInterface|null $serializer
++ * @param ScopeConfigInterface|null $scopeConfig
++ * @param array $attributesForPreload
* @codeCoverageIgnore
*/
public function __construct(
-@@ -336,7 +336,9 @@ class Config
+@@ -138,7 +172,9 @@ class Config
+ \Magento\Eav\Model\ResourceModel\Entity\Type\CollectionFactory $entityTypeCollectionFactory,
+ \Magento\Framework\App\Cache\StateInterface $cacheState,
+ \Magento\Framework\Validator\UniversalFactory $universalFactory,
+- SerializerInterface $serializer = null
++ SerializerInterface $serializer = null,
++ ScopeConfigInterface $scopeConfig = null,
++ $attributesForPreload = []
+ ) {
+ $this->_cache = $cache;
+ $this->_entityTypeFactory = $entityTypeFactory;
+@@ -146,6 +182,8 @@ class Config
+ $this->_cacheState = $cacheState;
+ $this->_universalFactory = $universalFactory;
+ $this->serializer = $serializer ?: ObjectManager::getInstance()->get(SerializerInterface::class);
++ $this->scopeConfig = $scopeConfig ?: ObjectManager::getInstance()->get(ScopeConfigInterface::class);
++ $this->attributesForPreload = $attributesForPreload;
+ }
+
+ /**
+@@ -207,8 +245,8 @@ class Config
+ /**
+ * Associate object with identifier
+ *
+- * @param mixed $obj
+- * @param mixed $id
++ * @param mixed $obj
++ * @param mixed $id
+ * @return void
+ * @codeCoverageIgnore
+ */
+@@ -233,8 +271,8 @@ class Config
+ /**
+ * Specify reference for entity type id
+ *
+- * @param int $id
+- * @param string $code
++ * @param int $id
++ * @param string $code
+ * @return $this
+ * @codeCoverageIgnore
+ */
+@@ -258,9 +296,9 @@ class Config
+ /**
+ * Specify reference between entity attribute id and attribute code
+ *
+- * @param int $id
+- * @param string $code
+- * @param string $entityTypeCode
++ * @param int $id
++ * @param string $code
++ * @param string $entityTypeCode
+ * @return $this
+ */
+ protected function _addAttributeReference($id, $code, $entityTypeCode)
+@@ -336,7 +374,9 @@ class Config
}
\Magento\Framework\Profiler::start('EAV: ' . __METHOD__, ['group' => 'EAV', 'method' => __METHOD__]);
-
+
- if ($this->isCacheEnabled() && ($cache = $this->_cache->load(self::ENTITIES_CACHE_ID))) {
+ if ($this->isCacheEnabled() &&
+ ($cache = $this->_cache->load(self::ENTITIES_CACHE_ID))
@@ -158,13 +145,253 @@ diff --git a/vendor/magento/module-eav/Model/Config.php b/vendor/magento/module-
$this->_entityTypeData = $this->serializer->unserialize($cache);
foreach ($this->_entityTypeData as $typeCode => $data) {
$typeId = $data['entity_type_id'];
-diff --git a/vendor/magento/module-eav/Model/ResourceModel/Entity/Attribute/Set.php b/vendor/magento/module-eav/Model/ResourceModel/Entity/Attribute/Set.php
+@@ -484,10 +524,10 @@ class Config
+ /**
+ * Get attribute by code for entity type
+ *
+- * @param mixed $entityType
+- * @param mixed $code
+- * @return AbstractAttribute
+- * @throws \Magento\Framework\Exception\LocalizedException
++ * @param mixed $entityType
++ * @param mixed $code
++ * @return AbstractAttribute
++ * @throws LocalizedException
+ */
+ public function getAttribute($entityType, $code)
+ {
+@@ -507,8 +547,152 @@ class Config
+ return $this->attributes[$entityTypeCode][$code];
+ }
+
++ if (array_key_exists($entityTypeCode, $this->attributesForPreload)
++ && array_key_exists($code, $this->attributesForPreload[$entityTypeCode])
++ ) {
++ $this->initSystemAttributes($entityType, $this->attributesForPreload[$entityTypeCode]);
++ }
++ if (isset($this->attributes[$entityTypeCode][$code])) {
++ \Magento\Framework\Profiler::stop('EAV: ' . __METHOD__);
++ return $this->attributes[$entityTypeCode][$code];
++ }
++
++ if ($this->scopeConfig->getValue(self::XML_PATH_CACHE_USER_DEFINED_ATTRIBUTES)) {
++ $attribute = $this->cacheUserDefinedAttribute($entityType, $entityTypeCode, $code);
++ } else {
++ $attribute = $this->initUserDefinedAttribute($entityType, $entityTypeCode, $code);
++ }
++
++ \Magento\Framework\Profiler::stop('EAV: ' . __METHOD__);
++ return $attribute;
++ }
++
++ /**
++ * Initialize predefined system attributes for preload.
++ *
++ * @param string $entityType
++ * @param array $systemAttributes
++ * @return $this|bool|void
++ * @throws LocalizedException
++ */
++ private function initSystemAttributes($entityType, $systemAttributes)
++ {
++ $entityType = $this->getEntityType($entityType);
++ $entityTypeCode = $entityType->getEntityTypeCode();
++ if (!empty($this->isSystemAttributesLoaded[$entityTypeCode])) {
++ return;
++ }
++
++ $cacheKey = self::ATTRIBUTES_CACHE_ID . '-' . $entityTypeCode . '-preload';
++ if ($this->isCacheEnabled() && ($attributes = $this->_cache->load($cacheKey))) {
++ $attributes = $this->serializer->unserialize($attributes);
++ if ($attributes) {
++ foreach ($attributes as $attribute) {
++ $attributeObject = $this->_createAttribute($entityType, $attribute);
++ $this->saveAttribute($attributeObject, $entityTypeCode, $attributeObject->getAttributeCode());
++ }
++ return true;
++ }
++ }
++
++ \Magento\Framework\Profiler::start('EAV: ' . __METHOD__, ['group' => 'EAV', 'method' => __METHOD__]);
++
++ /** @var \Magento\Eav\Model\ResourceModel\Entity\Attribute\Collection $attributes */
++ $attributes = $this->_universalFactory->create(
++ $entityType->getEntityAttributeCollection()
++ )->setEntityTypeFilter(
++ $entityType
++ )->addFieldToFilter(
++ 'attribute_code',
++ ['in' => array_keys($systemAttributes)]
++ )->getData();
++
++ $attributeData = [];
++ foreach ($attributes as $attribute) {
++ if (empty($attribute['attribute_model'])) {
++ $attribute['attribute_model'] = $entityType->getAttributeModel();
++ }
++ $attributeObject = $this->_createAttribute($entityType, $attribute);
++ $this->saveAttribute($attributeObject, $entityTypeCode, $attributeObject->getAttributeCode());
++ $attributeData[$attribute['attribute_code']] = $attributeObject->toArray();
++ }
++ if ($this->isCacheEnabled()) {
++ $this->_cache->save(
++ $this->serializer->serialize($attributeData),
++ $cacheKey,
++ [
++ \Magento\Eav\Model\Cache\Type::CACHE_TAG,
++ \Magento\Eav\Model\Entity\Attribute::CACHE_TAG
++ ]
++ );
++ }
++
++ \Magento\Framework\Profiler::stop('EAV: ' . __METHOD__);
++ $this->isSystemAttributesLoaded[$entityTypeCode] = true;
++
++ return $this;
++ }
++
++ /**
++ * Initialize user defined attribute from cache or cache it.
++ *
++ * @param string $entityType
++ * @param mixed $entityTypeCode
++ * @param string $code
++ * @return AbstractAttribute
++ * @throws LocalizedException
++ */
++ private function cacheUserDefinedAttribute($entityType, $entityTypeCode, $code): AbstractAttribute
++ {
++ $cacheKey = self::ATTRIBUTES_CACHE_ID . '-attribute-' . $entityTypeCode . '-' . $code;
++ $attributeData = $this->isCacheEnabled() && ($attribute = $this->_cache->load($cacheKey))
++ ? $this->serializer->unserialize($attribute)
++ : null;
++ if ($attributeData) {
++ if (isset($attributeData['attribute_id'])) {
++ $attribute = $this->_createAttribute($entityType, $attributeData);
++ } else {
++ $entityType = $this->getEntityType($entityType);
++ $attribute = $this->createAttribute($entityType->getAttributeModel());
++ $attribute->setAttributeCode($code);
++ $attribute = $this->setAttributeData($attribute, $entityType);
++ }
++ } else {
++ $attribute = $this->createAttributeByAttributeCode($entityType, $code);
++ $this->_addAttributeReference(
++ $attribute->getAttributeId(),
++ $attribute->getAttributeCode(),
++ $entityTypeCode
++ );
++ $this->saveAttribute($attribute, $entityTypeCode, $attribute->getAttributeCode());
++ if ($this->isCacheEnabled()) {
++ $this->_cache->save(
++ $this->serializer->serialize($attribute->getData()),
++ $cacheKey,
++ [
++ \Magento\Eav\Model\Cache\Type::CACHE_TAG,
++ \Magento\Eav\Model\Entity\Attribute::CACHE_TAG
++ ]
++ );
++ }
++ }
++
++ return $attribute;
++ }
++
++ /**
++ * Initialize user defined attribute and save it to memory cache.
++ *
++ * @param mixed $entityType
++ * @param string $entityTypeCode
++ * @param string $code
++ * @return AbstractAttribute|null
++ * @throws LocalizedException
++ */
++ private function initUserDefinedAttribute($entityType, $entityTypeCode, $code): ?AbstractAttribute
++ {
+ $attributes = $this->loadAttributes($entityTypeCode);
+- $attribute = isset($attributes[$code]) ? $attributes[$code] : null;
++ $attribute = $attributes[$code] ?? null;
+ if (!$attribute) {
+ $attribute = $this->createAttributeByAttributeCode($entityType, $code);
+ $this->_addAttributeReference(
+@@ -518,7 +702,7 @@ class Config
+ );
+ $this->saveAttribute($attribute, $entityTypeCode, $attribute->getAttributeCode());
+ }
+- \Magento\Framework\Profiler::stop('EAV: ' . __METHOD__);
++
+ return $attribute;
+ }
+
+@@ -555,8 +739,8 @@ class Config
+ /**
+ * Get all entity type attributes
+ *
+- * @param int|string|Type $entityType
+- * @param \Magento\Framework\DataObject|null $object
++ * @param int|string|Type $entityType
++ * @param \Magento\Framework\DataObject|null $object
+ * @return AbstractAttribute[]
+ *
+ * @SuppressWarnings(PHPMD.CyclomaticComplexity)
+@@ -639,7 +823,11 @@ class Config
+ $existsFullAttribute = $attribute->hasIsRequired();
+ $fullAttributeData = array_key_exists('is_required', $attributeData);
+
+- if ($existsFullAttribute || !$existsFullAttribute && !$fullAttributeData) {
++ if ($existsFullAttribute || (!$existsFullAttribute && !$fullAttributeData)) {
++ $scopeIsRequired = $attributeData['scope_is_required'] ?? null;
++ if ($scopeIsRequired !== null) {
++ $attribute->setData('scope_is_required', $scopeIsRequired);
++ }
+ return $attribute;
+ }
+ }
+@@ -708,6 +896,7 @@ class Config
+ * @param string $entityType
+ * @param string $attributeCode
+ * @return AbstractAttribute
++ * @throws LocalizedException
+ */
+ private function createAttributeByAttributeCode($entityType, $attributeCode)
+ {
+@@ -723,13 +912,28 @@ class Config
+ $attribute->setAttributeCode($attributeCode);
+ }
+
++ $attribute = $this->setAttributeData($attribute, $entityType);
++
++ return $attribute;
++ }
++
++ /**
++ * Set entity type id, backend type, is global to attribute.
++ *
++ * @param AbstractAttribute $attribute
++ * @param AbstractModel $entityType
++ * @return AbstractAttribute
++ */
++ private function setAttributeData($attribute, $entityType): AbstractAttribute
++ {
+ $entity = $entityType->getEntity();
+- if ($entity instanceof \Magento\Eav\Model\ResourceModel\Attribute\DefaultEntityAttributes\ProviderInterface
++ if ($entity instanceof ProviderInterface
+ && in_array($attribute->getAttributeCode(), $entity->getDefaultAttributes(), true)
+ ) {
+ $attribute->setBackendType(AbstractAttribute::TYPE_STATIC)->setIsGlobal(1);
+ }
+ $attribute->setEntityType($entityType)->setEntityTypeId($entityType->getId());
++
+ return $attribute;
+ }
+
+diff -Nuar a/vendor/magento/module-eav/Model/ResourceModel/Entity/Attribute/Set.php b/vendor/magento/module-eav/Model/ResourceModel/Entity/Attribute/Set.php
--- a/vendor/magento/module-eav/Model/ResourceModel/Entity/Attribute/Set.php
+++ b/vendor/magento/module-eav/Model/ResourceModel/Entity/Attribute/Set.php
@@ -6,6 +6,9 @@
-
+
namespace Magento\Eav\Model\ResourceModel\Entity\Attribute;
-
+
+/**
+ * Basic implementation for attribute sets
+ */
@@ -173,7 +400,7 @@ diff --git a/vendor/magento/module-eav/Model/ResourceModel/Entity/Attribute/Set.
/**
@@ -24,8 +27,6 @@ class Set extends \Magento\Framework\Model\ResourceModel\Db\AbstractDb
protected $eavConfig;
-
+
/**
- * Constructor
- *
@@ -182,14 +409,14 @@ diff --git a/vendor/magento/module-eav/Model/ResourceModel/Entity/Attribute/Set.
* @param \Magento\Eav\Model\Config $eavConfig
@@ -54,7 +55,7 @@ class Set extends \Magento\Framework\Model\ResourceModel\Db\AbstractDb
}
-
+
/**
- * Perform actions after object save
+ * Perform actions after object save.
*
* @param \Magento\Framework\Model\AbstractModel $object
* @return $this
-diff --git a/vendor/magento/module-eav/etc/di.xml b/vendor/magento/module-eav/etc/di.xml
+diff -Nuar a/vendor/magento/module-eav/etc/di.xml b/vendor/magento/module-eav/etc/di.xml
--- a/vendor/magento/module-eav/etc/di.xml
+++ b/vendor/magento/module-eav/etc/di.xml
@@ -209,4 +209,14 @@
@@ -207,7 +434,7 @@ diff --git a/vendor/magento/module-eav/etc/di.xml b/vendor/magento/module-eav/et
+
+
-diff --git a/vendor/magento/module-theme/etc/di.xml b/vendor/magento/module-theme/etc/di.xml
+diff -Nuar a/vendor/magento/module-theme/etc/di.xml b/vendor/magento/module-theme/etc/di.xml
--- a/vendor/magento/module-theme/etc/di.xml
+++ b/vendor/magento/module-theme/etc/di.xml
@@ -285,4 +285,24 @@
@@ -235,7 +462,7 @@ diff --git a/vendor/magento/module-theme/etc/di.xml b/vendor/magento/module-them
+
+
-diff --git a/app/etc/di.xml b/app/etc/di.xml
+diff -Nuar a/app/etc/di.xml b/app/etc/di.xml
--- a/app/etc/di.xml
+++ b/app/etc/di.xml
@@ -1779,8 +1779,6 @@
@@ -262,19 +489,19 @@ diff --git a/app/etc/di.xml b/app/etc/di.xml
+
+
-diff --git a/vendor/magento/framework/App/Cache.php b/vendor/magento/framework/App/Cache.php
+diff -Nuar a/vendor/magento/framework/App/Cache.php b/vendor/magento/framework/App/Cache.php
--- a/vendor/magento/framework/App/Cache.php
+++ b/vendor/magento/framework/App/Cache.php
@@ -4,12 +4,11 @@
* See COPYING.txt for license details.
*/
-
+
-/**
- * System cache model
- * support id and tags prefix support,
- */
namespace Magento\Framework\App;
-
+
+/**
+ * System cache model support id and tags prefix support.
+ */
@@ -283,7 +510,7 @@ diff --git a/vendor/magento/framework/App/Cache.php b/vendor/magento/framework/A
/**
@@ -30,12 +29,13 @@ class Cache implements CacheInterface
protected $_frontend;
-
+
/**
- * @param \Magento\Framework\App\Cache\Frontend\Pool $frontendPool
+ * @param Cache\Frontend\Pool $frontendPool
@@ -296,35 +523,35 @@ diff --git a/vendor/magento/framework/App/Cache.php b/vendor/magento/framework/A
- $this->_frontend = $frontendPool->get($this->_frontendIdentifier);
+ $this->_frontend = $frontendPool->get($cacheIdentifier ?? $this->_frontendIdentifier);
}
-
+
/**
-diff --git a/vendor/magento/framework/App/Cache/Frontend/Pool.php b/vendor/magento/framework/App/Cache/Frontend/Pool.php
+diff -Nuar a/vendor/magento/framework/App/Cache/Frontend/Pool.php b/vendor/magento/framework/App/Cache/Frontend/Pool.php
--- a/vendor/magento/framework/App/Cache/Frontend/Pool.php
+++ b/vendor/magento/framework/App/Cache/Frontend/Pool.php
-@@ -147,6 +147,15 @@ class Pool implements \Iterator
+@@ -152,6 +152,15 @@ class Pool implements \Iterator
if (isset($this->_instances[$identifier])) {
return $this->_instances[$identifier];
}
- throw new \InvalidArgumentException("Cache frontend '{$identifier}' is not recognized.");
+
+ if (!isset($this->_instances[self::DEFAULT_FRONTEND_ID])) {
-+ throw new \InvalidArgumentException(sprintf(
-+ 'Cache frontend \'%s\' is not recognized. As well as %s cache is not configured',
-+ $identifier,
-+ self::DEFAULT_FRONTEND_ID
-+ ));
++ throw new \InvalidArgumentException(
++ "Cache frontend '{$identifier}' is not recognized. As well as " .
++ self::DEFAULT_FRONTEND_ID .
++ "cache is not configured"
++ );
+ }
+
+ return $this->_instances[self::DEFAULT_FRONTEND_ID];
}
}
-diff --git a/vendor/magento/framework/App/Router/ActionList.php b/vendor/magento/framework/App/Router/ActionList.php
+diff -Nuar a/vendor/magento/framework/App/Router/ActionList.php b/vendor/magento/framework/App/Router/ActionList.php
--- a/vendor/magento/framework/App/Router/ActionList.php
+++ b/vendor/magento/framework/App/Router/ActionList.php
@@ -5,6 +5,8 @@
*/
namespace Magento\Framework\App\Router;
-
+
+use Magento\Framework\App\Filesystem\DirectoryList;
+use Magento\Framework\App\State;
use Magento\Framework\Serialize\SerializerInterface;
@@ -361,8 +588,8 @@ diff --git a/vendor/magento/framework/App/Router/ActionList.php b/vendor/magento
+ }
}
}
-
-diff --git a/vendor/magento/framework/Cache/Backend/Redis.php b/vendor/magento/framework/Cache/Backend/Redis.php
+
+diff -Nuar a/2.3.5/vendor/magento/framework/Cache/Backend/Redis.php b/vendor/magento/framework/Cache/Backend/Redis.php
new file mode 100644
--- /dev/null
+++ b/vendor/magento/framework/Cache/Backend/Redis.php
@@ -406,7 +633,7 @@ new file mode 100644
+ /**
+ * Load value with given id from cache
+ *
-+ * @param string $id Cache id
++ * @param string $id Cache id
+ * @param boolean $doNotTestCacheValidity If set to true, the cache validity won't be tested
+ * @return bool|string
+ */
@@ -450,7 +677,7 @@ new file mode 100644
+ return true;
+ }
+}
-diff --git a/vendor/magento/framework/Cache/Backend/RemoteSynchronizedCache.php b/vendor/magento/framework/Cache/Backend/RemoteSynchronizedCache.php
+diff -Nuar a/vendor/magento/framework/Cache/Backend/RemoteSynchronizedCache.php b/vendor/magento/framework/Cache/Backend/RemoteSynchronizedCache.php
--- a/vendor/magento/framework/Cache/Backend/RemoteSynchronizedCache.php
+++ b/vendor/magento/framework/Cache/Backend/RemoteSynchronizedCache.php
@@ -9,8 +9,10 @@ namespace Magento\Framework\Cache\Backend;
@@ -468,7 +695,7 @@ diff --git a/vendor/magento/framework/Cache/Backend/RemoteSynchronizedCache.php
{
@@ -36,11 +38,15 @@ class RemoteSynchronizedCache extends \Zend_Cache_Backend implements \Zend_Cache
protected $cacheInvalidationTime;
-
+
/**
- * {@inheritdoc}
+ * Suffix for hash to compare data version in cache storage.
@@ -485,7 +712,7 @@ diff --git a/vendor/magento/framework/Cache/Backend/RemoteSynchronizedCache.php
'remote_backend_autoload' => true,
'remote_backend_options' => [],
@@ -52,6 +58,7 @@ class RemoteSynchronizedCache extends \Zend_Cache_Backend implements \Zend_Cache
-
+
/**
* @param array $options
+ * @throws \Zend_Cache_Exception
@@ -494,7 +721,7 @@ diff --git a/vendor/magento/framework/Cache/Backend/RemoteSynchronizedCache.php
{
@@ -97,76 +104,137 @@ class RemoteSynchronizedCache extends \Zend_Cache_Backend implements \Zend_Cache
}
-
+
/**
- * Update remote cache status info
+ * @inheritdoc
@@ -518,7 +745,7 @@ diff --git a/vendor/magento/framework/Cache/Backend/RemoteSynchronizedCache.php
- $this->cacheInvalidationTime = null;
+ return \hash('sha256', $data);
}
-
+
/**
- * {@inheritdoc}
+ * Load data version by id from remote.
@@ -534,7 +761,7 @@ diff --git a/vendor/magento/framework/Cache/Backend/RemoteSynchronizedCache.php
+ $id . self::HASH_SUFFIX
+ );
}
-
+
/**
- * {@inheritdoc}
+ * Save new data version to remote.
@@ -556,7 +783,7 @@ diff --git a/vendor/magento/framework/Cache/Backend/RemoteSynchronizedCache.php
+ * @param string $id
+ * @return bool
+ */
-+ private function removeRemoteDataVersion($id): bool
++ private function removeRemoteDataVersion($id)
+ {
+ return $this->remote->remove($id . self::HASH_SUFFIX);
+ }
@@ -596,7 +823,7 @@ diff --git a/vendor/magento/framework/Cache/Backend/RemoteSynchronizedCache.php
+
+ return $localData;
}
-
+
/**
- * {@inheritdoc}
+ * @inheritdoc
@@ -606,7 +833,7 @@ diff --git a/vendor/magento/framework/Cache/Backend/RemoteSynchronizedCache.php
- return $this->local->test($id);
+ return $this->local->test($id) ?? $this->remote->test($id);
}
-
+
/**
- * {@inheritdoc}
+ * @inheritdoc
@@ -626,7 +853,7 @@ diff --git a/vendor/magento/framework/Cache/Backend/RemoteSynchronizedCache.php
+
+ return $this->local->save($dataToSave, $id, [], $specificLifetime);
}
-
+
/**
- * {@inheritdoc}
+ * @inheritdoc
@@ -639,7 +866,7 @@ diff --git a/vendor/magento/framework/Cache/Backend/RemoteSynchronizedCache.php
+ $this->remote->remove($id) &&
+ $this->local->remove($id);
}
-
+
/**
- * {@inheritdoc}
+ * @inheritdoc
@@ -650,7 +877,7 @@ diff --git a/vendor/magento/framework/Cache/Backend/RemoteSynchronizedCache.php
- return $this->local->clean($mode, $tags);
+ return $this->remote->clean($mode, $tags);
}
-
+
/**
- * {@inheritdoc}
+ * @inheritdoc
@@ -659,7 +886,7 @@ diff --git a/vendor/magento/framework/Cache/Backend/RemoteSynchronizedCache.php
{
@@ -174,7 +242,7 @@ class RemoteSynchronizedCache extends \Zend_Cache_Backend implements \Zend_Cache
}
-
+
/**
- * {@inheritdoc}
+ * @inheritdoc
@@ -668,7 +895,7 @@ diff --git a/vendor/magento/framework/Cache/Backend/RemoteSynchronizedCache.php
{
@@ -182,7 +250,7 @@ class RemoteSynchronizedCache extends \Zend_Cache_Backend implements \Zend_Cache
}
-
+
/**
- * {@inheritdoc}
+ * @inheritdoc
@@ -677,7 +904,7 @@ diff --git a/vendor/magento/framework/Cache/Backend/RemoteSynchronizedCache.php
{
@@ -190,7 +258,7 @@ class RemoteSynchronizedCache extends \Zend_Cache_Backend implements \Zend_Cache
}
-
+
/**
- * {@inheritdoc}
+ * @inheritdoc
@@ -686,7 +913,7 @@ diff --git a/vendor/magento/framework/Cache/Backend/RemoteSynchronizedCache.php
{
@@ -198,7 +266,7 @@ class RemoteSynchronizedCache extends \Zend_Cache_Backend implements \Zend_Cache
}
-
+
/**
- * {@inheritdoc}
+ * @inheritdoc
@@ -695,7 +922,7 @@ diff --git a/vendor/magento/framework/Cache/Backend/RemoteSynchronizedCache.php
{
@@ -206,7 +274,7 @@ class RemoteSynchronizedCache extends \Zend_Cache_Backend implements \Zend_Cache
}
-
+
/**
- * {@inheritdoc}
+ * @inheritdoc
@@ -704,7 +931,7 @@ diff --git a/vendor/magento/framework/Cache/Backend/RemoteSynchronizedCache.php
{
@@ -214,7 +282,7 @@ class RemoteSynchronizedCache extends \Zend_Cache_Backend implements \Zend_Cache
}
-
+
/**
- * {@inheritdoc}
+ * @inheritdoc
@@ -713,7 +940,7 @@ diff --git a/vendor/magento/framework/Cache/Backend/RemoteSynchronizedCache.php
{
@@ -222,7 +290,7 @@ class RemoteSynchronizedCache extends \Zend_Cache_Backend implements \Zend_Cache
}
-
+
/**
- * {@inheritdoc}
+ * @inheritdoc
@@ -722,20 +949,21 @@ diff --git a/vendor/magento/framework/Cache/Backend/RemoteSynchronizedCache.php
{
@@ -230,7 +298,7 @@ class RemoteSynchronizedCache extends \Zend_Cache_Backend implements \Zend_Cache
}
-
+
/**
- * {@inheritdoc}
+ * @inheritdoc
*/
public function getCapabilities()
{
-diff --git a/vendor/magento/framework/Cache/LockGuardedCacheLoader.php b/vendor/magento/framework/Cache/LockGuardedCacheLoader.php
+diff -Nuar a/vendor/magento/framework/Cache/LockGuardedCacheLoader.php b/vendor/magento/framework/Cache/LockGuardedCacheLoader.php
--- a/vendor/magento/framework/Cache/LockGuardedCacheLoader.php
+++ b/vendor/magento/framework/Cache/LockGuardedCacheLoader.php
-@@ -37,18 +37,43 @@ class LockGuardedCacheLoader
+@@ -36,19 +36,43 @@ class LockGuardedCacheLoader
+ */
private $delayTimeout;
- /**
++ /**
+ * Timeout for information to be collected and saved.
+ * If timeout passed that means that data cannot be saved right now.
+ * And we will just return collected data.
@@ -753,8 +981,7 @@ diff --git a/vendor/magento/framework/Cache/LockGuardedCacheLoader.php b/vendor/
+ */
+ private $minimalDelayTimeout;
+
-+ /**
-+ * LockGuardedCacheLoader constructor.
+ /**
* @param LockManagerInterface $locker
* @param int $lockTimeout
* @param int $delayTimeout
@@ -777,7 +1004,7 @@ diff --git a/vendor/magento/framework/Cache/LockGuardedCacheLoader.php b/vendor/
}
/**
-@@ -67,25 +92,25 @@ class LockGuardedCacheLoader
+@@ -67,25 +91,25 @@ class LockGuardedCacheLoader
callable $dataSaver
) {
$cachedData = $dataLoader(); //optimistic read
@@ -813,7 +1040,7 @@ diff --git a/vendor/magento/framework/Cache/LockGuardedCacheLoader.php b/vendor/
$cachedData = $dataLoader();
}
}
-@@ -103,14 +128,21 @@ class LockGuardedCacheLoader
+@@ -103,14 +127,21 @@ class LockGuardedCacheLoader
public function lockedCleanData(string $lockName, callable $dataCleaner)
{
while ($this->locker->isLocked($lockName)) {
@@ -843,7 +1070,7 @@ diff --git a/vendor/magento/framework/Cache/LockGuardedCacheLoader.php b/vendor/
+ return rand($this->minimalDelayTimeout, $this->delayTimeout);
}
}
-diff --git a/vendor/magento/framework/Interception/Config/Config.php b/vendor/magento/framework/Interception/Config/Config.php
+diff -Nuar a/vendor/magento/framework/Interception/Config/Config.php b/vendor/magento/framework/Interception/Config/Config.php
--- a/vendor/magento/framework/Interception/Config/Config.php
+++ b/vendor/magento/framework/Interception/Config/Config.php
@@ -187,8 +187,6 @@ class Config implements \Magento\Framework\Interception\ConfigInterface
@@ -853,22 +1080,23 @@ diff --git a/vendor/magento/framework/Interception/Config/Config.php b/vendor/ma
- $this->cacheManager->clean($this->_cacheId);
-
$this->generateIntercepted($classDefinitions);
-
+
$this->cacheManager->save($this->_cacheId, $this->_intercepted);
-diff --git a/vendor/magento/framework/Lock/Backend/Cache.php b/vendor/magento/framework/Lock/Backend/Cache.php
+diff -Nuar a/vendor/magento/framework/Lock/Backend/Cache.php b/vendor/magento/framework/Lock/Backend/Cache.php
--- a/vendor/magento/framework/Lock/Backend/Cache.php
+++ b/vendor/magento/framework/Lock/Backend/Cache.php
-@@ -25,11 +25,19 @@ class Cache implements \Magento\Framework\Lock\LockManagerInterface
+@@ -24,12 +24,20 @@ class Cache implements \Magento\Framework\Lock\LockManagerInterface
+ */
private $cache;
- /**
++ /**
+ * Sign for locks, helps to avoid removing a lock that was created by another client
+ *
-+ * @var string
++ * @string
+ */
+ private $lockSign;
+
-+ /**
+ /**
* @param FrontendInterface $cache
*/
public function __construct(FrontendInterface $cache)
@@ -878,7 +1106,7 @@ diff --git a/vendor/magento/framework/Lock/Backend/Cache.php b/vendor/magento/fr
}
/**
-@@ -37,7 +45,24 @@ class Cache implements \Magento\Framework\Lock\LockManagerInterface
+@@ -37,7 +45,26 @@ class Cache implements \Magento\Framework\Lock\LockManagerInterface
*/
public function lock(string $name, int $timeout = -1): bool
{
@@ -890,9 +1118,11 @@ diff --git a/vendor/magento/framework/Lock/Backend/Cache.php b/vendor/magento/fr
+ $data = $this->cache->load($this->getIdentifier($name));
+
+ if (false !== $data) {
-+ return false;
++ return false;
+ }
-+ $this->cache->save($this->lockSign, $this->getIdentifier($name), [], $timeout * 100);
++
++ $timeout = $timeout <= 0 ? null : $timeout;
++ $this->cache->save($this->lockSign, $this->getIdentifier($name), [], $timeout);
+
+ $data = $this->cache->load($this->getIdentifier($name));
+
@@ -904,7 +1134,7 @@ diff --git a/vendor/magento/framework/Lock/Backend/Cache.php b/vendor/magento/fr
}
/**
-@@ -45,7 +70,22 @@ class Cache implements \Magento\Framework\Lock\LockManagerInterface
+@@ -45,7 +72,22 @@ class Cache implements \Magento\Framework\Lock\LockManagerInterface
*/
public function unlock(string $name): bool
{
@@ -928,7 +1158,7 @@ diff --git a/vendor/magento/framework/Lock/Backend/Cache.php b/vendor/magento/fr
}
/**
-@@ -66,4 +106,27 @@ class Cache implements \Magento\Framework\Lock\LockManagerInterface
+@@ -66,4 +108,27 @@ class Cache implements \Magento\Framework\Lock\LockManagerInterface
{
return self::LOCK_PREFIX . $cacheIdentifier;
}
@@ -956,26 +1186,7 @@ diff --git a/vendor/magento/framework/Lock/Backend/Cache.php b/vendor/magento/fr
+ return $sign;
+ }
}
-diff --git a/vendor/magento/framework/Lock/Backend/FileLock.php b/vendor/magento/framework/Lock/Backend/FileLock.php
---- a/vendor/magento/framework/Lock/Backend/FileLock.php
-+++ b/vendor/magento/framework/Lock/Backend/FileLock.php
-@@ -91,6 +91,7 @@ class FileLock implements LockManagerInterface
-
- while (!$this->tryToLock($fileResource)) {
- if (!$skipDeadline && $deadline <= microtime(true)) {
-+ $this->tryToUnlock($fileResource);
- $this->fileDriver->fileClose($fileResource);
- return false;
- }
-@@ -124,6 +125,7 @@ class FileLock implements LockManagerInterface
- } else {
- $result = true;
- }
-+ $this->tryToUnlock($fileResource);
- $this->fileDriver->fileClose($fileResource);
- }
- } catch (FileSystemException $exception) {
-diff --git a/setup/src/Magento/Setup/Console/Command/DiCompileCommand.php b/setup/src/Magento/Setup/Console/Command/DiCompileCommand.php
+diff -Nuar a/setup/src/Magento/Setup/Console/Command/DiCompileCommand.php b/setup/src/Magento/Setup/Console/Command/DiCompileCommand.php
--- a/setup/src/Magento/Setup/Console/Command/DiCompileCommand.php
+++ b/setup/src/Magento/Setup/Console/Command/DiCompileCommand.php
@@ -260,9 +260,12 @@ class DiCompileCommand extends Command
@@ -991,7 +1202,7 @@ diff --git a/setup/src/Magento/Setup/Console/Command/DiCompileCommand.php b/setu
+ },
+ $libraryPaths
+ );
-
+
$excludedLibraryPaths = [
'#^(?:' . join('|', $libraryPaths) . ')/([\\w]+/)?Test#',
@@ -395,7 +398,8 @@ class DiCompileCommand extends Command
@@ -1002,49 +1213,9 @@ diff --git a/setup/src/Magento/Setup/Console/Command/DiCompileCommand.php b/setu
+ ],
+ OperationFactory::APPLICATION_ACTION_LIST_GENERATOR => [],
];
-
+
return $operations;
-diff --git a/setup/src/Magento/Setup/Model/ConfigOptionsList/Cache.php b/setup/src/Magento/Setup/Model/ConfigOptionsList/Cache.php
---- a/setup/src/Magento/Setup/Model/ConfigOptionsList/Cache.php
-+++ b/setup/src/Magento/Setup/Model/ConfigOptionsList/Cache.php
-@@ -20,7 +20,7 @@ use Magento\Setup\Validator\RedisConnectionValidator;
- class Cache implements ConfigOptionsListInterface
- {
- const INPUT_VALUE_CACHE_REDIS = 'redis';
-- const CONFIG_VALUE_CACHE_REDIS = 'Cm_Cache_Backend_Redis';
-+ const CONFIG_VALUE_CACHE_REDIS = '\\Magento\\Framework\\Cache\\Backend\Redis';
-
- const INPUT_KEY_CACHE_BACKEND = 'cache-backend';
- const INPUT_KEY_CACHE_BACKEND_REDIS_SERVER = 'cache-backend-redis-server';
-@@ -282,6 +282,6 @@ class Cache implements ConfigOptionsListInterface
- */
- private function generateCachePrefix(): string
- {
-- return substr(\md5(dirname(__DIR__, 6)), 0, 3) . '_';
-+ return substr(\hash('sha256', dirname(__DIR__, 6)), 0, 3) . '_';
- }
- }
-diff --git a/setup/src/Magento/Setup/Model/ConfigOptionsList/PageCache.php b/setup/src/Magento/Setup/Model/ConfigOptionsList/PageCache.php
---- a/setup/src/Magento/Setup/Model/ConfigOptionsList/PageCache.php
-+++ b/setup/src/Magento/Setup/Model/ConfigOptionsList/PageCache.php
-@@ -20,7 +20,7 @@ use Magento\Setup\Validator\RedisConnectionValidator;
- class PageCache implements ConfigOptionsListInterface
- {
- const INPUT_VALUE_PAGE_CACHE_REDIS = 'redis';
-- const CONFIG_VALUE_PAGE_CACHE_REDIS = 'Cm_Cache_Backend_Redis';
-+ const CONFIG_VALUE_PAGE_CACHE_REDIS = '\\Magento\\Framework\\Cache\\Backend\Redis';
-
- const INPUT_KEY_PAGE_CACHE_BACKEND = 'page-cache';
- const INPUT_KEY_PAGE_CACHE_BACKEND_REDIS_SERVER = 'page-cache-redis-server';
-@@ -284,6 +284,6 @@ class PageCache implements ConfigOptionsListInterface
- */
- private function generateCachePrefix(): string
- {
-- return substr(\md5(dirname(__DIR__, 6)), 0, 3) . '_';
-+ return substr(\hash('sha256', dirname(__DIR__, 6)), 0, 3) . '_';
- }
- }
-diff --git a/setup/src/Magento/Setup/Module/Di/App/Task/Operation/AppActionListGenerator.php b/setup/src/Magento/Setup/Module/Di/App/Task/Operation/AppActionListGenerator.php
+diff -Nuar a/2.3.5/setup/src/Magento/Setup/Module/Di/App/Task/Operation/AppActionListGenerator.php b/setup/src/Magento/Setup/Module/Di/App/Task/Operation/AppActionListGenerator.php
new file mode 100644
--- /dev/null
+++ b/setup/src/Magento/Setup/Module/Di/App/Task/Operation/AppActionListGenerator.php
@@ -1090,7 +1261,7 @@ new file mode 100644
+ /**
+ * @inheritDoc
+ */
-+ public function doOperation(): void
++ public function doOperation()
+ {
+ $actionList = $this->moduleReader->getActionFiles();
+ $this->configWriter->write(
@@ -1102,18 +1273,18 @@ new file mode 100644
+ /**
+ * @inheritDoc
+ */
-+ public function getName(): string
++ public function getName()
+ {
+ return 'App action list generation';
+ }
+}
-diff --git a/setup/src/Magento/Setup/Module/Di/App/Task/OperationFactory.php b/setup/src/Magento/Setup/Module/Di/App/Task/OperationFactory.php
+diff -Nuar a/setup/src/Magento/Setup/Module/Di/App/Task/OperationFactory.php b/setup/src/Magento/Setup/Module/Di/App/Task/OperationFactory.php
--- a/setup/src/Magento/Setup/Module/Di/App/Task/OperationFactory.php
+++ b/setup/src/Magento/Setup/Module/Di/App/Task/OperationFactory.php
@@ -5,6 +5,12 @@
*/
namespace Magento\Setup\Module\Di\App\Task;
-
+
+use Magento\Setup\Module\Di\App\Task\Operation\AppActionListGenerator;
+use Magento\Setup\Module\Di\App\Task\Operation\PluginListGenerator;
+
@@ -1123,23 +1294,195 @@ diff --git a/setup/src/Magento/Setup/Module/Di/App/Task/OperationFactory.php b/s
class OperationFactory
{
/**
-@@ -48,6 +54,11 @@ class OperationFactory
+@@ -47,6 +53,11 @@ class OperationFactory
+ */
const APPLICATION_CODE_GENERATOR = 'application_code_generator';
-
- /**
+
++ /**
+ * Application action list generator
+ */
+ const APPLICATION_ACTION_LIST_GENERATOR = 'application_action_list_generator';
+
-+ /**
+ /**
* Operations definitions
*
- * @var array
@@ -61,6 +72,7 @@ class OperationFactory
self::INTERCEPTION_CACHE => \Magento\Setup\Module\Di\App\Task\Operation\InterceptionCache::class,
self::REPOSITORY_GENERATOR => \Magento\Setup\Module\Di\App\Task\Operation\RepositoryGenerator::class,
self::PROXY_GENERATOR => \Magento\Setup\Module\Di\App\Task\Operation\ProxyGenerator::class,
+ self::APPLICATION_ACTION_LIST_GENERATOR => AppActionListGenerator::class,
];
+
+ /**
+
+diff -Nuar a/vendor/magento/module-catalog/Model/Product.php b/vendor/magento/module-catalog/Model/Product.php
+--- a/vendor/magento/module-catalog/Model/Product.php
++++ b/vendor/magento/module-catalog/Model/Product.php
+@@ -74,9 +74,9 @@ class Product extends \Magento\Catalog\Model\AbstractModel implements
+ const STORE_ID = 'store_id';
+
+ /**
+- * @var string
++ * @var string|bool
+ */
+- protected $_cacheTag = self::CACHE_TAG;
++ protected $_cacheTag = false;
+
+ /**
+ * @var string
+@@ -912,7 +912,6 @@ class Product extends \Magento\Catalog\Model\AbstractModel implements
+ */
+ public function beforeSave()
+ {
+- $this->cleanCache();
+ $this->setTypeHasOptions(false);
+ $this->setTypeHasRequiredOptions(false);
+ $this->setHasOptions(false);
+diff -Nuar a/vendor/magento/module-config/App/Config/Type/System.php b/vendor/magento/module-config/App/Config/Type/System.php
+--- a/vendor/magento/module-config/App/Config/Type/System.php
++++ b/vendor/magento/module-config/App/Config/Type/System.php
+@@ -20,6 +20,8 @@ use Magento\Framework\Serialize\SerializerInterface;
+ use Magento\Store\Model\Config\Processor\Fallback;
+ use Magento\Framework\Encryption\Encryptor;
+ use Magento\Store\Model\ScopeInterface as StoreScope;
++use Magento\Framework\App\Cache\StateInterface;
++use Magento\Framework\App\Cache\Type\Config;
+
+ /**
+ * System configuration type
+@@ -98,6 +100,12 @@ class System implements ConfigTypeInterface
+ private $lockQuery;
+
+ /**
++ * @var StateInterface
++ */
++ private $cacheState;
++
++ /**
++ * System constructor.
+ * @param ConfigSourceInterface $source
+ * @param PostProcessorInterface $postProcessor
+ * @param Fallback $fallback
+@@ -110,6 +118,7 @@ class System implements ConfigTypeInterface
+ * @param Encryptor|null $encryptor
+ * @param LockManagerInterface|null $locker
+ * @param LockGuardedCacheLoader|null $lockQuery
++ * @param StateInterface|null $cacheState
+ * @SuppressWarnings(PHPMD.UnusedFormalParameter)
+ * @SuppressWarnings(PHPMD.ExcessiveParameterList)
+ */
+@@ -125,7 +134,8 @@ class System implements ConfigTypeInterface
+ Reader $reader = null,
+ Encryptor $encryptor = null,
+ LockManagerInterface $locker = null,
+- LockGuardedCacheLoader $lockQuery = null
++ LockGuardedCacheLoader $lockQuery = null,
++ StateInterface $cacheState = null
+ ) {
+ $this->postProcessor = $postProcessor;
+ $this->cache = $cache;
+@@ -136,6 +146,8 @@ class System implements ConfigTypeInterface
+ ?: ObjectManager::getInstance()->get(Encryptor::class);
+ $this->lockQuery = $lockQuery
+ ?: ObjectManager::getInstance()->get(LockGuardedCacheLoader::class);
++ $this->cacheState = $cacheState
++ ?: ObjectManager::getInstance()->get(StateInterface::class);
+ }
/**
+@@ -220,6 +232,10 @@ class System implements ConfigTypeInterface
+ */
+ private function loadAllData()
+ {
++ if (!$this->cacheState->isEnabled(Config::TYPE_IDENTIFIER)) {
++ return $this->readData();
++ }
++
+ $loadAction = function () {
+ $cachedData = $this->cache->load($this->configType);
+ $data = false;
+@@ -245,6 +261,10 @@ class System implements ConfigTypeInterface
+ */
+ private function loadDefaultScopeData($scopeType)
+ {
++ if (!$this->cacheState->isEnabled(Config::TYPE_IDENTIFIER)) {
++ return $this->readData();
++ }
++
+ $loadAction = function () use ($scopeType) {
+ $cachedData = $this->cache->load($this->configType . '_' . $scopeType);
+ $scopeData = false;
+@@ -271,6 +291,10 @@ class System implements ConfigTypeInterface
+ */
+ private function loadScopeData($scopeType, $scopeId)
+ {
++ if (!$this->cacheState->isEnabled(Config::TYPE_IDENTIFIER)) {
++ return $this->readData();
++ }
++
+ $loadAction = function () use ($scopeType, $scopeId) {
+ $cachedData = $this->cache->load($this->configType . '_' . $scopeType . '_' . $scopeId);
+ $scopeData = false;
+@@ -393,6 +417,10 @@ class System implements ConfigTypeInterface
+ $this->cache->clean(\Zend_Cache::CLEANING_MODE_MATCHING_TAG, [self::CACHE_TAG]);
+ };
+
++ if (!$this->cacheState->isEnabled(Config::TYPE_IDENTIFIER)) {
++ return $cleanAction();
++ }
++
+ $this->lockQuery->lockedCleanData(
+ self::$lockName,
+ $cleanAction
+diff -Nuar a/vendor/magento/module-config/etc/di.xml b/vendor/magento/module-config/etc/di.xml
+--- a/vendor/magento/module-config/etc/di.xml
++++ b/vendor/magento/module-config/etc/di.xml
+@@ -97,8 +97,6 @@
+
+
+ Magento\Framework\Lock\Backend\Cache
+- 42000
+- 100
+
+
+
+diff -Nuar a/setup/src/Magento/Setup/Model/ConfigOptionsList/Cache.php b/setup/src/Magento/Setup/Model/ConfigOptionsList/Cache.php
+--- a/setup/src/Magento/Setup/Model/ConfigOptionsList/Cache.php
++++ b/setup/src/Magento/Setup/Model/ConfigOptionsList/Cache.php
+@@ -20,7 +20,7 @@ use Magento\Setup\Validator\RedisConnectionValidator;
+ class Cache implements ConfigOptionsListInterface
+ {
+ const INPUT_VALUE_CACHE_REDIS = 'redis';
+- const CONFIG_VALUE_CACHE_REDIS = 'Cm_Cache_Backend_Redis';
++ const CONFIG_VALUE_CACHE_REDIS = \Magento\Framework\Cache\Backend\Redis::class;
+
+ const INPUT_KEY_CACHE_BACKEND = 'cache-backend';
+ const INPUT_KEY_CACHE_BACKEND_REDIS_SERVER = 'cache-backend-redis-server';
+@@ -282,6 +282,6 @@ class Cache implements ConfigOptionsListInterface
+ */
+ private function generateCachePrefix(): string
+ {
+- return substr(\md5(dirname(__DIR__, 6)), 0, 3) . '_';
++ return substr(\hash('sha256', dirname(__DIR__, 6)), 0, 3) . '_';
+ }
+ }
+diff -Nuar a/setup/src/Magento/Setup/Model/ConfigOptionsList/PageCache.php b/setup/src/Magento/Setup/Model/ConfigOptionsList/PageCache.php
+--- a/setup/src/Magento/Setup/Model/ConfigOptionsList/PageCache.php
++++ b/setup/src/Magento/Setup/Model/ConfigOptionsList/PageCache.php
+@@ -20,7 +20,7 @@ use Magento\Setup\Validator\RedisConnectionValidator;
+ class PageCache implements ConfigOptionsListInterface
+ {
+ const INPUT_VALUE_PAGE_CACHE_REDIS = 'redis';
+- const CONFIG_VALUE_PAGE_CACHE_REDIS = 'Cm_Cache_Backend_Redis';
++ const CONFIG_VALUE_PAGE_CACHE_REDIS = \Magento\Framework\Cache\Backend\Redis::class;
+
+ const INPUT_KEY_PAGE_CACHE_BACKEND = 'page-cache';
+ const INPUT_KEY_PAGE_CACHE_BACKEND_REDIS_SERVER = 'page-cache-redis-server';
+@@ -284,6 +284,6 @@ class PageCache implements ConfigOptionsListInterface
+ */
+ private function generateCachePrefix(): string
+ {
+- return substr(\md5(dirname(__DIR__, 6)), 0, 3) . '_';
++ return substr(\hash('sha256', dirname(__DIR__, 6)), 0, 3) . '_';
+ }
+ }
diff --git a/patches/MDVA-27538__fix_performance_issue_in_cache_locking_mechanism__2.3.3-p1.patch b/patches/MCLOUD-6139_MCLOUD-6211__redis_improvement_patches__2.3.4.patch
similarity index 86%
rename from patches/MDVA-27538__fix_performance_issue_in_cache_locking_mechanism__2.3.3-p1.patch
rename to patches/MCLOUD-6139_MCLOUD-6211__redis_improvement_patches__2.3.4.patch
index f2bb855..a46048a 100644
--- a/patches/MDVA-27538__fix_performance_issue_in_cache_locking_mechanism__2.3.3-p1.patch
+++ b/patches/MCLOUD-6139_MCLOUD-6211__redis_improvement_patches__2.3.4.patch
@@ -1,139 +1,8 @@
-diff --git a/vendor/magento/module-catalog/Model/Product.php b/vendor/magento/module-catalog/Model/Product.php
---- a/vendor/magento/module-catalog/Model/Product.php
-+++ b/vendor/magento/module-catalog/Model/Product.php
-@@ -74,9 +74,9 @@ class Product extends \Magento\Catalog\Model\AbstractModel implements
- const STORE_ID = 'store_id';
-
- /**
-- * @var string
-+ * @var string|bool
- */
-- protected $_cacheTag = self::CACHE_TAG;
-+ protected $_cacheTag = false;
-
- /**
- * @var string
-@@ -912,7 +912,6 @@ class Product extends \Magento\Catalog\Model\AbstractModel implements
- */
- public function beforeSave()
- {
-- $this->cleanCache();
- $this->setTypeHasOptions(false);
- $this->setTypeHasRequiredOptions(false);
- $this->setHasOptions(false);
-diff --git a/vendor/magento/module-config/App/Config/Type/System.php b/vendor/magento/module-config/App/Config/Type/System.php
---- a/vendor/magento/module-config/App/Config/Type/System.php
-+++ b/vendor/magento/module-config/App/Config/Type/System.php
-@@ -20,6 +20,8 @@ use Magento\Framework\Serialize\SerializerInterface;
- use Magento\Store\Model\Config\Processor\Fallback;
- use Magento\Framework\Encryption\Encryptor;
- use Magento\Store\Model\ScopeInterface as StoreScope;
-+use Magento\Framework\App\Cache\StateInterface;
-+use Magento\Framework\App\Cache\Type\Config;
-
- /**
- * System configuration type
-@@ -98,6 +100,12 @@ class System implements ConfigTypeInterface
- private $lockQuery;
-
- /**
-+ * @var StateInterface
-+ */
-+ private $cacheState;
-+
-+ /**
-+ * System constructor.
- * @param ConfigSourceInterface $source
- * @param PostProcessorInterface $postProcessor
- * @param Fallback $fallback
-@@ -110,6 +118,7 @@ class System implements ConfigTypeInterface
- * @param Encryptor|null $encryptor
- * @param LockManagerInterface|null $locker
- * @param LockGuardedCacheLoader|null $lockQuery
-+ * @param StateInterface|null $cacheState
- * @SuppressWarnings(PHPMD.UnusedFormalParameter)
- * @SuppressWarnings(PHPMD.ExcessiveParameterList)
- */
-@@ -125,7 +134,8 @@ class System implements ConfigTypeInterface
- Reader $reader = null,
- Encryptor $encryptor = null,
- LockManagerInterface $locker = null,
-- LockGuardedCacheLoader $lockQuery = null
-+ LockGuardedCacheLoader $lockQuery = null,
-+ StateInterface $cacheState = null
- ) {
- $this->postProcessor = $postProcessor;
- $this->cache = $cache;
-@@ -136,6 +146,8 @@ class System implements ConfigTypeInterface
- ?: ObjectManager::getInstance()->get(Encryptor::class);
- $this->lockQuery = $lockQuery
- ?: ObjectManager::getInstance()->get(LockGuardedCacheLoader::class);
-+ $this->cacheState = $cacheState
-+ ?: ObjectManager::getInstance()->get(StateInterface::class);
- }
-
- /**
-@@ -220,6 +232,10 @@ class System implements ConfigTypeInterface
- */
- private function loadAllData()
- {
-+ if (!$this->cacheState->isEnabled(Config::TYPE_IDENTIFIER)) {
-+ return $this->readData();
-+ }
-+
- $loadAction = function () {
- $cachedData = $this->cache->load($this->configType);
- $data = false;
-@@ -245,6 +261,10 @@ class System implements ConfigTypeInterface
- */
- private function loadDefaultScopeData($scopeType)
- {
-+ if (!$this->cacheState->isEnabled(Config::TYPE_IDENTIFIER)) {
-+ return $this->readData();
-+ }
-+
- $loadAction = function () use ($scopeType) {
- $cachedData = $this->cache->load($this->configType . '_' . $scopeType);
- $scopeData = false;
-@@ -271,6 +291,10 @@ class System implements ConfigTypeInterface
- */
- private function loadScopeData($scopeType, $scopeId)
- {
-+ if (!$this->cacheState->isEnabled(Config::TYPE_IDENTIFIER)) {
-+ return $this->readData();
-+ }
-+
- $loadAction = function () use ($scopeType, $scopeId) {
- $cachedData = $this->cache->load($this->configType . '_' . $scopeType . '_' . $scopeId);
- $scopeData = false;
-@@ -393,6 +417,10 @@ class System implements ConfigTypeInterface
- $this->cache->clean(\Zend_Cache::CLEANING_MODE_MATCHING_TAG, [self::CACHE_TAG]);
- };
-
-+ if (!$this->cacheState->isEnabled(Config::TYPE_IDENTIFIER)) {
-+ return $cleanAction();
-+ }
-+
- $this->lockQuery->lockedCleanData(
- self::$lockName,
- $cleanAction
-diff --git a/vendor/magento/module-config/etc/di.xml b/vendor/magento/module-config/etc/di.xml
---- a/vendor/magento/module-config/etc/di.xml
-+++ b/vendor/magento/module-config/etc/di.xml
-@@ -97,8 +97,6 @@
-
-
- Magento\Framework\Lock\Backend\Cache
-- 42000
-- 100
-
-
-
-diff --git a/vendor/magento/module-eav/Model/Config.php b/vendor/magento/module-eav/Model/Config.php
+diff -Nuar a/vendor/magento/module-eav/Model/Config.php b/vendor/magento/module-eav/Model/Config.php
--- a/vendor/magento/module-eav/Model/Config.php
+++ b/vendor/magento/module-eav/Model/Config.php
-@@ -125,11 +125,11 @@ class Config
-
+@@ -157,12 +157,12 @@ class Config
+
/**
* @param \Magento\Framework\App\CacheInterface $cache
- * @param \Magento\Eav\Model\Entity\TypeFactory $entityTypeFactory
@@ -143,14 +12,51 @@ diff --git a/vendor/magento/module-eav/Model/Config.php b/vendor/magento/module-
* @param \Magento\Framework\App\Cache\StateInterface $cacheState
* @param \Magento\Framework\Validator\UniversalFactory $universalFactory
- * @param SerializerInterface $serializer
+- * @param ScopeConfigInterface $scopeConfig
+ * @param SerializerInterface|null $serializer
++ * @param ScopeConfigInterface|null $scopeConfig
+ * @param array $attributesForPreload
* @codeCoverageIgnore
*/
- public function __construct(
-@@ -336,7 +336,9 @@ class Config
+@@ -245,8 +245,8 @@ class Config
+ /**
+ * Associate object with identifier
+ *
+- * @param mixed $obj
+- * @param mixed $id
++ * @param mixed $obj
++ * @param mixed $id
+ * @return void
+ * @codeCoverageIgnore
+ */
+@@ -271,8 +271,8 @@ class Config
+ /**
+ * Specify reference for entity type id
+ *
+- * @param int $id
+- * @param string $code
++ * @param int $id
++ * @param string $code
+ * @return $this
+ * @codeCoverageIgnore
+ */
+@@ -296,9 +296,9 @@ class Config
+ /**
+ * Specify reference between entity attribute id and attribute code
+ *
+- * @param int $id
+- * @param string $code
+- * @param string $entityTypeCode
++ * @param int $id
++ * @param string $code
++ * @param string $entityTypeCode
+ * @return $this
+ */
+ protected function _addAttributeReference($id, $code, $entityTypeCode)
+@@ -374,7 +374,9 @@ class Config
}
\Magento\Framework\Profiler::start('EAV: ' . __METHOD__, ['group' => 'EAV', 'method' => __METHOD__]);
-
+
- if ($this->isCacheEnabled() && ($cache = $this->_cache->load(self::ENTITIES_CACHE_ID))) {
+ if ($this->isCacheEnabled() &&
+ ($cache = $this->_cache->load(self::ENTITIES_CACHE_ID))
@@ -158,13 +64,48 @@ diff --git a/vendor/magento/module-eav/Model/Config.php b/vendor/magento/module-
$this->_entityTypeData = $this->serializer->unserialize($cache);
foreach ($this->_entityTypeData as $typeCode => $data) {
$typeId = $data['entity_type_id'];
-diff --git a/vendor/magento/module-eav/Model/ResourceModel/Entity/Attribute/Set.php b/vendor/magento/module-eav/Model/ResourceModel/Entity/Attribute/Set.php
+@@ -522,9 +524,9 @@ class Config
+ /**
+ * Get attribute by code for entity type
+ *
+- * @param mixed $entityType
+- * @param mixed $code
+- * @return AbstractAttribute
++ * @param mixed $entityType
++ * @param mixed $code
++ * @return AbstractAttribute
+ * @throws LocalizedException
+ */
+ public function getAttribute($entityType, $code)
+@@ -737,8 +739,8 @@ class Config
+ /**
+ * Get all entity type attributes
+ *
+- * @param int|string|Type $entityType
+- * @param \Magento\Framework\DataObject|null $object
++ * @param int|string|Type $entityType
++ * @param \Magento\Framework\DataObject|null $object
+ * @return AbstractAttribute[]
+ *
+ * @SuppressWarnings(PHPMD.CyclomaticComplexity)
+@@ -822,6 +824,10 @@ class Config
+ $fullAttributeData = array_key_exists('is_required', $attributeData);
+
+ if ($existsFullAttribute || (!$existsFullAttribute && !$fullAttributeData)) {
++ $scopeIsRequired = $attributeData['scope_is_required'] ?? null;
++ if ($scopeIsRequired !== null) {
++ $attribute->setData('scope_is_required', $scopeIsRequired);
++ }
+ return $attribute;
+ }
+ }
+diff -Nuar a/vendor/magento/module-eav/Model/ResourceModel/Entity/Attribute/Set.php b/vendor/magento/module-eav/Model/ResourceModel/Entity/Attribute/Set.php
--- a/vendor/magento/module-eav/Model/ResourceModel/Entity/Attribute/Set.php
+++ b/vendor/magento/module-eav/Model/ResourceModel/Entity/Attribute/Set.php
@@ -6,6 +6,9 @@
-
+
namespace Magento\Eav\Model\ResourceModel\Entity\Attribute;
-
+
+/**
+ * Basic implementation for attribute sets
+ */
@@ -173,7 +114,7 @@ diff --git a/vendor/magento/module-eav/Model/ResourceModel/Entity/Attribute/Set.
/**
@@ -24,8 +27,6 @@ class Set extends \Magento\Framework\Model\ResourceModel\Db\AbstractDb
protected $eavConfig;
-
+
/**
- * Constructor
- *
@@ -182,14 +123,14 @@ diff --git a/vendor/magento/module-eav/Model/ResourceModel/Entity/Attribute/Set.
* @param \Magento\Eav\Model\Config $eavConfig
@@ -54,7 +55,7 @@ class Set extends \Magento\Framework\Model\ResourceModel\Db\AbstractDb
}
-
+
/**
- * Perform actions after object save
+ * Perform actions after object save.
*
* @param \Magento\Framework\Model\AbstractModel $object
* @return $this
-diff --git a/vendor/magento/module-eav/etc/di.xml b/vendor/magento/module-eav/etc/di.xml
+diff -Nuar a/vendor/magento/module-eav/etc/di.xml b/vendor/magento/module-eav/etc/di.xml
--- a/vendor/magento/module-eav/etc/di.xml
+++ b/vendor/magento/module-eav/etc/di.xml
@@ -209,4 +209,14 @@
@@ -207,7 +148,7 @@ diff --git a/vendor/magento/module-eav/etc/di.xml b/vendor/magento/module-eav/et
+
+
-diff --git a/vendor/magento/module-theme/etc/di.xml b/vendor/magento/module-theme/etc/di.xml
+diff -Nuar a/vendor/magento/module-theme/etc/di.xml b/vendor/magento/module-theme/etc/di.xml
--- a/vendor/magento/module-theme/etc/di.xml
+++ b/vendor/magento/module-theme/etc/di.xml
@@ -285,4 +285,24 @@
@@ -235,10 +176,10 @@ diff --git a/vendor/magento/module-theme/etc/di.xml b/vendor/magento/module-them
+
+
-diff --git a/app/etc/di.xml b/app/etc/di.xml
+diff -Nuar a/app/etc/di.xml b/app/etc/di.xml
--- a/app/etc/di.xml
+++ b/app/etc/di.xml
-@@ -1779,8 +1779,6 @@
+@@ -1780,8 +1780,6 @@
Magento\Framework\Lock\Backend\Cache
@@ -247,9 +188,9 @@ diff --git a/app/etc/di.xml b/app/etc/di.xml
-@@ -1795,4 +1793,14 @@
-
+@@ -1800,4 +1798,14 @@
+
+
+
+
@@ -258,23 +199,23 @@ diff --git a/app/etc/di.xml b/app/etc/di.xml
+
+
+
-+ configured_block_cache
++ configured_block_cache
+
+
-diff --git a/vendor/magento/framework/App/Cache.php b/vendor/magento/framework/App/Cache.php
+diff -Nuar a/vendor/magento/framework/App/Cache.php b/vendor/magento/framework/App/Cache.php
--- a/vendor/magento/framework/App/Cache.php
+++ b/vendor/magento/framework/App/Cache.php
@@ -4,12 +4,11 @@
* See COPYING.txt for license details.
*/
-
+
-/**
- * System cache model
- * support id and tags prefix support,
- */
namespace Magento\Framework\App;
-
+
+/**
+ * System cache model support id and tags prefix support.
+ */
@@ -283,7 +224,7 @@ diff --git a/vendor/magento/framework/App/Cache.php b/vendor/magento/framework/A
/**
@@ -30,12 +29,13 @@ class Cache implements CacheInterface
protected $_frontend;
-
+
/**
- * @param \Magento\Framework\App\Cache\Frontend\Pool $frontendPool
+ * @param Cache\Frontend\Pool $frontendPool
@@ -296,35 +237,35 @@ diff --git a/vendor/magento/framework/App/Cache.php b/vendor/magento/framework/A
- $this->_frontend = $frontendPool->get($this->_frontendIdentifier);
+ $this->_frontend = $frontendPool->get($cacheIdentifier ?? $this->_frontendIdentifier);
}
-
+
/**
-diff --git a/vendor/magento/framework/App/Cache/Frontend/Pool.php b/vendor/magento/framework/App/Cache/Frontend/Pool.php
+diff -Nuar a/vendor/magento/framework/App/Cache/Frontend/Pool.php b/vendor/magento/framework/App/Cache/Frontend/Pool.php
--- a/vendor/magento/framework/App/Cache/Frontend/Pool.php
+++ b/vendor/magento/framework/App/Cache/Frontend/Pool.php
-@@ -147,6 +147,15 @@ class Pool implements \Iterator
+@@ -152,6 +152,15 @@ class Pool implements \Iterator
if (isset($this->_instances[$identifier])) {
return $this->_instances[$identifier];
}
- throw new \InvalidArgumentException("Cache frontend '{$identifier}' is not recognized.");
+
+ if (!isset($this->_instances[self::DEFAULT_FRONTEND_ID])) {
-+ throw new \InvalidArgumentException(sprintf(
-+ 'Cache frontend \'%s\' is not recognized. As well as %s cache is not configured',
-+ $identifier,
-+ self::DEFAULT_FRONTEND_ID
-+ ));
++ throw new \InvalidArgumentException(
++ "Cache frontend '{$identifier}' is not recognized. As well as " .
++ self::DEFAULT_FRONTEND_ID .
++ "cache is not configured"
++ );
+ }
+
+ return $this->_instances[self::DEFAULT_FRONTEND_ID];
}
}
-diff --git a/vendor/magento/framework/App/Router/ActionList.php b/vendor/magento/framework/App/Router/ActionList.php
+diff -Nuar a/vendor/magento/framework/App/Router/ActionList.php b/vendor/magento/framework/App/Router/ActionList.php
--- a/vendor/magento/framework/App/Router/ActionList.php
+++ b/vendor/magento/framework/App/Router/ActionList.php
@@ -5,6 +5,8 @@
*/
namespace Magento\Framework\App\Router;
-
+
+use Magento\Framework\App\Filesystem\DirectoryList;
+use Magento\Framework\App\State;
use Magento\Framework\Serialize\SerializerInterface;
@@ -361,8 +302,8 @@ diff --git a/vendor/magento/framework/App/Router/ActionList.php b/vendor/magento
+ }
}
}
-
-diff --git a/vendor/magento/framework/Cache/Backend/Redis.php b/vendor/magento/framework/Cache/Backend/Redis.php
+
+diff -Nuar a/2.3.5/vendor/magento/framework/Cache/Backend/Redis.php b/vendor/magento/framework/Cache/Backend/Redis.php
new file mode 100644
--- /dev/null
+++ b/vendor/magento/framework/Cache/Backend/Redis.php
@@ -406,7 +347,7 @@ new file mode 100644
+ /**
+ * Load value with given id from cache
+ *
-+ * @param string $id Cache id
++ * @param string $id Cache id
+ * @param boolean $doNotTestCacheValidity If set to true, the cache validity won't be tested
+ * @return bool|string
+ */
@@ -450,7 +391,7 @@ new file mode 100644
+ return true;
+ }
+}
-diff --git a/vendor/magento/framework/Cache/Backend/RemoteSynchronizedCache.php b/vendor/magento/framework/Cache/Backend/RemoteSynchronizedCache.php
+diff -Nuar a/vendor/magento/framework/Cache/Backend/RemoteSynchronizedCache.php b/vendor/magento/framework/Cache/Backend/RemoteSynchronizedCache.php
--- a/vendor/magento/framework/Cache/Backend/RemoteSynchronizedCache.php
+++ b/vendor/magento/framework/Cache/Backend/RemoteSynchronizedCache.php
@@ -9,8 +9,10 @@ namespace Magento\Framework\Cache\Backend;
@@ -468,7 +409,7 @@ diff --git a/vendor/magento/framework/Cache/Backend/RemoteSynchronizedCache.php
{
@@ -36,11 +38,15 @@ class RemoteSynchronizedCache extends \Zend_Cache_Backend implements \Zend_Cache
protected $cacheInvalidationTime;
-
+
/**
- * {@inheritdoc}
+ * Suffix for hash to compare data version in cache storage.
@@ -485,7 +426,7 @@ diff --git a/vendor/magento/framework/Cache/Backend/RemoteSynchronizedCache.php
'remote_backend_autoload' => true,
'remote_backend_options' => [],
@@ -52,6 +58,7 @@ class RemoteSynchronizedCache extends \Zend_Cache_Backend implements \Zend_Cache
-
+
/**
* @param array $options
+ * @throws \Zend_Cache_Exception
@@ -494,7 +435,7 @@ diff --git a/vendor/magento/framework/Cache/Backend/RemoteSynchronizedCache.php
{
@@ -97,76 +104,137 @@ class RemoteSynchronizedCache extends \Zend_Cache_Backend implements \Zend_Cache
}
-
+
/**
- * Update remote cache status info
+ * @inheritdoc
@@ -518,7 +459,7 @@ diff --git a/vendor/magento/framework/Cache/Backend/RemoteSynchronizedCache.php
- $this->cacheInvalidationTime = null;
+ return \hash('sha256', $data);
}
-
+
/**
- * {@inheritdoc}
+ * Load data version by id from remote.
@@ -534,7 +475,7 @@ diff --git a/vendor/magento/framework/Cache/Backend/RemoteSynchronizedCache.php
+ $id . self::HASH_SUFFIX
+ );
}
-
+
/**
- * {@inheritdoc}
+ * Save new data version to remote.
@@ -556,7 +497,7 @@ diff --git a/vendor/magento/framework/Cache/Backend/RemoteSynchronizedCache.php
+ * @param string $id
+ * @return bool
+ */
-+ private function removeRemoteDataVersion($id): bool
++ private function removeRemoteDataVersion($id)
+ {
+ return $this->remote->remove($id . self::HASH_SUFFIX);
+ }
@@ -596,7 +537,7 @@ diff --git a/vendor/magento/framework/Cache/Backend/RemoteSynchronizedCache.php
+
+ return $localData;
}
-
+
/**
- * {@inheritdoc}
+ * @inheritdoc
@@ -606,7 +547,7 @@ diff --git a/vendor/magento/framework/Cache/Backend/RemoteSynchronizedCache.php
- return $this->local->test($id);
+ return $this->local->test($id) ?? $this->remote->test($id);
}
-
+
/**
- * {@inheritdoc}
+ * @inheritdoc
@@ -626,7 +567,7 @@ diff --git a/vendor/magento/framework/Cache/Backend/RemoteSynchronizedCache.php
+
+ return $this->local->save($dataToSave, $id, [], $specificLifetime);
}
-
+
/**
- * {@inheritdoc}
+ * @inheritdoc
@@ -639,7 +580,7 @@ diff --git a/vendor/magento/framework/Cache/Backend/RemoteSynchronizedCache.php
+ $this->remote->remove($id) &&
+ $this->local->remove($id);
}
-
+
/**
- * {@inheritdoc}
+ * @inheritdoc
@@ -650,7 +591,7 @@ diff --git a/vendor/magento/framework/Cache/Backend/RemoteSynchronizedCache.php
- return $this->local->clean($mode, $tags);
+ return $this->remote->clean($mode, $tags);
}
-
+
/**
- * {@inheritdoc}
+ * @inheritdoc
@@ -659,7 +600,7 @@ diff --git a/vendor/magento/framework/Cache/Backend/RemoteSynchronizedCache.php
{
@@ -174,7 +242,7 @@ class RemoteSynchronizedCache extends \Zend_Cache_Backend implements \Zend_Cache
}
-
+
/**
- * {@inheritdoc}
+ * @inheritdoc
@@ -668,7 +609,7 @@ diff --git a/vendor/magento/framework/Cache/Backend/RemoteSynchronizedCache.php
{
@@ -182,7 +250,7 @@ class RemoteSynchronizedCache extends \Zend_Cache_Backend implements \Zend_Cache
}
-
+
/**
- * {@inheritdoc}
+ * @inheritdoc
@@ -677,7 +618,7 @@ diff --git a/vendor/magento/framework/Cache/Backend/RemoteSynchronizedCache.php
{
@@ -190,7 +258,7 @@ class RemoteSynchronizedCache extends \Zend_Cache_Backend implements \Zend_Cache
}
-
+
/**
- * {@inheritdoc}
+ * @inheritdoc
@@ -686,7 +627,7 @@ diff --git a/vendor/magento/framework/Cache/Backend/RemoteSynchronizedCache.php
{
@@ -198,7 +266,7 @@ class RemoteSynchronizedCache extends \Zend_Cache_Backend implements \Zend_Cache
}
-
+
/**
- * {@inheritdoc}
+ * @inheritdoc
@@ -695,7 +636,7 @@ diff --git a/vendor/magento/framework/Cache/Backend/RemoteSynchronizedCache.php
{
@@ -206,7 +274,7 @@ class RemoteSynchronizedCache extends \Zend_Cache_Backend implements \Zend_Cache
}
-
+
/**
- * {@inheritdoc}
+ * @inheritdoc
@@ -704,7 +645,7 @@ diff --git a/vendor/magento/framework/Cache/Backend/RemoteSynchronizedCache.php
{
@@ -214,7 +282,7 @@ class RemoteSynchronizedCache extends \Zend_Cache_Backend implements \Zend_Cache
}
-
+
/**
- * {@inheritdoc}
+ * @inheritdoc
@@ -713,7 +654,7 @@ diff --git a/vendor/magento/framework/Cache/Backend/RemoteSynchronizedCache.php
{
@@ -222,7 +290,7 @@ class RemoteSynchronizedCache extends \Zend_Cache_Backend implements \Zend_Cache
}
-
+
/**
- * {@inheritdoc}
+ * @inheritdoc
@@ -722,20 +663,21 @@ diff --git a/vendor/magento/framework/Cache/Backend/RemoteSynchronizedCache.php
{
@@ -230,7 +298,7 @@ class RemoteSynchronizedCache extends \Zend_Cache_Backend implements \Zend_Cache
}
-
+
/**
- * {@inheritdoc}
+ * @inheritdoc
*/
public function getCapabilities()
{
-diff --git a/vendor/magento/framework/Cache/LockGuardedCacheLoader.php b/vendor/magento/framework/Cache/LockGuardedCacheLoader.php
+diff -Nuar a/vendor/magento/framework/Cache/LockGuardedCacheLoader.php b/vendor/magento/framework/Cache/LockGuardedCacheLoader.php
--- a/vendor/magento/framework/Cache/LockGuardedCacheLoader.php
+++ b/vendor/magento/framework/Cache/LockGuardedCacheLoader.php
-@@ -37,18 +37,43 @@ class LockGuardedCacheLoader
+@@ -36,19 +36,43 @@ class LockGuardedCacheLoader
+ */
private $delayTimeout;
- /**
++ /**
+ * Timeout for information to be collected and saved.
+ * If timeout passed that means that data cannot be saved right now.
+ * And we will just return collected data.
@@ -753,8 +695,7 @@ diff --git a/vendor/magento/framework/Cache/LockGuardedCacheLoader.php b/vendor/
+ */
+ private $minimalDelayTimeout;
+
-+ /**
-+ * LockGuardedCacheLoader constructor.
+ /**
* @param LockManagerInterface $locker
* @param int $lockTimeout
* @param int $delayTimeout
@@ -777,7 +718,7 @@ diff --git a/vendor/magento/framework/Cache/LockGuardedCacheLoader.php b/vendor/
}
/**
-@@ -67,25 +92,25 @@ class LockGuardedCacheLoader
+@@ -67,25 +91,25 @@ class LockGuardedCacheLoader
callable $dataSaver
) {
$cachedData = $dataLoader(); //optimistic read
@@ -813,7 +754,7 @@ diff --git a/vendor/magento/framework/Cache/LockGuardedCacheLoader.php b/vendor/
$cachedData = $dataLoader();
}
}
-@@ -103,14 +128,21 @@ class LockGuardedCacheLoader
+@@ -103,14 +127,21 @@ class LockGuardedCacheLoader
public function lockedCleanData(string $lockName, callable $dataCleaner)
{
while ($this->locker->isLocked($lockName)) {
@@ -843,7 +784,7 @@ diff --git a/vendor/magento/framework/Cache/LockGuardedCacheLoader.php b/vendor/
+ return rand($this->minimalDelayTimeout, $this->delayTimeout);
}
}
-diff --git a/vendor/magento/framework/Interception/Config/Config.php b/vendor/magento/framework/Interception/Config/Config.php
+diff -Nuar a/vendor/magento/framework/Interception/Config/Config.php b/vendor/magento/framework/Interception/Config/Config.php
--- a/vendor/magento/framework/Interception/Config/Config.php
+++ b/vendor/magento/framework/Interception/Config/Config.php
@@ -187,8 +187,6 @@ class Config implements \Magento\Framework\Interception\ConfigInterface
@@ -853,22 +794,23 @@ diff --git a/vendor/magento/framework/Interception/Config/Config.php b/vendor/ma
- $this->cacheManager->clean($this->_cacheId);
-
$this->generateIntercepted($classDefinitions);
-
+
$this->cacheManager->save($this->_cacheId, $this->_intercepted);
-diff --git a/vendor/magento/framework/Lock/Backend/Cache.php b/vendor/magento/framework/Lock/Backend/Cache.php
+diff -Nuar a/vendor/magento/framework/Lock/Backend/Cache.php b/vendor/magento/framework/Lock/Backend/Cache.php
--- a/vendor/magento/framework/Lock/Backend/Cache.php
+++ b/vendor/magento/framework/Lock/Backend/Cache.php
-@@ -25,11 +25,19 @@ class Cache implements \Magento\Framework\Lock\LockManagerInterface
+@@ -24,12 +24,20 @@ class Cache implements \Magento\Framework\Lock\LockManagerInterface
+ */
private $cache;
- /**
++ /**
+ * Sign for locks, helps to avoid removing a lock that was created by another client
+ *
-+ * @var string
++ * @string
+ */
+ private $lockSign;
+
-+ /**
+ /**
* @param FrontendInterface $cache
*/
public function __construct(FrontendInterface $cache)
@@ -878,7 +820,7 @@ diff --git a/vendor/magento/framework/Lock/Backend/Cache.php b/vendor/magento/fr
}
/**
-@@ -37,7 +45,24 @@ class Cache implements \Magento\Framework\Lock\LockManagerInterface
+@@ -37,7 +45,26 @@ class Cache implements \Magento\Framework\Lock\LockManagerInterface
*/
public function lock(string $name, int $timeout = -1): bool
{
@@ -890,9 +832,11 @@ diff --git a/vendor/magento/framework/Lock/Backend/Cache.php b/vendor/magento/fr
+ $data = $this->cache->load($this->getIdentifier($name));
+
+ if (false !== $data) {
-+ return false;
++ return false;
+ }
-+ $this->cache->save($this->lockSign, $this->getIdentifier($name), [], $timeout * 100);
++
++ $timeout = $timeout <= 0 ? null : $timeout;
++ $this->cache->save($this->lockSign, $this->getIdentifier($name), [], $timeout);
+
+ $data = $this->cache->load($this->getIdentifier($name));
+
@@ -904,7 +848,7 @@ diff --git a/vendor/magento/framework/Lock/Backend/Cache.php b/vendor/magento/fr
}
/**
-@@ -45,7 +70,22 @@ class Cache implements \Magento\Framework\Lock\LockManagerInterface
+@@ -45,7 +72,22 @@ class Cache implements \Magento\Framework\Lock\LockManagerInterface
*/
public function unlock(string $name): bool
{
@@ -928,7 +872,7 @@ diff --git a/vendor/magento/framework/Lock/Backend/Cache.php b/vendor/magento/fr
}
/**
-@@ -66,4 +106,27 @@ class Cache implements \Magento\Framework\Lock\LockManagerInterface
+@@ -66,4 +108,27 @@ class Cache implements \Magento\Framework\Lock\LockManagerInterface
{
return self::LOCK_PREFIX . $cacheIdentifier;
}
@@ -956,26 +900,7 @@ diff --git a/vendor/magento/framework/Lock/Backend/Cache.php b/vendor/magento/fr
+ return $sign;
+ }
}
-diff --git a/vendor/magento/framework/Lock/Backend/FileLock.php b/vendor/magento/framework/Lock/Backend/FileLock.php
---- a/vendor/magento/framework/Lock/Backend/FileLock.php
-+++ b/vendor/magento/framework/Lock/Backend/FileLock.php
-@@ -91,6 +91,7 @@ class FileLock implements LockManagerInterface
-
- while (!$this->tryToLock($fileResource)) {
- if (!$skipDeadline && $deadline <= microtime(true)) {
-+ $this->tryToUnlock($fileResource);
- $this->fileDriver->fileClose($fileResource);
- return false;
- }
-@@ -124,6 +125,7 @@ class FileLock implements LockManagerInterface
- } else {
- $result = true;
- }
-+ $this->tryToUnlock($fileResource);
- $this->fileDriver->fileClose($fileResource);
- }
- } catch (FileSystemException $exception) {
-diff --git a/setup/src/Magento/Setup/Console/Command/DiCompileCommand.php b/setup/src/Magento/Setup/Console/Command/DiCompileCommand.php
+diff -Nuar a/setup/src/Magento/Setup/Console/Command/DiCompileCommand.php b/setup/src/Magento/Setup/Console/Command/DiCompileCommand.php
--- a/setup/src/Magento/Setup/Console/Command/DiCompileCommand.php
+++ b/setup/src/Magento/Setup/Console/Command/DiCompileCommand.php
@@ -260,9 +260,12 @@ class DiCompileCommand extends Command
@@ -991,7 +916,7 @@ diff --git a/setup/src/Magento/Setup/Console/Command/DiCompileCommand.php b/setu
+ },
+ $libraryPaths
+ );
-
+
$excludedLibraryPaths = [
'#^(?:' . join('|', $libraryPaths) . ')/([\\w]+/)?Test#',
@@ -395,7 +398,8 @@ class DiCompileCommand extends Command
@@ -1002,49 +927,9 @@ diff --git a/setup/src/Magento/Setup/Console/Command/DiCompileCommand.php b/setu
+ ],
+ OperationFactory::APPLICATION_ACTION_LIST_GENERATOR => [],
];
-
+
return $operations;
-diff --git a/setup/src/Magento/Setup/Model/ConfigOptionsList/Cache.php b/setup/src/Magento/Setup/Model/ConfigOptionsList/Cache.php
---- a/setup/src/Magento/Setup/Model/ConfigOptionsList/Cache.php
-+++ b/setup/src/Magento/Setup/Model/ConfigOptionsList/Cache.php
-@@ -20,7 +20,7 @@ use Magento\Setup\Validator\RedisConnectionValidator;
- class Cache implements ConfigOptionsListInterface
- {
- const INPUT_VALUE_CACHE_REDIS = 'redis';
-- const CONFIG_VALUE_CACHE_REDIS = 'Cm_Cache_Backend_Redis';
-+ const CONFIG_VALUE_CACHE_REDIS = '\\Magento\\Framework\\Cache\\Backend\Redis';
-
- const INPUT_KEY_CACHE_BACKEND = 'cache-backend';
- const INPUT_KEY_CACHE_BACKEND_REDIS_SERVER = 'cache-backend-redis-server';
-@@ -282,6 +282,6 @@ class Cache implements ConfigOptionsListInterface
- */
- private function generateCachePrefix(): string
- {
-- return substr(\md5(dirname(__DIR__, 6)), 0, 3) . '_';
-+ return substr(\hash('sha256', dirname(__DIR__, 6)), 0, 3) . '_';
- }
- }
-diff --git a/setup/src/Magento/Setup/Model/ConfigOptionsList/PageCache.php b/setup/src/Magento/Setup/Model/ConfigOptionsList/PageCache.php
---- a/setup/src/Magento/Setup/Model/ConfigOptionsList/PageCache.php
-+++ b/setup/src/Magento/Setup/Model/ConfigOptionsList/PageCache.php
-@@ -20,7 +20,7 @@ use Magento\Setup\Validator\RedisConnectionValidator;
- class PageCache implements ConfigOptionsListInterface
- {
- const INPUT_VALUE_PAGE_CACHE_REDIS = 'redis';
-- const CONFIG_VALUE_PAGE_CACHE_REDIS = 'Cm_Cache_Backend_Redis';
-+ const CONFIG_VALUE_PAGE_CACHE_REDIS = '\\Magento\\Framework\\Cache\\Backend\Redis';
-
- const INPUT_KEY_PAGE_CACHE_BACKEND = 'page-cache';
- const INPUT_KEY_PAGE_CACHE_BACKEND_REDIS_SERVER = 'page-cache-redis-server';
-@@ -284,6 +284,6 @@ class PageCache implements ConfigOptionsListInterface
- */
- private function generateCachePrefix(): string
- {
-- return substr(\md5(dirname(__DIR__, 6)), 0, 3) . '_';
-+ return substr(\hash('sha256', dirname(__DIR__, 6)), 0, 3) . '_';
- }
- }
-diff --git a/setup/src/Magento/Setup/Module/Di/App/Task/Operation/AppActionListGenerator.php b/setup/src/Magento/Setup/Module/Di/App/Task/Operation/AppActionListGenerator.php
+diff -Nuar a/2.3.5/setup/src/Magento/Setup/Module/Di/App/Task/Operation/AppActionListGenerator.php b/setup/src/Magento/Setup/Module/Di/App/Task/Operation/AppActionListGenerator.php
new file mode 100644
--- /dev/null
+++ b/setup/src/Magento/Setup/Module/Di/App/Task/Operation/AppActionListGenerator.php
@@ -1090,7 +975,7 @@ new file mode 100644
+ /**
+ * @inheritDoc
+ */
-+ public function doOperation(): void
++ public function doOperation()
+ {
+ $actionList = $this->moduleReader->getActionFiles();
+ $this->configWriter->write(
@@ -1102,18 +987,18 @@ new file mode 100644
+ /**
+ * @inheritDoc
+ */
-+ public function getName(): string
++ public function getName()
+ {
+ return 'App action list generation';
+ }
+}
-diff --git a/setup/src/Magento/Setup/Module/Di/App/Task/OperationFactory.php b/setup/src/Magento/Setup/Module/Di/App/Task/OperationFactory.php
+diff -Nuar a/setup/src/Magento/Setup/Module/Di/App/Task/OperationFactory.php b/setup/src/Magento/Setup/Module/Di/App/Task/OperationFactory.php
--- a/setup/src/Magento/Setup/Module/Di/App/Task/OperationFactory.php
+++ b/setup/src/Magento/Setup/Module/Di/App/Task/OperationFactory.php
@@ -5,6 +5,12 @@
*/
namespace Magento\Setup\Module\Di\App\Task;
-
+
+use Magento\Setup\Module\Di\App\Task\Operation\AppActionListGenerator;
+use Magento\Setup\Module\Di\App\Task\Operation\PluginListGenerator;
+
@@ -1123,23 +1008,204 @@ diff --git a/setup/src/Magento/Setup/Module/Di/App/Task/OperationFactory.php b/s
class OperationFactory
{
/**
-@@ -48,6 +54,11 @@ class OperationFactory
+@@ -47,6 +53,11 @@ class OperationFactory
+ */
const APPLICATION_CODE_GENERATOR = 'application_code_generator';
-
- /**
+
++ /**
+ * Application action list generator
+ */
+ const APPLICATION_ACTION_LIST_GENERATOR = 'application_action_list_generator';
+
-+ /**
+ /**
* Operations definitions
*
- * @var array
@@ -61,6 +72,7 @@ class OperationFactory
self::INTERCEPTION_CACHE => \Magento\Setup\Module\Di\App\Task\Operation\InterceptionCache::class,
self::REPOSITORY_GENERATOR => \Magento\Setup\Module\Di\App\Task\Operation\RepositoryGenerator::class,
self::PROXY_GENERATOR => \Magento\Setup\Module\Di\App\Task\Operation\ProxyGenerator::class,
+ self::APPLICATION_ACTION_LIST_GENERATOR => AppActionListGenerator::class,
];
+
+ /**
+
+diff -Nuar a/vendor/magento/module-catalog/Model/Product.php b/vendor/magento/module-catalog/Model/Product.php
+--- a/vendor/magento/module-catalog/Model/Product.php
++++ b/vendor/magento/module-catalog/Model/Product.php
+@@ -72,9 +72,9 @@ class Product extends \Magento\Catalog\Model\AbstractModel implements
+ const STORE_ID = 'store_id';
+
+ /**
+- * @var string
++ * @var string|bool
+ */
+- protected $_cacheTag = self::CACHE_TAG;
++ protected $_cacheTag = false;
+
+ /**
+ * @var string
+@@ -874,7 +874,6 @@ class Product extends \Magento\Catalog\Model\AbstractModel implements
+ */
+ public function beforeSave()
+ {
+- $this->cleanCache();
+ $this->setTypeHasOptions(false);
+ $this->setTypeHasRequiredOptions(false);
+ $this->setHasOptions(false);
+@@ -1164,7 +1163,7 @@ class Product extends \Magento\Catalog\Model\AbstractModel implements
+ /**
+ * Get formatted by currency product price
+ *
+- * @return array|double
++ * @return array|double
+ * @since 102.0.6
+ */
+ public function getFormattedPrice()
+diff -Nuar a/vendor/magento/module-config/App/Config/Type/System.php b/vendor/magento/module-config/App/Config/Type/System.php
+--- a/vendor/magento/module-config/App/Config/Type/System.php
++++ b/vendor/magento/module-config/App/Config/Type/System.php
+@@ -20,6 +20,8 @@ use Magento\Framework\Serialize\SerializerInterface;
+ use Magento\Store\Model\Config\Processor\Fallback;
+ use Magento\Framework\Encryption\Encryptor;
+ use Magento\Store\Model\ScopeInterface as StoreScope;
++use Magento\Framework\App\Cache\StateInterface;
++use Magento\Framework\App\Cache\Type\Config;
+
+ /**
+ * System configuration type
+@@ -98,6 +100,12 @@ class System implements ConfigTypeInterface
+ private $lockQuery;
/**
++ * @var StateInterface
++ */
++ private $cacheState;
++
++ /**
++ * System constructor.
+ * @param ConfigSourceInterface $source
+ * @param PostProcessorInterface $postProcessor
+ * @param Fallback $fallback
+@@ -110,6 +118,7 @@ class System implements ConfigTypeInterface
+ * @param Encryptor|null $encryptor
+ * @param LockManagerInterface|null $locker
+ * @param LockGuardedCacheLoader|null $lockQuery
++ * @param StateInterface|null $cacheState
+ * @SuppressWarnings(PHPMD.UnusedFormalParameter)
+ * @SuppressWarnings(PHPMD.ExcessiveParameterList)
+ */
+@@ -125,7 +134,8 @@ class System implements ConfigTypeInterface
+ Reader $reader = null,
+ Encryptor $encryptor = null,
+ LockManagerInterface $locker = null,
+- LockGuardedCacheLoader $lockQuery = null
++ LockGuardedCacheLoader $lockQuery = null,
++ StateInterface $cacheState = null
+ ) {
+ $this->postProcessor = $postProcessor;
+ $this->cache = $cache;
+@@ -136,6 +146,8 @@ class System implements ConfigTypeInterface
+ ?: ObjectManager::getInstance()->get(Encryptor::class);
+ $this->lockQuery = $lockQuery
+ ?: ObjectManager::getInstance()->get(LockGuardedCacheLoader::class);
++ $this->cacheState = $cacheState
++ ?: ObjectManager::getInstance()->get(StateInterface::class);
+ }
+
+ /**
+@@ -220,6 +232,10 @@ class System implements ConfigTypeInterface
+ */
+ private function loadAllData()
+ {
++ if (!$this->cacheState->isEnabled(Config::TYPE_IDENTIFIER)) {
++ return $this->readData();
++ }
++
+ $loadAction = function () {
+ $cachedData = $this->cache->load($this->configType);
+ $data = false;
+@@ -245,6 +261,10 @@ class System implements ConfigTypeInterface
+ */
+ private function loadDefaultScopeData($scopeType)
+ {
++ if (!$this->cacheState->isEnabled(Config::TYPE_IDENTIFIER)) {
++ return $this->readData();
++ }
++
+ $loadAction = function () use ($scopeType) {
+ $cachedData = $this->cache->load($this->configType . '_' . $scopeType);
+ $scopeData = false;
+@@ -271,6 +291,10 @@ class System implements ConfigTypeInterface
+ */
+ private function loadScopeData($scopeType, $scopeId)
+ {
++ if (!$this->cacheState->isEnabled(Config::TYPE_IDENTIFIER)) {
++ return $this->readData();
++ }
++
+ $loadAction = function () use ($scopeType, $scopeId) {
+ $cachedData = $this->cache->load($this->configType . '_' . $scopeType . '_' . $scopeId);
+ $scopeData = false;
+@@ -393,6 +417,10 @@ class System implements ConfigTypeInterface
+ $this->cache->clean(\Zend_Cache::CLEANING_MODE_MATCHING_TAG, [self::CACHE_TAG]);
+ };
+
++ if (!$this->cacheState->isEnabled(Config::TYPE_IDENTIFIER)) {
++ return $cleanAction();
++ }
++
+ $this->lockQuery->lockedCleanData(
+ self::$lockName,
+ $cleanAction
+diff -Nuar a/vendor/magento/module-config/etc/di.xml b/vendor/magento/module-config/etc/di.xml
+--- a/vendor/magento/module-config/etc/di.xml
++++ b/vendor/magento/module-config/etc/di.xml
+@@ -97,8 +97,6 @@
+
+
+ Magento\Framework\Lock\Backend\Cache
+- 42000
+- 100
+
+
+
+diff -Nuar a/setup/src/Magento/Setup/Model/ConfigOptionsList/Cache.php b/setup/src/Magento/Setup/Model/ConfigOptionsList/Cache.php
+--- a/setup/src/Magento/Setup/Model/ConfigOptionsList/Cache.php
++++ b/setup/src/Magento/Setup/Model/ConfigOptionsList/Cache.php
+@@ -20,7 +20,7 @@ use Magento\Setup\Validator\RedisConnectionValidator;
+ class Cache implements ConfigOptionsListInterface
+ {
+ const INPUT_VALUE_CACHE_REDIS = 'redis';
+- const CONFIG_VALUE_CACHE_REDIS = 'Cm_Cache_Backend_Redis';
++ const CONFIG_VALUE_CACHE_REDIS = \Magento\Framework\Cache\Backend\Redis::class;
+
+ const INPUT_KEY_CACHE_BACKEND = 'cache-backend';
+ const INPUT_KEY_CACHE_BACKEND_REDIS_SERVER = 'cache-backend-redis-server';
+@@ -282,6 +282,6 @@ class Cache implements ConfigOptionsListInterface
+ */
+ private function generateCachePrefix(): string
+ {
+- return substr(\md5(dirname(__DIR__, 6)), 0, 3) . '_';
++ return substr(\hash('sha256', dirname(__DIR__, 6)), 0, 3) . '_';
+ }
+ }
+diff -Nuar a/setup/src/Magento/Setup/Model/ConfigOptionsList/PageCache.php b/setup/src/Magento/Setup/Model/ConfigOptionsList/PageCache.php
+--- a/setup/src/Magento/Setup/Model/ConfigOptionsList/PageCache.php
++++ b/setup/src/Magento/Setup/Model/ConfigOptionsList/PageCache.php
+@@ -20,7 +20,7 @@ use Magento\Setup\Validator\RedisConnectionValidator;
+ class PageCache implements ConfigOptionsListInterface
+ {
+ const INPUT_VALUE_PAGE_CACHE_REDIS = 'redis';
+- const CONFIG_VALUE_PAGE_CACHE_REDIS = 'Cm_Cache_Backend_Redis';
++ const CONFIG_VALUE_PAGE_CACHE_REDIS = \Magento\Framework\Cache\Backend\Redis::class;
+
+ const INPUT_KEY_PAGE_CACHE_BACKEND = 'page-cache';
+ const INPUT_KEY_PAGE_CACHE_BACKEND_REDIS_SERVER = 'page-cache-redis-server';
+@@ -284,6 +284,6 @@ class PageCache implements ConfigOptionsListInterface
+ */
+ private function generateCachePrefix(): string
+ {
+- return substr(\md5(dirname(__DIR__, 6)), 0, 3) . '_';
++ return substr(\hash('sha256', dirname(__DIR__, 6)), 0, 3) . '_';
+ }
+ }
diff --git a/patches/MCLOUD-6211__redis_improvement_patches__2.3.5.patch b/patches/MCLOUD-6211__redis_improvement_patches__2.3.5.patch
new file mode 100644
index 0000000..4e80e2a
--- /dev/null
+++ b/patches/MCLOUD-6211__redis_improvement_patches__2.3.5.patch
@@ -0,0 +1,382 @@
+diff -Nuar a/vendor/magento/module-catalog/Model/Product.php b/vendor/magento/module-catalog/Model/Product.php
+--- a/vendor/magento/module-catalog/Model/Product.php
++++ b/vendor/magento/module-catalog/Model/Product.php
+@@ -72,9 +72,9 @@ class Product extends \Magento\Catalog\Model\AbstractModel implements
+ const STORE_ID = 'store_id';
+
+ /**
+- * @var string
++ * @var string|bool
+ */
+- protected $_cacheTag = self::CACHE_TAG;
++ protected $_cacheTag = false;
+
+ /**
+ * @var string
+@@ -874,7 +874,6 @@ class Product extends \Magento\Catalog\Model\AbstractModel implements
+ */
+ public function beforeSave()
+ {
+- $this->cleanCache();
+ $this->setTypeHasOptions(false);
+ $this->setTypeHasRequiredOptions(false);
+ $this->setHasOptions(false);
+diff -Nuar a/vendor/magento/module-config/App/Config/Type/System.php b/vendor/magento/module-config/App/Config/Type/System.php
+--- a/vendor/magento/module-config/App/Config/Type/System.php
++++ b/vendor/magento/module-config/App/Config/Type/System.php
+@@ -20,6 +20,8 @@ use Magento\Framework\Serialize\SerializerInterface;
+ use Magento\Store\Model\Config\Processor\Fallback;
+ use Magento\Framework\Encryption\Encryptor;
+ use Magento\Store\Model\ScopeInterface as StoreScope;
++use Magento\Framework\App\Cache\StateInterface;
++use Magento\Framework\App\Cache\Type\Config;
+
+ /**
+ * System configuration type
+@@ -98,6 +100,12 @@ class System implements ConfigTypeInterface
+ private $lockQuery;
+
+ /**
++ * @var StateInterface
++ */
++ private $cacheState;
++
++ /**
++ * System constructor.
+ * @param ConfigSourceInterface $source
+ * @param PostProcessorInterface $postProcessor
+ * @param Fallback $fallback
+@@ -110,6 +118,7 @@ class System implements ConfigTypeInterface
+ * @param Encryptor|null $encryptor
+ * @param LockManagerInterface|null $locker
+ * @param LockGuardedCacheLoader|null $lockQuery
++ * @param StateInterface|null $cacheState
+ * @SuppressWarnings(PHPMD.UnusedFormalParameter)
+ * @SuppressWarnings(PHPMD.ExcessiveParameterList)
+ */
+@@ -125,7 +134,8 @@ class System implements ConfigTypeInterface
+ Reader $reader = null,
+ Encryptor $encryptor = null,
+ LockManagerInterface $locker = null,
+- LockGuardedCacheLoader $lockQuery = null
++ LockGuardedCacheLoader $lockQuery = null,
++ StateInterface $cacheState = null
+ ) {
+ $this->postProcessor = $postProcessor;
+ $this->cache = $cache;
+@@ -136,6 +146,8 @@ class System implements ConfigTypeInterface
+ ?: ObjectManager::getInstance()->get(Encryptor::class);
+ $this->lockQuery = $lockQuery
+ ?: ObjectManager::getInstance()->get(LockGuardedCacheLoader::class);
++ $this->cacheState = $cacheState
++ ?: ObjectManager::getInstance()->get(StateInterface::class);
+ }
+
+ /**
+@@ -220,6 +232,10 @@ class System implements ConfigTypeInterface
+ */
+ private function loadAllData()
+ {
++ if (!$this->cacheState->isEnabled(Config::TYPE_IDENTIFIER)) {
++ return $this->readData();
++ }
++
+ $loadAction = function () {
+ $cachedData = $this->cache->load($this->configType);
+ $data = false;
+@@ -245,6 +261,10 @@ class System implements ConfigTypeInterface
+ */
+ private function loadDefaultScopeData($scopeType)
+ {
++ if (!$this->cacheState->isEnabled(Config::TYPE_IDENTIFIER)) {
++ return $this->readData();
++ }
++
+ $loadAction = function () use ($scopeType) {
+ $cachedData = $this->cache->load($this->configType . '_' . $scopeType);
+ $scopeData = false;
+@@ -271,6 +291,10 @@ class System implements ConfigTypeInterface
+ */
+ private function loadScopeData($scopeType, $scopeId)
+ {
++ if (!$this->cacheState->isEnabled(Config::TYPE_IDENTIFIER)) {
++ return $this->readData();
++ }
++
+ $loadAction = function () use ($scopeType, $scopeId) {
+ $cachedData = $this->cache->load($this->configType . '_' . $scopeType . '_' . $scopeId);
+ $scopeData = false;
+@@ -393,6 +417,10 @@ class System implements ConfigTypeInterface
+ $this->cache->clean(\Zend_Cache::CLEANING_MODE_MATCHING_TAG, [self::CACHE_TAG]);
+ };
+
++ if (!$this->cacheState->isEnabled(Config::TYPE_IDENTIFIER)) {
++ return $cleanAction();
++ }
++
+ $this->lockQuery->lockedCleanData(
+ self::$lockName,
+ $cleanAction
+diff -Nuar a/vendor/magento/module-config/etc/di.xml b/vendor/magento/module-config/etc/di.xml
+--- a/vendor/magento/module-config/etc/di.xml
++++ b/vendor/magento/module-config/etc/di.xml
+@@ -97,8 +97,6 @@
+
+
+ Magento\Framework\Lock\Backend\Cache
+- 42000
+- 100
+
+
+
+diff -Nuar a/app/etc/di.xml b/app/etc/di.xml
+--- a/app/etc/di.xml
++++ b/app/etc/di.xml
+@@ -1780,8 +1780,6 @@
+
+
+ Magento\Framework\Lock\Backend\Cache
+- 10000
+- 20
+
+
+
+diff -Nuar a/vendor/magento/framework/Cache/LockGuardedCacheLoader.php b/vendor/magento/framework/Cache/LockGuardedCacheLoader.php
+--- a/vendor/magento/framework/Cache/LockGuardedCacheLoader.php
++++ b/vendor/magento/framework/Cache/LockGuardedCacheLoader.php
+@@ -48,22 +48,31 @@ class LockGuardedCacheLoader
+ private $loadTimeout;
+
+ /**
+- * LockGuardedCacheLoader constructor.
++ * Minimal delay timeout in ms.
++ *
++ * @var int
++ */
++ private $minimalDelayTimeout;
++
++ /**
+ * @param LockManagerInterface $locker
+ * @param int $lockTimeout
+ * @param int $delayTimeout
+ * @param int $loadTimeout
++ * @param int $minimalDelayTimeout
+ */
+ public function __construct(
+ LockManagerInterface $locker,
+ int $lockTimeout = 10000,
+ int $delayTimeout = 20,
+- int $loadTimeout = 10000
++ int $loadTimeout = 10000,
++ int $minimalDelayTimeout = 5
+ ) {
+ $this->locker = $locker;
+ $this->lockTimeout = $lockTimeout;
+ $this->delayTimeout = $delayTimeout;
+ $this->loadTimeout = $loadTimeout;
++ $this->minimalDelayTimeout = $minimalDelayTimeout;
+ }
+
+ /**
+@@ -82,7 +91,7 @@ class LockGuardedCacheLoader
+ callable $dataSaver
+ ) {
+ $cachedData = $dataLoader(); //optimistic read
+- $deadline = microtime(true) + $this->loadTimeout;
++ $deadline = microtime(true) + $this->loadTimeout / 100;
+
+ while ($cachedData === false) {
+ if ($deadline <= microtime(true)) {
+@@ -100,7 +109,7 @@ class LockGuardedCacheLoader
+ }
+
+ if ($cachedData === false) {
+- usleep($this->delayTimeout * 1000);
++ usleep($this->getLookupTimeout() * 1000);
+ $cachedData = $dataLoader();
+ }
+ }
+@@ -118,14 +127,21 @@ class LockGuardedCacheLoader
+ public function lockedCleanData(string $lockName, callable $dataCleaner)
+ {
+ while ($this->locker->isLocked($lockName)) {
+- usleep($this->delayTimeout * 1000);
+- }
+- try {
+- if ($this->locker->lock($lockName, $this->lockTimeout / 1000)) {
+- $dataCleaner();
+- }
+- } finally {
+- $this->locker->unlock($lockName);
++ usleep($this->getLookupTimeout() * 1000);
+ }
++
++ $dataCleaner();
++ }
++
++ /**
++ * Delay will be applied as rand($minimalDelayTimeout, $delayTimeout).
++ * This helps to desynchronize multiple clients trying
++ * to acquire the lock for the same resource at the same time
++ *
++ * @return int
++ */
++ private function getLookupTimeout()
++ {
++ return rand($this->minimalDelayTimeout, $this->delayTimeout);
+ }
+ }
+diff -Nuar a/vendor/magento/framework/Lock/Backend/Cache.php b/vendor/magento/framework/Lock/Backend/Cache.php
+--- a/vendor/magento/framework/Lock/Backend/Cache.php
++++ b/vendor/magento/framework/Lock/Backend/Cache.php
+@@ -24,12 +24,20 @@ class Cache implements \Magento\Framework\Lock\LockManagerInterface
+ */
+ private $cache;
+
++ /**
++ * Sign for locks, helps to avoid removing a lock that was created by another client
++ *
++ * @string
++ */
++ private $lockSign;
++
+ /**
+ * @param FrontendInterface $cache
+ */
+ public function __construct(FrontendInterface $cache)
+ {
+ $this->cache = $cache;
++ $this->lockSign = $this->generateLockSign();
+ }
+
+ /**
+@@ -37,11 +45,26 @@ class Cache implements \Magento\Framework\Lock\LockManagerInterface
+ */
+ public function lock(string $name, int $timeout = -1): bool
+ {
+- if ((bool)$this->cache->test($this->getIdentifier($name))) {
++ if (empty($this->lockSign)) {
++ $this->lockSign = $this->generateLockSign();
++ }
++
++ $data = $this->cache->load($this->getIdentifier($name));
++
++ if (false !== $data) {
+ return false;
+ }
+
+- return $this->cache->save('1', $this->getIdentifier($name), [], $timeout);
++ $timeout = $timeout <= 0 ? null : $timeout;
++ $this->cache->save($this->lockSign, $this->getIdentifier($name), [], $timeout);
++
++ $data = $this->cache->load($this->getIdentifier($name));
++
++ if ($data === $this->lockSign) {
++ return true;
++ }
++
++ return false;
+ }
+
+ /**
+@@ -49,7 +72,22 @@ class Cache implements \Magento\Framework\Lock\LockManagerInterface
+ */
+ public function unlock(string $name): bool
+ {
+- return $this->cache->remove($this->getIdentifier($name));
++ if (empty($this->lockSign)) {
++ return false;
++ }
++
++ $data = $this->cache->load($this->getIdentifier($name));
++
++ if (false === $data) {
++ return false;
++ }
++
++ $removeResult = false;
++ if ($data === $this->lockSign) {
++ $removeResult = (bool)$this->cache->remove($this->getIdentifier($name));
++ }
++
++ return $removeResult;
+ }
+
+ /**
+@@ -70,4 +108,27 @@ class Cache implements \Magento\Framework\Lock\LockManagerInterface
+ {
+ return self::LOCK_PREFIX . $cacheIdentifier;
+ }
++
++ /**
++ * Function that generates lock sign that helps to avoid removing a lock that was created by another client.
++ *
++ * @return string
++ */
++ private function generateLockSign()
++ {
++ $sign = implode(
++ '-',
++ [
++ \getmypid(), \crc32(\gethostname())
++ ]
++ );
++
++ try {
++ $sign .= '-' . \bin2hex(\random_bytes(4));
++ } catch (\Exception $e) {
++ $sign .= '-' . \uniqid('-uniqid-');
++ }
++
++ return $sign;
++ }
+ }
+diff -Nuar a/setup/src/Magento/Setup/Model/ConfigOptionsList/Cache.php b/setup/src/Magento/Setup/Model/ConfigOptionsList/Cache.php
+--- a/setup/src/Magento/Setup/Model/ConfigOptionsList/Cache.php
++++ b/setup/src/Magento/Setup/Model/ConfigOptionsList/Cache.php
+@@ -20,7 +20,7 @@ use Magento\Setup\Validator\RedisConnectionValidator;
+ class Cache implements ConfigOptionsListInterface
+ {
+ const INPUT_VALUE_CACHE_REDIS = 'redis';
+- const CONFIG_VALUE_CACHE_REDIS = 'Cm_Cache_Backend_Redis';
++ const CONFIG_VALUE_CACHE_REDIS = \Magento\Framework\Cache\Backend\Redis::class;
+
+ const INPUT_KEY_CACHE_BACKEND = 'cache-backend';
+ const INPUT_KEY_CACHE_BACKEND_REDIS_SERVER = 'cache-backend-redis-server';
+@@ -233,7 +233,7 @@ class Cache implements ConfigOptionsListInterface
+ self::CONFIG_PATH_CACHE_BACKEND_DATABASE,
+ $this->getDefaultConfigValue(self::INPUT_KEY_CACHE_BACKEND_REDIS_DATABASE)
+ );
+-
++
+ $config['password'] = isset($options[self::INPUT_KEY_CACHE_BACKEND_REDIS_PASSWORD])
+ ? $options[self::INPUT_KEY_CACHE_BACKEND_REDIS_PASSWORD]
+ : $deploymentConfig->get(
+@@ -282,6 +282,6 @@ class Cache implements ConfigOptionsListInterface
+ */
+ private function generateCachePrefix(): string
+ {
+- return substr(\md5(dirname(__DIR__, 6)), 0, 3) . '_';
++ return substr(\hash('sha256', dirname(__DIR__, 6)), 0, 3) . '_';
+ }
+ }
+diff -Nuar a/setup/src/Magento/Setup/Model/ConfigOptionsList/PageCache.php b/setup/src/Magento/Setup/Model/ConfigOptionsList/PageCache.php
+--- a/setup/src/Magento/Setup/Model/ConfigOptionsList/PageCache.php
++++ b/setup/src/Magento/Setup/Model/ConfigOptionsList/PageCache.php
+@@ -20,7 +20,7 @@ use Magento\Setup\Validator\RedisConnectionValidator;
+ class PageCache implements ConfigOptionsListInterface
+ {
+ const INPUT_VALUE_PAGE_CACHE_REDIS = 'redis';
+- const CONFIG_VALUE_PAGE_CACHE_REDIS = 'Cm_Cache_Backend_Redis';
++ const CONFIG_VALUE_PAGE_CACHE_REDIS = \Magento\Framework\Cache\Backend\Redis::class;
+
+ const INPUT_KEY_PAGE_CACHE_BACKEND = 'page-cache';
+ const INPUT_KEY_PAGE_CACHE_BACKEND_REDIS_SERVER = 'page-cache-redis-server';
+@@ -284,6 +284,6 @@ class PageCache implements ConfigOptionsListInterface
+ */
+ private function generateCachePrefix(): string
+ {
+- return substr(\md5(dirname(__DIR__, 6)), 0, 3) . '_';
++ return substr(\hash('sha256', dirname(__DIR__, 6)), 0, 3) . '_';
+ }
+ }
diff --git a/patches/MCLOUD_6139__improvement_flock_locks__2.3.2.patch b/patches/MCLOUD_6139__improvement_flock_locks__2.3.2.patch
new file mode 100644
index 0000000..785bec6
--- /dev/null
+++ b/patches/MCLOUD_6139__improvement_flock_locks__2.3.2.patch
@@ -0,0 +1,19 @@
+diff -Nuar a/vendor/magento/framework/Lock/Backend/FileLock.php b/vendor/magento/framework/Lock/Backend/FileLock.php
+--- a/vendor/magento/framework/Lock/Backend/FileLock.php
++++ b/vendor/magento/framework/Lock/Backend/FileLock.php
+@@ -91,6 +91,7 @@ class FileLock implements LockManagerInterface
+
+ while (!$this->tryToLock($fileResource)) {
+ if (!$skipDeadline && $deadline <= microtime(true)) {
++ $this->tryToUnlock($fileResource);
+ $this->fileDriver->fileClose($fileResource);
+ return false;
+ }
+@@ -124,6 +125,7 @@ class FileLock implements LockManagerInterface
+ } else {
+ $result = true;
+ }
++ $this->tryToUnlock($fileResource);
+ $this->fileDriver->fileClose($fileResource);
+ }
+ } catch (FileSystemException $exception) {