Skip to content

Commit

Permalink
Tested retry behavior
Browse files Browse the repository at this point in the history
  • Loading branch information
nikophil committed Mar 22, 2020
1 parent 2c64350 commit 1a7739c
Show file tree
Hide file tree
Showing 29 changed files with 171 additions and 83 deletions.
14 changes: 0 additions & 14 deletions psalm.xml
Original file line number Diff line number Diff line change
Expand Up @@ -13,18 +13,4 @@
<directory name="vendor"/>
</ignoreFiles>
</projectFiles>
<issueHandlers>
<MissingPropertyType>
<errorLevel type="suppress">
<file name="src/Storage/Doctrine/StoredMessage.php"/>
</errorLevel>
</MissingPropertyType>
<PossiblyFalseArgument>
<errorLevel type="suppress">
<file name="src/Storage/Doctrine/EventListener/UpdateStoredMessageListener.php"/>
<file name="src/Storage/Doctrine/EventListener/SaveRetriedMessageListener.php"/>
<file name="src/Storage/Doctrine/StoredMessage.php"/>
</errorLevel>
</PossiblyFalseArgument>
</issueHandlers>
</psalm>
8 changes: 4 additions & 4 deletions src/Statistics/MetricsPerMessageType.php
Original file line number Diff line number Diff line change
Expand Up @@ -41,14 +41,14 @@ public function getMessagesHandledPerHour(): float
return round($this->getMessagesCount() / $this->getNbHoursInPeriod(), 2);
}

public function getAverageWaitingTime(): float
public function getAverageWaitingTime(): ?float
{
return round($this->averageWaitingTime, 6);
return null !== $this->averageWaitingTime ? round($this->averageWaitingTime, 6) : null;
}

public function getAverageHandlingTime(): float
public function getAverageHandlingTime(): ?float
{
return round($this->averageHandlingTime, 6);
return null !== $this->averageHandlingTime ? round($this->averageHandlingTime, 6) : null;
}

private function getNbHoursInPeriod(): float
Expand Down
3 changes: 2 additions & 1 deletion src/Storage/Doctrine/Connection.php
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ public function findMessage(string $messageUid): ?StoredMessage
return null;
}

/** @psalm-suppress PossiblyFalseArgument */
return new StoredMessage(
$row['message_uid'],
$row['class'],
Expand All @@ -118,7 +119,7 @@ public function getStatistics(\DateTimeImmutable $fromDate, \DateTimeImmutable $
->getSQL(),
[
'from_date' => (float) $fromDate->format('U'),
'to_date' => (float) $toDate->format('U')
'to_date' => (float) $toDate->format('U'),
]
);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,12 @@ public function __construct(Connection $doctrineConnection)

public function onMessageRetried(MessageRetriedByUserEvent $retriedMessageEvent): void
{
/** @psalm-suppress PossiblyFalseArgument */
$this->doctrineConnection->saveMessage(
new StoredMessage(
$retriedMessageEvent->getMessageUid(), $retriedMessageEvent->getMessageClass(), \DateTimeImmutable::createFromFormat('U', (string) time())
$retriedMessageEvent->getMessageUid(),
$retriedMessageEvent->getMessageClass(),
\DateTimeImmutable::createFromFormat('0.u00 U', microtime())
)
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ public function onMessageFailed(WorkerMessageFailedEvent $event): void
return;
}

$storedMessage->updateFailingTime(\DateTimeImmutable::createFromFormat('U', (string) time()));
$storedMessage->updateFailingTime();
$this->doctrineConnection->updateMessage($storedMessage);
}

Expand Down
3 changes: 3 additions & 0 deletions src/Storage/Doctrine/StoredMessage.php
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ public static function fromEnvelope(Envelope $envelope): self
throw new MessengerIdStampMissingException();
}

/** @psalm-suppress PossiblyFalseArgument */
return new self(
$monitorIdStamp->getId(),
\get_class($envelope->getMessage()),
Expand Down Expand Up @@ -86,6 +87,7 @@ public function getWaitingTime(): ?float
public function updateWaitingTime(): void
{
$now = \DateTimeImmutable::createFromFormat('0.u00 U', microtime());
/** @psalm-suppress PossiblyFalseReference */
$this->waitingTime = round((float) $now->format('U.u') - (float) $this->dispatchedAt->format('U.u'), 6);
}

Expand Down Expand Up @@ -123,6 +125,7 @@ private function computePassedTimeSinceReceived(): float
{
$now = \DateTimeImmutable::createFromFormat('0.u00 U', microtime());

/** @psalm-suppress PossiblyFalseReference */
return round(
(float) $now->format('U.u')
- (float) $this->dispatchedAt->format('U.u')
Expand Down
2 changes: 1 addition & 1 deletion src/Twig/TimeDisplayExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ public function getFilters(): array
public function formatTime(?float $seconds): string
{
if (null === $seconds) {
return 'n/a';
return '-';
}

if ($seconds < 0.001) {
Expand Down
2 changes: 1 addition & 1 deletion tests/EventListener/AddStampOnMessageSentListenerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
use Symfony\Component\Messenger\Event\SendMessageToTransportsEvent;
use SymfonyCasts\MessengerMonitorBundle\EventListener\AddStampOnMessageSentListener;
use SymfonyCasts\MessengerMonitorBundle\Stamp\MonitorIdStamp;
use SymfonyCasts\MessengerMonitorBundle\Tests\TestableMessage;
use SymfonyCasts\MessengerMonitorBundle\Tests\Fixtures\TestableMessage;

final class AddStampOnMessageSentListenerTest extends TestCase
{
Expand Down
2 changes: 1 addition & 1 deletion tests/EventListener/SendEventOnRetriedMessageTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
use SymfonyCasts\MessengerMonitorBundle\EventListener\SendEventOnRetriedMessageListener;
use SymfonyCasts\MessengerMonitorBundle\FailedMessage\MessageRetriedByUserEvent;
use SymfonyCasts\MessengerMonitorBundle\Stamp\MonitorIdStamp;
use SymfonyCasts\MessengerMonitorBundle\Tests\TestableMessage;
use SymfonyCasts\MessengerMonitorBundle\Tests\Fixtures\TestableMessage;

final class SendEventOnRetriedMessageTest extends TestCase
{
Expand Down
2 changes: 1 addition & 1 deletion tests/FailedMessage/FailedMessageRejecterTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
use Symfony\Component\Messenger\Transport\Receiver\ListableReceiverInterface;
use SymfonyCasts\MessengerMonitorBundle\FailedMessage\FailedMessageRejecter;
use SymfonyCasts\MessengerMonitorBundle\FailureReceiver\FailureReceiverProvider;
use SymfonyCasts\MessengerMonitorBundle\Tests\TestableMessage;
use SymfonyCasts\MessengerMonitorBundle\Tests\Fixtures\TestableMessage;

class FailedMessageRejecterTest extends TestCase
{
Expand Down
2 changes: 1 addition & 1 deletion tests/FailedMessage/FailedMessageRepositoryTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
use SymfonyCasts\MessengerMonitorBundle\FailedMessage\FailedMessageDetails;
use SymfonyCasts\MessengerMonitorBundle\FailedMessage\FailedMessageRepository;
use SymfonyCasts\MessengerMonitorBundle\FailureReceiver\FailureReceiverProvider;
use SymfonyCasts\MessengerMonitorBundle\Tests\TestableMessage;
use SymfonyCasts\MessengerMonitorBundle\Tests\Fixtures\TestableMessage;

class FailedMessageRepositoryTest extends TestCase
{
Expand Down
21 changes: 21 additions & 0 deletions tests/Fixtures/FailureMessage.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<?php

declare(strict_types=1);

namespace SymfonyCasts\MessengerMonitorBundle\Tests\Fixtures;

final class FailureMessage extends Message
{
private $willFail = true;

public function shouldFail(): bool
{
if ($this->willFail) {
$this->willFail = false;

return true;
}

return $this->willFail;
}
}
10 changes: 10 additions & 0 deletions tests/Fixtures/Message.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?php

declare(strict_types=1);

namespace SymfonyCasts\MessengerMonitorBundle\Tests\Fixtures;

abstract class Message
{
abstract public function shouldFail(): bool;
}
21 changes: 21 additions & 0 deletions tests/Fixtures/RetryableMessage.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<?php

declare(strict_types=1);

namespace SymfonyCasts\MessengerMonitorBundle\Tests\Fixtures;

final class RetryableMessage extends Message
{
public $failures = 2;

public function shouldFail(): bool
{
if ($this->failures > 0) {
--$this->failures;

return true;
}

return false;
}
}
13 changes: 13 additions & 0 deletions tests/Fixtures/TestableMessage.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?php

declare(strict_types=1);

namespace SymfonyCasts\MessengerMonitorBundle\Tests\Fixtures;

final class TestableMessage extends Message
{
public function shouldFail(): bool
{
return false;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,15 @@

declare(strict_types=1);

namespace SymfonyCasts\MessengerMonitorBundle\Tests;
namespace SymfonyCasts\MessengerMonitorBundle\Tests\Fixtures;

use Symfony\Component\Messenger\Handler\MessageHandlerInterface;

final class TestableMessageHandler implements MessageHandlerInterface
{
public function __invoke(TestableMessage $message)
public function __invoke(Message $message)
{
if (true === $message->willFail) {
$message->willFail = false;
if (true === $message->shouldFail()) {
throw new \Exception('oops!');
}
}
Expand Down
29 changes: 21 additions & 8 deletions tests/FunctionalTests/AbstractFunctionalTests.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
namespace SymfonyCasts\MessengerMonitorBundle\Tests\FunctionalTests;

use Doctrine\DBAL\Connection;
use Doctrine\DBAL\FetchMode;
use Symfony\Bundle\FrameworkBundle\KernelBrowser;
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
use Symfony\Component\DomCrawler\Crawler;
Expand All @@ -18,7 +19,7 @@
use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;
use Symfony\Contracts\Service\ServiceProviderInterface;
use SymfonyCasts\MessengerMonitorBundle\Stamp\MonitorIdStamp;
use SymfonyCasts\MessengerMonitorBundle\Tests\TestableMessage;
use SymfonyCasts\MessengerMonitorBundle\Tests\Fixtures\Message;
use SymfonyCasts\MessengerMonitorBundle\Tests\TestKernel;

abstract class AbstractFunctionalTests extends WebTestCase
Expand Down Expand Up @@ -52,9 +53,9 @@ public function setUp(): void
$this->messageBus = self::$container->get('test.messenger.bus.default');
}

protected function dispatchMessage(bool $willFail = false): Envelope
protected function dispatchMessage(Message $message): Envelope
{
return $this->messageBus->dispatch(new Envelope(new TestableMessage($willFail)));
return $this->messageBus->dispatch(new Envelope($message));
}

protected function assertQueuesCounts(array $expectedQueues, Crawler $crawler): void
Expand Down Expand Up @@ -89,19 +90,24 @@ protected function assertFailedMessagesCount(int $count, Crawler $crawler): void
);
}

protected function assertStoredMessageIsInDB(Envelope $envelope): void
protected function assertStoredMessageIsInDB(Envelope $envelope, int $count = 1): void
{
/** @var Connection $connection */
$connection = self::$container->get('doctrine.dbal.default_connection');

/** @var MonitorIdStamp $monitorIdStamp */
$monitorIdStamp = $envelope->last(MonitorIdStamp::class);
$this->assertNotFalse(
$connection->executeQuery('SELECT id FROM messenger_monitor WHERE id = :id', ['id' => $monitorIdStamp->getId()])

$this->assertSame(
$count,
(int) $connection->executeQuery(
'SELECT count(id) FROM messenger_monitor WHERE message_uid = :message_uid',
['message_uid' => $monitorIdStamp->getId()]
)->fetch(FetchMode::COLUMN)
);
}

protected function handleMessage(Envelope $envelope, string $queueName): void
protected function handleLastMessageInQueue(string $queueName): void
{
/** @var EventDispatcherInterface $eventDispatcher */
$eventDispatcher = self::$container->get('event_dispatcher');
Expand All @@ -110,7 +116,7 @@ protected function handleMessage(Envelope $envelope, string $queueName): void
$receiver = $this->getReceiver($queueName);

$worker = new Worker(
[$queueName => new SingleMessageReceiver($receiver, $receiver->find($this->getMessageId($envelope)))],
[$queueName => new SingleMessageReceiver($receiver, $receiver->find($this->getLastMessageId($queueName)))],
$this->messageBus,
$eventDispatcher
);
Expand All @@ -127,6 +133,13 @@ protected function getMessageId(Envelope $envelope): string
return $transportMessageIdStamp->getId();
}

protected function getLastMessageId(string $queueName): string
{
$receiver = $this->getReceiver($queueName);

return $this->getMessageId(current($receiver->get()));
}

protected function getLastFailedMessageId(): string
{
$receiver = $this->getReceiver('failed');
Expand Down
49 changes: 34 additions & 15 deletions tests/FunctionalTests/DashboardControllerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,40 +4,59 @@

namespace SymfonyCasts\MessengerMonitorBundle\Tests\FunctionalTests;

use SymfonyCasts\MessengerMonitorBundle\Tests\Fixtures\FailureMessage;
use SymfonyCasts\MessengerMonitorBundle\Tests\Fixtures\RetryableMessage;
use SymfonyCasts\MessengerMonitorBundle\Tests\Fixtures\TestableMessage;

/** @group functional */
final class DashboardControllerTest extends AbstractFunctionalTests
{
public function testDashboardEmpty(): void
{
$crawler = $this->client->request('GET', '/');
self::assertResponseIsSuccessful();

$this->assertQueuesCounts(['queue' => 0, 'failed' => 0], $crawler);
$this->assertFailedMessagesCount(0, $crawler);
$this->assertDisplayedQueuesOnDashboard(['queue' => 0, 'queue_with_retry' => 0, 'failed' => 0]);
}

public function testDashboardWithOneQueuedMessage(): void
{
$envelope = $this->dispatchMessage();
$envelope = $this->dispatchMessage(new TestableMessage());

$crawler = $this->client->request('GET', '/');
self::assertResponseIsSuccessful();

$this->assertQueuesCounts(['queue' => 1, 'failed' => 0], $crawler);
$this->assertFailedMessagesCount(1, $crawler);
$this->assertDisplayedQueuesOnDashboard(['queue' => 1, 'queue_with_retry' => 0, 'failed' => 0]);
$this->assertStoredMessageIsInDB($envelope);

$this->handleMessage($envelope, 'queue');
$this->handleLastMessageInQueue('queue');
$this->testDashboardEmpty();
}

public function testDashboardWithOneFailedMessage(): void
{
$envelope = $this->dispatchMessage(true);
$this->handleMessage($envelope, 'queue');
$envelope = $this->dispatchMessage(new FailureMessage());
$this->handleLastMessageInQueue('queue');

$this->assertDisplayedQueuesOnDashboard(['queue' => 0, 'queue_with_retry' => 0, 'failed' => 1]);
$this->assertStoredMessageIsInDB($envelope);
}

public function testDashboardWithOneRetryableMessage(): void
{
$envelope = $this->dispatchMessage(new RetryableMessage());

$this->assertDisplayedQueuesOnDashboard(['queue' => 0, 'queue_with_retry' => 1, 'failed' => 0]);
$this->assertStoredMessageIsInDB($envelope);

$this->handleLastMessageInQueue('queue_with_retry');
$this->assertDisplayedQueuesOnDashboard(['queue' => 0, 'queue_with_retry' => 1, 'failed' => 0]);
$this->assertStoredMessageIsInDB($envelope, 2);

$this->handleLastMessageInQueue('queue_with_retry');
$this->assertDisplayedQueuesOnDashboard(['queue' => 0, 'queue_with_retry' => 0, 'failed' => 1]);
$this->assertStoredMessageIsInDB($envelope, 2);
}

private function assertDisplayedQueuesOnDashboard(array $queues): void
{
$crawler = $this->client->request('GET', '/');
self::assertResponseIsSuccessful();
$this->assertQueuesCounts(['queue' => 0, 'failed' => 1], $crawler);
$this->assertQueuesCounts($queues, $crawler);
$this->assertFailedMessagesCount($queues['failed'], $crawler);
}
}
Loading

0 comments on commit 1a7739c

Please sign in to comment.