Learning Platform
Глоссарий Troubleshooting
Урок 03.06 · 22 мин
Средний
statelessconcurrencyfault-tolerancearchitecture

Stateless-дизайн: конкурентность и отсутствие fault tolerance по умолчанию

Этот урок завершает модуль про MPP-архитектуру, и он связывает воедино всё, что мы разобрали: shared-nothing, координатор, воркеры, discovery, протокол. Все эти решения объединяет один принцип — stateless-дизайн. Воркеры Trino практически не хранят состояния, и это не мелкая деталь, а фундаментальный выбор, который определяет два важнейших свойства Trino: его выдающуюся конкурентность и отсутствие fault tolerance по умолчанию.

Эти два свойства — две стороны одной медали. Понять их связь критически важно. Без этого fault tolerance кажется случайным недостатком («почему такой мощный движок просто падает при сбое узла?»), а на самом деле это осознанная плата за конкретное преимущество. Этот урок объясняет компромисс — и почему он для Trino правильный.


Что значит stateless

Начнём с термина. Система или компонент stateless (без состояния), если он не хранит постоянного состояния между операциями. Каждая операция самодостаточна; компонент не накапливает в себе ничего, что нужно было бы беречь и переносить.

Применительно к воркеру Trino это значит вот что. Воркер не владеет данными — данные живут во внешних источниках (вспомните разделение storage и compute из модуля 1). Воркер не хранит на диске ничего постоянного. Всё, что у воркера есть в каждый момент времени, — это промежуточные данные текущих запросов в его оперативной памяти. Запрос завершился — эти данные исчезли. Между запросами воркер «пуст»: он не несёт никакого уникального состояния, которое отличало бы его от другого воркера.

Stateless-воркер: только память текущих запросов
WorkerНе владеет данными, не хранит ничего постоянного на диске
в каждый момент держит только
Промежуточные данные текущих запросов в RAMВременные данные исполняемых прямо сейчас запросов; запрос завершился — данные исчезли
между запросами
Воркер пустНикакого уникального постоянного состояния — воркеры неотличимы и взаимозаменяемы

Сравним с СУБД. PostgreSQL — глубоко stateful система: она владеет данными на диске, хранит индексы, буферный кэш, состояние транзакций, write-ahead log. Узел PostgreSQL уникален — на нём лежат конкретные данные, и потерять его — значит потерять их. Воркер Trino — противоположность: терять в нём, кроме промежуточных данных текущих запросов, нечего.

PostgreSQL: страницы и heap

Сторона выгоды: высокая конкурентность

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

Воркеры взаимозаменяемы. Раз воркеры не несут уникального состояния, они неотличимы друг от друга. Любую задачу можно отдать любому воркеру. Координатору не нужно искать «тот самый» воркер с нужными данными — нужных данных ни у кого нет, данные приходят из источника. Это даёт планировщику полную свободу раскладывать работу множества запросов по всему пулу воркеров максимально равномерно.

Работа дробится на мелкие части. MPP-модель режет каждый запрос на множество мелких задач. Когда в кластер приходят сотни запросов одновременно, все они превращаются в общее море мелких задач, и кластер раскладывает это море по воркерам и их ядрам. Запросы не ждут друг друга целиком — они перемешиваются на уровне мелких задач.

Узлы легко добавлять и убирать. Раз воркер stateless, ввод и вывод воркера — мгновенная операция (это мы видели в уроке про discovery). Под рост числа пользователей кластер просто расширяют воркерами, под спад — сужают.

Stateless даёт высокую конкурентность
Сотни запросовМножество пользователей шлют запросы в кластер одновременно
MPP дробит
Море мелких задачКаждый запрос разрезан на множество мелких задач — они перемешиваются
раскладываются по взаимозаменяемым воркерам
WorkerБерёт любые задачи любых запросов — воркеры неотличимы
WorkerБерёт любые задачи любых запросов — воркеры неотличимы
WorkerБерёт любые задачи любых запросов — воркеры неотличимы

Именно поэтому Trino так хорош для интерактивной аналитики из модуля 1: десятки аналитиков и множество BI-дашбордов могут долбить кластер запросами одновременно, и он это выдерживает. Это прямая заслуга stateless-дизайна.

Сторона платы: нет fault tolerance по умолчанию

Теперь обратная сторона той же медали. У того же самого stateless-дизайна есть цена: по умолчанию у Trino нет fault tolerance — отказоустойчивости.

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

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

Сбой воркера роняет весь запрос
Worker 1Исполняет свою часть запроса, промежуточные данные в его памяти
Worker 2 упалМашина выключилась — промежуточные данные в его памяти потеряны безвозвратно, копии нет
Worker 3Исполняет свою часть запроса, промежуточные данные в его памяти
часть работы исчезла, восстановить неоткуда
Весь запрос завершается с ошибкойПо умолчанию координатор роняет запрос целиком — один упавший воркер уронил всё

Сравните со Spark из модуля 1. Spark в классической пакетной модели материализует промежуточные результаты — сохраняет их, — и потому может повторить упавший кусок работы, не начиная всё заново. Это его встроенная отказоустойчивость. Но за неё Spark платит накладными расходами на материализацию. Trino делает противоположный выбор: не материализует, гонит данные потоком через память ради скорости и конкурентности — и потому по умолчанию не может пережить сбой узла.

WARNING

Это самое частое разочарование новичков: «Trino — мощный распределённый движок, почему он просто падает, если один воркер умер?». Ответ: это не баг и не недоработка, а прямое следствие stateless-дизайна, который и даёт Trino его скорость и конкурентность. Отказоустойчивость и отсутствие материализации — взаимоисключающие по умолчанию; Trino по умолчанию выбрал второе.

Почему компромисс для Trino правильный

Может показаться, что отсутствие fault tolerance — это слабость. Но для основного назначения Trino выбор правильный, и вот рассуждение.

Trino создан в первую очередь для интерактивных запросов — коротких, на секунды. Сбой воркера — событие сравнительно редкое. Если короткий интерактивный запрос изредка падает из-за сбоя узла, цена невелика: его просто перезапускают, и через секунды есть результат. Платить же за страховку от этого редкого события постоянными накладными расходами на материализацию каждого промежуточного результата — невыгодно: это замедлило бы вообще все запросы. Для интерактивной нагрузки «быстро и иногда перезапустить» лучше, чем «всегда чуть медленнее, но никогда не перезапускать».

Картина меняется для длинных запросов. Если запрос идёт не секунды, а часы, вероятность словить сбой узла за время исполнения становится заметной, а потерять часы работы — уже дорого. Для таких случаев Trino предлагает fault-tolerant execution (FTE) — отдельный, опциональный режим. В нём Trino начинает спулить промежуточные данные обмена (сохранять их во внешнее хранилище), и тогда при сбое воркера потерянную часть можно переиграть, не роняя весь запрос. FTE — это сознательное переключение компромисса: вы доплачиваете накладными расходами и получаете отказоустойчивость. По умолчанию FTE выключен; включают его осознанно под длинную batch-нагрузку. Этому режиму посвящён отдельный модуль курса.

АспектTrino по умолчаниюTrino с FTE
Промежуточные данныеТолько в памяти воркеровСпулятся во внешнее хранилище
Сбой воркера в запросеВесь запрос падаетПотерянную часть переигрывают
Накладные расходыМинимальныеВыше (спулинг обмена)
Для чего подходитИнтерактивные короткие запросыДлинные batch-запросы
NOTE

Запомните формулу этого модуля. Stateless-дизайн -> воркеры взаимозаменяемы и работа дробится на мелкие части -> высокая конкурентность (сотни запросов). Та же stateless-природа -> промежуточные данные только в памяти -> сбой воркера роняет запрос -> нет fault tolerance по умолчанию. Одно решение, два следствия. FTE — опциональный способ доплатить за отказоустойчивость, когда она нужна.

Попробуй сам

Если у вас поднят многоузловой кластер из заданий к прошлым урокам, проведите наглядный эксперимент. Запустите достаточно тяжёлый запрос к tpch.sf100 (большой масштаб, чтобы запрос шёл хотя бы несколько секунд) и, пока он исполняется, в другом терминале остановите один из контейнеров-воркеров командой docker stop. Посмотрите, что произойдёт с запросом в CLI: он должен завершиться с ошибкой. Это и есть отсутствие fault tolerance по умолчанию, увиденное вживую.

Затем подумайте над двумя сценариями и запишите ответ для каждого: (1) кластер обслуживает 200 аналитиков с короткими дашбордными запросами; (2) кластер по ночам гоняет один тяжёлый четырёхчасовой batch-запрос. Для какого из сценариев поведение по умолчанию приемлемо, а для какого стоит включить fault-tolerant execution, и почему? Обоснуйте через компромисс «накладные расходы против цены потери работы».


Проверка знанийKnowledge check
Что такое stateless-дизайн воркеров Trino и почему он одновременно даёт высокую конкурентность и лишает Trino fault tolerance по умолчанию?
ОтветAnswer
Stateless-дизайн означает, что воркер не хранит постоянного состояния между операциями: воркер не владеет данными (они во внешних источниках), не хранит ничего постоянного на диске, и всё, что у него есть в каждый момент, — это промежуточные данные текущих запросов в оперативной памяти; между запросами воркер пуст. Это одно решение даёт два противоположных следствия. Высокая конкурентность: раз воркеры не несут уникального состояния, они взаимозаменяемы — любую задачу можно отдать любому воркеру, и планировщик свободно раскладывает работу множества запросов по всему пулу; MPP дробит каждый запрос на мелкие задачи, и сотни одновременных запросов превращаются в общее море мелких задач, равномерно разложенное по воркерам; узлы легко добавлять и убирать. Поэтому кластер тянет сотни конкурентных пользователей. Отсутствие fault tolerance по умолчанию: та же stateless-природа означает, что промежуточные данные запроса лежат только в памяти воркеров и нигде не продублированы. Если воркер падает посреди запроса, его промежуточные данные теряются безвозвратно, восстановить их неоткуда, и координатор по умолчанию роняет весь запрос целиком с ошибкой. Это не баг, а осознанная плата: отказоустойчивость требовала бы материализации промежуточных результатов (как в Spark), а это постоянные накладные расходы, замедляющие все запросы. Для основной интерактивной нагрузки Trino выбор правильный — короткий запрос дешевле изредка перезапустить. Для длинных batch-запросов есть опциональный режим fault-tolerant execution (FTE), который спулит промежуточные данные во внешнее хранилище и позволяет пережить сбой узла ценой накладных расходов.

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

Результат: 0 из 0
Концептуальный
Вопрос 1 из 4. Что означает stateless-дизайн воркеров Trino?

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

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

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

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