Passed
Pull Request — master (#190)
by Dmitriy
04:22 queued 01:36
created

ExponentialDelayMiddleware::createNewMeta()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 7
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 4
nc 1
nop 1
dl 0
loc 7
ccs 5
cts 5
cp 1
crap 1
rs 10
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\Queue\Middleware;
6
7
use InvalidArgumentException;
8
use Yiisoft\Queue\Message\MessageInterface;
9
use Yiisoft\Queue\QueueInterface;
10
use Yiisoft\Queue\Message\FailureEnvelope;
11
12
/**
13
 * Failure strategy which resends the given message to a queue with an exponentially increasing delay.
14
 * The delay mechanism **must** be implemented by the used {@see AdapterInterface} implementation.
15
 */
16
final class ExponentialDelayMiddleware implements MiddlewareInterface
17
{
18
    public const META_KEY_ATTEMPTS = 'failure-strategy-exponential-delay-attempts';
19
    public const META_KEY_DELAY = 'failure-strategy-exponential-delay-delay';
20
21
    /**
22
     * @param string $id A unique id to differentiate two and more objects of this class
23
     * @param int $maxAttempts Maximum attempts count for this strategy with the given $id before it will give up
24
     * @param float $delayInitial The first delay period
25
     * @param float $delayMaximum The maximum delay period
26
     * @param float $exponent Message handling delay will be increased by this multiplication each time it fails
27
     */
28 18
    public function __construct(
29
        private string $id,
30
        private int $maxAttempts,
31
        private float $delayInitial,
32
        private float $delayMaximum,
33
        private float $exponent,
34
        private DelayMiddlewareInterface $delayMiddleware,
35
        private QueueInterface $queue,
36
    ) {
37 18
        if ($maxAttempts <= 0) {
38 2
            throw new InvalidArgumentException("maxAttempts parameter must be a positive integer, $this->maxAttempts given.");
39
        }
40
41 16
        if ($delayInitial <= 0) {
42 3
            throw new InvalidArgumentException("delayInitial parameter must be a positive float, $this->delayInitial given.");
43
        }
44
45 13
        if ($delayMaximum < $delayInitial) {
46 1
            throw new InvalidArgumentException("delayMaximum parameter must not be less then delayInitial, , $this->delayMaximum given.");
47
        }
48
49 12
        if ($exponent <= 0) {
50 1
            throw new InvalidArgumentException("exponent parameter must not be zero or less, $this->exponent given.");
51
        }
52
    }
53
54 9
    public function process(
55
        Request $request,
56
        MessageHandlerInterface $handler
57
    ): Request {
58 9
        $message = $request->getMessage();
59 9
        if ($this->suites($message)) {
60 8
            $envelope = new FailureEnvelope($message, $this->createNewMeta($message));
61 8
            $queue = $this->queue;
62 8
            $middlewareDefinitions = $this->delayMiddleware->withDelay($this->getDelay($envelope));
63 8
            $messageNew = $queue->push(
64 8
                $envelope,
65 8
                $middlewareDefinitions
66 8
            );
67
68 8
            return $request->withMessage($messageNew);
69
        }
70
71 2
        return $handler->handle($request);
72
    }
73
74 9
    private function suites(MessageInterface $message): bool
75
    {
76 9
        return $this->maxAttempts > $this->getAttempts($message);
77
    }
78
79 8
    private function createNewMeta(MessageInterface $message): array
80
    {
81 8
        $meta = $message->getMetadata();
82 8
        $meta[self::META_KEY_DELAY . "-$this->id"] = $this->getDelay($message);
83 8
        $meta[self::META_KEY_ATTEMPTS . "-$this->id"] = $this->getAttempts($message) + 1;
84
85 8
        return $meta;
86
    }
87
88 9
    private function getAttempts(MessageInterface $message): int
89
    {
90 9
        return $message->getMetadata()[self::META_KEY_ATTEMPTS . "-$this->id"] ?? 0;
91
    }
92
93 8
    private function getDelay(MessageInterface $message): float
94
    {
95 8
        $meta = $message->getMetadata();
96 8
        $key = self::META_KEY_DELAY . "-$this->id";
97
98 8
        $delayOriginal = (float) ($meta[$key] ?? 0);
99 8
        if ($delayOriginal <= 0) {
100 3
            $delayOriginal = $this->delayInitial;
101
        }
102
103 8
        $result = $delayOriginal * $this->exponent;
104
105 8
        return min($result, $this->delayMaximum);
106
    }
107
}
108