-
Notifications
You must be signed in to change notification settings - Fork 44
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
PIPRES-319: Lock process adapter (#839)
- Loading branch information
Showing
5 changed files
with
191 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
<?php | ||
|
||
namespace Mollie\Infrastructure\Adapter; | ||
|
||
use Mollie\Config\Config; | ||
use Mollie\Infrastructure\Exception\CouldNotHandleLocking; | ||
use Symfony\Component\Lock\Factory as LockFactoryV3; | ||
use Symfony\Component\Lock\LockFactory as LockFactoryV4; | ||
use Symfony\Component\Lock\LockInterface; | ||
use Symfony\Component\Lock\Store\FlockStore; | ||
|
||
class Lock | ||
{ | ||
private $lockFactory; | ||
/** @var ?LockInterface */ | ||
private $lock; | ||
|
||
public function __construct() | ||
{ | ||
$store = new FlockStore(); | ||
|
||
if (class_exists(LockFactoryV4::class)) { | ||
// Symfony 4.4+ | ||
$this->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; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
<?php | ||
|
||
namespace Mollie\Infrastructure\Exception; | ||
|
||
use Mollie\Exception\Code\ExceptionCode; | ||
use Mollie\Exception\MollieException; | ||
|
||
class CouldNotHandleLocking extends MollieException | ||
{ | ||
public static function lockExists(): self | ||
{ | ||
return new self( | ||
'Lock exists', | ||
ExceptionCode::INFRASTRUCTURE_LOCK_EXISTS | ||
); | ||
} | ||
|
||
public static function lockOnAcquireIsMissing(): self | ||
{ | ||
return new self( | ||
'Lock on acquire is missing', | ||
ExceptionCode::INFRASTRUCTURE_LOCK_ON_ACQUIRE_IS_MISSING | ||
); | ||
} | ||
|
||
public static function lockOnReleaseIsMissing(): self | ||
{ | ||
return new self( | ||
'Lock on release is missing', | ||
ExceptionCode::INFRASTRUCTURE_LOCK_ON_RELEASE_IS_MISSING | ||
); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
<?php | ||
|
||
namespace Mollie\Tests\Integration\Infrastructure\Adapter; | ||
|
||
use Mollie\Exception\Code\ExceptionCode; | ||
use Mollie\Infrastructure\Adapter\Lock; | ||
use Mollie\Infrastructure\Exception\CouldNotHandleLocking; | ||
use Mollie\Tests\Integration\BaseTestCase; | ||
|
||
class LockTest extends BaseTestCase | ||
{ | ||
public function testItSuccessfullyCompletesLockFlow(): void | ||
{ | ||
/** @var Lock $lock */ | ||
$lock = $this->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(); | ||
} | ||
} |