1
0
Fork 0
mirror of synced 2025-04-04 21:53:37 +03:00

Compare commits

...

7 commits

Author SHA1 Message Date
Alex Lushpai
a06ec22341
Merge pull request #9 from curse89/modify-middleware
add-ttl-for-middleware
2023-10-18 14:28:42 +03:00
Сергей Кривич
f15d9a2a86 add-ttl-for-middleware 2023-10-18 14:15:32 +03:00
Alex Lushpai
a104eebf2a
Bump symfony validator to 5.3 2021-10-18 11:08:08 +03:00
Sergey Chazov
3f87618424 bump symfony validator to 5.3 2021-08-17 17:53:44 +03:00
Alex Lushpai
5520c4ca68
Fix serialization for transport php serializer 2021-04-22 14:57:08 +03:00
Akolzin Dmitry
ac0a1ae95d fix serialization for transport php serializer 2021-04-22 14:55:30 +03:00
Akolzin Dmitry
d9dca7b78b
lockable middleware (#5) 2021-04-16 13:37:47 +03:00
6 changed files with 219 additions and 3 deletions

View file

@ -96,4 +96,18 @@ abstract class CommandMessage
return $options;
}
/**
* For lockable message
*
* @return array
*/
public function __serialize(): array
{
return [
'commandName' => $this->getCommandName(),
'arguments' => $this->getArguments(),
'options' => $this->getOptions()
];
}
}

View file

@ -0,0 +1,13 @@
<?php
namespace RetailCrm\ServiceBundle\Messenger\Middleware;
/**
* Interface LockableMessage
*
* @package RetailCrm\ServiceBundle\Messenger\Middleware
*/
interface LockableMessage
{
public function __serialize(): array;
}

View file

@ -0,0 +1,74 @@
<?php
namespace RetailCrm\ServiceBundle\Messenger\Middleware;
use Symfony\Component\Lock\LockFactory;
use Symfony\Component\Messenger\Envelope;
use Symfony\Component\Messenger\Middleware\MiddlewareInterface;
use Symfony\Component\Messenger\Middleware\StackInterface;
use Symfony\Component\Messenger\Stamp\ReceivedStamp;
use Throwable;
/**
* Class LockableMessageMiddleware
*
* @package RetailCrm\ServiceBundle\Messenger\Middleware
*/
class LockableMessageMiddleware implements MiddlewareInterface
{
/**
* @var LockFactory
*/
private $lockFactory;
/**
* @var int|null
*/
private $ttl;
public function __construct(LockFactory $lockFactory, int $ttl = null)
{
$this->lockFactory = $lockFactory;
$this->ttl = $ttl;
}
/**
* @param Envelope $envelope
* @param StackInterface $stack
*
* @return Envelope
*
* @throws Throwable
*/
public function handle(Envelope $envelope, StackInterface $stack): Envelope
{
$message = $envelope->getMessage();
if ($envelope->all(ReceivedStamp::class) && $message instanceof LockableMessage) {
$lock = $this->lockFactory->createLock($this->objectHash($message), $this->ttl);
if (!$lock->acquire()) {
return $envelope;
}
try {
return $stack->next()->handle($envelope, $stack);
} catch (Throwable $exception) {
throw $exception;
} finally {
$lock->release();
}
}
return $stack->next()->handle($envelope, $stack);
}
/**
* @param LockableMessage $message
*
* @return string
*/
private function objectHash(LockableMessage $message): string
{
return hash('crc32', serialize($message));
}
}

View file

@ -3,8 +3,9 @@
namespace RetailCrm\ServiceBundle\Tests\DataFixtures;
use RetailCrm\ServiceBundle\Messenger\CommandMessage;
use RetailCrm\ServiceBundle\Messenger\Middleware\LockableMessage;
class TestMessage extends CommandMessage
class TestMessage extends CommandMessage implements LockableMessage
{
public function __construct()
{

View file

@ -0,0 +1,113 @@
<?php
namespace RetailCrm\ServiceBundle\Tests\Messenger\Middleware;
use PHPUnit\Framework\TestCase;
use RetailCrm\ServiceBundle\Messenger\Middleware\LockableMessageMiddleware;
use RetailCrm\ServiceBundle\Tests\DataFixtures\TestMessage;
use Symfony\Component\Lock\Exception\LockConflictedException;
use Symfony\Component\Lock\Key;
use Symfony\Component\Lock\Lock;
use Symfony\Component\Lock\LockFactory;
use Symfony\Component\Lock\PersistingStoreInterface;
use Symfony\Component\Messenger\Envelope;
use Symfony\Component\Messenger\Middleware\MiddlewareInterface;
use Symfony\Component\Messenger\Middleware\StackInterface;
use Symfony\Component\Messenger\Stamp\ReceivedStamp;
/**
* Class LockableMessageMiddlewareTest
*
* @package RetailCrm\ServiceBundle\Tests\Messenger\Middleware
*/
class LockableMessageMiddlewareTest extends TestCase
{
/**
* @var LockFactory
*/
private $lockFactory;
protected function setUp(): void
{
$this->lockFactory = $this->createMock(LockFactory::class);
}
public function testHandle(): void
{
$store = $this->createMock(PersistingStoreInterface::class);
$key = new Key(uniqid());
$lock = new Lock($key, $store);
$this->lockFactory->expects(static::once())->method('createLock')->willReturn($lock);
$envelope = new Envelope(new TestMessage(), [new ReceivedStamp('test')]);
$next = $this->createMock(MiddlewareInterface::class);
$next->method('handle')->willReturn($envelope);
$stack = $this->createMock(StackInterface::class);
$stack->method('next')->willReturn($next);
$middleware = new LockableMessageMiddleware($this->lockFactory);
$result = $middleware->handle($envelope, $stack);
static::assertInstanceOf(Envelope::class, $result);
}
public function testLockHandle(): void
{
$store = $this->createMock(PersistingStoreInterface::class);
$store->method('save')->willThrowException(new LockConflictedException);
$key = new Key(uniqid());
$lock = new Lock($key, $store);
$this->lockFactory->expects(static::once())->method('createLock')->willReturn($lock);
$envelope = new Envelope(new TestMessage(), [new ReceivedStamp('test')]);
$next = $this->createMock(MiddlewareInterface::class);
$next->method('handle')->willReturn($envelope);
$stack = $this->createMock(StackInterface::class);
$stack->method('next')->willReturn($next);
$middleware = new LockableMessageMiddleware($this->lockFactory);
$result = $middleware->handle($envelope, $stack);
static::assertInstanceOf(Envelope::class, $result);
}
public function testNonLockableHandle(): void
{
$store = $this->createMock(PersistingStoreInterface::class);
$store->method('save')->willThrowException(new LockConflictedException);
$key = new Key(uniqid());
$lock = new Lock($key, $store);
$this->lockFactory->expects(static::never())->method('createLock')->willReturn($lock);
$envelope = new Envelope(new \stdClass(), [new ReceivedStamp('test')]);
$next = $this->createMock(MiddlewareInterface::class);
$next->method('handle')->willReturn($envelope);
$stack = $this->createMock(StackInterface::class);
$stack->method('next')->willReturn($next);
$middleware = new LockableMessageMiddleware($this->lockFactory);
$result = $middleware->handle($envelope, $stack);
static::assertInstanceOf(Envelope::class, $result);
}
public function testNonReceivedHandle(): void
{
$store = $this->createMock(PersistingStoreInterface::class);
$store->method('save')->willThrowException(new LockConflictedException);
$key = new Key(uniqid());
$lock = new Lock($key, $store);
$this->lockFactory->expects(static::never())->method('createLock')->willReturn($lock);
$envelope = new Envelope(new TestMessage());
$next = $this->createMock(MiddlewareInterface::class);
$next->method('handle')->willReturn($envelope);
$stack = $this->createMock(StackInterface::class);
$stack->method('next')->willReturn($next);
$middleware = new LockableMessageMiddleware($this->lockFactory);
$result = $middleware->handle($envelope, $stack);
static::assertInstanceOf(Envelope::class, $result);
}
}

View file

@ -15,12 +15,13 @@
"symfony/framework-bundle": "^4.0|^5.0",
"symfony/serializer": "^5.2",
"symfony/http-kernel": "^4.0|^5.0",
"symfony/validator": "^4.0|^5.0",
"symfony/validator": "^4.0|^5.3",
"symfony/security-guard": "^4.0|^5.0",
"symfony/console": "^5.2",
"symfony/messenger": "^5.2",
"symfony/process": "^5.2",
"symfony/event-dispatcher": "^5.2"
"symfony/event-dispatcher": "^5.2",
"symfony/lock": "^5.2"
},
"autoload": {
"psr-4": {