1C-Bitrix 25.700.0
Загрузка...
Поиск...
Не найдено
keyvalueengine.php
См. документацию.
1<?php
2
4
13
15{
16 const BX_BASE_LIST = 'BL:';
17 const BX_INIT_DIR_LIST = 'IL:';
18
19 protected static $engine = null;
20 protected static array $locks = [];
21 protected static bool $isConnected = false;
22 protected static array $baseDirVersion = [];
23 protected static array $initDirPartitions = [];
24 protected string $sid = 'BX';
25 protected bool $useLock = false;
26 protected int $ttlMultiplier = 2;
27 protected int $ttlOld = 60;
28 protected bool $old = false;
29 protected bool $fullClean = false;
30 protected static int $clusterGroup = 0;
31
33 protected int $written = 0;
34 protected int $read = 0;
35 protected string $path = '';
36
37 abstract public function getConnectionName(): string;
38 abstract public static function getConnectionClass();
39
40 abstract public function set($key, $ttl, $value);
41 abstract public function get($key);
42 abstract public function del($key);
43
44 abstract public function setNotExists($key, $ttl , $value);
45 abstract public function checkInSet($key, $value): bool;
46 abstract public function addToSet($key, $value);
47 abstract public function getSet($key): array;
48 abstract public function delFromSet($key, $member);
49 abstract public function deleteBySet($key, $prefix = '');
50
55 public function __construct(array $options = [])
56 {
57 $config = $this->configure($options);
58
59 $this->connect($config);
60
61 static::$clusterGroup = (defined('BX_CLUSTER_GROUP') ? (int)constant('BX_CLUSTER_GROUP') : 0);
62
63 if (self::$isConnected)
64 {
65 if ($this->lock($this->sid . '|cacheClean', 10))
66 {
67 Application::getInstance()->addBackgroundJob([$this, 'delayedDelete'], [], Application::JOB_PRIORITY_LOW);
68 }
69 }
70 }
71
72 protected function connect($config)
73 {
74 if (!self::$isConnected)
75 {
76 $connectionPool = Application::getInstance()->getConnectionPool();
77 $connectionPool->setConnectionParameters($this->getConnectionName(), $config);
78
80 $engineConnection = $connectionPool->getConnection($this->getConnectionName());
81
82 self::$engine = $engineConnection->getResource();
83 self::$isConnected = $engineConnection->isConnected();
84 }
85 }
86
87 protected function configure($options = []): array
88 {
89 $config = [];
90 $cacheConfig = Config\Configuration::getValue('cache');
91
92 if (!$cacheConfig || !is_array($cacheConfig))
93 {
94 return $config;
95 }
96
97 if (isset($options['type']))
98 {
99 $type = $options['type'];
100 }
101 else
102 {
103 if (is_array($cacheConfig['type']) && isset($cacheConfig['type']['extension']))
104 {
105 $type = $cacheConfig['type']['extension'];
106 }
107 else
108 {
109 $type = $cacheConfig['type'];
110 }
111 }
112
113 $config['type'] = $type;
114 $config['className'] = static::getConnectionClass();
115 $config['servers'] = [];
116
117 if (!empty($cacheConfig[$type]['host']))
118 {
119 $config['servers'][] = [
120 'host' => $cacheConfig[$type]['host'],
121 'port' => (int)($cacheConfig[$type]['port'] ?? 0)
122 ];
123 }
124
125 // Settings from .settings.php
126 if (isset($cacheConfig['servers']) && is_array($cacheConfig['servers']))
127 {
128 $config['servers'] = array_merge($config['servers'], $cacheConfig['servers']);
129 }
130
131 // Setting from cluster config
132 if (isset($options['servers']) && is_array($options['servers']))
133 {
134 $config['servers'] = array_merge($config['servers'], $options['servers']);
135 }
136
137 if (isset($options['actual_data']))
138 {
139 $cacheConfig['actual_data'] = $options['actual_data'];
140 }
141
142 if (isset($cacheConfig['use_lock']))
143 {
144 $this->useLock = (bool)$cacheConfig['use_lock'];
145 }
146
147 if (!empty($options['sid']))
148 {
149 $this->sid = $options['sid'];
150 }
151 elseif (!empty($cacheConfig['sid']))
152 {
153 $this->sid = $cacheConfig['sid'];
154 }
155 $this->sid .= '|v1.1';
156
157 if (isset($cacheConfig['actual_data']) && !$this->useLock)
158 {
159 $this->useLock = !$cacheConfig['actual_data'];
160 }
161
162 if (!$this->useLock)
163 {
164 $this->ttlMultiplier = 1;
165 }
166
167 if (isset($cacheConfig['ttl_multiplier']) && $this->useLock)
168 {
169 $this->ttlMultiplier = (int)$cacheConfig['ttl_multiplier'];
170 if ($this->ttlMultiplier < 1)
171 {
172 $this->ttlMultiplier = 1;
173 }
174 }
175
176 if (isset($cacheConfig['full_clean']))
177 {
178 $this->fullClean = (bool)$cacheConfig['full_clean'];
179 }
180
181 if (isset($cacheConfig['ttlOld']) && (int)$cacheConfig['ttlOld'] > 0)
182 {
183 $this->ttlOld = (int)$cacheConfig['ttlOld'];
184 }
185
186 return $config;
187 }
188
192 public function getReadBytes()
193 {
194 return $this->read;
195 }
196
200 public function getWrittenBytes()
201 {
202 return $this->written;
203 }
204
208 public function getCachePath()
209 {
210 return $this->path;
211 }
212
222 protected function lock(string $key = '', int $ttl = 0): bool
223 {
224 if ($key == '')
225 {
226 return false;
227 }
228
229 $key .= '~';
230 if (isset(self::$locks[$key]))
231 {
232 return true;
233 }
234 else
235 {
236 if ($this->setNotExists($key, $ttl, $this->ttlOld))
237 {
238 self::$locks[$key] = true;
239 return true;
240 }
241 }
242 return false;
243 }
244
250 protected function unlock(string $key = ''): void
251 {
252 if ($key != '')
253 {
254 $key .= '~';
255 $this->del($key);
256 unset(self::$locks[$key]);
257 }
258 }
259
264 function close(): void
265 {
266 if (self::$engine != null)
267 {
268 self::$engine->close();
269 self::$engine = null;
270 }
271 }
272
276 public function isAvailable()
277 {
278 return self::$isConnected;
279 }
280
286 public function isCacheExpired($path)
287 {
288 return false;
289 }
290
291 protected function getPartition($key): string
292 {
293 return substr(sha1($key), 0, 4);
294 }
295
296 protected function getInitDirKey($baseDirVersion, $baseDir, $initDir): string
297 {
298 return $this->sid . '|BDV:' . $baseDirVersion . '|IDH:' . sha1($baseDir . '|' . $initDir);
299 }
300
301 protected function getBaseDirKey($baseDir): string
302 {
303 return $this->sid . '|BDV:' . sha1($baseDir);
304 }
305
306 protected function getKeyPrefix($baseDirVersion, $initDirVersion): string
307 {
308 return $this->sid . '|' . sha1($baseDirVersion . '|' . $initDirVersion);
309 }
310
319 protected function getInitDirVersion($baseDir, $initDir = false, bool $create = true): string
320 {
321 $baseDirVersion = $this->getBaseDirVersion($baseDir);
322 $initDirHash = sha1($baseDir . '|' . $initDir);
323
324 $key = $this->getInitDirKey($baseDirVersion, $baseDir, $initDir);
325 $initDirVersion = $this->get($key);
326
327 if ($initDirVersion == '' && $create)
328 {
329 $initDirVersion = sha1($initDirHash . '|' . mt_rand() . '|' . microtime());
330 $this->set($key, 0, $initDirVersion);
331 }
332
333 return $initDirVersion;
334 }
335
342 protected function getBaseDirVersion($baseDir): string
343 {
344 $key = $this->getBaseDirKey($baseDir);
345
346 if (!isset(static::$baseDirVersion[$key]))
347 {
348 static::$baseDirVersion[$key] = $this->get($key);
349 }
350
351 if (static::$baseDirVersion[$key] == '')
352 {
353 static::$baseDirVersion[$key] = sha1(sha1($baseDir) . '|' . mt_rand() . '|' . microtime());
354 $this->set($key, 0, static::$baseDirVersion[$key]);
355 }
356
357 return static::$baseDirVersion[$key];
358 }
359
363 public function read(&$vars, $baseDir, $initDir, $filename, $ttl)
364 {
365 $baseDirVersion = $this->getBaseDirVersion($baseDir);
366 $initDirVersion = $this->getInitDirVersion($baseDir, $initDir, false);
367
368 if ($initDirVersion == '')
369 {
370 if ($this->useLock)
371 {
372 $initDirVersion = $this->get($this->getInitDirKey($baseDirVersion, $baseDir, $initDir) . '~');
373 if ($initDirVersion == '')
374 {
375 $vars = false;
376 return false;
377 }
378 else
379 {
380 $this->old = true;
381 }
382 }
383 else
384 {
385 $vars = false;
386 return false;
387 }
388 }
389
390 $key = $this->getKeyPrefix($baseDirVersion, $initDirVersion) . '|' . $filename;
391
392 if ($this->useLock)
393 {
394 $cachedData = $this->get($key);
395
396 if (!is_array($cachedData))
397 {
398 $cachedData = $this->get($key . '|old');
399
400 if (is_array($cachedData))
401 {
402 $this->old = true;
403 }
404 }
405
406 if (!is_array($cachedData))
407 {
408 return false;
409 }
410
411 if (($cachedData['expire'] < time() || $this->old) && $this->lock($key, $ttl))
412 {
413 return false;
414 }
415
416 $vars = $cachedData['content'];
417 }
418 else
419 {
420 $vars = $this->get($key);
421 }
422
423 if (Cache::getShowCacheStat())
424 {
425 $this->read = strlen(serialize($vars));
426 $this->path = $baseDir . $initDir . $filename;
427 }
428
429 return $vars !== false;
430 }
431
435 public function write($vars, $baseDir, $initDir, $filename, $ttl)
436 {
437 $baseDirVersion = $this->getBaseDirVersion($baseDir);
438 $initDirVersion = $this->getInitDirVersion($baseDir, $initDir);
439
440 $keyPrefix = $this->getKeyPrefix($baseDirVersion, $initDirVersion);
441 $key = $keyPrefix . '|' . $filename;
442
443 $exp = $this->ttlMultiplier * (int) $ttl;
444
445 if ($this->useLock)
446 {
447 $this->set($key, $exp, ['expire' => time() + $ttl, 'content' => $vars]);
448 $this->del($key . '|old');
449 $this->unlock($key);
450 }
451 else
452 {
453 $this->set($key, $exp, $vars);
454 }
455
456 $initListKey = $keyPrefix . '|' . self::BX_INIT_DIR_LIST;
457 $initPartition = $this->getPartition($filename);
458 $initListKeyPartition = $initListKey . '|' . $initPartition;
459
460 $this->addToSet($initListKeyPartition, $key);
461 if (empty(static::$initDirPartitions[$initListKey][$initPartition]))
462 {
463 static::$initDirPartitions[$initListKey][$initPartition] = true;
464 $this->addToSet($initListKey, $initPartition);
465 }
466
467 if ($this->fullClean)
468 {
469 $baseListKey = $this->sid . '|' . $baseDirVersion . '|' . self::BX_BASE_LIST;
470 $baseListKeyPartition = $this->getPartition($initListKeyPartition);
471 $this->addToSet($baseListKey . $baseListKeyPartition, $keyPrefix);
472 $this->addToSet($baseListKey, $baseListKeyPartition);
473 }
474
475 if (Cache::getShowCacheStat())
476 {
477 $this->written = strlen(serialize($vars));
478 $this->path = $baseDir . $initDir . $filename;
479 }
480 }
481
485 public function clean($baseDir, $initDir = false, $filename = false)
486 {
487 if (!self::isAvailable())
488 {
489 return;
490 }
491
492 $baseDirVersion = $this->getBaseDirVersion($baseDir);
493 $initDirVersion = $this->getInitDirVersion($baseDir, $initDir, false);
494
495 if ($initDirVersion == '' && $initDir != '')
496 {
497 return;
498 }
499
500 $keyPrefix = $this->getKeyPrefix($baseDirVersion, $initDirVersion);
501 $initListKey = $keyPrefix . '|' . self::BX_INIT_DIR_LIST;
502
503 if ($filename <> '')
504 {
505 $key = $keyPrefix . '|' . $filename;
506 $this->delFromSet($initListKey . '|' . $this->getPartition($filename), $filename);
507
508 if ($this->useLock && $cachedData = $this->get($key))
509 {
510 $this->set($key . '|old', $this->ttlOld, $cachedData);
511 }
512
513 $this->del($key);
514 if ($this->useLock)
515 {
516 $this->unlock($key);
517 }
518 }
519 elseif ($initDir != '')
520 {
521 $key = $this->getInitDirKey($baseDirVersion, $baseDir, $initDir);
522 $this->del($key);
523
524 if ($this->useLock)
525 {
526 $this->set($key . '~', $this->ttlOld, $initDirVersion);
527 $cleanFrom = (new DateTime())->add('+' . $this->ttlOld . ' seconds');
528 }
529 else
530 {
531 $cleanFrom = (new DateTime());
532 }
533
534 $this->addCleanPath([
535 'PREFIX' => $keyPrefix,
536 'CLEAN_FROM' => $cleanFrom,
537 'CLUSTER_GROUP' => static::$clusterGroup
538 ]);
539
540 $this->set($this->sid . '|needClean', 3600, 'Y');
541 }
542 else
543 {
544 if ($this->fullClean)
545 {
547 $this->useLock = false;
548
549 $baseDirVersion = $this->getBaseDirVersion($baseDir);
550 $baseListKey = $this->sid . '|' . $baseDirVersion . '|' . self::BX_BASE_LIST;
551
552 $partitionKeys = $this->getSet($baseListKey);
553 foreach ($partitionKeys as $partition)
554 {
555 $baseListKeyPartition = $baseListKey . $partition;
556 $paths = $this->getSet($baseListKeyPartition);
557 foreach ($paths as $path)
558 {
559 $this->addCleanPath([
560 'PREFIX' => $path,
561 'CLEAN_FROM' => (new DateTime()),
562 'CLUSTER_GROUP' => static::$clusterGroup
563 ]);
564 }
565
566 unset($paths);
567 }
568
569 $this->set($this->sid . '|needClean', 3600, 'Y');
570 $this->del($baseListKey);
571 $this->useLock = $useLock;
572 }
573
574 $baseDirKey = $this->getBaseDirKey($baseDir);
575 $this->del($baseDirKey);
576 unset(static::$baseDirVersion[$baseDirKey]);
577 }
578 }
579
580 public function addCleanPath(array $data): void
581 {
582 CacheCleanPathTable::add($data);
583 }
584
585 public function delayedDelete(): void
586 {
587 $delta = 10;
588 $deleted = 0;
589 $etime = time() + 5;
590 $needClean = $this->get($this->sid . '|needClean');
591 if ($needClean !== 'Y')
592 {
593 $this->unlock($this->sid . '|cacheClean');
594 return;
595 }
596
597 $count = (int) $this->get($this->sid . '|delCount');
598 if ($count < 1)
599 {
600 $count = 1;
601 }
602
603 $paths = CacheCleanPathTable::query()
604 ->setSelect(['ID', 'PREFIX'])
605 ->where('CLEAN_FROM', '<=', new DateTime())
606 ->where('CLUSTER_GROUP', static::$clusterGroup)
607 ->setLimit($count + $delta)
608 ->exec();
609
610 while ($path = $paths->fetch())
611 {
612 $finished = true;
613 $partitionKeys = $this->getSet($path['PREFIX'] . '|' . static::BX_INIT_DIR_LIST);
614 foreach ($partitionKeys as $partition)
615 {
616 if (time() > $etime)
617 {
618 $finished = false;
619 break;
620 }
621
622 $delKey = $path['PREFIX'] . '|' . static::BX_INIT_DIR_LIST . '|' . $partition;
623 $keys = $this->getSet($delKey);
624 if (!empty($keys))
625 {
626 $this->deleteBySet($delKey, $path['PREFIX']);
627 }
628 }
629
630 if ($finished)
631 {
632 CacheCleanPathTable::delete($path['ID']);
633 $deleted++;
634 }
635 if (time() > $etime)
636 {
637 break;
638 }
639 }
640
641 if ($deleted > $count)
642 {
643 $this->set($this->sid . '|delCount', 604800, $deleted);
644 }
645 elseif ($deleted < $count && $count > 1)
646 {
647 $this->set($this->sid . '|delCount', 604800, --$count);
648 }
649
650 if ($deleted == 0)
651 {
652 $this->set($this->sid . '|needClean', 3600, 'N');
653 }
654
655 $this->unlock($this->sid . '|cacheClean');
656 }
657}
$count
Определения admin_tab.php:4
$type
Определения options.php:106
const JOB_PRIORITY_LOW
Определения application.php:32
static getInstance()
Определения application.php:98
static getValue($name)
Определения configuration.php:24
write($vars, $baseDir, $initDir, $filename, $ttl)
Определения keyvalueengine.php:435
static array $initDirPartitions
Определения keyvalueengine.php:23
getKeyPrefix($baseDirVersion, $initDirVersion)
Определения keyvalueengine.php:306
__construct(array $options=[])
Определения keyvalueengine.php:55
addCleanPath(array $data)
Определения keyvalueengine.php:580
getInitDirKey($baseDirVersion, $baseDir, $initDir)
Определения keyvalueengine.php:296
lock(string $key='', int $ttl=0)
Определения keyvalueengine.php:222
read(&$vars, $baseDir, $initDir, $filename, $ttl)
Определения keyvalueengine.php:363
clean($baseDir, $initDir=false, $filename=false)
Определения keyvalueengine.php:485
unlock(string $key='')
Определения keyvalueengine.php:250
getInitDirVersion($baseDir, $initDir=false, bool $create=true)
Определения keyvalueengine.php:319
static array $baseDirVersion
Определения keyvalueengine.php:22
$options
Определения commerceml2.php:49
$data['IS_AVAILABLE']
Определения .description.php:13
$filename
Определения file_edit.php:47
</td ></tr ></table ></td ></tr >< tr >< td class="bx-popup-label bx-width30"><?=GetMessage("PAGE_NEW_TAGS")?> array( $site)
Определения file_new.php:804
Определения aliases.php:105
$delta
Определения prolog_main_admin.php:363
if( $daysToExpire >=0 &&$daysToExpire< 60 elseif)( $daysToExpire< 0)
Определения prolog_main_admin.php:393
$config
Определения quickway.php:69
if(empty($signedUserToken)) $key
Определения quickway.php:257
$paths
Определения options.php:2080
path
Определения template_copy.php:201