Completed
Push — master ( 89c60a...b32857 )
by Leonardo
19s queued 14s
created

RedisAdapter::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 3
c 1
b 0
f 0
dl 0
loc 5
rs 10
cc 1
nc 1
nop 2
1
<?php declare(strict_types=1);
2
3
namespace LeoCarmo\CircuitBreaker\Adapters;
4
5
class RedisAdapter implements AdapterInterface
6
{
7
    /**
8
     * @var \Redis
9
     */
10
    protected $redis;
11
12
    /**
13
     * @var string
14
     */
15
    protected string $redisNamespace;
16
17
    /**
18
     * Set settings for start circuit service
19
     *
20
     * @param $redis
21
     * @param string $redisNamespace
22
     */
23
    public function __construct($redis, string $redisNamespace)
24
    {
25
        $this->checkExtensionLoaded();
26
        $this->redis = $redis;
27
        $this->redisNamespace = $redisNamespace;
28
    }
29
30
    protected function checkExtensionLoaded()
31
    {
32
        if (! extension_loaded('redis')) {
33
            throw new \RuntimeException('Extension redis is required to use RedisAdapter.');
34
        }
35
    }
36
37
    /**
38
     * @param string $service
39
     * @return bool
40
     */
41
    public function isOpen(string $service): bool
42
    {
43
        return (bool) $this->redis->get(
44
            $this->makeNamespace($service) . ':open'
45
        );
46
    }
47
48
    /**
49
     * @param string $service
50
     * @param int $failureRateThreshold
51
     * @return bool
52
     */
53
    public function reachRateLimit(string $service, int $failureRateThreshold): bool
54
    {
55
        $failures = (int) $this->redis->get(
56
            $this->makeNamespace($service) . ':failures'
57
        );
58
59
        return ($failures >= $failureRateThreshold);
60
    }
61
62
    /**
63
     * @param string $service
64
     * @return bool|string
65
     */
66
    public function isHalfOpen(string $service): bool
67
    {
68
        return (bool) $this->redis->get(
69
            $this->makeNamespace($service) . ':half_open'
70
        );
71
    }
72
73
    /**
74
     * @param string $service
75
     * @param int $timeWindow
76
     * @return bool
77
     */
78
    public function incrementFailure(string $service, int $timeWindow) : bool
79
    {
80
        $serviceName = $this->makeNamespace($service) . ':failures';
81
82
        $this->resetKeyIfInfiniteTTL($serviceName);
83
84
        if (! $this->redis->get($serviceName)) {
85
            $this->redis->multi();
86
            $this->redis->incr($serviceName);
87
            $this->redis->expire($serviceName, $timeWindow);
88
            return (bool) ($this->redis->exec()[0] ?? false);
89
        }
90
91
        return (bool) $this->redis->incr($serviceName);
92
    }
93
94
    /**
95
     * @param string $service
96
     */
97
    public function setSuccess(string $service): void
98
    {
99
        $serviceName = $this->makeNamespace($service);
100
101
        $this->redis->multi();
102
        $this->redis->del($serviceName . ':open');
103
        $this->redis->del($serviceName . ':failures');
104
        $this->redis->del($serviceName . ':half_open');
105
        $this->redis->exec();
106
    }
107
108
    /**
109
     * @param string $service
110
     * @param int $timeWindow
111
     */
112
    public function setOpenCircuit(string $service, int $timeWindow): void
113
    {
114
        $this->redis->set(
115
            $this->makeNamespace($service) . ':open',
116
            time(),
117
            $timeWindow
118
        );
119
    }
120
121
    /**
122
     * @param string $service
123
     * @param int $timeWindow
124
     * @param int $intervalToHalfOpen
125
     */
126
    public function setHalfOpenCircuit(string $service, int $timeWindow, int $intervalToHalfOpen): void
127
    {
128
        $this->redis->set(
129
            $this->makeNamespace($service) . ':half_open',
130
            time(),
131
            ($timeWindow + $intervalToHalfOpen)
132
        );
133
    }
134
135
    public function getFailuresCounter(string $service): int
136
    {
137
        $failures = $this->redis->get(
138
            $this->makeNamespace($service) . ':failures'
139
        );
140
141
        return (int) $failures;
142
    }
143
144
    /**
145
     * @param string $service
146
     * @return string
147
     */
148
    protected function makeNamespace(string $service): string
149
    {
150
        return 'circuit-breaker:' . $this->redisNamespace . ':' . $service;
151
    }
152
153
    protected function resetKeyIfInfiniteTTL(string $service)
154
    {
155
        if ($this->redis->ttl($service) === -1) {
156
            $this->redis->del($service);
157
        }
158
    }
159
}
160