Перейти к основному содержимому

Фоновые задачи

Все тяжёлые и периодические операции — синхронизация с Wildberries, пересчёт витрин, мониторинг слотов, сбор метрик — выполняются в фоне на BullMQ (поверх Redis) внутри того же бэкенд-процесса, что и HTTP API (Бэкенд). Решения — ADR-0007, ADR-0009, ADR-0040, ADR-0042, ADR-0057.

Инфраструктура​

ЭлементРешение
Движок@nestjs/bullmq; воркеры — провайдеры с @Processor(queue), очереди — BullModule.registerQueue в фичевых модулях
СоединенияBullMQ создаёт собственные ioredis-соединения (maxRetriesPerRequest: null); общий REDIS_CLIENT остаётся за кэшем и лимитерами
Изоляция средпрефикс ключей QUEUE_PREFIX (optima на prod, optima-stage на stage)
Дефолты джобattempts: 5, exponential backoff от 1 с, успешные хранятся 24 ч / 1000 шт., неудачные не удаляются (removeOnFail: false); очереди плановых синков задают свои 3 попытки
ИдентификаторыcomposeJobId(...parts) склеивает части через -- (BullMQ v5 запрещает : в custom jobId)
Контексткаждая джоба исполняется в своём correlation-контексте — логи трассируются (Наблюдаемость)

Реестр расписаний​

Периодические расписания не используют @Cron/@Interval: каждый сервис регистрирует своё в onModuleInit через ScheduleRegistryService.register({ name, trigger, queue, jobName, payloadFactory }). Триггер — interval (мс) или cron (с таймзоной, почти везде Europe/Moscow).

Anti-overlap — на двух уровнях: оркестратор получает детерминированный jobId по «намеренному» времени тика (в кластере одно окно — один прогон), а джоба кабинета — jobId с номером окна каденции; повторная постановка того же ключа отбрасывается BullMQ. Повторы безопасны: импорты — идемпотентный upsert.

Очереди и расписания​

Время — московское (Europe/Moscow), если не указано иное.

Синхронизация данных WB​

ОчередьРасписаниеЧто делает
digitization-syncинкремент — interval 30 мин (DIGITIZATION_INCREMENT_INTERVAL_MS); бэкфилл — cron 0 3 * * *заказы, продажи, возвраты (wb_orders/wb_sales); пользовательские импорты фидов и отчёта идут в ту же очередь
rnp-finance-synccron 0 5 * * *отчёт о реализации за 14 дней (wb_report_lines)
rnp-ads-syncinterval 5 минреклама: статистика кампаний (ad_campaign_stats)
rnp-sections-syncinterval 1 чворонка и конверсии (rnp_funnel_stats)
catalog-syncкарточки — cron 0 4 * * *; цены — interval 30 мин; полный синк кабинета — по требованиюкаталог и цены
inventory-synccron 0 * * * * (каждый час)остатки
autobidder-syncпо требованию (POST /autobidder/sync → 202 + jobId)рекламные данные кабинета; отдельная очередь, чтобы пользовательский запуск не ждал бэклог крона
kb-wb-news-synccron 15 */3 * * *новости WB в базу знаний (метод домена common, лимит WB — 1 запрос в минуту, берётся токен любого активного кабинета)

Пересчёт витрин​

ОчередьРасписаниеЧто делает
re-aggregateпо событию (приоритет 10 — ниже ingest; 3 попытки)пересчёт дневных агрегатов (domain, accountId, day)
rnp-merge-synccron 0 6 * * *ночной пересчёт окна 3 дня и самовосстановление пропусков за 90 дней

Движки и прикладные задачи​

ОчередьРасписаниеЧто делает
autobidder-bid-engineinterval 60 с на кабинет, concurrency 1прогон движка ставок
repricer-scheduleinterval N часов на пару (кабинет, интервал правила); регистрируется на стартепрогон правил репрайсера
booking-slot-monitorinterval 12 смониторинг слотов приёмки для заданий бронировщика
pricing-bulkпо требованиюмассовое изменение цен чанками через WB upload/task
autoreply-syncкаждые 15 минзагрузка новых отзывов/вопросов, сверка отвеченных, автогенерация ответов и жалоб по авто-сценариям/правилам
autoreply-answer-sendпо требованиюотправка ответа на отзыв/вопрос в WB (ретраи BullMQ)
ab-test-metricscron 25 */3 * * *сбор метрик A/B-тестов после синхронизации рекламы
partition-managercron 0 3 * * 1 (UTC), еженедельносоздание партиций на 3 месяца вперёд и удаление по retention
example-jobs—шаблон-образец для новых воркеров

Значения заданы константами *.constants.ts в соответствующих модулях; каденции синков WB подобраны под реальное обновление данных на стороне Wildberries (фиды заказов и продаж — раз в 30 минут, отчёт о реализации — еженедельно).

Конкурентность и gate кабинета​

  • Concurrency. Шесть sync-очередей (digitization-sync, rnp-ads-sync, rnp-finance-sync, inventory-sync, catalog-sync, autobidder-sync) обрабатывают SYNC_QUEUE_CONCURRENCY кабинетов параллельно (по умолчанию 5): лимиты WB — на кабинет, поэтому параллель между кабинетами легальна. Значение читается из окружения процесса при загрузке декоратора @Processor; в dev с .env берётся дефолт. Остальные очереди работают по одной джобе (concurrency = 1).
  • Gate кабинета (WbAccountJobGate, ADR-0057). Ключ wb:gate:{accountId}, владелец <очередь>:<jobId>, TTL 90 с с heartbeat раз в 30 с (падение процесса не оставляет вечный замок). К «общим» бакетам WB одновременно ходит одна джоба кабинета. Если gate занят — джоба уходит в delayed на 15 с, попытка не расходуется.
  • Приоритеты. Пользовательские джобы (import-user-feeds, import-user-report, autobidder-sync-user) обгоняют плановые: плановый владелец отдаёт gate на ближайшей паузе лимита WB, а плановые джобы не копят хвост — ждут не дольше окна каденции (реклама — 5 мин, инкремент — свой интервал, бэкфилл — 3 ч, финансы — 6 ч), затем завершаются skipped: account-busy.
  • Перенос по лимиту WB (ADR-0051). Если ожидание лимитера дольше 5 с, джоба не спит в воркере, а переносится (delayed) на нужную паузу — слот воркера освобождается для других кабинетов. Применяется в процессорах длинных синков. Подробнее — Интеграция с Wildberries.
  • Фан-аут пропускает кабинеты не в статусе «Активен» и кабинеты с живым пользовательским импортом.

Конвейер пересчёта витрины РНП​

Витрина rnp_daily собирается только джобами очереди re-aggregate (домены rnp-sales, rnp-ads, rnp-finance, каскадом — merge rnp). Какие дни пересчитать, решает RnpRecomputeService (modules/rnp/rnp-recompute.service.ts):

ИсточникЧто ставитДни
Плановый синк фидов оцифровкиrnp-salesМСК-дни записанных заказов и продаж
Пользовательский импорт фидовrnp-salesвесь период [dateFrom, сегодня] + задетые дни
Импорт отчёта / rnp-finance-syncrnp-financeдни исходных заказов строк отчёта (srid → wb_orders.date, иначе rr_dt)
rnp-merge-syncвсе роллаперыокно 3 дня + самовосстановление: дни за 90 дней, где есть заказы, а строк витрины нет

Сбой постановки пересчёта не роняет импорт (данные уже в БД) — пропуск подберёт ночной merge-sync. Дни вне существующих партиций rnp_daily отбрасываются. Благодаря этому у нового кабинета РНП наполняется сразу после успешного импорта, а не через сутки. Модель данных — Данные, правила объединения — ADR-0030; подробности расписаний — backend/docs/rnp-schedules.md.

Метрики и мониторинг​

Метрики очередей (optima_queue_jobs, optima_queue_job_duration_ms), прогонов РНП и пересчётов (optima_reaggregate_*, optima_rnp_sync_*, optima_digitization_feed_sync_*) отдаёт /metrics; состояние очередей — Bull Board (/admin/queues). См. Наблюдаемость.