From 05aa8c5c06fde2fc7b5b77c6487a0e107feadbdf Mon Sep 17 00:00:00 2001 From: Bohdan Korablov Date: Tue, 19 Feb 2019 10:11:39 -0600 Subject: [PATCH 01/19] MAGETWO-98151: Add support ZooKeeper locks --- app/etc/di.xml | 2 +- .../Framework/Lock/Backend/Zookeeper.php | 185 ++++++++++++++++++ .../Magento/Framework/Lock/Factory.php | 90 +++++++++ lib/internal/Magento/Framework/Lock/Proxy.php | 80 ++++++++ 4 files changed, 356 insertions(+), 1 deletion(-) create mode 100644 lib/internal/Magento/Framework/Lock/Backend/Zookeeper.php create mode 100644 lib/internal/Magento/Framework/Lock/Factory.php create mode 100644 lib/internal/Magento/Framework/Lock/Proxy.php diff --git a/app/etc/di.xml b/app/etc/di.xml index 19543375aad58..d74377b6194c3 100755 --- a/app/etc/di.xml +++ b/app/etc/di.xml @@ -38,7 +38,7 @@ - + diff --git a/lib/internal/Magento/Framework/Lock/Backend/Zookeeper.php b/lib/internal/Magento/Framework/Lock/Backend/Zookeeper.php new file mode 100644 index 0000000000000..20f4dd26b8a08 --- /dev/null +++ b/lib/internal/Magento/Framework/Lock/Backend/Zookeeper.php @@ -0,0 +1,185 @@ +\Zookeeper::PERM_ALL, 'scheme' => 'world', 'id' => 'anyone']]; + + /** + * @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 = '/magento/locks') + { + if (empty($path)) { + throw new RuntimeException( + new Phrase('The path needs to be a non-empty string.') + ); + } + + $this->host = $host; + $this->path = preg_replace('#\/*$#', '', $path) ?: '/'; + } + + /** + * {@inheritdoc} + * @throws RuntimeException + */ + public function lock(string $name, int $timeout = -1): bool + { + $skipDeadline = $timeout < 0; + $lockPath = $this->getFullPathToLock($name); + $deadline = microtime(true) + $timeout; + + while($this->getProvider()->exists($lockPath)) { + if (!$skipDeadline && $deadline <= microtime(true)) { + return false; + } + + usleep($this->sleepCycle); + } + + if (!$this->getProvider()->create($lockPath, '1', $this->acl, \Zookeeper::EPHEMERAL)) { + throw new RuntimeException(new Phrase('Failed creating lock %1', [$lockPath])); + } else { + return true; + } + } + + /** + * @inheritdoc + */ + public function unlock(string $name): bool + { + $lockPath = $this->getFullPathToLock($name); + + if (!$this->getProvider()->exists($lockPath)) { + return true; + } + + return $this->getProvider()->delete($lockPath); + } + + /** + * @inheritdoc + */ + public function isLocked(string $name): bool + { + return (bool) $this->getProvider()->exists($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; + } + + /** + * 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); + } + + if (!$this->createBasePath($this->path)) { + throw new RuntimeException(new Phrase('Failed creating base path %1', [$this->path])); + } + } + + return $this->zookeeper; + } + + /** + * Checks and creates base path recursively + * + * @param $path + * @return bool + */ + private function createBasePath($path) + { + if ($this->zookeeper->exists($path)) { + return true; + } + + if (!$this->createBasePath(dirname($path))) { + return false; + } + + if ($this->zookeeper->create($path, '1', $this->acl)) { + return true; + } + + return $this->zookeeper->exists($path); + } +} diff --git a/lib/internal/Magento/Framework/Lock/Factory.php b/lib/internal/Magento/Framework/Lock/Factory.php new file mode 100644 index 0000000000000..1414358ae42e9 --- /dev/null +++ b/lib/internal/Magento/Framework/Lock/Factory.php @@ -0,0 +1,90 @@ + DatabaseLock::class, + self::LOCK_ZOOKEEPER => ZookeeperLock::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('locks/provider', self::LOCK_DB); + $config = $this->deploymentConfig->get('locks/config', []); + + if (!isset($this->lockers[$provider])) { + throw new RuntimeException(new Phrase('Unknown locks provider.')); + } + + return $this->objectManager->create($this->lockers[$provider], $config); + } +} diff --git a/lib/internal/Magento/Framework/Lock/Proxy.php b/lib/internal/Magento/Framework/Lock/Proxy.php new file mode 100644 index 0000000000000..e140078d05ab8 --- /dev/null +++ b/lib/internal/Magento/Framework/Lock/Proxy.php @@ -0,0 +1,80 @@ +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; + } +} From 7b352ce6c6b2ac0f4e1edf5bdd0d58a346eba3e3 Mon Sep 17 00:00:00 2001 From: Bohdan Korablov Date: Tue, 19 Feb 2019 14:48:17 -0600 Subject: [PATCH 02/19] MAGETWO-98151: Add support ZooKeeper locks --- .../Framework/Lock/Backend/Zookeeper.php | 133 ++++++++++++++---- .../{Factory.php => LockBackendFactory.php} | 2 +- lib/internal/Magento/Framework/Lock/Proxy.php | 6 +- .../Framework/Lock/Test/Unit/ProxyTest.php | 106 ++++++++++++++ 4 files changed, 215 insertions(+), 32 deletions(-) rename lib/internal/Magento/Framework/Lock/{Factory.php => LockBackendFactory.php} (98%) create mode 100644 lib/internal/Magento/Framework/Lock/Test/Unit/ProxyTest.php diff --git a/lib/internal/Magento/Framework/Lock/Backend/Zookeeper.php b/lib/internal/Magento/Framework/Lock/Backend/Zookeeper.php index 20f4dd26b8a08..d813f209ab96e 100644 --- a/lib/internal/Magento/Framework/Lock/Backend/Zookeeper.php +++ b/lib/internal/Magento/Framework/Lock/Backend/Zookeeper.php @@ -30,6 +30,11 @@ class Zookeeper implements LockManagerInterface */ private $path; + /** + * @var string + */ + private $lockName = 'lock-'; + /** * The host to connect to Zookeeper * @@ -58,6 +63,13 @@ class Zookeeper implements LockManagerInterface */ private $acl = [['perms'=>\Zookeeper::PERM_ALL, 'scheme' => 'world', 'id' => 'anyone']]; + /** + * The mapping list of the lock name with the full lock path + * + * @var array + */ + private $locks = []; + /** * @param string $host The host to connect to Zookeeper * @param string $path The base path to locks in Zookeeper @@ -77,6 +89,9 @@ public function __construct(string $host, string $path = '/magento/locks') /** * {@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 @@ -85,19 +100,29 @@ public function lock(string $name, int $timeout = -1): bool $lockPath = $this->getFullPathToLock($name); $deadline = microtime(true) + $timeout; - while($this->getProvider()->exists($lockPath)) { + 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); } - if (!$this->getProvider()->create($lockPath, '1', $this->acl, \Zookeeper::EPHEMERAL)) { - throw new RuntimeException(new Phrase('Failed creating lock %1', [$lockPath])); - } else { - return true; - } + $this->locks[$name] = $lockKey; + + return true; } /** @@ -105,13 +130,11 @@ public function lock(string $name, int $timeout = -1): bool */ public function unlock(string $name): bool { - $lockPath = $this->getFullPathToLock($name); - - if (!$this->getProvider()->exists($lockPath)) { + if (!isset($this->locks[$name])) { return true; } - return $this->getProvider()->delete($lockPath); + return $this->getProvider()->delete($this->locks[$name]); } /** @@ -119,7 +142,7 @@ public function unlock(string $name): bool */ public function isLocked(string $name): bool { - return (bool) $this->getProvider()->exists($this->getFullPathToLock($name)); + return $this->isAnyLock($this->getFullPathToLock($name)); } /** @@ -130,7 +153,7 @@ public function isLocked(string $name): bool */ private function getFullPathToLock(string $name): string { - return $this->path . '/' . $name; + return $this->path . '/' . $name . '/' . $this->lockName; } /** @@ -143,18 +166,14 @@ 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); - } - - if (!$this->createBasePath($this->path)) { - throw new RuntimeException(new Phrase('Failed creating base path %1', [$this->path])); + $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; @@ -163,23 +182,81 @@ private function getProvider(): \Zookeeper /** * Checks and creates base path recursively * - * @param $path + * @param string $path * @return bool + * @throws RuntimeException */ - private function createBasePath($path) + private function checkAndCreateParentNode(string $path): bool { - if ($this->zookeeper->exists($path)) { + $path = dirname($path); + if ($this->getProvider()->exists($path)) { return true; } - if (!$this->createBasePath(dirname($path))) { + if (!$this->checkAndCreateParentNode($path)) { return false; } - if ($this->zookeeper->create($path, '1', $this->acl)) { + if ($this->getProvider()->create($path, '1', $this->acl)) { return true; } - return $this->zookeeper->exists($path); + 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("/[0-9]+$/", $key, $matches)) + return null; + + return intval($matches[0]); + } + + /** + * 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 that $indexKey then returns true, + * if all the nodes are larger than $indexKey then 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); + + foreach($children as $childKey) { + + if (is_null($indexKey)) { + return true; + } + + $childIndex = $this->getIndex($childKey); + + if (is_null($childIndex)) { + continue; + } + + if ($childIndex < $indexKey) { + return true; + } + } + + return false; } } diff --git a/lib/internal/Magento/Framework/Lock/Factory.php b/lib/internal/Magento/Framework/Lock/LockBackendFactory.php similarity index 98% rename from lib/internal/Magento/Framework/Lock/Factory.php rename to lib/internal/Magento/Framework/Lock/LockBackendFactory.php index 1414358ae42e9..4c4896cb35623 100644 --- a/lib/internal/Magento/Framework/Lock/Factory.php +++ b/lib/internal/Magento/Framework/Lock/LockBackendFactory.php @@ -17,7 +17,7 @@ /** * The factory to create object that implements LockManagerInterface */ -class Factory +class LockBackendFactory { /** * The Object Manager instance diff --git a/lib/internal/Magento/Framework/Lock/Proxy.php b/lib/internal/Magento/Framework/Lock/Proxy.php index e140078d05ab8..b5f8eee0f2c4f 100644 --- a/lib/internal/Magento/Framework/Lock/Proxy.php +++ b/lib/internal/Magento/Framework/Lock/Proxy.php @@ -17,7 +17,7 @@ class Proxy implements LockManagerInterface /** * The factory to create LockManagerInterface implementation * - * @var Factory + * @var LockBackendFactory */ private $factory; @@ -29,9 +29,9 @@ class Proxy implements LockManagerInterface private $locker; /** - * @param Factory $factory The factory to create LockManagerInterface implementation + * @param LockBackendFactory $factory The factory to create LockManagerInterface implementation */ - public function __construct(Factory $factory) + public function __construct(LockBackendFactory $factory) { $this->factory = $factory; } diff --git a/lib/internal/Magento/Framework/Lock/Test/Unit/ProxyTest.php b/lib/internal/Magento/Framework/Lock/Test/Unit/ProxyTest.php new file mode 100644 index 0000000000000..c71dad701d715 --- /dev/null +++ b/lib/internal/Magento/Framework/Lock/Test/Unit/ProxyTest.php @@ -0,0 +1,106 @@ +factoryMock = $this->createMock(LockBackendFactory::class); + $this->lockerMock = $this->getMockForAbstractClass(LockManagerInterface::class); + $this->proxy = new Proxy($this->factoryMock); + } + + /** + * @return void + */ + public function testIsLocked() + { + $lockName = 'testLock'; + $this->factoryMock->expects($this->once()) + ->method('create') + ->willReturn($this->lockerMock); + $this->lockerMock->expects($this->exactly(2)) + ->method('isLocked') + ->with($lockName) + ->willReturn(true); + + $this->assertTrue($this->proxy->isLocked($lockName)); + + // Call one more time to check that method Factory::create is called one time + $this->assertTrue($this->proxy->isLocked($lockName)); + } + + /** + * @return void + */ + public function testLock() + { + $lockName = 'testLock'; + $timeout = 123; + $this->factoryMock->expects($this->once()) + ->method('create') + ->willReturn($this->lockerMock); + $this->lockerMock->expects($this->exactly(2)) + ->method('lock') + ->with($lockName, $timeout) + ->willReturn(true); + + $this->assertTrue($this->proxy->lock($lockName, $timeout)); + + // Call one more time to check that method Factory::create is called one time + $this->assertTrue($this->proxy->lock($lockName, $timeout)); + } + + /** + * @return void + */ + public function testUnlock() + { + $lockName = 'testLock'; + $this->factoryMock->expects($this->once()) + ->method('create') + ->willReturn($this->lockerMock); + $this->lockerMock->expects($this->exactly(2)) + ->method('unlock') + ->with($lockName) + ->willReturn(true); + + $this->assertTrue($this->proxy->unlock($lockName)); + + // Call one more time to check that method Factory::create is called one time + $this->assertTrue($this->proxy->unlock($lockName)); + } +} From 974a6a53ca7d5ad237654468177d182ff1972b4d Mon Sep 17 00:00:00 2001 From: Bohdan Korablov Date: Tue, 19 Feb 2019 15:34:35 -0600 Subject: [PATCH 03/19] MAGETWO-98151: Add support ZooKeeper locks --- lib/internal/Magento/Framework/Lock/Backend/Zookeeper.php | 2 +- lib/internal/Magento/Framework/Lock/LockBackendFactory.php | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/internal/Magento/Framework/Lock/Backend/Zookeeper.php b/lib/internal/Magento/Framework/Lock/Backend/Zookeeper.php index d813f209ab96e..884353eb63e5f 100644 --- a/lib/internal/Magento/Framework/Lock/Backend/Zookeeper.php +++ b/lib/internal/Magento/Framework/Lock/Backend/Zookeeper.php @@ -131,7 +131,7 @@ public function lock(string $name, int $timeout = -1): bool public function unlock(string $name): bool { if (!isset($this->locks[$name])) { - return true; + return false; } return $this->getProvider()->delete($this->locks[$name]); diff --git a/lib/internal/Magento/Framework/Lock/LockBackendFactory.php b/lib/internal/Magento/Framework/Lock/LockBackendFactory.php index 4c4896cb35623..ab839a6594a67 100644 --- a/lib/internal/Magento/Framework/Lock/LockBackendFactory.php +++ b/lib/internal/Magento/Framework/Lock/LockBackendFactory.php @@ -57,7 +57,6 @@ class LockBackendFactory self::LOCK_ZOOKEEPER => ZookeeperLock::class ]; - /** * @param ObjectManagerInterface $objectManager The Object Manager instance * @param DeploymentConfig $deploymentConfig The Application deployment configuration From 38ef69db1bc29ec157ab9401a2a1eed548f70485 Mon Sep 17 00:00:00 2001 From: Bohdan Korablov Date: Wed, 20 Feb 2019 11:32:24 -0600 Subject: [PATCH 04/19] MAGETWO-98151: Add support ZooKeeper locks --- .../Lock/Test/Unit/LockBackendFactoryTest.php | 96 +++++++++++++++++++ 1 file changed, 96 insertions(+) create mode 100644 lib/internal/Magento/Framework/Lock/Test/Unit/LockBackendFactoryTest.php diff --git a/lib/internal/Magento/Framework/Lock/Test/Unit/LockBackendFactoryTest.php b/lib/internal/Magento/Framework/Lock/Test/Unit/LockBackendFactoryTest.php new file mode 100644 index 0000000000000..cf8444dc0a0ba --- /dev/null +++ b/lib/internal/Magento/Framework/Lock/Test/Unit/LockBackendFactoryTest.php @@ -0,0 +1,96 @@ +objectManagerMock = $this->getMockForAbstractClass(ObjectManagerInterface::class); + $this->deploymentConfigMock = $this->createMock(DeploymentConfig::class); + $this->factory = new LockBackendFactory($this->objectManagerMock, $this->deploymentConfigMock); + } + + /** + * @expectedException \Magento\Framework\Exception\RuntimeException + * @expectedExceptionMessage Unknown locks provider. + */ + public function testCreateWithException() + { + $this->deploymentConfigMock->expects($this->exactly(2)) + ->method('get') + ->withConsecutive(['locks/provider', LockBackendFactory::LOCK_DB], ['locks/config', []]) + ->willReturnOnConsecutiveCalls('someProvider', []); + + $this->factory->create(); + } + + /** + * @param string $lockProvider + * @param string $lockProviderClass + * @param array $config + * @dataProvider createDataProvider + */ + public function testCreate(string $lockProvider, string $lockProviderClass, array $config) + { + $lockManagerMock = $this->getMockForAbstractClass(LockManagerInterface::class); + $this->deploymentConfigMock->expects($this->exactly(2)) + ->method('get') + ->withConsecutive(['locks/provider', LockBackendFactory::LOCK_DB], ['locks/config', []]) + ->willReturnOnConsecutiveCalls($lockProvider, $config); + $this->objectManagerMock->expects($this->once()) + ->method('create') + ->with($lockProviderClass, $config) + ->willReturn($lockManagerMock); + + $this->assertSame($lockManagerMock, $this->factory->create()); + } + + public function createDataProvider(): array + { + return [ + [ + 'lockProvider' => LockBackendFactory::LOCK_DB, + 'lockProviderClass' => DatabaseLock::class, + 'config' => ['prefix' => 'somePrefix'], + ], + [ + 'lockProvider' => LockBackendFactory::LOCK_ZOOKEEPER, + 'lockProviderClass' => ZookeeperLock::class, + 'config' => ['host' => 'some host'], + ], + ]; + } +} From 0f3c125972654768727dfa130c8badfd49e8cd68 Mon Sep 17 00:00:00 2001 From: Bohdan Korablov Date: Wed, 20 Feb 2019 12:07:26 -0600 Subject: [PATCH 05/19] MAGETWO-98151: Add support ZooKeeper locks --- lib/internal/Magento/Framework/Lock/Backend/Zookeeper.php | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/internal/Magento/Framework/Lock/Backend/Zookeeper.php b/lib/internal/Magento/Framework/Lock/Backend/Zookeeper.php index 884353eb63e5f..02e2739545696 100644 --- a/lib/internal/Magento/Framework/Lock/Backend/Zookeeper.php +++ b/lib/internal/Magento/Framework/Lock/Backend/Zookeeper.php @@ -126,7 +126,8 @@ public function lock(string $name, int $timeout = -1): bool } /** - * @inheritdoc + * {@inheritdoc} + * @throws RuntimeException */ public function unlock(string $name): bool { @@ -138,7 +139,8 @@ public function unlock(string $name): bool } /** - * @inheritdoc + * {@inheritdoc} + * @throws RuntimeException */ public function isLocked(string $name): bool { From 9accdd4f278c999c3e1d7b005708494c738f5187 Mon Sep 17 00:00:00 2001 From: Bohdan Korablov Date: Wed, 20 Feb 2019 15:35:22 -0600 Subject: [PATCH 06/19] MAGETWO-98151: Add support ZooKeeper locks --- .../testsuite/Magento/Framework/Lock/Backend/ZookeeperTest.php | 0 lib/internal/Magento/Framework/Lock/Backend/Zookeeper.php | 2 +- .../Magento/Framework/Lock/Test/Unit/Backend/ZookeeperTest.php | 0 3 files changed, 1 insertion(+), 1 deletion(-) create mode 100644 dev/tests/integration/testsuite/Magento/Framework/Lock/Backend/ZookeeperTest.php create mode 100644 lib/internal/Magento/Framework/Lock/Test/Unit/Backend/ZookeeperTest.php diff --git a/dev/tests/integration/testsuite/Magento/Framework/Lock/Backend/ZookeeperTest.php b/dev/tests/integration/testsuite/Magento/Framework/Lock/Backend/ZookeeperTest.php new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/lib/internal/Magento/Framework/Lock/Backend/Zookeeper.php b/lib/internal/Magento/Framework/Lock/Backend/Zookeeper.php index 02e2739545696..d19ba0dd947b0 100644 --- a/lib/internal/Magento/Framework/Lock/Backend/Zookeeper.php +++ b/lib/internal/Magento/Framework/Lock/Backend/Zookeeper.php @@ -242,7 +242,7 @@ private function isAnyLock(string $fullKey, int $indexKey = null): bool $children = $this->getProvider()->getChildren($parent); - foreach($children as $childKey) { + foreach ($children as $childKey) { if (is_null($indexKey)) { return true; diff --git a/lib/internal/Magento/Framework/Lock/Test/Unit/Backend/ZookeeperTest.php b/lib/internal/Magento/Framework/Lock/Test/Unit/Backend/ZookeeperTest.php new file mode 100644 index 0000000000000..e69de29bb2d1d From ba4f6baaea9cf2740f8e0c87ac2af192958742ce Mon Sep 17 00:00:00 2001 From: Bohdan Korablov Date: Wed, 20 Feb 2019 15:37:06 -0600 Subject: [PATCH 07/19] MAGETWO-98151: Add support ZooKeeper locks --- .../Lock/Test/Unit/Backend/ZookeeperTest.php | 54 +++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/lib/internal/Magento/Framework/Lock/Test/Unit/Backend/ZookeeperTest.php b/lib/internal/Magento/Framework/Lock/Test/Unit/Backend/ZookeeperTest.php index e69de29bb2d1d..c004179aa524a 100644 --- a/lib/internal/Magento/Framework/Lock/Test/Unit/Backend/ZookeeperTest.php +++ b/lib/internal/Magento/Framework/Lock/Test/Unit/Backend/ZookeeperTest.php @@ -0,0 +1,54 @@ +zookeeperProvider = new ZookeeperProvider($this->host, '/some/path/'); + } + + /** + * @expectedException \Magento\Framework\Exception\RuntimeException + * @expectedExceptionMessage The path needs to be a non-empty string. + */ + public function testConstructionWithException() + { + $this->zookeeperProvider = new ZookeeperProvider('some host', ''); + } + + +} From d1752d3cab4d04af1e1bbcf1b968450ed9c735eb Mon Sep 17 00:00:00 2001 From: Bohdan Korablov Date: Wed, 20 Feb 2019 16:00:40 -0600 Subject: [PATCH 08/19] MAGETWO-98151: Add support ZooKeeper locks --- lib/internal/Magento/Framework/Lock/LockBackendFactory.php | 4 ++++ .../Framework/Lock/Test/Unit/Backend/ZookeeperTest.php | 5 +++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/lib/internal/Magento/Framework/Lock/LockBackendFactory.php b/lib/internal/Magento/Framework/Lock/LockBackendFactory.php index ab839a6594a67..e2119ca3eee11 100644 --- a/lib/internal/Magento/Framework/Lock/LockBackendFactory.php +++ b/lib/internal/Magento/Framework/Lock/LockBackendFactory.php @@ -84,6 +84,10 @@ public function create(): LockManagerInterface throw new RuntimeException(new Phrase('Unknown locks 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 --git a/lib/internal/Magento/Framework/Lock/Test/Unit/Backend/ZookeeperTest.php b/lib/internal/Magento/Framework/Lock/Test/Unit/Backend/ZookeeperTest.php index c004179aa524a..c22c49d3427c5 100644 --- a/lib/internal/Magento/Framework/Lock/Test/Unit/Backend/ZookeeperTest.php +++ b/lib/internal/Magento/Framework/Lock/Test/Unit/Backend/ZookeeperTest.php @@ -38,6 +38,9 @@ class ZookeeperTest extends TestCase */ protected function setUp() { + if (!extension_loaded('zookeeper')) { + $this->markTestSkipped('Test was skipped because php extension Zookeeper is not installed.'); + } $this->zookeeperProvider = new ZookeeperProvider($this->host, '/some/path/'); } @@ -49,6 +52,4 @@ public function testConstructionWithException() { $this->zookeeperProvider = new ZookeeperProvider('some host', ''); } - - } From 7710c226f8b5a193aedda196e34d14e3a13f96af Mon Sep 17 00:00:00 2001 From: Bohdan Korablov Date: Wed, 20 Feb 2019 16:18:23 -0600 Subject: [PATCH 09/19] MAGETWO-98151: Add support ZooKeeper locks --- .../Lock/Test/Unit/LockBackendFactoryTest.php | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/lib/internal/Magento/Framework/Lock/Test/Unit/LockBackendFactoryTest.php b/lib/internal/Magento/Framework/Lock/Test/Unit/LockBackendFactoryTest.php index cf8444dc0a0ba..f3b141e9f902b 100644 --- a/lib/internal/Magento/Framework/Lock/Test/Unit/LockBackendFactoryTest.php +++ b/lib/internal/Magento/Framework/Lock/Test/Unit/LockBackendFactoryTest.php @@ -78,19 +78,27 @@ public function testCreate(string $lockProvider, string $lockProviderClass, arra $this->assertSame($lockManagerMock, $this->factory->create()); } + /** + * @return array + */ public function createDataProvider(): array { - return [ - [ + $data = [ + 'db' => [ 'lockProvider' => LockBackendFactory::LOCK_DB, 'lockProviderClass' => DatabaseLock::class, 'config' => ['prefix' => 'somePrefix'], ], - [ + ]; + + if (extension_loaded('zookeeper')) { + $data['zookeeper'] = [ 'lockProvider' => LockBackendFactory::LOCK_ZOOKEEPER, 'lockProviderClass' => ZookeeperLock::class, 'config' => ['host' => 'some host'], - ], - ]; + ]; + } + + return $data; } } From ec62bf7f1b19ca2fdbffeb4f41fd52b354c895bb Mon Sep 17 00:00:00 2001 From: Bohdan Korablov Date: Wed, 27 Feb 2019 15:50:13 -0600 Subject: [PATCH 10/19] MAGETWO-98151: Add support ZooKeeper locks --- .../Framework/Lock/Backend/ZookeeperTest.php | 90 ++++++ .../Framework/Lock/Backend/Zookeeper.php | 11 +- .../Framework/Lock/LockBackendFactory.php | 15 +- .../Lock/Test/Unit/LockBackendFactoryTest.php | 10 +- .../Magento/Setup/Model/ConfigOptionsList.php | 3 +- .../Setup/Model/ConfigOptionsList/Lock.php | 291 ++++++++++++++++++ .../Unit/Model/ConfigOptionsList/LockTest.php | 196 ++++++++++++ .../Test/Unit/Model/ConfigOptionsListTest.php | 17 +- 8 files changed, 619 insertions(+), 14 deletions(-) create mode 100644 setup/src/Magento/Setup/Model/ConfigOptionsList/Lock.php create mode 100644 setup/src/Magento/Setup/Test/Unit/Model/ConfigOptionsList/LockTest.php diff --git a/dev/tests/integration/testsuite/Magento/Framework/Lock/Backend/ZookeeperTest.php b/dev/tests/integration/testsuite/Magento/Framework/Lock/Backend/ZookeeperTest.php index e69de29bb2d1d..6e312637c7b5a 100644 --- a/dev/tests/integration/testsuite/Magento/Framework/Lock/Backend/ZookeeperTest.php +++ b/dev/tests/integration/testsuite/Magento/Framework/Lock/Backend/ZookeeperTest.php @@ -0,0 +1,90 @@ +markTestSkipped('php extension Zookeeper is not installed.'); + } + + $this->objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); + $this->configReader = $this->objectManager->get(FileReader::class); + $this->lockBackendFactory = $this->objectManager->create(LockBackendFactory::class); + $this->arrayManager = $this->objectManager->create(ArrayManager::class); + $config = $this->configReader->load(ConfigFilePool::APP_ENV); + + if ($this->arrayManager->get('lock/provider', $config) !== 'zookeeper') { + $this->markTestSkipped('Zookeeper is not configured during installation.'); + } + + $this->model = $this->lockBackendFactory->create(); + $this->assertInstanceOf(ZookeeperLock::class, $this->model); + } + + public function testLockAndUnlock() + { + $name = 'test_lock'; + + $this->assertFalse($this->model->isLocked($name)); + + $this->assertTrue($this->model->lock($name)); + $this->assertTrue($this->model->isLocked($name)); + + $this->assertTrue($this->model->unlock($name)); + $this->assertFalse($this->model->isLocked($name)); + } + + public function testUnlockWithoutExistingLock() + { + $name = 'test_lock'; + + $this->assertFalse($this->model->isLocked($name)); + $this->assertFalse($this->model->unlock($name)); + } +} + diff --git a/lib/internal/Magento/Framework/Lock/Backend/Zookeeper.php b/lib/internal/Magento/Framework/Lock/Backend/Zookeeper.php index d19ba0dd947b0..c2119df300c52 100644 --- a/lib/internal/Magento/Framework/Lock/Backend/Zookeeper.php +++ b/lib/internal/Magento/Framework/Lock/Backend/Zookeeper.php @@ -31,6 +31,8 @@ class Zookeeper implements LockManagerInterface private $path; /** + * The name of sequence nodes + * * @var string */ private $lockName = 'lock-'; @@ -70,12 +72,17 @@ class Zookeeper implements LockManagerInterface */ 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 = '/magento/locks') + public function __construct(string $host, string $path = self::DEFAULT_PATH) { if (empty($path)) { throw new RuntimeException( @@ -164,7 +171,7 @@ private function getFullPathToLock(string $name): string * @return \Zookeeper * @throws RuntimeException */ - private function getProvider(): \Zookeeper + public function getProvider(): \Zookeeper { if (!$this->zookeeper) { $this->zookeeper = new \Zookeeper($this->host); diff --git a/lib/internal/Magento/Framework/Lock/LockBackendFactory.php b/lib/internal/Magento/Framework/Lock/LockBackendFactory.php index e2119ca3eee11..ec15736bef534 100644 --- a/lib/internal/Magento/Framework/Lock/LockBackendFactory.php +++ b/lib/internal/Magento/Framework/Lock/LockBackendFactory.php @@ -13,6 +13,7 @@ use Magento\Framework\App\DeploymentConfig; use Magento\Framework\Lock\Backend\Database as DatabaseLock; use Magento\Framework\Lock\Backend\Zookeeper as ZookeeperLock; +use Magento\Framework\Lock\Backend\Cache as CacheLock; /** * The factory to create object that implements LockManagerInterface @@ -47,6 +48,13 @@ class LockBackendFactory */ const LOCK_ZOOKEEPER = 'zookeeper'; + /** + * Cache lock provider name + * + * @const string + */ + const LOCK_CACHE = 'cache'; + /** * The list of lock providers with mapping on classes * @@ -54,7 +62,8 @@ class LockBackendFactory */ private $lockers = [ self::LOCK_DB => DatabaseLock::class, - self::LOCK_ZOOKEEPER => ZookeeperLock::class + self::LOCK_ZOOKEEPER => ZookeeperLock::class, + self::LOCK_CACHE => CacheLock::class, ]; /** @@ -77,8 +86,8 @@ public function __construct( */ public function create(): LockManagerInterface { - $provider = $this->deploymentConfig->get('locks/provider', self::LOCK_DB); - $config = $this->deploymentConfig->get('locks/config', []); + $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.')); diff --git a/lib/internal/Magento/Framework/Lock/Test/Unit/LockBackendFactoryTest.php b/lib/internal/Magento/Framework/Lock/Test/Unit/LockBackendFactoryTest.php index f3b141e9f902b..18053f20f010c 100644 --- a/lib/internal/Magento/Framework/Lock/Test/Unit/LockBackendFactoryTest.php +++ b/lib/internal/Magento/Framework/Lock/Test/Unit/LockBackendFactoryTest.php @@ -9,6 +9,7 @@ use Magento\Framework\Lock\Backend\Database as DatabaseLock; use Magento\Framework\Lock\Backend\Zookeeper as ZookeeperLock; +use Magento\Framework\Lock\Backend\Cache as CacheLock; use Magento\Framework\Lock\LockBackendFactory; use Magento\Framework\ObjectManagerInterface; use Magento\Framework\Lock\LockManagerInterface; @@ -51,7 +52,7 @@ public function testCreateWithException() { $this->deploymentConfigMock->expects($this->exactly(2)) ->method('get') - ->withConsecutive(['locks/provider', LockBackendFactory::LOCK_DB], ['locks/config', []]) + ->withConsecutive(['lock/provider', LockBackendFactory::LOCK_DB], ['lock/config', []]) ->willReturnOnConsecutiveCalls('someProvider', []); $this->factory->create(); @@ -68,7 +69,7 @@ public function testCreate(string $lockProvider, string $lockProviderClass, arra $lockManagerMock = $this->getMockForAbstractClass(LockManagerInterface::class); $this->deploymentConfigMock->expects($this->exactly(2)) ->method('get') - ->withConsecutive(['locks/provider', LockBackendFactory::LOCK_DB], ['locks/config', []]) + ->withConsecutive(['lock/provider', LockBackendFactory::LOCK_DB], ['lock/config', []]) ->willReturnOnConsecutiveCalls($lockProvider, $config); $this->objectManagerMock->expects($this->once()) ->method('create') @@ -89,6 +90,11 @@ public function createDataProvider(): array 'lockProviderClass' => DatabaseLock::class, 'config' => ['prefix' => 'somePrefix'], ], + 'cache' => [ + 'lockProvider' => LockBackendFactory::LOCK_CACHE, + 'lockProviderClass' => CacheLock::class, + 'config' => [], + ], ]; if (extension_loaded('zookeeper')) { diff --git a/setup/src/Magento/Setup/Model/ConfigOptionsList.php b/setup/src/Magento/Setup/Model/ConfigOptionsList.php index afe1a5d9e2591..3f2aedae1373c 100644 --- 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 --git a/setup/src/Magento/Setup/Model/ConfigOptionsList/Lock.php b/setup/src/Magento/Setup/Model/ConfigOptionsList/Lock.php new file mode 100644 index 0000000000000..912b0e42ca822 --- /dev/null +++ b/setup/src/Magento/Setup/Model/ConfigOptionsList/Lock.php @@ -0,0 +1,291 @@ + [ + 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, + ], + ]; + + /** + * 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 + ), + ]; + } + + /** + * @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_CACHE: + case LockBackendFactory::LOCK_DB: + $errors = []; + break; + default: + $errors[] = 'The lock provider ' . $lockProvider . ' does not exist.'; + } + + 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 = (string) $options[self::INPUT_KEY_LOCK_ZOOKEEPER_HOST] + ?? $deploymentConfig->get( + self::CONFIG_PATH_LOCK_ZOOKEEPER_HOST, + $this->getDefaultValue(self::INPUT_KEY_LOCK_ZOOKEEPER_HOST) + ); + $path = (string) $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 (empty($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/setup/src/Magento/Setup/Test/Unit/Model/ConfigOptionsList/LockTest.php b/setup/src/Magento/Setup/Test/Unit/Model/ConfigOptionsList/LockTest.php new file mode 100644 index 0000000000000..f7ca6e0a09b5a --- /dev/null +++ b/setup/src/Magento/Setup/Test/Unit/Model/ConfigOptionsList/LockTest.php @@ -0,0 +1,196 @@ +deploymentConfigMock = $this->createMock(DeploymentConfig::class); + $this->lockConfigOptionsList = new LockConfigOptionsList(); + } + + /** + * @return void + */ + public function testGetOptions() + { + $options = $this->lockConfigOptionsList->getOptions(); + $this->assertSame(4, count($options)); + + $this->assertArrayHasKey(0, $options); + $this->assertInstanceOf(SelectConfigOption::class, $options[0]); + $this->assertEquals(LockConfigOptionsList::INPUT_KEY_LOCK_PROVIDER, $options[0]->getName()); + + $this->assertArrayHasKey(1, $options); + $this->assertInstanceOf(TextConfigOption::class, $options[1]); + $this->assertEquals(LockConfigOptionsList::INPUT_KEY_LOCK_DB_PREFIX, $options[1]->getName()); + + $this->assertArrayHasKey(2, $options); + $this->assertInstanceOf(TextConfigOption::class, $options[2]); + $this->assertEquals(LockConfigOptionsList::INPUT_KEY_LOCK_ZOOKEEPER_HOST, $options[2]->getName()); + + $this->assertArrayHasKey(3, $options); + $this->assertInstanceOf(TextConfigOption::class, $options[3]); + $this->assertEquals(LockConfigOptionsList::INPUT_KEY_LOCK_ZOOKEEPER_PATH, $options[3]->getName()); + } + + /** + * @param array $options + * @param array $expectedResult + * @dataProvider createConfigDataProvider + */ + public function testCreateConfig(array $options, array $expectedResult) + { + $this->deploymentConfigMock->expects($this->any()) + ->method('get') + ->willReturnArgument(1); + $data = $this->lockConfigOptionsList->createConfig($options, $this->deploymentConfigMock); + $this->assertInstanceOf(ConfigData::class, $data); + $this->assertTrue($data->isOverrideWhenSave()); + $this->assertSame($expectedResult, $data->getData()); + } + + /** + * @return array + */ + public function createConfigDataProvider(): array + { + return [ + 'Check default values' => [ + 'options' => [], + 'expectedResult' => [ + 'lock' => [ + 'provider' => LockBackendFactory::LOCK_DB, + 'config' => [ + 'prefix' => null, + ], + ], + ], + ], + 'Check default value for cache lock' => [ + 'options' => [ + LockConfigOptionsList::INPUT_KEY_LOCK_PROVIDER => LockBackendFactory::LOCK_CACHE, + ], + 'expectedResult' => [ + 'lock' => [ + 'provider' => LockBackendFactory::LOCK_CACHE, + ], + ], + ], + 'Check default value for zookeeper lock' => [ + 'options' => [ + LockConfigOptionsList::INPUT_KEY_LOCK_PROVIDER => LockBackendFactory::LOCK_ZOOKEEPER, + ], + 'expectedResult' => [ + 'lock' => [ + 'provider' => LockBackendFactory::LOCK_ZOOKEEPER, + 'config' => [ + 'host' => null, + 'path' => ZookeeperLock::DEFAULT_PATH, + ], + ], + ], + ], + 'Check specific db lock options' => [ + 'options' => [ + LockConfigOptionsList::INPUT_KEY_LOCK_PROVIDER => LockBackendFactory::LOCK_DB, + LockConfigOptionsList::INPUT_KEY_LOCK_DB_PREFIX => 'my_prefix' + ], + 'expectedResult' => [ + 'lock' => [ + 'provider' => LockBackendFactory::LOCK_DB, + 'config' => [ + 'prefix' => 'my_prefix', + ], + ], + ], + ], + 'Check specific zookeeper lock options' => [ + 'options' => [ + LockConfigOptionsList::INPUT_KEY_LOCK_PROVIDER => LockBackendFactory::LOCK_ZOOKEEPER, + LockConfigOptionsList::INPUT_KEY_LOCK_ZOOKEEPER_HOST => '123.45.67.89:10', + LockConfigOptionsList::INPUT_KEY_LOCK_ZOOKEEPER_PATH => '/some/path', + ], + 'expectedResult' => [ + 'lock' => [ + 'provider' => LockBackendFactory::LOCK_ZOOKEEPER, + 'config' => [ + 'host' => '123.45.67.89:10', + 'path' => '/some/path', + ], + ], + ], + ], + ]; + } + + /** + * @param array $options + * @param array $expectedResult + * @dataProvider validateDataProvider + */ + public function testValidate(array $options, array $expectedResult) + { + $this->deploymentConfigMock->expects($this->any()) + ->method('get') + ->willReturnArgument(1); + $this->assertSame($expectedResult, $this->lockConfigOptionsList->validate($options, $this->deploymentConfigMock)); + } + + /** + * @return array + */ + public function validateDataProvider(): array + { + return [ + 'Wrong lock provider' => [ + 'options' => [ + LockConfigOptionsList::INPUT_KEY_LOCK_PROVIDER => 'SomeProvider', + ], + 'expectedResult' => [ + 'The lock provider SomeProvider does not exist.', + ], + ], + 'Empty host and path for Zookeeper' => [ + 'options' => [ + LockConfigOptionsList::INPUT_KEY_LOCK_PROVIDER => LockBackendFactory::LOCK_ZOOKEEPER, + LockConfigOptionsList::INPUT_KEY_LOCK_ZOOKEEPER_HOST => '', + LockConfigOptionsList::INPUT_KEY_LOCK_ZOOKEEPER_PATH => '', + ], + 'expectedResult' => [ + 'Zookeeper path needs to be a non-empty string.', + 'Zookeeper host is should be set.', + ], + ], + ]; + } +} diff --git a/setup/src/Magento/Setup/Test/Unit/Model/ConfigOptionsListTest.php b/setup/src/Magento/Setup/Test/Unit/Model/ConfigOptionsListTest.php index d7f680309c9ef..a85b468cebc92 100644 --- a/setup/src/Magento/Setup/Test/Unit/Model/ConfigOptionsListTest.php +++ b/setup/src/Magento/Setup/Test/Unit/Model/ConfigOptionsListTest.php @@ -7,6 +7,7 @@ namespace Magento\Setup\Test\Unit\Model; use Magento\Framework\Config\ConfigOptionsListConstants; +use Magento\Setup\Model\ConfigOptionsList\Lock; use Magento\Setup\Model\ConfigGenerator; use Magento\Setup\Model\ConfigOptionsList; use Magento\Setup\Validator\DbValidator; @@ -82,7 +83,7 @@ public function testCreateOptions() $this->generator->expects($this->once())->method('createXFrameConfig')->willReturn($configDataMock); $this->generator->expects($this->once())->method('createCacheHostsConfig')->willReturn($configDataMock); - $configData = $this->object->createConfig([], $this->deploymentConfig); + $configData = $this->object->createConfig([Lock::INPUT_KEY_LOCK_PROVIDER => 'db'], $this->deploymentConfig); $this->assertGreaterThanOrEqual(6, count($configData)); } @@ -96,7 +97,7 @@ public function testCreateOptionsWithOptionalNull() $this->generator->expects($this->once())->method('createXFrameConfig')->willReturn($configDataMock); $this->generator->expects($this->once())->method('createCacheHostsConfig')->willReturn($configDataMock); - $configData = $this->object->createConfig([], $this->deploymentConfig); + $configData = $this->object->createConfig([Lock::INPUT_KEY_LOCK_PROVIDER => 'db'], $this->deploymentConfig); $this->assertGreaterThanOrEqual(6, count($configData)); } @@ -109,7 +110,8 @@ public function testValidateSuccess() ConfigOptionsListConstants::INPUT_KEY_DB_NAME => 'name', ConfigOptionsListConstants::INPUT_KEY_DB_HOST => 'host', ConfigOptionsListConstants::INPUT_KEY_DB_USER => 'user', - ConfigOptionsListConstants::INPUT_KEY_DB_PASSWORD => 'pass' + ConfigOptionsListConstants::INPUT_KEY_DB_PASSWORD => 'pass', + Lock::INPUT_KEY_LOCK_PROVIDER => 'db' ]; $this->prepareValidationMocks(); @@ -127,7 +129,8 @@ public function testValidateInvalidSessionHandler() ConfigOptionsListConstants::INPUT_KEY_DB_NAME => 'name', ConfigOptionsListConstants::INPUT_KEY_DB_HOST => 'host', ConfigOptionsListConstants::INPUT_KEY_DB_USER => 'user', - ConfigOptionsListConstants::INPUT_KEY_DB_PASSWORD => 'pass' + ConfigOptionsListConstants::INPUT_KEY_DB_PASSWORD => 'pass', + Lock::INPUT_KEY_LOCK_PROVIDER => 'db' ]; $this->prepareValidationMocks(); @@ -141,7 +144,8 @@ public function testValidateEmptyEncryptionKey() { $options = [ ConfigOptionsListConstants::INPUT_KEY_SKIP_DB_VALIDATION => true, - ConfigOptionsListConstants::INPUT_KEY_ENCRYPTION_KEY => '' + ConfigOptionsListConstants::INPUT_KEY_ENCRYPTION_KEY => '', + Lock::INPUT_KEY_LOCK_PROVIDER => 'db' ]; $this->assertEquals( ['Invalid encryption key. Encryption key must be 32 character string without any white space.'], @@ -167,7 +171,8 @@ public function testValidateCacheHosts($hosts, $expectedError) { $options = [ ConfigOptionsListConstants::INPUT_KEY_SKIP_DB_VALIDATION => true, - ConfigOptionsListConstants::INPUT_KEY_CACHE_HOSTS => $hosts + ConfigOptionsListConstants::INPUT_KEY_CACHE_HOSTS => $hosts, + Lock::INPUT_KEY_LOCK_PROVIDER => 'db' ]; $result = $this->object->validate($options, $this->deploymentConfig); if ($expectedError) { From bcb2116f116344d8767623d6d731b58cd2201383 Mon Sep 17 00:00:00 2001 From: Bohdan Korablov Date: Wed, 27 Feb 2019 16:05:46 -0600 Subject: [PATCH 11/19] MAGETWO-98151: Add support ZooKeeper locks --- lib/internal/Magento/Framework/Lock/Backend/Zookeeper.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/internal/Magento/Framework/Lock/Backend/Zookeeper.php b/lib/internal/Magento/Framework/Lock/Backend/Zookeeper.php index c2119df300c52..d491678d4d7fc 100644 --- a/lib/internal/Magento/Framework/Lock/Backend/Zookeeper.php +++ b/lib/internal/Magento/Framework/Lock/Backend/Zookeeper.php @@ -171,7 +171,7 @@ private function getFullPathToLock(string $name): string * @return \Zookeeper * @throws RuntimeException */ - public function getProvider(): \Zookeeper + private function getProvider(): \Zookeeper { if (!$this->zookeeper) { $this->zookeeper = new \Zookeeper($this->host); From e7a723ab66e887c2f5bb3798959809d2f2931b77 Mon Sep 17 00:00:00 2001 From: Bohdan Korablov Date: Mon, 18 Mar 2019 13:31:47 -0500 Subject: [PATCH 12/19] MAGETWO-98151: Add support ZooKeeper locks --- .../Framework/Lock/Backend/ZookeeperTest.php | 1 + .../Framework/Lock/LockBackendFactory.php | 2 +- .../Lock/Test/Unit/Backend/ZookeeperTest.php | 17 ++++++++++------- .../Lock/Test/Unit/LockBackendFactoryTest.php | 2 +- .../Setup/Model/ConfigOptionsList/Lock.php | 4 ++-- 5 files changed, 15 insertions(+), 11 deletions(-) diff --git a/dev/tests/integration/testsuite/Magento/Framework/Lock/Backend/ZookeeperTest.php b/dev/tests/integration/testsuite/Magento/Framework/Lock/Backend/ZookeeperTest.php index 6e312637c7b5a..ae218561377f2 100644 --- a/dev/tests/integration/testsuite/Magento/Framework/Lock/Backend/ZookeeperTest.php +++ b/dev/tests/integration/testsuite/Magento/Framework/Lock/Backend/ZookeeperTest.php @@ -74,6 +74,7 @@ public function testLockAndUnlock() $this->assertTrue($this->model->lock($name)); $this->assertTrue($this->model->isLocked($name)); + $this->assertFalse($this->model->lock($name, 2)); $this->assertTrue($this->model->unlock($name)); $this->assertFalse($this->model->isLocked($name)); diff --git a/lib/internal/Magento/Framework/Lock/LockBackendFactory.php b/lib/internal/Magento/Framework/Lock/LockBackendFactory.php index ec15736bef534..46cb2998ede70 100644 --- a/lib/internal/Magento/Framework/Lock/LockBackendFactory.php +++ b/lib/internal/Magento/Framework/Lock/LockBackendFactory.php @@ -90,7 +90,7 @@ public function create(): LockManagerInterface $config = $this->deploymentConfig->get('lock/config', []); if (!isset($this->lockers[$provider])) { - throw new RuntimeException(new Phrase('Unknown locks provider.')); + throw new RuntimeException(new Phrase('Unknown locks provider: %1', [$provider])); } if (self::LOCK_ZOOKEEPER === $provider && !extension_loaded(self::LOCK_ZOOKEEPER)) { diff --git a/lib/internal/Magento/Framework/Lock/Test/Unit/Backend/ZookeeperTest.php b/lib/internal/Magento/Framework/Lock/Test/Unit/Backend/ZookeeperTest.php index c22c49d3427c5..7c450a09df320 100644 --- a/lib/internal/Magento/Framework/Lock/Test/Unit/Backend/ZookeeperTest.php +++ b/lib/internal/Magento/Framework/Lock/Test/Unit/Backend/ZookeeperTest.php @@ -13,11 +13,6 @@ class ZookeeperTest extends TestCase { - /** - * @var \Zookeeper|MockObject - */ - private $zookeeperMock; - /** * @var ZookeeperProvider */ @@ -41,15 +36,23 @@ protected function setUp() if (!extension_loaded('zookeeper')) { $this->markTestSkipped('Test was skipped because php extension Zookeeper is not installed.'); } - $this->zookeeperProvider = new ZookeeperProvider($this->host, '/some/path/'); } /** * @expectedException \Magento\Framework\Exception\RuntimeException * @expectedExceptionMessage The path needs to be a non-empty string. + * @return void */ public function testConstructionWithException() { - $this->zookeeperProvider = new ZookeeperProvider('some host', ''); + $this->zookeeperProvider = new ZookeeperProvider($this->host, ''); + } + + /** + * @return void + */ + public function testConstructionWithoutException() + { + $this->zookeeperProvider = new ZookeeperProvider($this->host, $this->path); } } diff --git a/lib/internal/Magento/Framework/Lock/Test/Unit/LockBackendFactoryTest.php b/lib/internal/Magento/Framework/Lock/Test/Unit/LockBackendFactoryTest.php index 18053f20f010c..8864ab6f9ead1 100644 --- a/lib/internal/Magento/Framework/Lock/Test/Unit/LockBackendFactoryTest.php +++ b/lib/internal/Magento/Framework/Lock/Test/Unit/LockBackendFactoryTest.php @@ -46,7 +46,7 @@ protected function setUp() /** * @expectedException \Magento\Framework\Exception\RuntimeException - * @expectedExceptionMessage Unknown locks provider. + * @expectedExceptionMessage Unknown locks provider: someProvider */ public function testCreateWithException() { diff --git a/setup/src/Magento/Setup/Model/ConfigOptionsList/Lock.php b/setup/src/Magento/Setup/Model/ConfigOptionsList/Lock.php index 912b0e42ca822..ae236c1b5d740 100644 --- a/setup/src/Magento/Setup/Model/ConfigOptionsList/Lock.php +++ b/setup/src/Magento/Setup/Model/ConfigOptionsList/Lock.php @@ -210,12 +210,12 @@ private function validateZookeeperConfig(array $options, DeploymentConfig $deplo $errors[] = 'php extension Zookeeper is not installed.'; } - $host = (string) $options[self::INPUT_KEY_LOCK_ZOOKEEPER_HOST] + $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 = (string) $options[self::INPUT_KEY_LOCK_ZOOKEEPER_PATH] + $path = $options[self::INPUT_KEY_LOCK_ZOOKEEPER_PATH] ?? $deploymentConfig->get( self::CONFIG_PATH_LOCK_ZOOKEEPER_PATH, $this->getDefaultValue(self::INPUT_KEY_LOCK_ZOOKEEPER_PATH) From 9d65684659fce2eb58ead2ec8adc67896fb3e36e Mon Sep 17 00:00:00 2001 From: Bohdan Korablov Date: Mon, 18 Mar 2019 16:04:00 -0500 Subject: [PATCH 13/19] MAGETWO-98151: Add support ZooKeeper locks --- .../Framework/Lock/Backend/Zookeeper.php | 25 +++++++++++-------- .../Lock/Test/Unit/Backend/ZookeeperTest.php | 12 ++++++++- .../Setup/Model/ConfigOptionsList/Lock.php | 2 +- 3 files changed, 27 insertions(+), 12 deletions(-) diff --git a/lib/internal/Magento/Framework/Lock/Backend/Zookeeper.php b/lib/internal/Magento/Framework/Lock/Backend/Zookeeper.php index d491678d4d7fc..3b69c8734d9a9 100644 --- a/lib/internal/Magento/Framework/Lock/Backend/Zookeeper.php +++ b/lib/internal/Magento/Framework/Lock/Backend/Zookeeper.php @@ -84,12 +84,18 @@ class Zookeeper implements LockManagerInterface */ public function __construct(string $host, string $path = self::DEFAULT_PATH) { - if (empty($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 = preg_replace('#\/*$#', '', $path) ?: '/'; } @@ -221,18 +227,18 @@ private function checkAndCreateParentNode(string $path): bool */ private function getIndex(string $key) { - if (!preg_match("/[0-9]+$/", $key, $matches)) + if (!preg_match('/' . $this->lockName . '([0-9]+)$/', $key, $matches)) return null; - return intval($matches[0]); + 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 that $indexKey then returns true, - * if all the nodes are larger than $indexKey then 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 @@ -249,12 +255,11 @@ private function isAnyLock(string $fullKey, int $indexKey = null): bool $children = $this->getProvider()->getChildren($parent); - foreach ($children as $childKey) { - - if (is_null($indexKey)) { - return true; - } + if (is_null($indexKey) && !empty($children)) { + return true; + } + foreach ($children as $childKey) { $childIndex = $this->getIndex($childKey); if (is_null($childIndex)) { diff --git a/lib/internal/Magento/Framework/Lock/Test/Unit/Backend/ZookeeperTest.php b/lib/internal/Magento/Framework/Lock/Test/Unit/Backend/ZookeeperTest.php index 7c450a09df320..62521b9de3082 100644 --- a/lib/internal/Magento/Framework/Lock/Test/Unit/Backend/ZookeeperTest.php +++ b/lib/internal/Magento/Framework/Lock/Test/Unit/Backend/ZookeeperTest.php @@ -43,11 +43,21 @@ protected function setUp() * @expectedExceptionMessage The path needs to be a non-empty string. * @return void */ - public function testConstructionWithException() + public function testConstructionWithPathException() { $this->zookeeperProvider = new ZookeeperProvider($this->host, ''); } + /** + * @expectedException \Magento\Framework\Exception\RuntimeException + * @expectedExceptionMessage The host needs to be a non-empty string. + * @return void + */ + public function testConstructionWithHostException() + { + $this->zookeeperProvider = new ZookeeperProvider('', $this->path); + } + /** * @return void */ diff --git a/setup/src/Magento/Setup/Model/ConfigOptionsList/Lock.php b/setup/src/Magento/Setup/Model/ConfigOptionsList/Lock.php index ae236c1b5d740..37b5cc38dcbd8 100644 --- a/setup/src/Magento/Setup/Model/ConfigOptionsList/Lock.php +++ b/setup/src/Magento/Setup/Model/ConfigOptionsList/Lock.php @@ -241,7 +241,7 @@ private function validateZookeeperConfig(array $options, DeploymentConfig $deplo */ private function getLockProvider(array $options, DeploymentConfig $deploymentConfig): string { - if (empty($options[self::INPUT_KEY_LOCK_PROVIDER])) { + if (!isset($options[self::INPUT_KEY_LOCK_PROVIDER])) { return (string) $deploymentConfig->get( self::CONFIG_PATH_LOCK_PROVIDER, $this->getDefaultValue(self::INPUT_KEY_LOCK_PROVIDER) From 251ce3cfd20ac01617d64e6754ad0ca17f711c60 Mon Sep 17 00:00:00 2001 From: Bohdan Korablov Date: Tue, 19 Mar 2019 10:28:34 -0500 Subject: [PATCH 14/19] MAGETWO-98151: Add support ZooKeeper locks --- .../Framework/Lock/Backend/ZookeeperTest.php | 1 - .../Framework/Lock/Backend/Zookeeper.php | 22 +++++++++++-------- lib/internal/Magento/Framework/Lock/Proxy.php | 9 +++++--- .../Setup/Model/ConfigOptionsList/Lock.php | 7 +++--- .../Unit/Model/ConfigOptionsList/LockTest.php | 19 +++++++++++----- 5 files changed, 36 insertions(+), 22 deletions(-) diff --git a/dev/tests/integration/testsuite/Magento/Framework/Lock/Backend/ZookeeperTest.php b/dev/tests/integration/testsuite/Magento/Framework/Lock/Backend/ZookeeperTest.php index ae218561377f2..8d0caad5d55e4 100644 --- a/dev/tests/integration/testsuite/Magento/Framework/Lock/Backend/ZookeeperTest.php +++ b/dev/tests/integration/testsuite/Magento/Framework/Lock/Backend/ZookeeperTest.php @@ -88,4 +88,3 @@ public function testUnlockWithoutExistingLock() $this->assertFalse($this->model->unlock($name)); } } - diff --git a/lib/internal/Magento/Framework/Lock/Backend/Zookeeper.php b/lib/internal/Magento/Framework/Lock/Backend/Zookeeper.php index 3b69c8734d9a9..1e7ca069df79b 100644 --- a/lib/internal/Magento/Framework/Lock/Backend/Zookeeper.php +++ b/lib/internal/Magento/Framework/Lock/Backend/Zookeeper.php @@ -101,7 +101,8 @@ public function __construct(string $host, string $path = self::DEFAULT_PATH) } /** - * {@inheritdoc} + * @inheritdoc + * * You can see the lock algorithm by the link * @link https://zookeeper.apache.org/doc/r3.1.2/recipes.html#sc_recipes_Locks * @@ -124,7 +125,7 @@ public function lock(string $name, int $timeout = -1): bool throw new RuntimeException(new Phrase('Failed creating lock %1', [$lockPath])); } - while($this->isAnyLock($lockKey, $this->getIndex($lockKey))) { + while ($this->isAnyLock($lockKey, $this->getIndex($lockKey))) { if (!$skipDeadline && $deadline <= microtime(true)) { $this->getProvider()->delete($lockKey); return false; @@ -139,7 +140,8 @@ public function lock(string $name, int $timeout = -1): bool } /** - * {@inheritdoc} + * @inheritdoc + * * @throws RuntimeException */ public function unlock(string $name): bool @@ -152,7 +154,8 @@ public function unlock(string $name): bool } /** - * {@inheritdoc} + * @inheritdoc + * * @throws RuntimeException */ public function isLocked(string $name): bool @@ -184,7 +187,7 @@ private function getProvider(): \Zookeeper } $deadline = microtime(true) + $this->connectionTimeout; - while($this->zookeeper->getState() != \Zookeeper::CONNECTED_STATE) { + while ($this->zookeeper->getState() != \Zookeeper::CONNECTED_STATE) { if ($deadline <= microtime(true)) { throw new RuntimeException(new Phrase('Zookeeper connection timed out!')); } @@ -227,16 +230,17 @@ private function checkAndCreateParentNode(string $path): bool */ private function getIndex(string $key) { - if (!preg_match('/' . $this->lockName . '([0-9]+)$/', $key, $matches)) + 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. * + * 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. * @@ -255,14 +259,14 @@ private function isAnyLock(string $fullKey, int $indexKey = null): bool $children = $this->getProvider()->getChildren($parent); - if (is_null($indexKey) && !empty($children)) { + if (null === $indexKey && !empty($children)) { return true; } foreach ($children as $childKey) { $childIndex = $this->getIndex($childKey); - if (is_null($childIndex)) { + if (null === $childIndex) { continue; } diff --git a/lib/internal/Magento/Framework/Lock/Proxy.php b/lib/internal/Magento/Framework/Lock/Proxy.php index b5f8eee0f2c4f..2718bf6cb3456 100644 --- a/lib/internal/Magento/Framework/Lock/Proxy.php +++ b/lib/internal/Magento/Framework/Lock/Proxy.php @@ -37,7 +37,8 @@ public function __construct(LockBackendFactory $factory) } /** - * {@inheritdoc} + * @inheritdoc + * * @throws RuntimeException */ public function isLocked(string $name): bool @@ -46,7 +47,8 @@ public function isLocked(string $name): bool } /** - * {@inheritdoc} + * @inheritdoc + * * @throws RuntimeException */ public function lock(string $name, int $timeout = -1): bool @@ -55,7 +57,8 @@ public function lock(string $name, int $timeout = -1): bool } /** - * {@inheritdoc} + * @inheritdoc + * * @throws RuntimeException */ public function unlock(string $name): bool diff --git a/setup/src/Magento/Setup/Model/ConfigOptionsList/Lock.php b/setup/src/Magento/Setup/Model/ConfigOptionsList/Lock.php index 37b5cc38dcbd8..799409d1560ac 100644 --- a/setup/src/Magento/Setup/Model/ConfigOptionsList/Lock.php +++ b/setup/src/Magento/Setup/Model/ConfigOptionsList/Lock.php @@ -212,9 +212,9 @@ private function validateZookeeperConfig(array $options, DeploymentConfig $deplo $host = $options[self::INPUT_KEY_LOCK_ZOOKEEPER_HOST] ?? $deploymentConfig->get( - self::CONFIG_PATH_LOCK_ZOOKEEPER_HOST, - $this->getDefaultValue(self::INPUT_KEY_LOCK_ZOOKEEPER_HOST) - ); + 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, @@ -251,7 +251,6 @@ private function getLockProvider(array $options, DeploymentConfig $deploymentCon return (string) $options[self::INPUT_KEY_LOCK_PROVIDER]; } - /** * Sets default configuration for locks * diff --git a/setup/src/Magento/Setup/Test/Unit/Model/ConfigOptionsList/LockTest.php b/setup/src/Magento/Setup/Test/Unit/Model/ConfigOptionsList/LockTest.php index f7ca6e0a09b5a..5a150ad3dcd86 100644 --- a/setup/src/Magento/Setup/Test/Unit/Model/ConfigOptionsList/LockTest.php +++ b/setup/src/Magento/Setup/Test/Unit/Model/ConfigOptionsList/LockTest.php @@ -163,7 +163,10 @@ public function testValidate(array $options, array $expectedResult) $this->deploymentConfigMock->expects($this->any()) ->method('get') ->willReturnArgument(1); - $this->assertSame($expectedResult, $this->lockConfigOptionsList->validate($options, $this->deploymentConfigMock)); + $this->assertSame( + $expectedResult, + $this->lockConfigOptionsList->validate($options, $this->deploymentConfigMock) + ); } /** @@ -186,10 +189,16 @@ public function validateDataProvider(): array LockConfigOptionsList::INPUT_KEY_LOCK_ZOOKEEPER_HOST => '', LockConfigOptionsList::INPUT_KEY_LOCK_ZOOKEEPER_PATH => '', ], - 'expectedResult' => [ - 'Zookeeper path needs to be a non-empty string.', - 'Zookeeper host is should be set.', - ], + 'expectedResult' => extension_loaded('zookeeper') + ? [ + 'Zookeeper path needs to be a non-empty string.', + 'Zookeeper host is should be set.', + ] + : [ + 'php extension Zookeeper is not installed.', + 'Zookeeper path needs to be a non-empty string.', + 'Zookeeper host is should be set.', + ], ], ]; } From f8b955c552c44e4e7478f324d2ecff8f2f0c90e8 Mon Sep 17 00:00:00 2001 From: Bohdan Korablov Date: Thu, 21 Mar 2019 09:00:45 -0500 Subject: [PATCH 15/19] MAGETWO-98151: Add support ZooKeeper and flock locks --- .../Framework/Lock/Backend/FileTest.php | 52 +++++ .../Magento/Framework/Lock/Backend/File.php | 194 ++++++++++++++++++ .../Framework/Lock/Backend/Zookeeper.php | 2 +- .../Framework/Lock/LockBackendFactory.php | 9 + .../Lock/Test/Unit/LockBackendFactoryTest.php | 6 + .../Setup/Model/ConfigOptionsList/Lock.php | 52 +++++ .../Unit/Model/ConfigOptionsList/LockTest.php | 29 ++- 7 files changed, 342 insertions(+), 2 deletions(-) create mode 100644 dev/tests/integration/testsuite/Magento/Framework/Lock/Backend/FileTest.php create mode 100644 lib/internal/Magento/Framework/Lock/Backend/File.php diff --git a/dev/tests/integration/testsuite/Magento/Framework/Lock/Backend/FileTest.php b/dev/tests/integration/testsuite/Magento/Framework/Lock/Backend/FileTest.php new file mode 100644 index 0000000000000..fd550ac14f22f --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Framework/Lock/Backend/FileTest.php @@ -0,0 +1,52 @@ +objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); + $this->model = $this->objectManager->create(\Magento\Framework\Lock\Backend\File::class, ['path' => '/tmp']); + } + + public function testLockAndUnlock() + { + $name = 'test_lock'; + + $this->assertFalse($this->model->isLocked($name)); + + $this->assertTrue($this->model->lock($name)); + $this->assertTrue($this->model->isLocked($name)); + $this->assertFalse($this->model->lock($name, 2)); + + $this->assertTrue($this->model->unlock($name)); + $this->assertFalse($this->model->isLocked($name)); + } + + public function testUnlockWithoutExistingLock() + { + $name = 'test_lock'; + + $this->assertFalse($this->model->isLocked($name)); + $this->assertFalse($this->model->unlock($name)); + } +} diff --git a/lib/internal/Magento/Framework/Lock/Backend/File.php b/lib/internal/Magento/Framework/Lock/Backend/File.php new file mode 100644 index 0000000000000..79f41829f1091 --- /dev/null +++ b/lib/internal/Magento/Framework/Lock/Backend/File.php @@ -0,0 +1,194 @@ +fileDriver = $fileDriver; + $this->path = preg_replace('#\/*$#', '', $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->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->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 --git a/lib/internal/Magento/Framework/Lock/Backend/Zookeeper.php b/lib/internal/Magento/Framework/Lock/Backend/Zookeeper.php index 1e7ca069df79b..1dbedaaaa5e1b 100644 --- a/lib/internal/Magento/Framework/Lock/Backend/Zookeeper.php +++ b/lib/internal/Magento/Framework/Lock/Backend/Zookeeper.php @@ -54,7 +54,7 @@ class Zookeeper implements LockManagerInterface /** * How many microseconds to wait before recheck connections or nodes * - * @var float + * @var int */ private $sleepCycle = 100000; diff --git a/lib/internal/Magento/Framework/Lock/LockBackendFactory.php b/lib/internal/Magento/Framework/Lock/LockBackendFactory.php index 46cb2998ede70..20e0f69328c88 100644 --- a/lib/internal/Magento/Framework/Lock/LockBackendFactory.php +++ b/lib/internal/Magento/Framework/Lock/LockBackendFactory.php @@ -14,6 +14,7 @@ use Magento\Framework\Lock\Backend\Database as DatabaseLock; use Magento\Framework\Lock\Backend\Zookeeper as ZookeeperLock; use Magento\Framework\Lock\Backend\Cache as CacheLock; +use Magento\Framework\Lock\Backend\File as FileLock; /** * The factory to create object that implements LockManagerInterface @@ -55,6 +56,13 @@ class LockBackendFactory */ const LOCK_CACHE = 'cache'; + /** + * File lock provider name + * + * @const string + */ + const LOCK_FILE = 'file'; + /** * The list of lock providers with mapping on classes * @@ -64,6 +72,7 @@ class LockBackendFactory self::LOCK_DB => DatabaseLock::class, self::LOCK_ZOOKEEPER => ZookeeperLock::class, self::LOCK_CACHE => CacheLock::class, + self::LOCK_FILE => FileLock::class, ]; /** diff --git a/lib/internal/Magento/Framework/Lock/Test/Unit/LockBackendFactoryTest.php b/lib/internal/Magento/Framework/Lock/Test/Unit/LockBackendFactoryTest.php index 8864ab6f9ead1..030b126284db4 100644 --- a/lib/internal/Magento/Framework/Lock/Test/Unit/LockBackendFactoryTest.php +++ b/lib/internal/Magento/Framework/Lock/Test/Unit/LockBackendFactoryTest.php @@ -10,6 +10,7 @@ use Magento\Framework\Lock\Backend\Database as DatabaseLock; use Magento\Framework\Lock\Backend\Zookeeper as ZookeeperLock; use Magento\Framework\Lock\Backend\Cache as CacheLock; +use Magento\Framework\Lock\Backend\File as FileLock; use Magento\Framework\Lock\LockBackendFactory; use Magento\Framework\ObjectManagerInterface; use Magento\Framework\Lock\LockManagerInterface; @@ -95,6 +96,11 @@ public function createDataProvider(): array 'lockProviderClass' => CacheLock::class, 'config' => [], ], + 'file' => [ + 'lockProvider' => LockBackendFactory::LOCK_FILE, + 'lockProviderClass' => FileLock::class, + 'config' => ['path' => '/my/path'], + ], ]; if (extension_loaded('zookeeper')) { diff --git a/setup/src/Magento/Setup/Model/ConfigOptionsList/Lock.php b/setup/src/Magento/Setup/Model/ConfigOptionsList/Lock.php index 799409d1560ac..b4cc14e25ca19 100644 --- a/setup/src/Magento/Setup/Model/ConfigOptionsList/Lock.php +++ b/setup/src/Magento/Setup/Model/ConfigOptionsList/Lock.php @@ -49,6 +49,13 @@ class Lock implements ConfigOptionsListInterface */ const INPUT_KEY_LOCK_ZOOKEEPER_PATH = 'lock-zookeeper-path'; + /** + * The name of an option to set File path + * + * @const string + */ + const INPUT_KEY_LOCK_FILE_PATH = 'lock-file-path'; + /** * The configuration path to save lock provider * @@ -77,6 +84,13 @@ class Lock implements ConfigOptionsListInterface */ const CONFIG_PATH_LOCK_ZOOKEEPER_PATH = 'lock/config/path'; + /** + * The configuration path to save locks directory path + * + * @const string + */ + const CONFIG_PATH_LOCK_FILE_PATH = 'lock/config/path'; + /** * The list of lock providers * @@ -86,6 +100,7 @@ class Lock implements ConfigOptionsListInterface LockBackendFactory::LOCK_DB, LockBackendFactory::LOCK_ZOOKEEPER, LockBackendFactory::LOCK_CACHE, + LockBackendFactory::LOCK_FILE, ]; /** @@ -106,6 +121,10 @@ class Lock implements ConfigOptionsListInterface 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, + ], ]; /** @@ -151,6 +170,12 @@ public function getOptions() 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.' + ), ]; } @@ -184,6 +209,9 @@ public function validate(array $options, DeploymentConfig $deploymentConfig) 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 = []; @@ -195,6 +223,30 @@ public function validate(array $options, DeploymentConfig $deploymentConfig) 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_ZOOKEEPER_PATH) + ); + + if (!$path) { + $errors[] = 'The path needs to be a non-empty string.'; + } + + return $errors; + } + /** * Validates Zookeeper configuration * diff --git a/setup/src/Magento/Setup/Test/Unit/Model/ConfigOptionsList/LockTest.php b/setup/src/Magento/Setup/Test/Unit/Model/ConfigOptionsList/LockTest.php index 5a150ad3dcd86..1a46bddf5f21a 100644 --- a/setup/src/Magento/Setup/Test/Unit/Model/ConfigOptionsList/LockTest.php +++ b/setup/src/Magento/Setup/Test/Unit/Model/ConfigOptionsList/LockTest.php @@ -44,7 +44,7 @@ protected function setUp() public function testGetOptions() { $options = $this->lockConfigOptionsList->getOptions(); - $this->assertSame(4, count($options)); + $this->assertSame(5, count($options)); $this->assertArrayHasKey(0, $options); $this->assertInstanceOf(SelectConfigOption::class, $options[0]); @@ -61,6 +61,10 @@ public function testGetOptions() $this->assertArrayHasKey(3, $options); $this->assertInstanceOf(TextConfigOption::class, $options[3]); $this->assertEquals(LockConfigOptionsList::INPUT_KEY_LOCK_ZOOKEEPER_PATH, $options[3]->getName()); + + $this->assertArrayHasKey(4, $options); + $this->assertInstanceOf(TextConfigOption::class, $options[4]); + $this->assertEquals(LockConfigOptionsList::INPUT_KEY_LOCK_FILE_PATH, $options[4]->getName()); } /** @@ -150,6 +154,20 @@ public function createConfigDataProvider(): array ], ], ], + 'Check specific file lock options' => [ + 'options' => [ + LockConfigOptionsList::INPUT_KEY_LOCK_PROVIDER => LockBackendFactory::LOCK_FILE, + LockConfigOptionsList::INPUT_KEY_LOCK_FILE_PATH => '/my/path' + ], + 'expectedResult' => [ + 'lock' => [ + 'provider' => LockBackendFactory::LOCK_FILE, + 'config' => [ + 'path' => '/my/path', + ], + ], + ], + ], ]; } @@ -200,6 +218,15 @@ public function validateDataProvider(): array 'Zookeeper host is should be set.', ], ], + 'Empty path for File lock' => [ + 'options' => [ + LockConfigOptionsList::INPUT_KEY_LOCK_PROVIDER => LockBackendFactory::LOCK_FILE, + LockConfigOptionsList::INPUT_KEY_LOCK_FILE_PATH => '', + ], + 'expectedResult' => [ + 'The path needs to be a non-empty string.', + ], + ], ]; } } From b5eaa81580e7c9fe262e81e0602732eb1e37b49a Mon Sep 17 00:00:00 2001 From: Bohdan Korablov Date: Thu, 21 Mar 2019 09:08:29 -0500 Subject: [PATCH 16/19] MAGETWO-98151: Add support ZooKeeper and flock locks --- .../testsuite/Magento/Framework/Lock/Backend/FileTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev/tests/integration/testsuite/Magento/Framework/Lock/Backend/FileTest.php b/dev/tests/integration/testsuite/Magento/Framework/Lock/Backend/FileTest.php index fd550ac14f22f..feb111e436b1d 100644 --- a/dev/tests/integration/testsuite/Magento/Framework/Lock/Backend/FileTest.php +++ b/dev/tests/integration/testsuite/Magento/Framework/Lock/Backend/FileTest.php @@ -8,7 +8,7 @@ namespace Magento\Framework\Lock\Backend; /** - * \Magento\Framework\Lock\Backend\Database test case + * \Magento\Framework\Lock\Backend\File test case */ class FileTest extends \PHPUnit\Framework\TestCase { From a2ad4424d5db9c41f50c4b8090f2e8a981b02014 Mon Sep 17 00:00:00 2001 From: Bohdan Korablov Date: Thu, 21 Mar 2019 09:40:01 -0500 Subject: [PATCH 17/19] MAGETWO-98151: Add support ZooKeeper and flock locks --- lib/internal/Magento/Framework/Lock/Backend/File.php | 4 ++-- lib/internal/Magento/Framework/Lock/Backend/Zookeeper.php | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/internal/Magento/Framework/Lock/Backend/File.php b/lib/internal/Magento/Framework/Lock/Backend/File.php index 79f41829f1091..47aed8a6fe3ec 100644 --- a/lib/internal/Magento/Framework/Lock/Backend/File.php +++ b/lib/internal/Magento/Framework/Lock/Backend/File.php @@ -59,7 +59,7 @@ public function __construct(FileDriver $fileDriver, string $path) } $this->fileDriver = $fileDriver; - $this->path = preg_replace('#\/*$#', '', $path) ?: '/'; + $this->path = rtrim($path, '/') . '/'; try { if (!$this->fileDriver->isExists($this->path)) { @@ -159,7 +159,7 @@ public function unlock(string $name): bool */ private function getLockPath(string $name): string { - return $this->path . '/' . $name; + return $this->path . $name; } /** diff --git a/lib/internal/Magento/Framework/Lock/Backend/Zookeeper.php b/lib/internal/Magento/Framework/Lock/Backend/Zookeeper.php index 1dbedaaaa5e1b..cbba981ae1b51 100644 --- a/lib/internal/Magento/Framework/Lock/Backend/Zookeeper.php +++ b/lib/internal/Magento/Framework/Lock/Backend/Zookeeper.php @@ -97,7 +97,7 @@ public function __construct(string $host, string $path = self::DEFAULT_PATH) } $this->host = $host; - $this->path = preg_replace('#\/*$#', '', $path) ?: '/'; + $this->path = rtrim($path, '/') . '/'; } /** @@ -171,7 +171,7 @@ public function isLocked(string $name): bool */ private function getFullPathToLock(string $name): string { - return $this->path . '/' . $name . '/' . $this->lockName; + return $this->path . $name . '/' . $this->lockName; } /** From 2d9c915275cbd5b5b5da1f8b968b56175ca64e0c Mon Sep 17 00:00:00 2001 From: Bohdan Korablov Date: Thu, 21 Mar 2019 12:00:33 -0500 Subject: [PATCH 18/19] MAGETWO-98151: Add support ZooKeeper and flock locks --- .../Lock/Backend/{FileTest.php => FileLockTest.php} | 9 ++++++--- .../Framework/Lock/Backend/{File.php => FileLock.php} | 2 +- .../Magento/Framework/Lock/LockBackendFactory.php | 2 +- .../Framework/Lock/Test/Unit/LockBackendFactoryTest.php | 2 +- 4 files changed, 9 insertions(+), 6 deletions(-) rename dev/tests/integration/testsuite/Magento/Framework/Lock/Backend/{FileTest.php => FileLockTest.php} (81%) rename lib/internal/Magento/Framework/Lock/Backend/{File.php => FileLock.php} (99%) diff --git a/dev/tests/integration/testsuite/Magento/Framework/Lock/Backend/FileTest.php b/dev/tests/integration/testsuite/Magento/Framework/Lock/Backend/FileLockTest.php similarity index 81% rename from dev/tests/integration/testsuite/Magento/Framework/Lock/Backend/FileTest.php rename to dev/tests/integration/testsuite/Magento/Framework/Lock/Backend/FileLockTest.php index feb111e436b1d..e64b3c505acf1 100644 --- a/dev/tests/integration/testsuite/Magento/Framework/Lock/Backend/FileTest.php +++ b/dev/tests/integration/testsuite/Magento/Framework/Lock/Backend/FileLockTest.php @@ -10,10 +10,10 @@ /** * \Magento\Framework\Lock\Backend\File test case */ -class FileTest extends \PHPUnit\Framework\TestCase +class FileLockTest extends \PHPUnit\Framework\TestCase { /** - * @var \Magento\Framework\Lock\Backend\File + * @var \Magento\Framework\Lock\Backend\FileLock */ private $model; @@ -25,7 +25,10 @@ class FileTest extends \PHPUnit\Framework\TestCase protected function setUp() { $this->objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); - $this->model = $this->objectManager->create(\Magento\Framework\Lock\Backend\File::class, ['path' => '/tmp']); + $this->model = $this->objectManager->create( + \Magento\Framework\Lock\Backend\FileLock::class, + ['path' => '/tmp'] + ); } public function testLockAndUnlock() diff --git a/lib/internal/Magento/Framework/Lock/Backend/File.php b/lib/internal/Magento/Framework/Lock/Backend/FileLock.php similarity index 99% rename from lib/internal/Magento/Framework/Lock/Backend/File.php rename to lib/internal/Magento/Framework/Lock/Backend/FileLock.php index 47aed8a6fe3ec..d168e910a4ab7 100644 --- a/lib/internal/Magento/Framework/Lock/Backend/File.php +++ b/lib/internal/Magento/Framework/Lock/Backend/FileLock.php @@ -16,7 +16,7 @@ /** * LockManager using the file system for locks */ -class File implements LockManagerInterface +class FileLock implements LockManagerInterface { /** * The file driver instance diff --git a/lib/internal/Magento/Framework/Lock/LockBackendFactory.php b/lib/internal/Magento/Framework/Lock/LockBackendFactory.php index 20e0f69328c88..b142085ef6563 100644 --- a/lib/internal/Magento/Framework/Lock/LockBackendFactory.php +++ b/lib/internal/Magento/Framework/Lock/LockBackendFactory.php @@ -14,7 +14,7 @@ use Magento\Framework\Lock\Backend\Database as DatabaseLock; use Magento\Framework\Lock\Backend\Zookeeper as ZookeeperLock; use Magento\Framework\Lock\Backend\Cache as CacheLock; -use Magento\Framework\Lock\Backend\File as FileLock; +use Magento\Framework\Lock\Backend\FileLock; /** * The factory to create object that implements LockManagerInterface diff --git a/lib/internal/Magento/Framework/Lock/Test/Unit/LockBackendFactoryTest.php b/lib/internal/Magento/Framework/Lock/Test/Unit/LockBackendFactoryTest.php index 030b126284db4..ebf2f54f3e093 100644 --- a/lib/internal/Magento/Framework/Lock/Test/Unit/LockBackendFactoryTest.php +++ b/lib/internal/Magento/Framework/Lock/Test/Unit/LockBackendFactoryTest.php @@ -10,7 +10,7 @@ use Magento\Framework\Lock\Backend\Database as DatabaseLock; use Magento\Framework\Lock\Backend\Zookeeper as ZookeeperLock; use Magento\Framework\Lock\Backend\Cache as CacheLock; -use Magento\Framework\Lock\Backend\File as FileLock; +use Magento\Framework\Lock\Backend\FileLock; use Magento\Framework\Lock\LockBackendFactory; use Magento\Framework\ObjectManagerInterface; use Magento\Framework\Lock\LockManagerInterface; From 1cb451a2dd2aa2867a9b4b72ec5cfb8f72aa6852 Mon Sep 17 00:00:00 2001 From: Bohdan Korablov Date: Thu, 21 Mar 2019 13:15:55 -0500 Subject: [PATCH 19/19] MAGETWO-98151: Add support ZooKeeper and flock locks --- setup/src/Magento/Setup/Model/ConfigOptionsList/Lock.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup/src/Magento/Setup/Model/ConfigOptionsList/Lock.php b/setup/src/Magento/Setup/Model/ConfigOptionsList/Lock.php index b4cc14e25ca19..66f41128c46b1 100644 --- a/setup/src/Magento/Setup/Model/ConfigOptionsList/Lock.php +++ b/setup/src/Magento/Setup/Model/ConfigOptionsList/Lock.php @@ -237,7 +237,7 @@ private function validateFileConfig(array $options, DeploymentConfig $deployment $path = $options[self::INPUT_KEY_LOCK_FILE_PATH] ?? $deploymentConfig->get( self::CONFIG_PATH_LOCK_FILE_PATH, - $this->getDefaultValue(self::INPUT_KEY_LOCK_ZOOKEEPER_PATH) + $this->getDefaultValue(self::INPUT_KEY_LOCK_FILE_PATH) ); if (!$path) {