Эксплуатация FTE: выделенный кластер, шифрование обмена, ограничения
Мы разобрали механику FTE: retry policies, exchange manager, task sizing, adaptive planning. Остался последний и самый практический слой — эксплуатация. Как FTE живёт в продакшене: на каком кластере его включать, как защитить спулённый обмен и где у режима границы, за которыми он не помогает.
Этот урок закрывает модуль практическими ответами: почему retry-policy=TASK хочет отдельный кластер, зачем спулённый обмен шифруют и что FTE не умеет в принципе.
Почему TASK хочет выделенный кластер
Главная эксплуатационная рекомендация: для retry-policy=TASK разворачивай выделенный кластер — отдельный от интерактивной нагрузки. Причина — в накладных расходах FTE, которые мы видели по всему модулю.
retry-policy=TASK материализует промежуток через exchange manager всегда, при каждом запросе, сбоя или нет. Спулинг промежутка стоит I/O и латентности: стадия-консьюмер ждёт, пока продьюсер полностью спулится, вместо старта на первых строках. Для долгого batch-запроса эта добавка теряется в часах работы. Для интерактивного запроса на полсекунды спулинг промежутка способен сам по себе превысить полезное время — пользователь получит ответ ощутимо медленнее, чем без FTE.
retry-policy задаётся на уровне кластера, в etc/config.properties. Нельзя сказать «этому запросу — TASK, тому — NONE»: вся нагрузка кластера живёт под одной политикой. Значит, кластер с TASK замедлит каждый запрос на нём, включая короткие. Если на нём же висят дашборды — они станут медленными.
Отсюда типовая продакшен-архитектура: интерактивный кластер с retry-policy=NONE (быстрые дашборды и ad-hoc) и отдельный batch-кластер с retry-policy=TASK и настроенным exchange manager (отказоустойчивый ETL). Каждая нагрузка — на кластере с подходящей именно ей политикой.
Маршрутизировать запросы между такими кластерами помогает Trino Gateway (ему посвящён отдельный урок в модуле про безопасность и деплой): он даёт клиентам единый URL и направляет интерактивные запросы на NONE-кластер, а batch — на TASK-кластер по правилам маршрутизации. Так разделение кластеров остаётся прозрачным для пользователей.
Шифрование спулённого обмена
FTE создаёт то, чего в классическом Trino не было, — промежуточные данные запроса, лежащие в хранилище. Это вопрос безопасности. Промежуток — это реальные данные запроса: значения после фильтров, ключи и строки join, частичные агрегаты. Если они оседают в S3/GCS незашифрованными, любой, у кого есть доступ к бакету, может их прочитать.
Поэтому FTE по умолчанию шифрует спулённый обмен. За это отвечает свойство fault-tolerant-execution.exchange-encryption-enabled — и по умолчанию оно true. Шифрование включено из коробки, отключать его в продакшене не следует.
Механика устроена аккуратно: на каждый запрос генерируется отдельный случайный ключ шифрования. Промежуток запроса шифруется этим ключом перед записью в хранилище и расшифровывается при чтении. Ключ живёт ровно столько, сколько запрос. Запрос завершился — ключ больше не нужен. Свой ключ на каждый запрос означает, что компрометация данных одного запроса не открывает данные других.
# etc/config.properties — шифрование спулённого обмена FTE
# Дефолт true; в продакшене оставляем включённым
fault-tolerant-execution.exchange-encryption-enabled=true
Не отключай fault-tolerant-execution.exchange-encryption-enabled в продакшене. Без шифрования промежуточные данные всех запросов оседают в объектном хранилище в открытом виде. Это утечка: фрагменты чувствительных данных — после WHERE-фильтров, в join-ключах — становятся доступны любому с доступом к спул-бакету. Накладные расходы шифрования малы по сравнению с этим риском.
Границы FTE: что режим не умеет
FTE мощный, но не всемогущий. Понимать его границы так же важно, как механику, — иначе на него возлагают надежды, которые он не оправдает.
FTE покрывает только инфраструктурные сбои. Режим ретраит сбои инфраструктуры: упавший воркер, потерянная нода, сбой железа, обрыв сети, вытеснение пода Kubernetes. Только это.
FTE не ретраит ошибки пользователя. Запрос с синтаксической ошибкой SQL, обращение к несуществующей таблице, деление на ноль, нарушение типов — такие запросы FTE не делает «отказоустойчивыми». И это правильно: такой запрос неверен по сути, повторять его бессмысленно — он упадёт с той же ошибкой хоть сто раз. FTE ретраит инфраструктуру, а не логику запроса.
Не все коннекторы поддерживают FTE. FTE-режим совместим не с каждым коннектором. Перед тем как полагаться на FTE с конкретным источником данных, это нужно проверить по документации той версии Trino, что развёрнута, — поддержка коннекторов в FTE расширяется со временем, и список меняется между релизами.
| Класс ситуации | FTE помогает |
|---|---|
| Воркер упал по OOM | Да — инфраструктурный сбой |
| Нода потеряна, сбой железа | Да — инфраструктурный сбой |
| Под Kubernetes вытеснен | Да — инфраструктурный сбой |
| Обрыв сети между нодами | Да — инфраструктурный сбой |
| Синтаксическая ошибка в SQL | Нет — ошибка пользователя |
| Обращение к несуществующей таблице | Нет — ошибка пользователя |
| Деление на ноль, нарушение типов | Нет — ошибка пользователя |
| Коннектор без поддержки FTE | Нет — проверять по документации |
Частое заблуждение: «включили FTE — Trino стал отказоустойчив как Spark, теперь не упадёт никогда». Нет. FTE спасает от сбоев инфраструктуры, но запрос всё равно упадёт от собственной ошибки, от исчерпания лимита попыток ретраев, от неподдерживаемого коннектора. FTE снижает класс рисков, а не отменяет падения как явление.
Эксплуатационный чек-лист FTE
Соберём практику модуля в список — что проверить, разворачивая FTE в продакшене:
- Профиль нагрузки. Кластер batch (долгие ETL, отчёты)? ->
retry-policy=TASK. Кластер с множеством мелких запросов? ->retry-policy=QUERY. Интерактив? ->retry-policy=NONE, FTE не нужен. - Отдельный кластер под TASK. Не вешай
retry-policy=TASKна тот же кластер, что обслуживает дашборды, — он замедлит каждый запрос. Batch — на выделенном кластере. - Exchange manager на всех нодах. Для
TASK—etc/exchange-manager.propertiesидентичен на координаторе и всех воркерах;exchange.base-directoriesуказывает на нодонезависимое хранилище (S3/GCS/Azure/HDFS), не на локальный диск. - Шифрование включено.
fault-tolerant-execution.exchange-encryption-enabledоставьtrue. - Объём спул-хранилища. Рассчитай под пиковый промежуток одновременно идущих FTE-запросов; опционально настрой lifecycle policy бакета как страховку от мусора.
- Совместимость коннекторов. Проверь по документации текущей версии, что коннекторы твоих источников поддерживают FTE.
- Параметры ретраев. При необходимости настрой
query-retry-attempts/task-retry-attempts-per-taskи параметры backoff под свой кластер.
Пройдя этот список, ты получаешь то, ради чего затевался весь модуль: кластер, на котором долгий batch-запрос переживает сбой воркера, не начиная с нуля, — при честном понимании цены и границ режима.
Попробуй сам
Доведи FTE-кластер до продакшен-готовности на тестовом стенде.
- Возьми batch-кластер из прошлых уроков (
retry-policy=TASK, exchange manager на MinIO). Проверь, чтоfault-tolerant-execution.exchange-encryption-enabledне выключен. - Запусти долгий batch-запрос и во время исполнения убей воркер (
docker kill) — убедись, что запрос пережил сбой за счёт переисполнения задач. - Теперь проверь границу режима: запусти запрос с заведомой ошибкой пользователя — например,
SELECT * FROM nonexistent_tableили запрос с делением на ноль. Убедись, что FTE его НЕ спасает — он падает сразу, без ретраев. Прочитай текст ошибки. - Продумай раздельную архитектуру: какой кластер ты дашь дашбордам и какой ETL, какие
retry-policyпропишешь каждому и почемуretry-policyнельзя задать на уровне отдельного запроса.
Цель — закрепить, что FTE спасает от инфраструктурных сбоев, но не от ошибок пользователя, и что retry-policy=TASK — это режим выделенного batch-кластера.