Memcached::reset()   A
last analyzed

Complexity

Conditions 6
Paths 6

Size

Total Lines 28
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Importance

Changes 3
Bugs 1 Features 0
Metric Value
cc 6
eloc 17
c 3
b 1
f 0
nc 6
nop 0
dl 0
loc 28
rs 9.0777
1
<?php
2
namespace Ackintosh\Ganesha\Storage\Adapter;
3
4
use Ackintosh\Ganesha;
5
use Ackintosh\Ganesha\Configuration;
6
use Ackintosh\Ganesha\Exception\StorageException;
7
use Ackintosh\Ganesha\Storage;
8
use Ackintosh\Ganesha\Storage\AdapterInterface;
9
use RuntimeException;
10
11
class Memcached implements AdapterInterface, TumblingTimeWindowInterface
12
{
13
    /**
14
     * @var \Memcached
15
     */
16
    private $memcached;
17
18
    /**
19
     * @var Configuration
20
     */
21
    private $configuration;
22
23
    /**
24
     * Memcached constructor.
25
     * @param \Memcached $memcached
26
     */
27
    public function __construct(\Memcached $memcached)
28
    {
29
        // initial_value in (increment|decrement) requires \Memcached::OPT_BINARY_PROTOCOL
30
        $memcached->setOption(\Memcached::OPT_BINARY_PROTOCOL, true);
31
        $this->memcached = $memcached;
32
    }
33
34
    /**
35
     * @return bool
36
     */
37
    public function supportCountStrategy(): bool
38
    {
39
        return true;
40
    }
41
42
    /**
43
     * @return bool
44
     */
45
    public function supportRateStrategy(): bool
46
    {
47
        return true;
48
    }
49
50
    /**
51
     * @param Configuration $configuration
52
     * @return void
53
     */
54
    public function setConfiguration(Configuration $configuration): void
55
    {
56
        $this->configuration = $configuration;
57
    }
58
59
    /**
60
     * @param string $service
61
     * @return int
62
     * @throws StorageException
63
     */
64
    public function load(string $service): int
65
    {
66
        $r = (int)$this->memcached->get($service);
67
        $this->throwExceptionIfErrorOccurred();
68
        return $r;
69
    }
70
71
    /**
72
     * @param string $service
73
     * @param int $count
74
     * @return void
75
     * @throws StorageException
76
     */
77
    public function save(string $service, int $count): void
78
    {
79
        if (!$this->memcached->set($service, $count)) {
80
            throw new StorageException('failed to set the value : ' . $this->memcached->getResultMessage());
81
        }
82
    }
83
84
    /**
85
     * @param string $service
86
     * @return void
87
     * @throws StorageException
88
     */
89
    public function increment(string $service): void
90
    {
91
        // requires \Memcached::OPT_BINARY_PROTOCOL
92
        if ($this->memcached->increment($service, 1, 1) === false) {
93
            throw new StorageException('failed to increment failure count : ' . $this->memcached->getResultMessage());
94
        }
95
    }
96
97
    /**
98
     * @param string $service
99
     * @return void
100
     * @throws StorageException
101
     */
102
    public function decrement(string $service): void
103
    {
104
        // requires \Memcached::OPT_BINARY_PROTOCOL
105
        if ($this->memcached->decrement($service, 1, 0) === false) {
106
            throw new StorageException('failed to decrement failure count : ' . $this->memcached->getResultMessage());
107
        }
108
    }
109
110
    /**
111
     * @param string $service
112
     * @param int    $lastFailureTime
113
     * @throws StorageException
114
     */
115
    public function saveLastFailureTime(string $service, int $lastFailureTime): void
116
    {
117
        if (!$this->memcached->set($service, $lastFailureTime)) {
118
            throw new StorageException('failed to set the last failure time : ' . $this->memcached->getResultMessage());
119
        }
120
    }
121
122
    /**
123
     * @param  string $service
124
     * @return int
125
     * @throws StorageException
126
     */
127
    public function loadLastFailureTime(string $service): int
128
    {
129
        $r = $this->memcached->get($service);
130
        $this->throwExceptionIfErrorOccurred();
131
        return $r;
132
    }
133
134
    /**
135
     * @param string $service
136
     * @param int    $status
137
     * @throws StorageException
138
     */
139
    public function saveStatus(string $service, int $status): void
140
    {
141
        if (!$this->memcached->set($service, $status)) {
142
            throw new StorageException('failed to set the status : ' . $this->memcached->getResultMessage());
143
        }
144
    }
145
146
    /**
147
     * @param  string $service
148
     * @return int
149
     * @throws StorageException
150
     */
151
    public function loadStatus(string $service): int
152
    {
153
        $status = $this->memcached->get($service);
154
        $this->throwExceptionIfErrorOccurred();
155
        if ($status === false && $this->memcached->getResultCode() === \Memcached::RES_NOTFOUND) {
156
            $this->saveStatus($service, Ganesha::STATUS_CALMED_DOWN);
157
            return Ganesha::STATUS_CALMED_DOWN;
158
        }
159
160
        return $status;
161
    }
162
163
    public function reset(): void
164
    {
165
        if (!$this->memcached->getStats()) {
166
            throw new RuntimeException("Couldn't connect to memcached.");
167
        }
168
169
        // getAllKeys() with OPT_BINARY_PROTOCOL is not suppoted.
170
        // So temporarily disable it.
171
        $this->memcached->setOption(\Memcached::OPT_BINARY_PROTOCOL, false);
172
        $keys = $this->memcached->getAllKeys();
173
        $this->memcached->setOption(\Memcached::OPT_BINARY_PROTOCOL, true);
174
        if (!$keys) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $keys of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
175
            $resultCode = $this->memcached->getResultCode();
176
            if ($resultCode === 0) {
177
                // no keys
178
                return;
179
            }
180
            $message = sprintf(
181
                'failed to get memcached keys. resultCode: %d, resultMessage: %s',
182
                $resultCode,
183
                $this->memcached->getResultMessage()
184
            );
185
            throw new RuntimeException($message);
186
        }
187
188
        foreach ($keys as $k) {
189
            if ($this->isGaneshaData($k)) {
190
                $this->memcached->delete($k);
191
            }
192
        }
193
    }
194
195
    public function isGaneshaData(string $key): bool
196
    {
197
        $regex = sprintf(
198
            '#\A%s.+(%s|%s|%s|%s|%s)\z#',
199
            Storage\StorageKeys::KEY_PREFIX,
200
            Storage\StorageKeys::KEY_SUFFIX_SUCCESS,
201
            Storage\StorageKeys::KEY_SUFFIX_FAILURE,
202
            Storage\StorageKeys::KEY_SUFFIX_REJECTION,
203
            Storage\StorageKeys::KEY_SUFFIX_LAST_FAILURE_TIME,
204
            Storage\StorageKeys::KEY_SUFFIX_STATUS
205
        );
206
207
        return preg_match($regex, $key) === 1;
208
    }
209
210
    /**
211
     * Throws an exception if some error occurs in memcached.
212
     *
213
     * @return void
214
     * @throws StorageException
215
     */
216
    private function throwExceptionIfErrorOccurred(): void
217
    {
218
        $errorResultCodes = [
219
            \Memcached::RES_FAILURE,
220
            \Memcached::RES_SERVER_TEMPORARILY_DISABLED,
221
            \Memcached::RES_SERVER_MEMORY_ALLOCATION_FAILURE,
222
        ];
223
224
        if (in_array($this->memcached->getResultCode(), $errorResultCodes, true)) {
225
            throw new StorageException($this->memcached->getResultMessage());
226
        }
227
    }
228
}
229