Files
XC_VM/docs/ru/development/caching-and-redis.md
T
Divarion_D d4da90f37b fix(docs): translate bold spans atomically and auto-prune orphaned ru pages
The line-by-line web translator reordered words inside `**bold**` spans and
misplaced/dropped the markers, producing `**LB` or `****` (empty bold). Mask
each `**...**` as ONE atomic sentinel: translate the inner text on its own,
then store the whole balanced `**inner**` — the engine never sees the markers
and cannot reorder or collapse them. Also harden the anthropic prompt to keep
emphasis balanced.

Auto-prune: after translating, delete generated docs/ru files whose docs/en
source no longer exists (renamed/removed) and drop now-empty dirs, so the tree
mirrors docs/en 1:1 (removes the stale development/modules.md and
guides/geoip-and-device-detection.md).

Bump PROMPT_VERSION to 6 to invalidate the contaminated cache and regenerate
docs/ru (0 broken bold spans remaining, aside from pre-existing multi-line
bold that spans a soft line break).
2026-08-27 18:07:43 +03:00

297 lines
16 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Стратегия кэширования и Redis
XC_VM использует двухуровневую стратегию кэширования:
- **Файловый кэш (igbinary)** — основной уровень, используемый как потоковыми, так и административными путями
- **Redis/KeyDB** — дополнительный высокопроизводительный уровень для определения состояния соединения и расширенных операций
Путь потоковой передачи считывается исключительно из файлового кэша (никаких запросов к базе данных).
Путь администратора считывается из базы данных с дополнительным кратковременным кэшем.
---
## Интерфейс кэширования
Файл: `src/Core/Cache/CacheInterface.php`
```php
get($key, $maxAge = null)
set($key, $data, $ttl = 0)
delete($key)
has($key, $maxAge = null)
flush()
```
- `$ttl = 0` означает кэширование навсегда (до ручного удаления или промывки).
- `$maxAge` проверяет время изменения файла на свежесть (только для файлового кэша).
---
## Файловый кэш
Файл: `src/Core/Cache/FileCache.php`
Реализация кэша по умолчанию. Данные, сериализованные в двоичном формате, хранятся в виде плоских файлов.
```php
$cache = new FileCache(CACHE_TMP_PATH);
$cache->set('my_key', $data, 3600);
$data = $cache->get('my_key', 120); // only if < 2 min old
```
Статический удобный API (обратная совместимость):
```php
FileCache::setCache($key, $data)
FileCache::getCache($key, $maxAge = null)
```
Характеристики:
- Сериализация: igbinary (если доступно) или PHP выполнить резервную сериализацию.
- Блокировка: `LOCK_EX` при записи для предотвращения повреждения.
- Расположение файла: `{basePath}/{key}` (нет подкаталогов для основных ключей).
- Восстановление поврежденных файлов: обнаруживает поврежденные данные, автоматически удаляет поврежденные файлы.
---
## Заново спрятать
Файл: `src/Core/Cache/RedisCache.php`
Дополнительная высокопроизводительная реализация.
```php
$redis = new RedisCache('127.0.0.1', 6379, $password, 'prefix:');
$redis->set($key, $data, 600); // 10-minute TTL via SETEX
$redis->getConnection(); // raw phpredis for sorted sets, pipelines
```
- Отложенное подключение: подключается при первой операции.
- Встроенная поддержка TTL через Redis `SETEX`.
- Используется в основном для `ConnectionTracker` (отсортированных наборов для текущего состояния соединения).
---
## Redis Управление подключениями
Файл: `src/Infrastructure/Redis/RedisManager.php`
Жизненный цикл синглтона:
```php
RedisManager::instance() // get active Redis or null
RedisManager::ensureConnected() // connect if not already
RedisManager::isConnected() // health check
RedisManager::closeInstance() // disconnect
```
Проверка работоспособности выдает сигнал Redis каждые 30 секунд (отменено). При сбое автоматически восстанавливается соединение. При сбое соединения возвращается значение null (постепенное ухудшение).
Конфигурация:
|Установка|Источник|По умолчанию|
| --- | --- | --- |
| `hostname` | `config.ini` |—|
| `port` |жестко запрограммированный| `6379` |
| `password` | `settings.redis_password` |—|
| `read_timeout` |жестко запрограммированный| `2.0s` |
| `tcp_keepalive` |жестко запрограммированный| `60s` |
### Сохраняющиеся при отключении в режиме ожидания (долгоживущие демоны)
Кратковременные запросы (PHP-FPM stream/admin) открывают новое соединение для каждого процесса
и на них не влияют тайм-ауты простоя. Демоны-долгожители — цикл `watchdog`,
`fanout_sync` — вместо этого удерживайте соединение **один** через синглтон для их
весь срок службы, при котором возможны два режима сбоя на загруженном сервере или на межсерверном сервере
(LB → ГЛАВНАЯ) ссылка:
- **Сервер простаивает - закрывается.** Redis закрывает любой клиент, который простаивает дольше своего `timeout` (`300s`
в комплекте `bin/redis/redis.conf`). затем phpredis прозрачно откроется снова.
сокет в следующей команде **без повторного воспроизведения аутентификации**, поэтому более поздняя команда
отвечает `NOAUTH` — или просто возвращает `false`.
- **Устранен пробел в проверке работоспособности.** `instance()` пингуется только каждые 30 секунд, так что между
пингует, что сброшенное соединение еще не замечено.
Охранники на месте:
- `instance()` обрабатывает любой ответ, не связанный с`PONG` пингом (беззвучное повторное подключение / `NOAUTH`
состояние) как отключенное соединение и принудительно выполняет полное, **повторная аутентификация** повторное подключение
через `\XC_VM::redis_connect()` — это не просто повторная попытка на уровне сокета.
- Вызывайте сайты, которые командами конвейера проверяют объект конвейера. Например
`ConnectionTracker::getCapacity()` проверяет, что `$redis->multi()` вернул
`\Redis` (сломанный сокет возвращает `false` и вызывает `zCard()` для этого bool
был бы фатальным вне пути повторного подключения) и выдает, чтобы его цикл повторных попыток снова подключился.
The server-side alternative (`timeout 0`) is deliberately **not** used — the
вместо этого клиент становится устойчивым, и `tcp-keepalive` по-прежнему получает доступ к мертвым одноранговым узлам.
---
## Заполнение кэша
Файлы кэша генерируются двумя заданиями cron:
### Облегченный кэш (CacheCronJob)
Запускает каждый цикл cron. Восстанавливает быстро меняющиеся данные (~1 секунда):
- `settings` — настройки панели
- `servers` — список серверов
- `bouquets` — пакеты каналов
- `categories` — категории потоков
- Блокирующие списки: `blocked_isp`, `blocked_ua`, `blocked_ips`, `blocked_servers`
- `allowed_ips`, `output_formats`, `hmac_keys`, `rtmp_ips`
### Большой объем кэша (CacheEngineCronJob)
Перестраивает потоковые, линейные и последовательные данные. Регулируется до одного раза в 5 минут с помощью маркера `heavy_cache_built`:
- `STREAMS_TMP_PATH/stream_{id}` — метаданные отдельного потока
- `LINES_TMP_PATH/line_i_{user_id}` — данные учетной записи пользователя
- `LINES_TMP_PATH/line_c_{username_password}` — имя пользователя → поиск идентификатора пользователя
- `LINES_TMP_PATH/line_t_{access_token}` — токен → поиск идентификатора пользователя
- `SERIES_TMP_PATH/series_{id}` — метаданные серии
Режим обнаружения изменений (если включено `cache_changes`): сравнивает временную метку базы данных `updated` с файлом `mtime`, восстанавливает только измененные элементы.
Режим полной перестройки: восстанавливает все записи. Регулируется параметром `cache_thread_count`.
### Готовность кэша
После каждой полной сборки кэша записывается файл `cache_complete`. Путь потоковой передачи проверяет наличие этого файла и завершает работу с ошибкой, если он отсутствует.
### Безопасность холодного кэширования
Потоковый загрузчик (`LegacyInitializer::initStreaming()`) считывает `servers`,
блок-листы и `proxy_servers` из файлового кэша. Перед первой сборкой
(fresh boot, cleared tmp) those files do not exist and `CacheReader::get()`
возвращает `null`, поэтому для каждого такого глобального массива по умолчанию используется пустой массив. Холодный кэш
следовательно, **не удается закрыть** — запрос не находит серверов и показывает "нет в эфире". —
вместо предупреждения `foreach(null)` или `in_array($x, null)` со смертельным исходом (PHP 8)
вниз по течению. Действительно поврежденный кэш *сборка* все еще отображается отдельно с помощью
`FileCache` предупреждение о сбое записи, поэтому это значение по умолчанию маскирует только временный
окно холодного пуска, а не настоящий сбой.
---
## Соглашения о ключах кэширования
### Системные ключи (CACHE_TMP_PATH)
|Ключ|Содержание|
| --- | --- |
| `settings` |массив настроек панели|
| `servers` |`array[server_id]` → конфигурация сервера|
| `bouquets` |`array[bouquet_id]` → bouquet определение|
| `categories` |`array[category_id]` → данные категории|
| `bouquet_map` |`array[stream_id]` → `array[bouquet_id]`|
| `category_map` |`array[bouquet_id]` → `array[category_id]`|
| `permissions_{group_id}` |набор групповых разрешений|
| `cache_complete` |`time()` временная метка последней полной сборки|
### Ключи потока (STREAMS_TMP_PATH)
|Ключ|Содержание|
| --- | --- |
| `stream_{id}` |информация о потоке + букеты + состояние каждого сервера|
| `channels_categories` |`array[stream_id]` → `array[category_id]`|
### Линейные ключи (LINES_TMP_PATH)
|Ключ|Содержание|
| --- | --- |
| `line_i_{user_id}` |полная запись о пользователе|
| `line_c_{username_password}` |user_id (поиск учетных данных)|
| `line_t_{access_token}` |user_id (поиск токена)|
### Ключи серии (SERIES_TMP_PATH)
|Ключ|Содержание|
| --- | --- |
| `series_{id}` |метаданные серии|
| `series_map` |`array[stream_id]` → идентификатор серии|
| `episodes_{series_id}` |`array[season_num]` → список эпизодов|
---
## Шаблоны аннулирования
|Спусковой крючок|Затронутые ключи|Механизм|
| --- | --- | --- |
|Администратор редактирует поток|`stream_{id}`, `bouquet_map`|сигнал → следующий `cron:cache_engine`|
|Администратор редактирует строку|`line_i_*`, `line_c_*`, `line_t_*`|следующий `cron:cache_engine`|
|Настройки изменены|`settings`, категории, блок-листы|`SettingsManager::clearCache()` + хрон|
|Обновлен список серверов|`servers`, `bouquet_map`|крон|
|Начало потока (FFprobe)| `{md5(source)}` |5-минутный TTL с помощью проверки mtime файла|
|Кнопка сброса администратора|все файлы в `CACHE_TMP_PATH`| `rm -rf` |
---
## Потоковая передача против пути администратора
### Путь потоковой передачи (`www/stream/*`)
- `cached: true` по умолчанию.
- Считывает данные исключительно из файлового кэша (никаких запросов к базе данных).
- Raw igbinary deserialization: `igbinary_unserialize(file_get_contents(...))`.
- Если `cache_complete` отсутствует: завершите работу с ошибкой.
### Путь администратора (`Public/Controllers/Admin/*`)
- `cached: false` по умолчанию.
- Считывает данные из базы данных непосредственно через доменные службы.
- Необязательный кратковременный кэш (пример из `BouquetService::getAll()`):
```php
$rCache = FileCache::getCache('bouquets', 60); // only if < 60s old
if (!empty($rCache)) {
return $rCache;
}
// miss: query database and write cache
FileCache::setCache('bouquets', $rOutput);
```
---
## Расположение файла кэша
```text
/home/xc_vm/tmp/cache/
├── settings
├── servers
├── bouquets
├── categories
├── bouquet_map
├── category_map
├── cache_complete
├── heavy_cache_built
├── streams/
│ ├── stream_{id}
│ └── channels_categories
├── lines/
│ ├── line_i_{user_id}
│ ├── line_c_{username_password}
│ └── line_t_{access_token}
└── series/
├── series_{id}
├── series_map
└── episodes_{series_id}
```
---
## Связанные файлы
|Файл|Цель|
| --- | --- |
| `src/Core/Cache/CacheInterface.php` |контракт на кэширование|
| `src/Core/Cache/FileCache.php` |реализация кэша на основе файлов|
| `src/Core/Cache/RedisCache.php` |Redis реализация кэширования|
| `src/Infrastructure/Redis/RedisManager.php` |Redis одноэлементное соединение|
| `src/Infrastructure/Cache/CacheReader.php` |устаревший мост для чтения кэша|
| `src/Cli/CronJobs/CacheCronJob.php` |облегченная генерация кэша|
| `src/Cli/CronJobs/CacheEngineCronJob.php` |генерация большого объема кэша (потоки, строки, серии)|
| `src/Domain/Bouquet/BouquetService.php` |пример кэширования пути администратора|
| `src/Domain/Stream/ConnectionTracker.php` |Redis отсортированные наборы для определения состояния соединения|