From 8d7b7164f8653eb4be0d0fc61b17fbdb262d0002 Mon Sep 17 00:00:00 2001 From: mandan2 <61560082+mandan2@users.noreply.github.com> Date: Mon, 13 Nov 2023 13:29:51 +0200 Subject: [PATCH] PIPRES-319: Lock process adapter (#839) --- src/Config/Config.php | 2 + src/Exception/Code/ExceptionCode.php | 3 + src/Infrastructure/Adapter/Lock.php | 79 +++++++++++++++++++ .../Exception/CouldNotHandleLocking.php | 33 ++++++++ .../Infrastructure/Adapter/LockTest.php | 74 +++++++++++++++++ 5 files changed, 191 insertions(+) create mode 100644 src/Infrastructure/Adapter/Lock.php create mode 100644 src/Infrastructure/Exception/CouldNotHandleLocking.php create mode 100644 tests/Integration/Infrastructure/Adapter/LockTest.php diff --git a/src/Config/Config.php b/src/Config/Config.php index 1509c77ba..6f348ef25 100644 --- a/src/Config/Config.php +++ b/src/Config/Config.php @@ -282,6 +282,8 @@ class Config const APPLE_PAY_DIRECT_ORDER_CREATION_MAX_WAIT_RETRIES = 10; const BANCONTACT_ORDER_CREATION_MAX_WAIT_RETRIES = 600; + public const LOCK_TIME_TO_LIVE = 60; + /** @var array */ public static $methods = [ 'banktransfer' => 'Bank', diff --git a/src/Exception/Code/ExceptionCode.php b/src/Exception/Code/ExceptionCode.php index cdd7ddb31..d7f21cef6 100644 --- a/src/Exception/Code/ExceptionCode.php +++ b/src/Exception/Code/ExceptionCode.php @@ -8,6 +8,9 @@ class ExceptionCode public const INFRASTRUCTURE_FAILED_TO_INSTALL_ORDER_STATE = 1001; public const INFRASTRUCTURE_UNKNOWN_ERROR = 1002; + public const INFRASTRUCTURE_LOCK_EXISTS = 1003; + public const INFRASTRUCTURE_LOCK_ON_ACQUIRE_IS_MISSING = 1004; + public const INFRASTRUCTURE_LOCK_ON_RELEASE_IS_MISSING = 1005; public const FAILED_TO_FIND_CUSTOMER_ADDRESS = 2001; diff --git a/src/Infrastructure/Adapter/Lock.php b/src/Infrastructure/Adapter/Lock.php new file mode 100644 index 000000000..8c3978734 --- /dev/null +++ b/src/Infrastructure/Adapter/Lock.php @@ -0,0 +1,79 @@ +lockFactory = new LockFactoryV4($store); + + return; + } + + // Symfony 3.4+ + $this->lockFactory = new LockFactoryV3($store); + } + + /** + * @throws CouldNotHandleLocking + */ + public function create(string $resource, int $ttl = Config::LOCK_TIME_TO_LIVE, bool $autoRelease = true): void + { + if ($this->lock) { + throw CouldNotHandleLocking::lockExists(); + } + + $this->lock = $this->lockFactory->createLock($resource, $ttl, $autoRelease); + } + + /** + * @throws CouldNotHandleLocking + */ + public function acquire(bool $blocking = false): bool + { + if (!$this->lock) { + throw CouldNotHandleLocking::lockOnAcquireIsMissing(); + } + + return $this->lock->acquire($blocking); + } + + /** + * @throws CouldNotHandleLocking + */ + public function release(): void + { + if (!$this->lock) { + throw CouldNotHandleLocking::lockOnReleaseIsMissing(); + } + + $this->lock->release(); + + $this->lock = null; + } + + public function __destruct() + { + try { + $this->release(); + } catch (CouldNotHandleLocking $exception) { + return; + } + } +} diff --git a/src/Infrastructure/Exception/CouldNotHandleLocking.php b/src/Infrastructure/Exception/CouldNotHandleLocking.php new file mode 100644 index 000000000..c6c23fe58 --- /dev/null +++ b/src/Infrastructure/Exception/CouldNotHandleLocking.php @@ -0,0 +1,33 @@ +getService(Lock::class); + + $lock->create('test-lock-name'); + + $this->assertTrue($lock->acquire()); + + $lock->release(); + } + + public function testItSuccessfullyLocksResourceFromAnotherProcess(): void + { + /** @var Lock $lock */ + $lock = $this->getService(Lock::class); + + $lock->create('test-lock-name'); + + $this->assertTrue($lock->acquire()); + + /** @var Lock $newLock */ + $newLock = $this->getService(Lock::class); + + $newLock->create('test-lock-name'); + + $this->assertFalse($newLock->acquire()); + } + + public function testItUnsuccessfullyCompletesLockFlowFailedToCreateLockWithMissingLock(): void + { + /** @var Lock $lock */ + $lock = $this->getService(Lock::class); + + $this->expectException(CouldNotHandleLocking::class); + $this->expectExceptionCode(ExceptionCode::INFRASTRUCTURE_LOCK_EXISTS); + + $lock->create('test-lock-name'); + $lock->create('test-lock-name'); + } + + public function testItUnsuccessfullyCompletesLockFlowFailedToAcquireLockWithMissingLock(): void + { + /** @var Lock $lock */ + $lock = $this->getService(Lock::class); + + $this->expectException(CouldNotHandleLocking::class); + $this->expectExceptionCode(ExceptionCode::INFRASTRUCTURE_LOCK_ON_ACQUIRE_IS_MISSING); + + $lock->acquire(); + } + + public function testItUnsuccessfullyCompletesLockFlowFailedToReleaseLockWithMissingLock(): void + { + /** @var Lock $lock */ + $lock = $this->getService(Lock::class); + + $this->expectException(CouldNotHandleLocking::class); + $this->expectExceptionCode(ExceptionCode::INFRASTRUCTURE_LOCK_ON_RELEASE_IS_MISSING); + + $lock->release(); + } +}