Completed
Push — master ( 0dfb70...6f61e6 )
by Jorge
01:46
created

TimeBackoff::waitTime()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 3
nc 1
nop 1
dl 0
loc 5
rs 9.4285
c 0
b 0
f 0
ccs 4
cts 4
cp 1
crap 1
1
<?php
2
3
namespace JVelasco\CircuitBreaker\AvailabilityStrategy;
4
5
use JVelasco\CircuitBreaker\AvailabilityStrategy;
6
use JVelasco\CircuitBreaker\StorageException;
7
8
class TimeBackoff implements AvailabilityStrategy
9
{
10
    const ATTEMPTS_KEY = "attempts";
11
    const LAST_ATTEMPT_TIME_KEY = "last_attempt";
12
13
    /** @var Storage */
14
    protected $storage;
15
    /** @var BackoffStrategy */
16
    private $backoffStrategy;
17
    /** @var int */
18
    protected $maxFailures;
19
    /** @var int initial time to wait in milliseconds */
20
    protected $baseWaitTime;
21
    /** @var int */
22
    private $maxWaitTime;
23
24 7
    public function __construct(
25
        Storage $storage,
26
        BackoffStrategy $backoffStrategy,
27
        int $maxFailures,
28
        int $baseWaitTime,
29
        int $maxWaitTime
30
    ) {
31 7
        $this->storage = $storage;
32 7
        $this->maxFailures = $maxFailures;
33 7
        $this->baseWaitTime = $baseWaitTime;
34 7
        $this->backoffStrategy = $backoffStrategy;
35 7
        $this->maxWaitTime = $maxWaitTime;
36 7
    }
37
38 7
    public function isAvailable(string $serviceName): bool
39
    {
40
        try {
41 7
            if ($this->storage->numberOfFailures($serviceName) < $this->maxFailures) {
42 2
                return true;
43
            }
44
45 5
            $attempt = $this->getLastAttempt($serviceName);
46 5
            if ($this->millisecondsSinceLastAttempt($serviceName) > $this->waitTime($attempt)) {
47 3
                $this->saveAttempt($serviceName, $attempt+1);
48 3
                return true;
49
            }
50
51 2
            return false;
52 1
        } catch (StorageException $ex) {
53 1
            return true;
54
        }
55
    }
56
57 5
    public function getId(): string
58
    {
59 5
        return $this->backoffStrategy->id();
60
    }
61
62 5
    private function getLastAttemptTime(string $serviceName): int
63
    {
64 5
        $lastTryTimestamp = $this->storage->getStrategyData(
65 5
            $this,
66 5
            $serviceName,
67 5
            self::LAST_ATTEMPT_TIME_KEY
68
        );
69 5
        return $lastTryTimestamp ? $lastTryTimestamp : $this->now();
0 ignored issues
show
Bug Best Practice introduced by
The expression return $lastTryTimestamp...imestamp : $this->now() could return the type string which is incompatible with the type-hinted return integer. Consider adding an additional type-check to rule them out.
Loading history...
70
    }
71
72 5
    private function getLastAttempt($serviceName): int
73
    {
74 5
        return (int) $this->storage->getStrategyData($this, $serviceName, self::ATTEMPTS_KEY);
75
    }
76
77 5
    private function millisecondsSinceLastAttempt(string $serviceName): int
78
    {
79 5
        $lastAttempt = $this->getLastAttemptTime($serviceName);
80 5
        return $this->now() - $lastAttempt;
81
    }
82
83 5
    private function now(): int
84
    {
85 5
        return floor(microtime(true) * 1000);
0 ignored issues
show
Bug Best Practice introduced by
The expression return floor(microtime(true) * 1000) returns the type double which is incompatible with the type-hinted return integer.
Loading history...
86
    }
87
88 5
    private function waitTime($attempt): int
89
    {
90 5
        return min(
91 5
            $this->backoffStrategy->waitTime($attempt, $this->baseWaitTime),
92 5
            $this->maxWaitTime
93
        );
94
    }
95
96 3
    private function saveAttempt(string $serviceName, int $attempt)
97
    {
98 3
        $this->storage->saveStrategyData(
99 3
            $this,
100 3
            $serviceName,
101 3
            self::ATTEMPTS_KEY,
102 3
            $attempt
103
        );
104 3
        $this->storage->saveStrategyData(
105 3
            $this,
106 3
            $serviceName,
107 3
            self::LAST_ATTEMPT_TIME_KEY,
108 3
            $this->now()
109
        );
110 3
        $this->storage->resetFailuresCounter($serviceName);
111 3
    }
112
}
113