Passed
Pull Request — master (#9)
by Leonardo
01:33
created

RedisAdapter::validateRedisInstance()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

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