Что происходит при сбоях и больших объёмах: модуль не теряет данные и переживает падения. Любая синхронизация — это фоновая задача на SAQ поверх Redis, а не запрос в рамках HTTP-сессии. Администратор запускает синхронизацию и может закрыть вкладку: держать соединение не требуется, прерывание сессии не останавливает работу — прогон ведёт воркер. Дальше — путь устойчивости одного прогона: SyncRun → checkpoint → ретраи → DLQ → видимость. Сами режимы, которые гоняют эти прогоны, — на странице режимы синхронизации.
SyncRun —
единую точку правды о ходе работы. В ней живут состояние
(queued / running /
succeeded / failed /
cancelled), прогресс
«обработано N из M», текущий чекпоинт, heartbeat воркера и
накопленные ошибки. UI ничего не вычисляет сам: он читает
SyncRun и рисует по ней прогресс и итог.
SyncRun. Остановившийся heartbeat означает, что
воркер упал (перезапуск пода, пересоздание контейнера): прогон
подхватывается заново и продолжается с последнего чекпоинта.
Интервал heartbeat — 30 с, а падением молчание считается
после трёх пропущенных ударов (90 с): этого хватает
пережить паузу GC или краткий рестарт пода, но зависший прогон
подхватывается за минуту-две. Оба значения — фиксированные
константы воркера, не настройка источника. Падение воркера
превращается в паузу, а не в потерю. Heartbeat бьётся из
отдельной корутины, независимой от стека
ретраев: иначе долгая
пауза внутри попытки выглядела бы как смерть воркера и вызывала
ложный перезапуск. Короткий backoff (потолок 60 с)
безопасен — он ниже порога молчания 90 с; длинные паузы
вообще не держат воркер, а уходят в отложенную постановку.
cancelled),
терминален, его чекпоинт не возобновляется — следующий прогон
стартует с нуля. Так суточная пауза источника не приводит к
загрузке устаревшей выборки. Resume применим только к
инкрементальному прогону коннектора, чей поток глобально
упорядочен по времени изменения (флаг манифеста): у
неупорядоченного потока watermark не гарантирует, что всё
более раннее обработано. Reconciliation и точечный DLQ-retry
не возобновляются и курсор не двигают — их выборка не
отражает фронтир потока.
429 или 5xx
от API источника — повод повторить, а не упасть. Запрос
повторяется с backoff: пауза между попытками растёт, чтобы не
перегружать источник и переждать всплеск. После исчерпания
попыток элемент не теряется, а уходит дальше по пути, в
DLQ.
429 · 500 · 502 ·
503 · 504 · 408 ·
сетевые таймауты и обрывы
400 · 401 · 404 ·
422 · ошибки нормализации и валидации
403 у
GitHub/GitLab: чаще это rate-limit, а не отказ в доступе, поэтому
перед отправкой в DLQ коннектор сверяется с
Retry-After / X-RateLimit-Remaining и
трактует такой 403 как транзиентный.
Retry-After, деградация
источника) воркер не отсыпает — он откладывает
задачу обратно в очередь (SAQ defer / re-enqueue) и освобождает
слот. Так
бюджет свежести
чекпоинта
(6 часов) реализуется отложенной постановкой, а не
удержанием воркера на sleep. Если источник прислал
Retry-After, он
переопределяет расчётный backoff.
succeeded с
error_count > 0 — завершён успешно, но
не безупречно: счётчик ошибок отделяет частичный сбой от полного
провала (failed). DLQ — durable-таблица
dead_letters
в Postgres, не эфемерное состояние прогона: backoff-ретраи выше
живут в памяти задачи-воркера и считаются секундами, а сбойный
item ждёт ручного разбора и переживает перезапуск воркера.
DLQ
SyncRun показывает «обработано N/M, K в DLQ» — по
ней видно и ход, и итог любого прогона.
SyncRun.