Skip to content

Commit e45d07b

Browse files
committed
Added retry
- use `RetryTrait` to handler when you would like to retry failed handlers - for proper retry funcionality you have to use driver that supports delayed executions with `executeAt` message param
1 parent e04e5a2 commit e45d07b

File tree

7 files changed

+83
-7
lines changed

7 files changed

+83
-7
lines changed

src/Dispatcher.php

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,9 @@ public function handle(): void
108108
} catch (RestartException $e) {
109109
$this->log(LogLevel::NOTICE, 'Existing hermes dispatcher - restart');
110110
} catch (Exception $exception) {
111-
Debugger::log($exception, Debugger::EXCEPTION);
111+
if (Debugger::isEnabled()) {
112+
Debugger::log($exception, Debugger::EXCEPTION);
113+
}
112114
}
113115
}
114116

@@ -169,12 +171,36 @@ private function handleMessage(HandlerInterface $handler, MessageInterface $mess
169171
"Handler " . get_class($handler) . " throws exception - {$e->getMessage()}",
170172
['error' => $e, 'message' => $this->messageLoggerContext($message), 'exception' => $e]
171173
);
172-
Debugger::log($e, Debugger::EXCEPTION);
174+
if (Debugger::isEnabled()) {
175+
Debugger::log($e, Debugger::EXCEPTION);
176+
}
177+
178+
if (method_exists($handler, 'canRetry')) {
179+
if ($message->getRetries() < $handler->maxRetry()) {
180+
$executeAt = $this->nextRetry($message);
181+
$newMessage = new Message($message->getType(), $message->getPayload(), $message->getId(), $message->getCreated(), $executeAt, $message->getRetries() + 1);
182+
$this->driver->send($newMessage);
183+
}
184+
}
185+
173186
$result = false;
174187
}
175188
return $result;
176189
}
177190

191+
/**
192+
* Calculate next retry
193+
*
194+
* Inspired by ruby sidekiq (https://github.com/mperham/sidekiq/wiki/Error-Handling#automatic-job-retry)
195+
*
196+
* @param MessageInterface $message
197+
* @return float
198+
*/
199+
private function nextRetry(MessageInterface $message): float
200+
{
201+
return microtime(true) + pow($message->getRetries(), 4) + 15 + (rand(1, 30) * ($message->getRetries() + 1));
202+
}
203+
178204
/**
179205
* Check if actual dispatcher has handler for given type
180206
*

src/Driver/RedisSetDriver.php

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,6 @@ public function wait(Closure $callback): void
9898
}
9999
if ($this->redis instanceof \Redis) {
100100
$messagesString = $this->redis->zRangeByScore($this->scheduleKey, '-inf', microtime(true), ['limit' => [0, 1]]);
101-
$messagesString = [];
102101
if (count($messagesString)) {
103102
foreach ($messagesString as $messageString) {
104103
$this->redis->zRem($this->scheduleKey, $messageString);

src/Handler/EchoHandler.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77

88
class EchoHandler implements HandlerInterface
99
{
10+
use RetryTrait;
11+
1012
/**
1113
* {@inheritdoc}
1214
*/

src/Handler/RetryTrait.php

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<?php
2+
declare(strict_types=1);
3+
4+
namespace Tomaj\Hermes\Handler;
5+
6+
trait RetryTrait
7+
{
8+
public function canRetry(): bool
9+
{
10+
return true;
11+
}
12+
13+
public function maxRetry(): int
14+
{
15+
return 25;
16+
}
17+
}

src/Message.php

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ class Message implements MessageInterface
2323
private $messageId;
2424

2525
/**
26-
* @var string
26+
* @var float
2727
*/
2828
private $created;
2929

@@ -32,6 +32,11 @@ class Message implements MessageInterface
3232
*/
3333
private $executeAt;
3434

35+
/**
36+
* @var int
37+
*/
38+
private $retries;
39+
3540
/**
3641
* Native implementation of message.
3742
*
@@ -40,12 +45,18 @@ class Message implements MessageInterface
4045
* @var string $messageId
4146
* @var float $created timestamp (microtime(true))
4247
* @var float $executeAt timestamp (microtime(true))
48+
* @var int $retries
4349
*/
44-
public function __construct(string $type, array $payload = null, string $messageId = null, float $created = null, float $executeAt = null)
50+
public function __construct(string $type, array $payload = null, string $messageId = null, float $created = null, float $executeAt = null, int $retries = 0)
4551
{
4652
$this->messageId = $messageId;
4753
if (!$messageId) {
48-
$this->messageId = Uuid::uuid4()->toString();
54+
try {
55+
$this->messageId = Uuid::uuid4()->toString();
56+
} catch (\Exception $e) {
57+
$this->messageId = rand(10000, 99999999);
58+
}
59+
4960
}
5061
$this->created = $created;
5162
if (!$created) {
@@ -54,6 +65,7 @@ public function __construct(string $type, array $payload = null, string $message
5465
$this->type = $type;
5566
$this->payload = $payload;
5667
$this->executeAt = $executeAt;
68+
$this->retries = $retries;
5769
}
5870

5971
/**
@@ -95,4 +107,12 @@ public function getPayload(): ?array
95107
{
96108
return $this->payload;
97109
}
110+
111+
/**
112+
* {@inheritdoc}
113+
*/
114+
public function getRetries(): int
115+
{
116+
return $this->retries;
117+
}
98118
}

src/MessageInterface.php

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,4 +50,11 @@ public function getType(): string;
5050
* @return array
5151
*/
5252
public function getPayload(): ?array;
53+
54+
/**
55+
* Total retries for message
56+
*
57+
* @return int
58+
*/
59+
public function getRetries(): int;
5360
}

src/MessageSerializer.php

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ public function serialize(MessageInterface $message): string
1717
'created' => $message->getCreated(),
1818
'payload' => $message->getPayload(),
1919
'execute_at' => $message->getExecuteAt(),
20+
'retries' => $message->getRetries(),
2021
]
2122
]);
2223
}
@@ -32,6 +33,10 @@ public function unserialize(string $string): MessageInterface
3233
if (isset($message['execute_at'])) {
3334
$executeAt = $message['execute_at'];
3435
}
35-
return new Message($message['type'], $message['payload'], $message['id'], $message['created'], $executeAt);
36+
$retries = 0;
37+
if (isset($message['retries'])) {
38+
$retries = intval($message['retries']);
39+
}
40+
return new Message($message['type'], $message['payload'], $message['id'], $message['created'], $executeAt, $retries);
3641
}
3742
}

0 commit comments

Comments
 (0)