Learning Platform
Глоссарий Troubleshooting
Урок 14.02 · 22 мин
Средний
fault-toleranceretry-policyconfiguration

Retry policies: NONE, QUERY, TASK — когда какой

В прошлом уроке мы установили идею FTE: материализовать промежуток, чтобы пережить сбой воркера. Но «пережить сбой» можно на разном уровне — повторить запрос целиком или повторить только упавшую задачу. За этот выбор отвечает одно свойство — retry-policy. У него три значения: NONE, QUERY, TASK. Они образуют шкалу «нет отказоустойчивости -> грубая -> тонкая», и каждое следующее значение мощнее, но требовательнее.

Разберём все три: что именно ретраит каждое, какие у него требования и накладные расходы, и по какому профилю нагрузки выбирать.


retry-policy=NONE — дефолт без отказоустойчивости

retry-policy=NONE — значение по умолчанию. Это классический stateless-Trino из прошлого урока: pipelined-исполнение, нет материализации промежутка, сбой любого воркера роняет весь запрос, восстановления нет.

# etc/config.properties — дефолт, можно не указывать
retry-policy=NONE

NONE — правильный выбор для интерактивных кластеров: дашборды, ad-hoc аналитика, короткие запросы. Запрос длится секунды, шанс наткнуться на сбой мал, а отсутствие накладных расходов FTE означает максимальную скорость. Платой за надёжность здесь была бы материализация промежутка на каждом запросе — для коротких запросов это невыгодный размен.


retry-policy=QUERY — ретрай всего запроса целиком

retry-policy=QUERY — первый уровень отказоустойчивости. Если во время исполнения происходит инфраструктурный сбой, Trino повторяет весь запрос целиком — заново, с самого начала.

# etc/config.properties
retry-policy=QUERY

Звучит грубо — «упало, начали с нуля». Но при QUERY ретрай прозрачен для клиента. Без FTE упавший запрос — это ошибка в лицо пользователю. С retry-policy=QUERY пользователь о сбое и ретрае может вообще не узнать: координатор сам перезапускает запрос, а клиент просто получает результат чуть позже.

Чтобы ретрай был прозрачным, координатор не должен отдать клиенту частичный результат, который потом окажется неверным после перезапуска. Поэтому координатор буферизует результат запроса, прежде чем начать его отдавать. Если запрос упал до того, как результат начал уходить клиенту, — буфер сбрасывается, запрос ретраится, клиент ничего не заметил. Размер этого буфера задаёт свойство exchange.deduplication-buffer-size, по умолчанию 32MB.

retry-policy=QUERY: прозрачный ретрай через буфер дедупликации
Запрос исполняется, результат копится в буфереКоординатор буферизует результат, не отдавая клиенту сразу. Размер буфера — exchange.deduplication-buffer-size, по умолчанию 32MB
сбой воркера ДО отдачи результата
Буфер сброшен, весь запрос повторён с нуляКоординатор перезапускает запрос целиком. Клиент частичного результата ещё не видел — ретрай для него полностью прозрачен

У retry-policy=QUERY есть естественное ограничение. Повторять с нуля дёшево для коротких запросов и дорого для длинных. Если запрос идёт 5 секунд — перезапуск стоит ещё 5 секунд, мелочь. Если запрос идёт 3 часа и падает на 2:59 — QUERY начнёт все 3 часа заново. Поэтому QUERY хорош для кластеров с множеством мелких запросов: каждый отдельный ретрай дёшев, а отказоустойчивость есть.


retry-policy=TASK — ретрай отдельной задачи

retry-policy=TASK — тонкий уровень отказоустойчивости. Вместо перезапуска всего запроса Trino ретраит только упавшую задачу (task) — ту единицу работы, что исполнялась на сбойном воркере.

Вспомни таксономию из модуля про распределённое исполнение: запрос дробится на стадии, стадия — на задачи, задача исполняется на конкретном воркере. При retry-policy=TASK, когда воркер падает, переисполняется не весь запрос, а лишь его задачи. Их назначают на другие, живые воркеры, и общий прогресс запроса сохраняется.

Именно ради этого нужна материализация промежутка из прошлого урока. Чтобы переисполнить одну задачу на другом воркере, её входные данные должны быть доступны — а значит, промежуточные результаты предыдущих стадий обязаны быть спулены в надёжное хранилище.

Spark: устойчивость через RDD-lineage и материализацию shuffle Поэтому:

WARNING

retry-policy=TASK ТРЕБУЕТ настроенного exchange manager. Exchange manager — компонент, который спулит промежуточные данные обмена во внешнее хранилище (filesystem, S3, GCS); ему посвящён следующий урок. Без exchange manager retry-policy=TASK работать не может: переисполнять задачу попросту неоткуда — её входные данные негде взять. retry-policy=QUERY и NONE exchange manager не требуют.

# etc/config.properties
retry-policy=TASK
# плюс обязательно настроенный exchange-manager.properties — см. следующий урок

retry-policy=TASK рекомендуется для больших batch-запросов — долгих ETL, перестроек таблиц, тяжёлых отчётов. Здесь его преимущество максимально: четырёхчасовой запрос при сбое воркера переисполнит лишь несколько задач, а не все 4 часа.

Цена за тонкость — повышенная латентность для коротких запросов. Материализация промежутка через exchange manager стоит I/O всегда, даже когда никакой сбой не случился. Для долгого batch это незаметная добавка; для запроса на полсекунды спулинг промежутка может сам по себе превысить полезное время. Поэтому TASK — для batch, а не для интерактива.

QUERY ретраит весь запрос, TASK — отдельную задачу
retry-policy=QUERYСбой воркера -> весь запрос повторяется с нуля. Не требует exchange manager. Дёшево для коротких запросов, разорительно для долгих
более тонкая гранулярность
retry-policy=TASKСбой воркера -> переисполняются только задачи сбойного воркера на живых воркерах, прогресс запроса сохранён. ТРЕБУЕТ exchange manager

Сводная таблица: три политики

СвойствоNONEQUERYTASK
Что ретраит при сбоеничего, запрос падаетвесь запрос с нулятолько упавшие задачи
Требует exchange managerнетнетда
Накладные расходынулевыебуфер дедупликации (32MB)спулинг промежутка всегда
Цена сбояпотеря всего запросаповтор всего запросапереисполнение нескольких задач
Оптимален дляинтерактив, дашбордыкластеры с множеством мелких запросовбольшие долгие batch-запросы
Латентность коротких запросовминимальнаяминимальнаяповышенная

Параметры ретраев

Сколько раз и с какими паузами повторять — задаётся отдельными свойствами. Дефолтные значения:

СвойствоДефолтСмысл
query-retry-attempts4Сколько раз повторять весь запрос (для QUERY)
task-retry-attempts-per-task4Сколько раз повторять одну задачу (для TASK)
retry-initial-delay10sПауза перед первым ретраем
retry-max-delay1mМаксимальная пауза между ретраями
retry-delay-scale-factor2.0Множитель роста паузы (экспоненциальный backoff)

Паузы растут по схеме экспоненциального backoff: первая пауза retry-initial-delay (10s), каждая следующая умножается на retry-delay-scale-factor (2.0) — 10s, 20s, 40s, — пока не упрётся в retry-max-delay (1m). Backoff не даёт ретраям молотить кластер вплотную: если сбой вызван временной перегрузкой, пауза даёт системе шанс прийти в себя, прежде чем повторить.

Лимит попыток (query-retry-attempts / task-retry-attempts-per-task) защищает от бесконечного цикла. Если запрос или задача упали 4 раза подряд, сбой почти наверняка не случайный — продолжать ретраить бессмысленно, запрос завершается ошибкой.

NOTE

Точные имена и дефолты FTE-свойств между релизами Trino меняются — список в admin/fault-tolerant-execution для версии 481 не обязан совпадать со старыми статьями. Перед настройкой продакшен-кластера сверяй имена и значения со страницей FTE той версии, что разворачиваешь.


Как выбрать политику

Алгоритм выбора простой и опирается на профиль нагрузки кластера:

  1. Кластер интерактивный — дашборды, ad-hoc, короткие запросы? -> NONE. Накладные расходы FTE не окупятся, перезапуск редкого упавшего запроса дёшев.
  2. Много мелких и средних запросов, нужна отказоустойчивость без сложной инфраструктуры? -> QUERY. Прозрачный ретрай, не требует exchange manager, повтор короткого запроса дёшев.
  3. Большие долгие batch-запросы — ETL, перестройки, тяжёлые отчёты? -> TASK. Переисполнение задач вместо всего запроса критично, повышенная латентность коротких запросов не важна — их тут и нет. Готовь exchange manager.

Главный водораздел — длина запросов. Короткие живут с NONE или QUERY; длинные batch требуют TASK. А поскольку TASK добавляет латентность коротким запросам, смешивать интерактив и batch на одном кластере с TASK — плохая идея; об этом — в уроке про эксплуатацию.


Попробуй сам

Сравни поведение NONE и QUERY на тестовом Trino.

  1. Подними кластер с координатором и 2-3 воркерами. Оставь дефолтный retry-policy=NONE. Запусти минутный join по tpch.sf100, во время исполнения убей один воркер (docker kill) — запрос упадёт с ошибкой.
  2. Теперь задай в etc/config.properties координатора retry-policy=QUERY, перезапусти координатор. Повтори: запусти тот же запрос, убей воркер во время исполнения. Сравни поведение — запрос должен пережить сбой и завершиться успешно (если воркеров осталось достаточно).
  3. Подумай: почему retry-policy=TASK ты не можешь включить тем же одним свойством, в отличие от QUERY? Чего ему не хватает и какой урок это закрывает?

Цель — увидеть разницу между «запрос упал» (NONE) и «запрос пережил сбой прозрачно для клиента» (QUERY), и понять, что TASK требует дополнительной инфраструктуры.


Проверка знанийKnowledge check
Почему retry-policy=TASK требует настроенного exchange manager, а retry-policy=QUERY — нет? И почему QUERY дёшев для коротких запросов, но плох для долгих?
ОтветAnswer
retry-policy=TASK переисполняет только упавшую задачу — ту единицу работы, что шла на сбойном воркере, — назначая её на другой, живой воркер. Чтобы переисполнить задачу на новом воркере, ей нужны входные данные: промежуточные результаты предыдущих стадий. А значит, эти промежуточные данные обмена должны быть материализованы в надёжном хранилище — иначе переисполнять задачу попросту неоткуда, её вход негде взять. Спулингом промежутка занимается exchange manager, поэтому TASK без него работать не может. retry-policy=QUERY устроен иначе: при сбое он повторяет весь запрос целиком, с нуля, а для повтора с нуля никакой сохранённый промежуток не нужен — нужны лишь исходные данные источника, которые и так на месте. Координатору хватает небольшого буфера дедупликации (exchange.deduplication-buffer-size, по умолчанию 32MB), чтобы не отдать клиенту частичный результат до завершения. Поэтому QUERY включается одним свойством, а TASK требует ещё и exchange manager. QUERY дёшев для коротких запросов и плох для долгих именно из-за гранулярности повтора с нуля: повтор 5-секундного запроса стоит ещё 5 секунд — мелочь; но запрос на 3 часа, упавший на 2:59, QUERY начнёт целиком заново, потеряв почти 3 часа работы кластера. Долгим batch-запросам нужен TASK, который переисполнит лишь несколько задач, а не весь запрос.

Проверьте понимание

Результат: 0 из 0
Концептуальный
Вопрос 1 из 4. Что делает retry-policy=QUERY при инфраструктурном сбое во время исполнения запроса?

Закончили урок?

Отметьте его как пройденный, чтобы отслеживать свой прогресс

Войдите чтобы оценить урок

Прогресс модуля
0 из 5