Автоматический сбор на Hetzner
Что и когда запускается на боевом сервере. Актуализировано 26.08.2026 по факту
(systemctl list-timers, unit-файлы в /etc/systemd/system).
Главное
🔴 Расписание живёт в systemd-таймерах, а не в crontab. В crontab лежит только
утренняя сводка операционки (/root/ops/bot/opsbot.py), к mp-system она не относится.
Новую регулярную задачу заводить таймером, а не строкой в crontab — иначе она выпадет
из общей картины и не будет видна в list-timers.
- Рабочая директория:
/home/mi/Bot_TG/mp-system, интерпретатор.venv/bin/python. - Боевая база:
/home/mi/Bot_TG/mp-system/mp.db. ⚠ Локальнаяmp.db— другая база. - Во всех таймерах
OnCalendarзадан сEurope/Moscow: сервер живёт в CEST/CET, без явной зоны время поедет при переходе на зимнее.
Расписание
| Время МСК | Unit | Что делает |
|---|---|---|
| 01:00 | mp-collect@3, mp-collect@5 |
ежедневный сбор WB (Siluetta, Tote) |
| 02:00 | mp-collect-ozon@2, mp-collect-ozon@4 |
ежедневный сбор Ozon |
| 02:10 | ozon-sku-traffic-snapshot |
трафик карточек Ozon |
| 02:30 | mp-report-cache |
кэш Telegram-отчётов |
| 03:00 | mp-signal |
дайджест «сигнал или шум» |
| 03:30 | mp-warehouse-sheet |
синхронизация своего склада с Google Sheets |
| 12:00 | mp-finreport@3, mp-finreport@5 |
финреп WB (реализация) |
| 12:30 | mp-report-cache |
кэш отчётов, второй прогон |
| каждые 3 ч | marketplace-returns-collect |
история возвратов Ozon/WB |
| ежечасно | mp-db-sync |
синк mp.db на ru-web (демо) |
Persistent: где true, а где false — и почему
Persistent=true означает «если сервер спал, догнать пропущенный запуск при старте».
trueу сбора, кэша, сигнала и синка склада — пропущенный день лучше догнать; у Google Sheets лимитов запросов нет, догоняющий прогон безвреден.- 🔴
falseуmp-finreport@намеренно: finance-api WB даёт 2 запроса в 24 ч. Если сервер спал в 12:00, догоняющий запуск при включении сожжёт лимит впустую. То же соображение уozon-sku-traffic-snapshot.
Заводя новый таймер, решать этот вопрос осознанно: есть ли у источника лимит и вредит ли запоздалый прогон.
Лимиты WB, из-за которых нельзя двигать расписание вслепую
- seller-analytics (воронка) — ~2 запроса/час;
- advert-api — общий bucket, не гонять параллельно со сбором;
- finance-api — 2 запроса/24 ч, интервал 12 ч;
- stocks/orders/sales — щадящие.
Поэтому WB-магазины разведены по минутам (RandomizedDelaySec=120), а finreport вынесен
в отдельный полуденный таймер.
Синхронизация склада — что делает mp-warehouse-sheet
scripts/import_warehouse_sheet.py --apply --alert --max-delta 200
Пишет в mp.db движения adjust / return по расхождению с Google-таблицей.
🔴 --max-delta 200 — порог вменяемости. Суммарная дельта больше 200 шт не
записывается: скрипт шлёт алерт и выходит с кодом 3, systemd помечает запуск упавшим.
200 выбрано потому, что самая крупная разовая отгрузка была 170 шт — всё, что больше,
скорее авария в листе, чем день работы. Настоящее крупное расхождение проводится вручную:
--max-delta 0.
⚠ Порог не защищает от подмены значения внутри него: 220 → 180 пройдёт как обычная
убыль. Это ловится только глазами.
Алерты: шлём при записи движений и при отказе/ошибке. Молчим, когда расхождений
нет — в отличие от run_collect, который шлёт хартбит каждый раз. Плата за это:
тишина двусмысленна (то ли сошлось, то ли таймер умер), поэтому при сомнении смотреть
systemctl list-timers.
Как посмотреть и как чинить
ssh hetzner
systemctl list-timers --all --no-pager | grep -E 'mp-|marketplace-|ozon-'
systemctl status mp-collect@3.service
journalctl -u mp-collect@3.service -n 100 --no-pager
Ручной прогон одной задачи:
sudo systemctl start mp-collect@3.service
Как завести новый таймер
/etc/systemd/system/<имя>.service—Type=oneshot,WorkingDirectory=/home/mi/Bot_TG/mp-system,ExecStart=/home/mi/Bot_TG/mp-system/.venv/bin/python scripts/<скрипт>.py./etc/systemd/system/<имя>.timer—OnCalendarсEurope/Moscow,Persistentпо правилу выше,RandomizedDelaySecесли задач несколько.systemctl daemon-reload && systemctl enable --now <имя>.timer.- Проверить:
systemctl list-timers | grep <имя>и разовыйsystemctl start <имя>.service. - Вписать в таблицу выше.
⚠ Systemd сам не запустит второй экземпляр, пока работает первый, — отдельный лок не нужен.
⚠ Новый регулярный джоб — зона /grill-me (правило из корневого CLAUDE.md): где
запускается, что если предыдущий не закончился, кому идёт алерт при падении.