1 | <?php |
||||
2 | /** |
||||
3 | * Created by PhpStorm. |
||||
4 | * User: hugh.li |
||||
5 | * Date: 2021/6/8 |
||||
6 | * Time: 5:56 下午. |
||||
7 | */ |
||||
8 | |||||
9 | namespace HughCube\Laravel\OTS\Cache; |
||||
10 | |||||
11 | use Aliyun\OTS\Consts\ColumnTypeConst; |
||||
12 | use Aliyun\OTS\Consts\ComparatorTypeConst; |
||||
13 | use Aliyun\OTS\Consts\DirectionConst; |
||||
14 | use Aliyun\OTS\Consts\OperationTypeConst; |
||||
15 | use Aliyun\OTS\Consts\PrimaryKeyTypeConst; |
||||
16 | use Aliyun\OTS\Consts\RowExistenceExpectationConst; |
||||
17 | use Aliyun\OTS\OTSClientException; |
||||
18 | use Aliyun\OTS\OTSServerException; |
||||
19 | use Closure; |
||||
20 | use Exception; |
||||
21 | use HughCube\Laravel\OTS\Connection; |
||||
22 | use Illuminate\Cache\TaggableStore; |
||||
23 | use Illuminate\Contracts\Cache\LockProvider; |
||||
24 | use Illuminate\Contracts\Cache\Store as IlluminateStore; |
||||
25 | use Illuminate\Support\Collection; |
||||
26 | use InvalidArgumentException; |
||||
27 | |||||
28 | class Store extends TaggableStore implements IlluminateStore, LockProvider |
||||
29 | { |
||||
30 | use Attribute; |
||||
31 | |||||
32 | /** |
||||
33 | * @var string |
||||
34 | */ |
||||
35 | protected $indexTable; |
||||
36 | |||||
37 | /** |
||||
38 | * Store constructor. |
||||
39 | * |
||||
40 | * @param Connection $ots |
||||
41 | * @param string $table |
||||
42 | * @param string $prefix |
||||
43 | * @param string|null $indexTable |
||||
44 | */ |
||||
45 | public function __construct(Connection $ots, string $table, string $prefix, ?string $indexTable) |
||||
46 | { |
||||
47 | $this->ots = $ots; |
||||
48 | $this->table = $table; |
||||
49 | $this->prefix = $prefix; |
||||
50 | $this->type = 'cache'; |
||||
51 | $this->indexTable = $indexTable; |
||||
52 | } |
||||
53 | |||||
54 | /** |
||||
55 | * @return string|null |
||||
56 | */ |
||||
57 | public function getIndexTable(): ?string |
||||
58 | { |
||||
59 | return $this->indexTable; |
||||
60 | } |
||||
61 | |||||
62 | /** |
||||
63 | * Retrieve an item from the cache by key. |
||||
64 | * |
||||
65 | * @inheritDoc |
||||
66 | * |
||||
67 | * @throws OTSServerException |
||||
68 | * @throws OTSClientException |
||||
69 | */ |
||||
70 | public function get($key) |
||||
71 | { |
||||
72 | $request = [ |
||||
73 | 'table_name' => $this->getTable(), |
||||
74 | 'primary_key' => $this->makePrimaryKey($key), |
||||
75 | 'max_versions' => 1, |
||||
76 | ]; |
||||
77 | |||||
78 | $response = $this->getOts()->getRow($request); |
||||
0 ignored issues
–
show
Bug
introduced
by
![]() |
|||||
79 | |||||
80 | return $this->parseValueInOtsResponse($response); |
||||
81 | } |
||||
82 | |||||
83 | /** |
||||
84 | * Store an item in the cache for a given number of seconds. |
||||
85 | * |
||||
86 | * @inheritDoc |
||||
87 | * |
||||
88 | * @throws OTSClientException |
||||
89 | * @throws OTSServerException |
||||
90 | */ |
||||
91 | public function put($key, $value, $seconds): bool |
||||
92 | { |
||||
93 | $request = [ |
||||
94 | 'table_name' => $this->getTable(), |
||||
95 | 'condition' => RowExistenceExpectationConst::CONST_IGNORE, |
||||
96 | 'primary_key' => $this->makePrimaryKey($key), |
||||
97 | 'attribute_columns' => $this->makeAttributeColumns($value, $seconds), |
||||
98 | ]; |
||||
99 | |||||
100 | $response = $this->getOts()->putRow($request); |
||||
0 ignored issues
–
show
The method
putRow() does not exist on HughCube\Laravel\OTS\Connection . Since you implemented __call , consider adding a @method annotation.
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||
101 | |||||
102 | return isset($response['primary_key'], $response['attribute_columns']); |
||||
103 | } |
||||
104 | |||||
105 | /** |
||||
106 | * @param string|int $key |
||||
107 | * @param mixed $value |
||||
108 | * @param int|null $seconds |
||||
109 | * |
||||
110 | * @throws OTSServerException |
||||
111 | * @throws OTSClientException |
||||
112 | * |
||||
113 | * @return bool |
||||
114 | */ |
||||
115 | public function add($key, $value, int $seconds = null): bool |
||||
116 | { |
||||
117 | $request = [ |
||||
118 | 'table_name' => $this->getTable(), |
||||
119 | 'condition' => [ |
||||
120 | 'row_existence' => RowExistenceExpectationConst::CONST_IGNORE, |
||||
121 | /** (Col2 <= 10) */ |
||||
122 | 'column_condition' => [ |
||||
123 | 'column_name' => 'expiration', |
||||
124 | 'value' => [$this->currentTime(), ColumnTypeConst::CONST_INTEGER], |
||||
125 | 'comparator' => ComparatorTypeConst::CONST_LESS_EQUAL, |
||||
126 | 'pass_if_missing' => true, |
||||
127 | 'latest_version_only' => true, |
||||
128 | ], |
||||
129 | ], |
||||
130 | 'primary_key' => $this->makePrimaryKey($key), |
||||
131 | 'attribute_columns' => $this->makeAttributeColumns($value, $seconds), |
||||
132 | ]; |
||||
133 | |||||
134 | try { |
||||
135 | $response = $this->getOts()->putRow($request); |
||||
136 | |||||
137 | return isset($response['primary_key'], $response['attribute_columns']); |
||||
138 | } catch (OTSServerException $exception) { |
||||
139 | if ('OTSConditionCheckFail' === $exception->getOTSErrorCode()) { |
||||
140 | return false; |
||||
141 | } |
||||
142 | |||||
143 | throw $exception; |
||||
144 | } |
||||
145 | } |
||||
146 | |||||
147 | /** |
||||
148 | * @inheritDoc |
||||
149 | * |
||||
150 | * @throws OTSClientException |
||||
151 | * @throws OTSServerException |
||||
152 | */ |
||||
153 | public function many(array $keys): array |
||||
154 | { |
||||
155 | if (empty($keys)) { |
||||
156 | return []; |
||||
157 | } |
||||
158 | |||||
159 | $primaryKeys = Collection::make($keys)->values()->map(function ($key) { |
||||
160 | return $this->makePrimaryKey($key); |
||||
161 | }); |
||||
162 | |||||
163 | $request = [ |
||||
164 | 'tables' => [ |
||||
165 | [ |
||||
166 | 'table_name' => $this->getTable(), |
||||
167 | 'max_versions' => 1, |
||||
168 | 'primary_keys' => $primaryKeys->toArray(), |
||||
169 | ], |
||||
170 | ], |
||||
171 | ]; |
||||
172 | |||||
173 | $response = $this->getOts()->batchGetRow($request); |
||||
0 ignored issues
–
show
The method
batchGetRow() does not exist on HughCube\Laravel\OTS\Connection . Since you implemented __call , consider adding a @method annotation.
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||
174 | |||||
175 | $results = Collection::make($keys)->values()->mapWithKeys(function ($key) { |
||||
176 | return [$key => null]; |
||||
177 | })->toArray(); |
||||
178 | |||||
179 | foreach ($response['tables'] as $table) { |
||||
180 | if ('cache' !== $table['table_name']) { |
||||
181 | continue; |
||||
182 | } |
||||
183 | |||||
184 | foreach ($table['rows'] as $row) { |
||||
185 | if (!$row['is_ok']) { |
||||
186 | continue; |
||||
187 | } |
||||
188 | |||||
189 | if (!is_null($key = $this->parseKeyInOtsResponse($row))) { |
||||
190 | $results[$key] = $this->parseValueInOtsResponse($row); |
||||
191 | } |
||||
192 | } |
||||
193 | } |
||||
194 | |||||
195 | return $results; |
||||
196 | } |
||||
197 | |||||
198 | /** |
||||
199 | * @inheritDoc |
||||
200 | * |
||||
201 | * @throws OTSClientException |
||||
202 | * @throws OTSServerException |
||||
203 | */ |
||||
204 | public function putMany(array $values, $seconds): bool |
||||
205 | { |
||||
206 | if (empty($values)) { |
||||
207 | return true; |
||||
208 | } |
||||
209 | |||||
210 | $rows = Collection::make($values)->map(function ($value, $key) use ($seconds) { |
||||
211 | return [ |
||||
212 | 'operation_type' => OperationTypeConst::CONST_PUT, |
||||
213 | 'condition' => RowExistenceExpectationConst::CONST_IGNORE, |
||||
214 | 'primary_key' => $this->makePrimaryKey($key), |
||||
215 | 'attribute_columns' => $this->makeAttributeColumns($value, $seconds), |
||||
216 | ]; |
||||
217 | }); |
||||
218 | $request = ['tables' => [['table_name' => $this->getTable(), 'rows' => $rows->values()->toArray()]]]; |
||||
219 | $response = $this->getOts()->batchWriteRow($request); |
||||
0 ignored issues
–
show
The method
batchWriteRow() does not exist on HughCube\Laravel\OTS\Connection . Since you implemented __call , consider adding a @method annotation.
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||
220 | |||||
221 | $rowResults = []; |
||||
222 | foreach ($response['tables'] as $table) { |
||||
223 | if ($this->getTable() !== $table['table_name']) { |
||||
224 | continue; |
||||
225 | } |
||||
226 | foreach ($table['rows'] as $row) { |
||||
227 | $rowResults[] = $row['is_ok']; |
||||
228 | } |
||||
229 | } |
||||
230 | |||||
231 | return !empty($rowResults) && !in_array(false, $rowResults, true); |
||||
232 | } |
||||
233 | |||||
234 | /** |
||||
235 | * @param array $values |
||||
236 | * |
||||
237 | * @throws OTSServerException |
||||
238 | * @throws OTSClientException |
||||
239 | * |
||||
240 | * @return bool |
||||
241 | */ |
||||
242 | public function putManyForever(array $values): bool |
||||
243 | { |
||||
244 | return $this->putMany($values, null); |
||||
245 | } |
||||
246 | |||||
247 | /** |
||||
248 | * @inheritDoc |
||||
249 | * |
||||
250 | * @throws OTSClientException |
||||
251 | * @throws OTSServerException |
||||
252 | */ |
||||
253 | public function increment($key, $value = 1) |
||||
254 | { |
||||
255 | return $this->incrementOrDecrement($key, $value, function ($current, $value) { |
||||
256 | return $current + $value; |
||||
257 | }); |
||||
258 | } |
||||
259 | |||||
260 | /** |
||||
261 | * @inheritDoc |
||||
262 | * |
||||
263 | * @throws OTSClientException |
||||
264 | * @throws OTSServerException |
||||
265 | */ |
||||
266 | public function decrement($key, $value = 1) |
||||
267 | { |
||||
268 | return $this->incrementOrDecrement($key, $value, function ($current, $value) { |
||||
269 | return $current - $value; |
||||
270 | }); |
||||
271 | } |
||||
272 | |||||
273 | /** |
||||
274 | * @param string|int $key |
||||
275 | * @param mixed $value |
||||
276 | * @param Closure $callback |
||||
277 | * |
||||
278 | * @throws OTSServerException |
||||
279 | * @throws OTSClientException |
||||
280 | * |
||||
281 | * @return false|mixed |
||||
282 | */ |
||||
283 | protected function incrementOrDecrement($key, $value, Closure $callback) |
||||
284 | { |
||||
285 | if (!is_numeric($current = $this->get($key))) { |
||||
286 | return false; |
||||
287 | } |
||||
288 | |||||
289 | $new = $callback((int) $current, $value); |
||||
290 | |||||
291 | $request = [ |
||||
292 | 'table_name' => $this->getTable(), |
||||
293 | 'condition' => [ |
||||
294 | 'row_existence' => RowExistenceExpectationConst::CONST_EXPECT_EXIST, |
||||
295 | 'column_condition' => [ |
||||
296 | 'column_name' => 'value', |
||||
297 | 'value' => [$this->serialize($current), ColumnTypeConst::CONST_BINARY], |
||||
298 | 'comparator' => ComparatorTypeConst::CONST_EQUAL, |
||||
299 | ], |
||||
300 | ], |
||||
301 | 'primary_key' => $this->makePrimaryKey($key), |
||||
302 | 'update_of_attribute_columns' => [ |
||||
303 | 'PUT' => $this->makeAttributeColumns($new), |
||||
304 | ], |
||||
305 | ]; |
||||
306 | |||||
307 | try { |
||||
308 | $response = $this->getOts()->updateRow($request); |
||||
0 ignored issues
–
show
The method
updateRow() does not exist on HughCube\Laravel\OTS\Connection . Since you implemented __call , consider adding a @method annotation.
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||
309 | if (isset($response['primary_key'], $response['attribute_columns'])) { |
||||
310 | return $new; |
||||
311 | } |
||||
312 | } catch (OTSServerException $exception) { |
||||
313 | if ('OTSConditionCheckFail' !== $exception->getOTSErrorCode()) { |
||||
314 | throw $exception; |
||||
315 | } |
||||
316 | } |
||||
317 | |||||
318 | return false; |
||||
319 | } |
||||
320 | |||||
321 | /** |
||||
322 | * @inheritDoc |
||||
323 | * |
||||
324 | * @throws OTSClientException |
||||
325 | * @throws OTSServerException |
||||
326 | */ |
||||
327 | public function forever($key, $value): bool |
||||
328 | { |
||||
329 | $request = [ |
||||
330 | 'table_name' => $this->getTable(), |
||||
331 | 'condition' => RowExistenceExpectationConst::CONST_IGNORE, |
||||
332 | 'primary_key' => $this->makePrimaryKey($key), |
||||
333 | 'attribute_columns' => $this->makeAttributeColumns($value), |
||||
334 | ]; |
||||
335 | |||||
336 | $response = $this->getOts()->putRow($request); |
||||
337 | |||||
338 | return isset($response['primary_key'], $response['attribute_columns']); |
||||
339 | } |
||||
340 | |||||
341 | /** |
||||
342 | * @inheritDoc |
||||
343 | * |
||||
344 | * @throws OTSClientException |
||||
345 | * @throws OTSServerException |
||||
346 | */ |
||||
347 | public function forget($key): bool |
||||
348 | { |
||||
349 | $request = [ |
||||
350 | 'table_name' => $this->getTable(), |
||||
351 | 'condition' => RowExistenceExpectationConst::CONST_IGNORE, |
||||
352 | 'primary_key' => $this->makePrimaryKey($key), |
||||
353 | ]; |
||||
354 | |||||
355 | $response = $this->getOts()->deleteRow($request); |
||||
0 ignored issues
–
show
The method
deleteRow() does not exist on HughCube\Laravel\OTS\Connection . Since you implemented __call , consider adding a @method annotation.
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||
356 | |||||
357 | return isset($response['primary_key'], $response['attribute_columns']); |
||||
358 | } |
||||
359 | |||||
360 | /** |
||||
361 | * @inheritDoc |
||||
362 | */ |
||||
363 | public function flush(): bool |
||||
364 | { |
||||
365 | return true; |
||||
366 | } |
||||
367 | |||||
368 | /** |
||||
369 | * Get a lock instance. |
||||
370 | * |
||||
371 | * @param string $name |
||||
372 | * @param int $seconds |
||||
373 | * @param string|null $owner |
||||
374 | * |
||||
375 | * @return \Illuminate\Contracts\Cache\Lock |
||||
376 | */ |
||||
377 | public function lock($name, $seconds = 0, $owner = null) |
||||
378 | { |
||||
379 | return new Lock($this->getOts(), $this->getTable(), $this->prefix, $name, $seconds, $owner); |
||||
380 | } |
||||
381 | |||||
382 | /** |
||||
383 | * Restore a lock instance using the owner identifier. |
||||
384 | * |
||||
385 | * @param string $name |
||||
386 | * @param string $owner |
||||
387 | * |
||||
388 | * @return \Illuminate\Contracts\Cache\Lock |
||||
389 | */ |
||||
390 | public function restoreLock($name, $owner) |
||||
391 | { |
||||
392 | return $this->lock($name, 0, $owner); |
||||
393 | } |
||||
394 | |||||
395 | /** |
||||
396 | * @throws OTSServerException |
||||
397 | * @throws OTSClientException |
||||
398 | * @throws InvalidArgumentException |
||||
399 | * @throws Exception |
||||
400 | */ |
||||
401 | public function flushExpiredRows($count = 200, $expiredDuration = 24 * 3600) |
||||
402 | { |
||||
403 | $indexTable = $this->getIndexTable(); |
||||
404 | if (empty($indexTable)) { |
||||
405 | throw new Exception('Configure the INDEX table first!'); |
||||
406 | } |
||||
407 | |||||
408 | if ($expiredDuration < 24 * 3600) { |
||||
409 | throw new InvalidArgumentException('At least one day expired before it can be cleaned.'); |
||||
410 | } |
||||
411 | |||||
412 | /** Query */ |
||||
413 | $response = $this->getOts()->getRange([ |
||||
0 ignored issues
–
show
The method
getRange() does not exist on HughCube\Laravel\OTS\Connection . Since you implemented __call , consider adding a @method annotation.
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||
414 | 'table_name' => $indexTable, |
||||
415 | 'max_versions' => 1, |
||||
416 | 'direction' => DirectionConst::CONST_FORWARD, |
||||
417 | 'inclusive_start_primary_key' => [ |
||||
418 | ['expiration', 1], |
||||
419 | ['key', null, PrimaryKeyTypeConst::CONST_INF_MIN], |
||||
420 | ['prefix', null, PrimaryKeyTypeConst::CONST_INF_MIN], |
||||
421 | ['type', null, PrimaryKeyTypeConst::CONST_INF_MIN], |
||||
422 | ], |
||||
423 | 'exclusive_end_primary_key' => [ |
||||
424 | ['expiration', ($this->currentTime() - $expiredDuration)], |
||||
425 | ['key', null, PrimaryKeyTypeConst::CONST_INF_MAX], |
||||
426 | ['prefix', null, PrimaryKeyTypeConst::CONST_INF_MAX], |
||||
427 | ['type', null, PrimaryKeyTypeConst::CONST_INF_MAX], |
||||
428 | ], |
||||
429 | 'limit' => $count, |
||||
430 | ]); |
||||
431 | if (empty($response['rows'])) { |
||||
432 | return 0; |
||||
433 | } |
||||
434 | |||||
435 | /** Delete */ |
||||
436 | $this->getOts()->batchWriteRow([ |
||||
437 | 'tables' => [ |
||||
438 | [ |
||||
439 | 'table_name' => $this->getTable(), |
||||
440 | 'rows' => Collection::make($response['rows'])->map(function ($row) { |
||||
441 | $row = Collection::make($row['primary_key'])->keyBy(0); |
||||
442 | |||||
443 | return [ |
||||
444 | 'operation_type' => OperationTypeConst::CONST_DELETE, |
||||
445 | 'condition' => [ |
||||
446 | 'row_existence' => RowExistenceExpectationConst::CONST_IGNORE, |
||||
447 | /** (Col2 <= 10) */ |
||||
448 | 'column_condition' => [ |
||||
449 | 'column_name' => 'expiration', |
||||
450 | 'value' => [$this->currentTime(), ColumnTypeConst::CONST_INTEGER], |
||||
451 | 'comparator' => ComparatorTypeConst::CONST_LESS_EQUAL, |
||||
452 | 'pass_if_missing' => true, |
||||
453 | 'latest_version_only' => true, |
||||
454 | ], |
||||
455 | ], |
||||
456 | 'primary_key' => [ |
||||
457 | $row->get('key'), |
||||
458 | $row->get('prefix'), |
||||
459 | $row->get('type'), |
||||
460 | ], |
||||
461 | ]; |
||||
462 | })->values()->toArray(), |
||||
463 | ], |
||||
464 | ], |
||||
465 | ]); |
||||
466 | |||||
467 | return count($response['rows']); |
||||
468 | } |
||||
469 | } |
||||
470 |