Data Storager
data-storager (в коде и переменных окружения также встречается историческое имя es-storager) — микросервис, который читает данные из реляционных баз Webim и переносит их в поисковый кластер (Elasticsearch или OpenSearch) и в ClickHouse. Исходные записи в meta-БД и базах pro сервис не изменяет.
Компонент не входит в основной пакет Webim Server. Его выдаёт техническая поддержка вместе с инструкцией по установке конкретной поставки. Статья описывает архитектуру, настройки и типовые операции, которые нужно знать администратору hosted-инсталляции.
Связанные материалы:
-
Подключение Elasticsearch или OpenSearch — поиск в истории диалогов;
-
Режимы подсчёта статистики — роль сервиса в Статистике версии 2;
Как устроен сервис
Один процесс producer раз в три минуты просматривает список аккаунтов и публикует в общий топик Apache Kafka задание DiscoverAndStoreRequest (имя аккаунта). Несколько независимых consumer-процессов читают тот же топик и каждый делает свою работу:
meta-БД (таблица accountkeyvalue)
│ раз в 3 минуты
▼
producer ──► топик Kafka (DISCOVER_AND_STORE_CHATS_TOPIC)
│
┌────────────┼────────────────────┐
▼ ▼ ▼
subscriber-chats subscriber-stats subscriber-report-stats (опционально)
│ ▼ ▼
▼ ClickHouse ClickHouse
Elasticsearch / (статистика v2, (отчётный стор)
OpenSearch базы webim_stats_*)
Маркеры прогресса и список временно «заблокированных» аккаунтов хранятся в Redis. Благодаря этому consumer-ы можно запускать, останавливать и масштабировать независимо, не теряя позицию переноса.
Сервис не получает содержимое чатов из Kafka: в топике только триггер. Данные consumer читает сам из баз pro.
Компоненты
| Компонент | Роль | Обязателен |
|---|---|---|
| producer | Планировщик: один экземпляр на весь контур. Не запускайте отдельный producer на каждый consumer. | Да |
| subscriber-chats | Индексация тредов, сообщений, посетителей, оценок и опросов в Elasticsearch / OpenSearch. | Для поиска в истории |
| subscriber-stats | Наполнение ClickHouse для Статистики версии 2. | Для статистики v2 через storager |
| subscriber-report-stats | Отдельный ETL в отчётный ClickHouse (виджеты статистики и дашборда поверх HTTP API). | Если используете отчётный HTTP API |
| stats.py | HTTP-приложение (FastAPI): служебные эндпоинты, метрики, при включении — API отчётов и статика виджетов. | Да, если нужны /health, /metrics или отчётный API |
| dashboard-realtime | Периодический снимок «живых» показателей дашборда в Redis. | Только для realtime-дашборда |
| subscriber-chat-states, subscriber-periods | Индексация истории состояний чата и периодов онлайна операторов в поисковый кластер. | По необходимости поставки |
Функции
-
индексация истории чатов и связанных объектов в Elasticsearch / OpenSearch;
-
индексация истории состояний чатов и периодов онлайна операторов в тот же кластер — опционально, отдельными компонентами
subscriber-chat-statesиsubscriber-periods; -
перенос статистических данных в ClickHouse для Статистики версии 2;
-
инкрементальное обновление по триггерам из Kafka;
-
опциональный отчётный HTTP API и встраиваемые виджеты статистики / дашборда;
-
публикация метрик Prometheus и проверка состояния.
Что должно быть в инфраструктуре
Перед запуском согласуйте с поддержкой и подготовьте:
-
Apache Kafka (и права на чтение/запись общего топика; по умолчанию имя топика —
es-discover-and-store-chats, 2 партиции); -
Redis — маркеры, кэш и список аккаунтов, для которых недавно произошла ошибка обработки;
-
meta-БД Webim (MySQL или PostgreSQL) — таблица
accountkeyvalue; -
базы pro каждого индексируемого аккаунта (MySQL или PostgreSQL);
-
кластер Elasticsearch или OpenSearch, если нужен поиск в истории;
-
ClickHouse, если нужна статистика v2 и/или отчётный API;
-
файл геоданных Sypex Geo по пути
GEO_DB_PATH(если в поставке используется определение страны в статистике).
Версии смежных продуктов см. в требованиях к ПО сервера. Типовая поставка сервиса рассчитана на Python 3.10.
Какие аккаунты обрабатываются
producer собирает список аккаунтов из двух источников и отбрасывает те, что временно попали в Redis-множество storager.failed_accounts (после ошибки обработки; срок — 15 минут).
Маркер в meta-БД (основной способ)
В таблицу accountkeyvalue добавьте строку с ключом es_storager_key. Значение должно посимвольно совпадать с переменной ES_KEY_VALUE в .env того инстанса data-storager, который должен обрабатывать аккаунт. Так несколько инстансов сервиса могут делить аккаунты между собой.
Пример для MySQL:
INSERT INTO accountkeyvalue (`accountname`, `key`, `value`)
VALUES ('shop', 'es_storager_key', '1.0');
Подробнее о маркере — в статье Подключение Elasticsearch или OpenSearch.
Список в .env
Переменная PRODUCE_FOR_ACCOUNTS — имена аккаунтов через запятую. Такие аккаунты producer обрабатывает даже без строки es_storager_key в meta-БД:
PRODUCE_FOR_ACCOUNTS=shop,support
Подключение к базе pro
По умолчанию URL берётся из PRO_DATABASE_DEFAULT_URL, имя базы подставляется из PRO_DATABASE_TEMPLATE (плейсхолдер {account_name}).
Если имя аккаунта не совпадает с ключом подключения (другой хост или другое имя базы), задайте в meta-БД ключ pro_db_connection_key. Сервис ищет переменную окружения DATABASE_CONNECTION_<NAME>_URL, где <NAME> — значение ключа в верхнем регистре.
INSERT INTO accountkeyvalue (`accountname`, `key`, `value`)
VALUES ('shop', 'pro_db_connection_key', 'db1');
DATABASE_CONNECTION_DB1_URL=mysql+aiomysql://webim_service:PASSWORD@db.example.local:3306/{database}
Внимание
Если pro_db_connection_key задан, а соответствующей переменной DATABASE_CONNECTION_<NAME>_URL нет, сервис молча использует PRO_DATABASE_DEFAULT_URL. При неверном ключе это выглядит как ошибка «Unknown database» в логах consumer, а аккаунт периодически попадает в failed_accounts.
Глубина первого переноса в поисковый кластер
Если маркеров в Redis ещё нет, subscriber-chats ограничивает «назад» по ключам в accountkeyvalue. Приоритет у es_store_since.
| Ключ | Смысл | Формат значения |
|---|---|---|
es_store_since |
Фиксированная дата начала индексации | YYYY-MM-DDTHH:MM:SS+TZ, например 2026-01-01T00:00:00+03:00 |
es_store_period |
Глубина от текущего момента | микросекунды, например 2592000000000 — 30 суток |
Для subscriber-stats (ClickHouse статистики v2) при отсутствии маркера глубина по умолчанию — последние 30 дней (начало суток). Маркеры этого контура в Redis живут 7 суток: если процессы не работали дольше, прогресс сбрасывается, и перенос снова начнётся с этой границы.
Сетевые требования
HTTP-порт веб-приложения задаётся STATS_WEB_PORT (по умолчанию 8000/tcp), адрес прослушивания — STATS_BIND_IP (по умолчанию 0.0.0.0). Прямой доступ из интернета к этому порту, как правило, не нужен: откройте его только для внутренней сети, балансировщика и системы мониторинга.
На том же порту:
-
/health— проверка: если в Redis есть аккаунты изfailed_accounts, ответ будет с кодом 500; -
/metrics— метрики Prometheus веб-приложения; -
/static/…и/reports…— при включённом отчётном контуре.
Каждый Faust-воркер (producer, consumer-ы) дополнительно поднимает HTTP-сервер метрик на порту PROMETHEUS_PORT (по умолчанию 9090). Если несколько воркеров запущены на одном хосте без изоляции портов, назначьте им разные значения.
Для hosted-инсталляции на узле с сервисом нужны следующие направления (порты — значения по умолчанию; актуальные смотрите в .env).
| Направление | Назначение | Порт/протокол | Примечание |
|---|---|---|---|
| Входящие | HTTP веб-приложения | STATS_WEB_PORT (8000/tcp) |
/health, /metrics, при необходимости отчётный API |
| Входящие | Метрики воркеров | PROMETHEUS_PORT (9090/tcp) |
Сбор Prometheus с процессов Faust |
| Исходящие | meta-БД и базы pro | 3306/tcp или 5432/tcp | Сервис читает данные напрямую из СУБД, не через HTTPS бэкенда |
| Исходящие | Redis | порт из REDIS_CACHE_LOCATION |
Маркеры и кэш |
| Исходящие | Elasticsearch / OpenSearch | 9200/tcp | Запись индексов истории |
| Исходящие | ClickHouse | 9000/tcp (native); при необходимости 8123/tcp (HTTP) | Статистика v2 и/или отчётный стор |
| Исходящие | Apache Kafka | порт брокера | Публикация и чтение заданий |
| Исходящие | Sentry | 443/tcp | Только если задан SENTRY_DSN |
Если data-storager и Speller стоят на одном хосте без сетевой изоляции, назначьте им разные HTTP-порты.
Переменные окружения
Сервис настраивается через .env (или эквивалент в оркестраторе). Полный шаблон поставляется вместе с компонентом. Ниже — параметры, которые нужно согласовать при развёртывании.
Ядро и Kafka
| Переменная | Назначение |
|---|---|
KAFKA_BROKER |
Адрес брокера Apache Kafka. |
DISCOVER_AND_STORE_CHATS_TOPIC |
Имя общего топика заданий. |
ES_KEY_VALUE |
Значение маркера es_storager_key в meta-БД. |
PRODUCE_FOR_ACCOUNTS |
Дополнительный список аккаунтов через запятую. |
BATCH_SIZE |
Размер пакета при чтении БД и записи в поисковый кластер. |
LOGGING_LEVEL, LOGGING_FORMAT |
Уровень и формат логов. |
SENTRY_DSN, SENTRY_ENV |
Опциональная отправка ошибок в Sentry. |
DEFAULT_TZ |
Часовой пояс по умолчанию. |
GEO_DB_PATH |
Путь к файлу Sypex Geo. |
DEFAULT_HIDDEN_COUNTRIES |
Страны, скрываемые в статистике. |
Базы данных
| Переменная | Назначение |
|---|---|
META_DATABASE_URL |
Подключение к meta-БД. Для MySQL — префикс mysql+aiomysql://, для PostgreSQL — postgresql+asyncpg:// (схема meta задаётся параметром schema=). |
PRO_DATABASE_TEMPLATE |
Шаблон имени базы pro, например webim_service_pro_{account_name}. |
PRO_DATABASE_DEFAULT_URL |
URL по умолчанию; плейсхолдер {database} заменяется именем из шаблона. |
DATABASE_CONNECTION_<NAME>_URL |
Именованные подключения для pro_db_connection_key. |
REDIS_CACHE_LOCATION |
URL Redis, например redis://redis.example.local:6379/2. |
Поисковый кластер
| Переменная | Назначение |
|---|---|
SEARCH_ENGINE_TYPE |
elasticsearch или opensearch. |
SEARCH_ENGINE_URLS |
URL узлов кластера (через запятую). Допустимы устаревшие имена ELASTIC_URLS, ELASTIC_USER, ELASTIC_PASSWORD, ELASTIC_INDEX_TEMPLATE. |
SEARCH_ENGINE_USER, SEARCH_ENGINE_PASSWORD |
Учётная запись кластера. Нужны права на работу с индексами. |
SEARCH_ENGINE_INDEX_TEMPLATE |
Шаблон имён индексов, например {account}-{object}. |
ES_INDEX_VERSION |
Суффикс версии в ключах маркеров Redis. |
На стороне Webim Server подключение к тому же кластеру задаётся в /etc/webim/db.json и параметрах account config elasticsearch_history / elasticsearch_threadprocessor — см. Подключение Elasticsearch или OpenSearch.
ClickHouse статистики v2
| Переменная | Назначение |
|---|---|
STATS_DATABASE_TEMPLATE |
Шаблон имени базы, по умолчанию webim_stats_{account_name}. |
STATS_DATABASE_DEFAULT_URLS |
URL реплик ClickHouse через запятую. Список трактуется как реплики одной и той же копии данных. Не указывайте здесь разные шарды: запись уйдёт на один узел, а чтение увидит неполные данные. |
HTTP и мониторинг
| Переменная | Назначение |
|---|---|
STATS_BIND_IP |
Адрес прослушивания веб-приложения (по умолчанию 0.0.0.0). |
STATS_WEB_PORT |
HTTP-порт (по умолчанию 8000). |
PROMETHEUS_PORT |
Порт метрик Faust-воркера (по умолчанию 9090). |
Отчётный HTTP API (опционально)
Эти переменные нужны, если в поставке включены виджеты статистики / дашборда (subscriber-report-stats и stats.py).
| Переменная | Назначение | По умолчанию |
|---|---|---|
REPORT_CH_URLS |
URL одного экземпляра (или реплик) отчётного ClickHouse. | — |
REPORT_CH_DATABASE_TEMPLATE |
Имя общей базы отчётов на все аккаунты (без плейсхолдера {account_name}: изоляция — колонкой account_name). |
— |
REPORT_STATS_MAX_AGE_DAYS |
Глубина хранения отчётных данных, дни. Уменьшение значения при следующем populate удалит более старые строки. | 730 |
REPORTS_MAX_RANGE_DAYS |
Максимальная ширина одного запроса отчёта, дни; 0 — без ограничения. |
31 |
REPORT_SCAN_LOOKBACK_DAYS |
Запас вниз по дате создания треда при скане; 0 — полное чтение таблицы. |
90 |
REPORTS_REQUIRE_SIGNATURE |
Требовать подписанный токен. В промышленной среде оставляйте True. |
True |
REPORTS_SIGNING_SECRET |
Секрет HMAC (≥ 32 символов), только на сервере. При включённой подписи и коротком секрете веб-приложение не стартует. | — |
REPORTS_SIGNING_SECRET_PREVIOUS |
Предыдущий секрет на время ротации. | — |
REPORTS_MAX_TOKEN_TTL_DAYS |
Потолок остаточного срока токена; 0 — без ограничения. |
0 |
REPORTS_CORS_ORIGINS |
Origin страниц, с которых браузер может читать API (через запятую). Это не замена токену. | * |
REPORTS_RATE_LIMIT / REPORTS_RATE_LIMIT_WINDOW |
Лимит запросов на IP и окно в секундах; 0 в лимите — выключить. |
120 / 60 |
REPORTS_TRUST_PROXY_HEADER |
Брать IP клиента из X-Forwarded-For. Включайте только за прокси, который сам выставляет заголовок. |
False |
DASHBOARD_REALTIME_UPDATE_INTERVAL |
Интервал живого опроса дашборда, сек. | 15 |
DASHBOARD_PREV_PERIODS_UPDATE_INTERVAL |
Интервал исторического опроса дашборда, сек. | 60 |
N.B.
База отчётного контура создаётся администратором заранее. Процесс ETL не выполняет CREATE DATABASE. Пользователю ClickHouse нужны права на таблицы этой базы, включая DDL и мутации (CREATE TABLE / VIEW, DROP, ALTER, OPTIMIZE, TRUNCATE — уточняйте по поставке).
Масштабирование и отказоустойчивость
-
producer — ровно один экземпляр на контур.
-
Consumer-ы масштабируются по числу партиций топика (в типовой конфигурации — 2). Все экземпляры одного consumer должны использовать общий Redis.
-
Веб-приложение
stats.pyможно поставить за прокси в нескольких процессах. -
Отчётный ClickHouse: в
REPORT_CH_URLSперечисляйте только реплики. Если кластер шардирован и реплик нет, указывайте один узел: несколько шардов в этом списке разнесут запись и испортят отчёты без явной ошибки в логе. -
Источник истины для чатов — базы pro. Поисковый кластер и ClickHouse — производные витрины: после потери витрины данные можно перезалить (см. ниже).
Эксплуатация
Команды выполняются через CLI Faust. В Docker подставьте имя контейнера вашей поставки, например:
docker exec -it es-storager-subscriber-chats-1 faust -A subscriber_chats -l info health
Поиск (subscriber-chats)
| Задача | Команда |
|---|---|
| Проверить связь с кластером поиска | faust -A subscriber_chats -l info health |
Сбросить список failed_accounts в Redis |
faust -A subscriber_chats -l info reset |
| Поставить задание на индексацию аккаунта | faust -A subscriber_chats -l info indexchats --account_name=shop |
| Проиндексировать один тред | faust -A subscriber_chats -l info indexchat --account_name=shop --chat_id=<id> |
| Состояние маркеров | faust -A subscriber_chats -l info info --account_name=shop |
| Сбросить маркеры (переиндексация с начала глубины) | faust -A subscriber_chats -l info refresh --account_name=shop |
| Удалить индекс аккаунта | faust -A subscriber_chats -l info drop --account_name=shop |
| Обновить mapping индекса | faust -A subscriber_chats -l info mapping --account_name=shop |
Статистика v2 (subscriber-stats)
Даты: %d-%m-%Y или %d-%m-%YT%H:%M:%S.
faust -A subscriber_stats -l info populate --account_name=shop --from=01-01-2026 --to=31-01-2026
faust -A subscriber_stats -l info nextstep --account_name=shop
faust -A subscriber_stats -l info refresh --account_name=shop
refresh сбрасывает маркеры всех storager-ов статистики v2 и запускает пересчёт.
Для «дыр» в ClickHouse, которые наполняет Chat Backend, по-прежнему можно использовать скрипт populate_clickhouse.sh — см. Перенос данных в ClickHouse.
Отчётный контур (subscriber-report-stats)
faust -A subscriber_report_stats -l info migrate_schema --account_name=shop
faust -A subscriber_report_stats -l info populate --account_name=shop --from=01-01-2026 --to=31-01-2026
faust -A subscriber_report_stats -l info refresh --account_name=shop
faust -A subscriber_report_stats -l info purge --account_name=shop
Схема таблиц создаётся идемпотентно при первом событии по аккаунту; migrate_schema прогревает её заранее. Очистка старше REPORT_STATS_MAX_AGE_DAYS выполняется автоматически не чаще раза в сутки (можно вызвать purge вручную).
Отчётный HTTP API и встраивание
Если в поставке включён отчётный контур, веб-приложение отдаёт:
| Метод | Путь | Назначение |
|---|---|---|
GET |
/reports |
Один отчёт (report, start, end; необязательно department, locale, category_id) |
GET |
/reports/list |
Список отчётов |
GET |
/reports/filters |
Значения фильтров и граница хранения min_date |
GET |
/l/o/current-stats |
Данные дашборда (realtime=true или false) |
Авторизация: параметр token (подписанный токен). В режиме разработки при REPORTS_REQUIRE_SIGNATURE=False допускается account_name — в промышленной среде не используйте.
Токен несёт имя аккаунта и срок действия, подписывается HMAC-SHA256 секретом REPORTS_SIGNING_SECRET. Бэкенд страницы-хоста выпускает новый токен на каждый рендер с коротким TTL. Отдельного отзыва токенов нет: экстренно отзывают все токены ротацией секрета (REPORTS_SIGNING_SECRET_PREVIOUS на время перекрытия). Формат токена и примеры реализации на PHP / Node.js / Python входят в поставку компонента (файл report_storager/EMBED.md).
Виджеты подключаются одним тегом (скрипт сам догружает недостающие стили и библиотеки). Origin API по умолчанию совпадает с origin скрипта; при другом origin задайте его на странице. Для кросс-доменных запросов со страниц Webim Server укажите эти origin в REPORTS_CORS_ORIGINS.
Лимит REPORTS_MAX_RANGE_DAYS отклоняет слишком широкий период кодом 400 (range_too_wide), а не сужает его молча. Частотный лимит (429) не затрагивает /health, /metrics и /static. Если Redis недоступен, частотный лимитер пропускает запрос (fail-open): это защита от нагрузки, а не авторизация.
Логирование и мониторинг
Логи пишутся в стандартный вывод процесса.
Метрики воркеров (имена с префиксом es_storager_) включают счётчики проиндексированных и неуспешных чатов, маркеры прогресса, число опубликованных заданий. На отчётном API: report_storager_auth_rejected (причины отказа видны в метке, в HTTP-ответе всегда общий 401) и report_storager_rate_limited.
N.B.
При инфраструктурной ошибке (нет базы, таймаут СУБД) счётчик неуспешных чатов может вырасти сразу на величину BATCH_SIZE, хотя документы в кластер ещё не отправлялись. Ориентируйтесь также на логи и на отсутствие роста es_storager_indexed_chats.
Типовые проблемы
| Симптом | Что проверить |
|---|---|
| Аккаунт не обрабатывается | Есть es_storager_key = ES_KEY_VALUE или имя в PRODUCE_FOR_ACCOUNTS; аккаунт не в failed_accounts (reset) |
| Ошибка «Unknown database» | pro_db_connection_key и наличие DATABASE_CONNECTION_<NAME>_URL; иначе используется PRO_DATABASE_DEFAULT_URL |
| Нет связи с поиском | faust -A subscriber_chats -l info health; URL, учётная запись, сеть до 9200/tcp |
| Нужно переиндексировать историю | refresh или drop + indexchats |
| В статистике v2 дыра за период | populate у subscriber_stats и/или populate_clickhouse.sh |
| Отчёты пустые / 500 | Доступен ли ClickHouse по REPORT_CH_URLS; создана ли схема (migrate_schema); эмитит ли producer аккаунт |
401, «Ссылка устарела» |
Истёк или неверно подписан токен; секрет на хосте и в сервисе совпадает |
| Фронт не загружает отчёты | REPORTS_CORS_ORIGINS; прокси до STATS_WEB_PORT |
| Realtime-дашборд пустой | Запущен dashboard-realtime; недавно открывали дашборд этого аккаунта |
data-storager обеспечивает перенос данных из баз Webim в поисковый кластер и ClickHouse. Для работы нужны Kafka, Redis, доступ к meta-БД и базам pro, согласованный маркер es_storager_key / ES_KEY_VALUE и (по задачам инсталляции) кластер поиска и ClickHouse.