# Кэширование в Webim Server (hosted-режим)

В статье описывается система кэширования **Webim Server** при работе в *hosted*-режиме.
Понимание механизмов кэширования помогает быстрее находить причины задержки применения настроек, появления устаревших данных в виджете и других подобных проблем.

## Уровни кэширования

**Webim Server** использует многоуровневую архитектуру кэширования. Каждый уровень отвечает за свою область данных и имеет собственное время жизни записей.

| Уровень | Хранение | Что кэшируется | TTL / inactive по умолчанию |
|---------|----------|-----------------|-----------------------------|
| **Nginx** proxy cache | `/var/cache/webim/nginx` | visitor-configs, HTTP-ответы | задаётся в конфиге развёртывания (`proxy_cache_valid`, `fastcgi_cache_valid`, `inactive`; примеры см. ниже) |
| **Admin Backend** (Python) | RAM процесса | операторы, отделы, настройки аккаунта, кнопки, размещения, опросы, роли, токены авторизации, тарифы, категории, приглашения, правила маршрутизации, политики доступа, конфигурации метрик, отчётов и дашбордов (полный список типов см. в разделе «Сброс кэша Admin Backend») | 5 мин (300 сек; константа `DEFAULT_CACHE_INVALIDATION_TIMEOUT` в коде Admin Backend) |
| **Chat Backend** (PHP) | файловая система | настройки аккаунта и отделов, операторы, данные аккаунта, тариф | `settings` и `settings_department_*`: 6000 сек; `tariff`: 900 сек; `operators` и `accounts`: 3600 сек |
| **button.js** | файл на диске | скомпилированный JS-виджет посетителя | до явного сброса |

### Nginx proxy cache

Кэш **Nginx** в hosted-режиме хранится в каталоге `/var/cache/webim/nginx`; при развёртывании без hosted-режима — в `/var/cache/nginx`. Используются зоны `fastcgi_cache` и `proxy_cache` с `levels=1:2`. Разбиение по подкаталогам задаётся путями `proxy_cache_path` и `fastcgi_cache_path` в конфиге Nginx.

**Где что хранится:** в этой директории лежат ответы, закэшированные разными location-блоками. Пути к каталогам задаются в директивах `proxy_cache_path` / `fastcgi_cache_path`: первый аргумент — путь на диске, по нему создаётся подкаталог верхнего уровня.

Среди каталогов кэша в конфигурации развёртывания есть `visitor-configs` (конфигурации виджета для посетителя): `/var/cache/webim/nginx/visitor-configs` в hosted-режиме и `/var/cache/nginx/visitor-configs` без него.

Внутри каждого такого каталога Nginx с `levels=1:2` создаёт двухуровневую структуру подкаталогов (один символ + два символа от хэша ключа), затем имя файла — MD5 от ключа кэша. Для **visitor-configs** в webim-dockchain ключ кэша в конфиге задаётся как `$account_name:$1:$query_string` (имя аккаунта, ключ размещения из URL, строка запроса, например `jsonp=true` для JSONP). Путь к файлу: `visitor-configs/{последний_символ_MD5}/{два_символа_MD5}/{полный_MD5}`.

При точечной инвалидации visitor-configs код **Admin Backend** формирует ключ как `account_name:location_key`. Поскольку точечная инвалидация не учитывает `$query_string`, она не затрагивает записи с непустой строкой запроса (например, JSONP-запросы). Для гарантированного сброса используйте полный сброс кэша Nginx по аккаунту (например, при изменении размещения) — он выполняется по содержимому файлов и затрагивает все записи аккаунта. Конкретный набор зон и имён подкаталогов зависит от конфигурации развёртывания.

!!! Warning "Нагрузка при полном сбросе Nginx по аккаунту"
    Полный сброс кэша Nginx по аккаунту читает содержимое файлов кэша в поисках имени аккаунта. На большом кэше это нагружает диск и процесс **Admin Backend**.

**Служебные каталоги Nginx** (`proxy_temp`, `client_temp`, `fastcgi_temp`, `scgi_temp`, `uwsgi_temp`) находятся в том же корне кэша и используются для временных данных. При ручной очистке их содержимое удалять нельзя.

**Почему в разных location разные параметры кэша:** каждому location (например, отдача visitor-configs, статика виджета, API) задаётся свой ключ кэша и свои директивы `proxy_cache_valid` / `fastcgi_cache_valid`. Так можно выставлять разный TTL и точечно инвалидировать только нужные данные при изменении настроек (например, сброс только visitor-configs без затрагивания статики).

**Примеры TTL и рекомендации по окружениям:**

| Окружение | Рекомендация | Пример для visitor-configs |
|-----------|---------------|----------------------------|
| **Тест**  | Уменьшенный TTL или отключение кэша для части location, чтобы быстрее видеть изменения при отладке. | 1–5 мин или `proxy_cache off` для соответствующего location. |
| **Прод**  | Увеличенный TTL для снижения нагрузки на бэкенды; кэш включать везде, где допустима задержка применения настроек. | 10–60 мин в зависимости от частоты изменений конфигурации. |

Значения в таблице — ориентир; итоговые настройки задаются в nginx-конфиге развёртывания (директивы `proxy_cache_valid`, `fastcgi_cache_valid` и при необходимости `proxy_cache_key`). Для зоны visitor-configs в продакшн-конфигах задано `inactive=5m`.

Для диагностики при наличии в конфиге Nginx директивы `add_header X-Cache-Status $upstream_cache_status;` в ответах доступен заголовок `X-Cache-Status` со значениями `HIT`, `MISS`, `EXPIRED`, `STALE`, `BYPASS`, `REVALIDATED` или `UPDATING`, позволяющий определить, был ли ответ отдан из кэша и в каком состоянии. Без этой директивы заголовок не добавляется.

### Кэш Admin Backend (Python)

Хранится в оперативной памяти процесса **Admin Backend**. Кэшируются данные операторов, отделов, настроек аккаунта, кнопок, размещений, опросов, ролей, токенов авторизации, тарифов, категорий, приглашений, правил маршрутизации, политик доступа, конфигураций метрик, отчётов и дашбордов. Полный список типов см. в разделе [Сброс кэша Admin Backend](#sbros-kesha-admin-backend). При перезапуске процесса кэш создаётся заново.

При работе нескольких узлов **Admin Backend** инвалидация кэша между ними выполняется через **Redis** Streams (см. раздел [Многоузловая инвалидация](#mnogouzlovaya-invalidatsiya-redis)).

### Файловый кэш Chat Backend (PHP)

Файловый кэш **Chat Backend** хранится на диске по пути:

```
{online_dir}/{первый_символ_md5(accountName)}/{accountName}/key-value-{kind}/
```

Где `{первый_символ_md5(accountName)}` — один символ (первый символ MD5-хэша от имени аккаунта), а не полный хэш. Переменная `{online_dir}` и допустимые значения `{kind}` задаются в конфигурации **Chat Backend** (PHP).

Каждая запись — файл с сериализованным массивом: ключ, TTL, данные и версия **Webim**. Кэшируются настройки аккаунта (`settings`), настройки отделов (`settings_department_{id}`, где `{id}` — идентификатор отдела), данные операторов (`operators`), данные текущего аккаунта (`accounts`) и тариф (`tariff`).

| Вид (`kind`) | TTL |
|---|---|
| `settings`, `settings_department_*` | 6000 сек (~1.67 ч) |
| `tariff` | 900 сек (15 мин) |
| `operators`, `accounts` | 3600 сек (1 ч) |

Срок 30 000 сек — запасное значение `KeyValueCache::put()`, если при записи TTL не передан. Для видов из таблицы используются сроки выше.

Просроченные записи автоматически удаляются при чтении. При обновлении версии **Webim** кэш инвалидируется автоматически.

!!! Note "Chat Backend (Python)"
    **Chat Backend** на Python имеет отдельный эндпоинт сброса кэша: `GET или POST /l/i/reset-cache`. Обработчик принимает оба метода: `POST` выполняет тот же сброс, что и `GET`. **Admin Backend** вызывает эндпоинт методом POST. При диагностике учитывайте оба компонента (PHP и Python).

### Кэш button.js

Скомпилированный виджет посетителя хранится по пути:

```
{web_cache_dir}/{accountName}/js/button.js
```

Переменная `{web_cache_dir}` задаётся в конфигурации развёртывания (hosted-режим). Файл пересоздаётся при изменении конфигурации кнопки. Отдаётся клиенту с заголовком `Last-Modified`.

## Автоматическая инвалидация

При изменении данных через административный интерфейс **Webim** автоматически сбрасывает соответствующие кэши. Например, при изменении оператора сбрасывается кэш операторов и токенов, при изменении кнопки — кэш кнопок и файл `button.js`, при изменении размещения — кэш размещений и **Nginx** (visitor-configs).

В большинстве случаев ручное вмешательство не требуется — изменения применяются автоматически в течение TTL соответствующего уровня кэширования.

## Многоузловая инвалидация (Redis) {: #mnogouzlovaya-invalidatsiya-redis }

**Admin Backend** при нескольких узлах синхронизирует инвалидацию кэша через **Redis** Streams. Узел, на котором изменились данные, сбрасывает свой RAM-кэш напрямую. Для остальных узлов в поток отправляется событие `invalidate_cache`. Получатель пропускает сообщения со своим идентификатором узла. В конфигурации **Admin Backend** указываются параметры `cluster_name` и `redis_connection`. В потоке хранятся события за последние примерно 5 минут; при появлении новых событий старые удаляются по порогу времени (xtrim). При длительном отсутствии новых событий записи в потоке могут сохраняться дольше.

**Chat Backend (PHP)** при нескольких узлах рассылает сброс кэша через MQService, а не через Redis. При расхождении кэша на PHP-узлах проверяйте доступность и настройки этого сервиса очередей, а не только Redis.

!!! Warning "Недоступность Redis"
    Если **Redis** недоступен (обрыв сети, перезапуск, ошибка конфигурации), события инвалидации не доходят до остальных узлов. Кэш на других узлах **Admin Backend** будет обновлён только по истечении TTL RAM-кэша (порядка 5 минут) или после перезапуска процесса. Рекомендуется мониторить доступность Redis и при длительной недоступности рассмотреть ручной сброс кэша на всех узлах или перезапуск **Admin Backend**.

## Сброс кэшей

!!! Note "Доступность эндпоинтов"
    Описанные ниже эндпоинты сброса кэша (`/service/flush`, `/internal/admin-backend/cache`, `/service/reset-cache.php`) доступны только из внутренней сети. При стандартной конфигурации **Nginx** прямой доступ к ним извне закрыт.

### Полный сброс всех уровней

```
GET /service/flush
```

Последовательно сбрасывает все уровни кэширования: `button.js`, файловый кэш **Chat Backend** (PHP), кэш **Chat Backend** (Python, эндпоинт `/l/i/reset-cache`), кэш **Admin Backend** и **Nginx** proxy cache.

!!! Warning "Важно!"
    Полный сброс увеличивает нагрузку на сервер — все данные будут перечитаны из базы при следующем обращении. Используйте только при реальной необходимости.

### Сброс кэша Admin Backend {: #sbros-kesha-admin-backend }

```
DELETE /internal/admin-backend/cache
DELETE /internal/admin-backend/cache/{type}
DELETE /internal/admin-backend/cache/{type}/{id}
```

Позволяет сбросить все кэши **Admin Backend**, конкретный тип или отдельную сущность по идентификатору. В URL типа кэша указываются **через дефис** (например, `account-config`); подчёркивание в пути не допускается.

Типы кэша (в URL — через дефис):

| Тип в URL | Описание |
|-----------|----------|
| `account` | Объект аккаунта (данные аккаунта как сущности). **Не путать с** `account-config`. |
| `account-config` | Настройки аккаунта (конфигурация). |
| `account-key-value` | Сбрасывает кэш только на уровне мультиаккаунта; кэш отдельного аккаунта не затрагивается. |
| `account-policy-tree` | Дерево политик доступа. |
| `auth-token` | Сессии авторизации операторов. |
| `button` | Кнопки размещения, скомпилированный button.js. |
| `category` | Категории. |
| `dashboard-report-config` | Конфигурации отчётов дашборда. |
| `departments` | Отделы. |
| `invitation` | Приглашения. |
| `location` | Размещения. |
| `metric-config` | Конфигурации метрик. |
| `nginx` | Кэш Nginx (visitor-configs по аккаунту). |
| `operators` | Операторы. |
| `private-keys` | Приватные ключи. |
| `prompter` | Суфлёры. |
| `report-config` | Конфигурации отчётов. |
| `report-group` | Группы отчётов. |
| `role` | Роли. |
| `security-group` | Группы безопасности. |
| `survey-configs` | Конфигурации опросов. |
| `tariff` | Тарифные опции. |
| `visitor-routing-rule` | Правила маршрутизации посетителей. |

### Сброс файлового кэша Chat Backend (PHP)

```
GET /service/reset-cache.php?type={type}
```

Допустимые значения `type`: `all`, `accounts`, `operators`, `departments`, `auth-token`, `settings`, `tariff`. При указании `departments` очищается кэш настроек каждого отдела (`settings_department_*`).

Значение `auth-token` без параметров `auth_token` или `operator_id` обрабатывается как ошибка: ответ **400 Bad Request**. Скрипт `reset-cache.php` передаёт в обработчик только `type` и не подставляет эти параметры из запроса, поэтому отдельно сбросить кэш токенов этим эндпоинтом нельзя. Токены очищаются при `type=all`.

### Очистка nginx-кэша вручную

Команда с явным исключением содержимого служебных каталогов (флаг `-prune` исключает только сами каталоги глубины 1, но не их содержимое на глубине 2; для корректного исключения используйте `-not -path '…/DIR/*'`):

```bash
sudo find /var/cache/webim/nginx -mindepth 2 -maxdepth 2 \
  -not -path '/var/cache/webim/nginx/proxy_temp/*' \
  -not -path '/var/cache/webim/nginx/client_temp/*' \
  -not -path '/var/cache/webim/nginx/fastcgi_temp/*' \
  -not -path '/var/cache/webim/nginx/scgi_temp/*' \
  -not -path '/var/cache/webim/nginx/uwsgi_temp/*' \
  -exec rm -rf {} +
sudo nginx -s reload
```

Удаляются только объекты кэша на глубине 2; содержимое каталогов `*_temp` не затрагивается.

### Автоматическая очистка при деплое

При обновлении **Webim** deploy-скрипт автоматически очищает:

  * `/var/cache/nginx` — объекты на глубине 2. Условие `-prune` из‑за `-mindepth 2` не исключает содержимое каталогов `*_temp` на этой глубине, поэтому оно тоже удаляется. Сами каталоги `*_temp` (глубина 1) остаются. Это отличается от ручной очистки выше: там `-not -path` оставляет содержимое `*_temp` нетронутым;

  * `/var/cache/webim/*` — всё содержимое рекурсивно (кроме подкаталогов `compiles` и `nginx`);

  * `/var/cache/webim/compiles/*` — всё содержимое рекурсивно.

Сами родительские каталоги не удаляются.

!!! Warning "Кэш Nginx в hosted-режиме"
    Каталог `/var/cache/webim/nginx` (кэш **Nginx** в hosted-режиме) **не очищается** при деплое — он исключён в скрипте. При необходимости сбросить его используйте ручную очистку (см. выше).

## Типичные сценарии

**Оператор изменил настройки, но они не применились**

Сбросьте кэши `account-config` и `settings`:

```
DELETE /internal/admin-backend/cache/account-config
GET /service/reset-cache.php?type=settings
```

**Виджет на сайте показывает старую конфигурацию**

Сбросьте кэш кнопки и **Nginx**:

```
DELETE /internal/admin-backend/cache/button
DELETE /internal/admin-backend/cache/nginx
```

**После обновления Webim видны старые данные**

Выполните полный сброс:

```
GET /service/flush
```

**Настройки размещения не обновляются**

Сбросьте кэш размещений и **Nginx** (visitor-configs):

```
DELETE /internal/admin-backend/cache/location
DELETE /internal/admin-backend/cache/nginx
```

---

Источник: https://webim.ru/kb/devops/hosted-deployment/caching.html
