Computation в YTsaurus Flow (C++)
Примечание
На этой странице описаны режимы исполнения Computation на C++. Языконезависимое описание концепции см. в разделе Computation.
Важно
Новую пользовательскую логику на C++ реализуйте только как process function. Не создавайте классы-наследники от базовых классов Computation. Эта страница описывает режимы исполнения, встроенные компьютейшены и low-level API, необходимый для устройства фреймворка и сопровождения legacy-кода.
В этом разделе приведено описание базовых классов Computation, на которых работают встроенные адаптеры process function, и важные детали режимов исполнения.
Во Flow в данный момент реализовано четыре базовых класса Computation:
TTransformComputationTTransformOrderedSourceComputationTSwiftOrderedSourceComputationTSwiftMapComputation
Классы, содержащие Swift в названии, реализуют принцип Swift. Подробнее — в разделе Swift.
Общее
Следующие детали прямого API нужны при сопровождении фреймворка и существующего legacy-кода; они не являются инструкцией по созданию нового пользовательского компьютейшена.
-
Параметры process function объявляйте обычной
TYsonStruct, указывайте её тип вYT_FLOW_DEFINE_PROCESS_FUNCTIONи передавайте значения черезprocessing_function_parameters. -
В пользовательской process function сложную инициализацию выполняйте в
Init(const IRuntimeInitContextPtr&). -
Выберите одну гранулярность обработки:
IProcessFunctionдля сообщений/таймеров/визитов по одному,IBatchProcessFunctionдля всей эпохи илиIKeyedBatchProcessFunctionдля батча одного ключа. -
Для ручной записи в транзакцию transform-режима дополнительно реализуйте
ISyncProcessFunction; в остальных случаях используйте Sink или ExternalState. -
Выходные сообщения и таймеры добавляйте через
IOutputCollector; создавайте и конвертируйте их черезIRuntimeContext. -
Код одной партиции выполняется строго однопоточно. Распараллеливайте обработку увеличением числа партиций.
-
Есть возможность конвертировать входные сообщения в
NYTree::TYsonStruct. Для этого необходимо:- Завести класс-наследник от
TYsonMessage(это специальный наследникNYTree::TYsonStruct). - Зарегистрировать его в глобальном реестре с помощью
YT_FLOW_DEFINE_YSON_MESSAGE. - В функции
mainнеобходимо будет создать объект типаTSimpleSpecBuilderи зарегистрировать в нём соответствующиеstream_id. - При использовании
TSimpleRunnerProgramможно передать данныйTSimpleSpecBuilderсразу в конструкторTSimpleRunnerProgram. - При самостоятельной реализации
mainнеобходимо будет передать вTSimpleSpecBuilderспеки для обогащения информацией о потоках. - Заполнять
spec/streamsв случае использованияTYsonMessageсамостоятельно не нужно — вся информация будет выведена из зарегистрированныхTYsonMessage + stream_idс помощьюTSimpleSpecBuilder. - В process function используйте
context->ConvertToYsonMessage<T>(message)иcontext->ConvertToMessage(ysonMessage)для преобразованийTMessage => TYsonMessageи обратно.
- Завести класс-наследник от
OutputCollector
Объект IOutputCollectorPtr output передаётся в методы process function и предназначен для отправки результатов обработки:
| Метод | Описание |
|---|---|
output->AddMessage(message) |
Добавить выходное сообщение (объект TMessage, полученный через context->MakeOutputMessageBuilder().Finish()) |
output->AddTimer(timer) |
Добавить таймер |
output->SetParents(parentIds) |
Задать parent ID для отслеживания lineage. Возвращает новый IOutputCollectorPtr с привязанным контекстом lineage |
SetParents используйте, когда выходное сообщение логически произведено от конкретного подмножества входных, а не от всего батча. В небатчевых ProcessMessage и ProcessTimer lineage устанавливается автоматически.
TMessage
Структура TMessage, используемая в методах ProcessMessage и AddMessage:
Источник: yt/yt/flow/library/cpp/common/message-inl.h
|
Параметр |
Описание |
|
|
Тип: |
|
|
Тип: |
|
|
Тип: |
|
|
Тип: |
|
|
Тип: |
|
|
Тип: |
|
|
Тип: |
TTransformComputation
Transform-режим предназначен для произвольных преобразований входных данных. Он не работает с Source. Результат обязательно сохраняется в YT, поэтому требований к детерминированности преобразований нет.
Свойства TTransformComputation:
- Может писать в YT «вхолостую», то есть без реальных изменений, перезаписывая существующее содержимое. Ожидается, что такой поток будет создавать незначительную нагрузку.
Для новой пользовательской логики этот режим выбирается адаптером TProcessFunctionComputation. Реализуйте IProcessFunction, IBatchProcessFunction или IKeyedBatchProcessFunction; для sync-фазы дополнительно реализуйте ISyncProcessFunction. Полный пример см. в разделе Process function.
TTimer
Структура TTimer, используемая в методах ProcessTimer и AddTimer:
Источник: yt/yt/flow/library/cpp/common/timer-inl.h
|
Параметр |
Описание |
|
|
Тип: |
|
|
Тип: |
|
|
Тип: |
|
|
Тип: |
|
|
Тип: |
|
|
Тип: |
|
|
Тип: |
ProcessingMode
У TransformComputation есть параметр parameters/processing_mode, позволяющий снижать гарантии процессинга в обмен на снижение нагрузки на YTsaurus.
|
Возможные значения |
Описание |
|
|
Значение по умолчанию. Результат работы |
|
|
В данном режиме отключается дедупликация входных сообщений. В этом режиме |
TPassthroughComputation
Наследник TTransformComputation. Реализует passthrough-компьютейшен — создан в основном для демонстрации возможностей.
Особенности:
- Не имеет переопределяемых методов.
- Не может иметь более одного выхода.
- Все входящие
inputпотоки превращаются вoutputпутём конвертации сообщения с помощьюConvertMessageToNewSchema.
TTransformOrderedSourceComputation
Этот режим обрабатывает сообщения source произвольной пользовательской логикой: парсит, фильтрует или разворачивает одно входное сообщение в несколько выходных. Новую process function запускайте под TProcessFunctionTransformOrderedSourceComputation; он заменяет связку TSwiftPassthroughOrderedSourceComputation → TProcessFunctionComputation, когда единственная задача промежуточного компьютейшена — обработать данные источника.
Результат трансформации материализуется в YTsaurus так же, как у TTransformComputation:
- Выходные сообщения получают уникальные
MessageIdи надёжно сохраняются в YTsaurus до распределения в downstream. После рестарта ещё не доставленные материализованные сообщения дораспределяются с теми жеMessageId, а не вычисляются заново, поэтому требований к детерминированности трансформации нет. - Сообщение можно добавить в
outputс явным флагомdistribute, напримерoutput->AddMessage(std::move(message), /*distribute*/ false). Такое сообщение не публикуется в downstream, но участвует в оценке вотермарка наравне с публикуемыми: генератор вотермарка регистрирует чтение по полному набору выходных сообщений ещё до применения фильтра публикации, поэтому вотермарк можно корректно оценивать по полному потоку, даже когда значимая его часть отфильтровывается. Смещение источника в любом случае продвигается в транзакции эпохи. - Смещение
source, материализованный выход и стейты коммитятся в одной транзакции эпохи, поэтому обработка каждого сообщения источника применяется ровно один раз.
Для новой пользовательской логики реализуйте process function: Init(const IRuntimeInitContextPtr&) для инициализации, ProcessMessage или Process для обработки и, при необходимости, ISyncProcessFunction::Sync для ручной записи в транзакцию эпохи.
Собственный стейт process function хранит в поле TMutableStateKeyClient<T> (см. Работа со стейтами), инициализирует через initContext->InitClient(...) и читает через GetState(message->Key). Перед обработкой адаптер сам загружает стейт для ключей сообщений текущей эпохи. Инстанс компьютейшена всегда привязан к единственному source-ключу, поэтому все сообщения эпохи несут один и тот же ключ и обращаются к одной строке стейта.
Стейт-клиенты, созданные через IRuntimeInitContext, фреймворк синхронизирует в транзакции эпохи атомарно со смещением source, поэтому обычная мутация (например, инкремент счётчика) корректна exactly-once — дополнительная дедупликация по MessageId не нужна.
Спека компьютейшена проверяется при запуске; следующие поля приводят к ошибке валидации:
input-стримы;- таймеры;
- key-visitor-стримы;
- непустой
group_by_schema; external_state_managers;external_state_joiners, у которых не заданjoin_on/key_schema_override(ключ сообщения источника не описываетсяgroup_by_schema, поэтому схема ключа должна быть задана явно).
watermark_strategy поддерживается: watermark_generator оценивает вотермарки источника, watermark_alignment выравнивает чтение источника относительно других потоков (read_delays задерживают чтение, а не публикацию), event_timestamp_assigner назначает event_timestamp выходным сообщениям. Атомарность коммита смещения source, материализованных выходных сообщений и стейта от выравнивания не зависит. skip_if_expression также поддерживается.
skip_if_expression применяется до обработки, но после того, как входной батч учтён в метриках и в подсчёте опоздавших сообщений: отфильтрованное сообщение не попадает ни в стейт, ни в выход. На оценку вотермарка оно тоже не влияет: генератор регистрирует чтение только по выходным сообщениям, поэтому полностью отфильтрованный батч не сдвигает EventWatermark, а на длинной серии таких батчей вотермарк партиции стоит на месте. Маркеры EventWatermark во входных записях источник учитывает при чтении независимо от фильтра, но при use_source_watermark = false (значение по умолчанию) вотермарк источника только ограничивает оценку сверху и вперёд её не двигает; единственным источником вотермарка партиции он становится при use_source_watermark = true. Это полностью совпадает с поведением TSwiftOrderedSourceComputation.
Пользовательскую логику пишите как process function и указывайте в спеке адаптер NYT::NFlow::TProcessFunctionTransformOrderedSourceComputation: он исполняет функцию в этом режиме — с той же материализацией выхода, теми же стейтами и той же валидацией спеки.
Пример — NYT::NFlow::NExample::TLogParserProcessFunction из examples/cpp/log_parser: разбирает строку лога на записи, эмитит YSON-структуру TLogRecordMessage (level, text, worst_level_so_far) и ведёт стейт TWorstSeverityState — бегущий максимум severity по партиции источника. Подробнее, вместе с полным исходным кодом, — в разделе Log Parser.
TProtoTransformOrderedSourceComputation
Для новой пользовательской логики используйте TProtoParsingProcessFunctionBase<TProto> из yt/yt/flow/library/cpp/parsers/proto.h. База читает строковую колонку, заданную параметром processing_function_parameters/data_column (по умолчанию "data"), разбирает её в TProto и вызывает ProcessProto(message, proto, output, context). Ошибку чтения или разбора она передаёт в ProcessUnparsed(message, error, output, context), который по умолчанию перебрасывает ошибку дальше.
Стейт храните в TMutableStateKeyClient<T> и инициализируйте в Init; ключ доступен как message->Key. Для materialized ordered-source режима запускайте функцию под TProcessFunctionTransformOrderedSourceComputation.
TProtoTransformOrderedSourceComputation<TProto> — low-level аналог для сопровождения существующего legacy-кода. Не используйте его как базу нового пользовательского класса.
Пример process function с тем же способом разбора — NYT::NFlow::NExample::TProtoLogParserFunction из examples/cpp/proto_parser. Она наследуется от TProtoParsingProcessFunctionBase<TLogRecordProto> и запускается под TProcessFunctionTransformOrderedSourceComputation: разбирает TLogRecordProto, эмитит TLogRecordMessage (level, text, seen_at_level) и ведёт стейт TLevelCountsState — счётчик записей каждого уровня по партиции источника. Счётчик неидемпотентен к повторной обработке и корректен ровно потому, что стейт коммитится в одной транзакции со смещением source. Подробнее — в разделе Proto Parser.
TSwiftMapComputation
Swift-map режим реализует детерминированный простой Map без материализации результатов в YT. Пользовательскую process function запускайте под TProcessFunctionSwiftMapComputation.
Особенности:
- Не поддерживает
sourcesиsinks. - Поддерживает
timer_streamsиkey_visitor_streamsтолько для работы со стейтом: эмит выходных сообщений из обработки таймера или визита запрещён, поэтому output-стримы не могут зависеть от таймер- и visit-стримов вstreams_dependency. - Должен возвращать один и тот же результат (включая порядок) для каждой входной строки. Если результат меняется при повторных запусках, могут возникать разные отрицательные эффекты. Возможно, что отдельные части системы обработают разные версии выхода, вплоть до дубликатов, если меняется значение полей для последующих
group-by. - Как следствие, у каждого результирующего сообщения должен быть ровно один родитель.
TSwiftPassthroughComputation
Наследник TSwiftMapComputation. Аналогичен TPassthroughComputation: просто превращает input в output путём приведения сообщений к новой схеме.
TSwiftOrderedSourceComputation
Swift ordered-source режим читает данные из внешних источников и требует, чтобы поток данных из каждого инстанса был упорядочен. Пользовательскую process function запускайте под TProcessFunctionSourceComputation.
Особенности:
- Должен быть ровно один
source Sourceдолжен быть наследникомIOrderedSource.- Может использовать
watermark_strategy/event_timestamp_assignerдля назначенияevent_timestampвыходным сообщениям при условии указания колонки. Иначе в качествеevent_timestampвыходного сообщения будет взятevent_timestampсообщения изsource— то есть время создания исходного сообщения. - Использует
watermark_strategy/watermark_generatorдля оценки вотермарков входных источников. - Использует
watermark_strategy/watermark_alignmentдля выравнивания чтения потока относительно других потоков. - Позволяет отфильтровать часть событий: сообщение, добавленное в
outputсdistribute=false, не публикуется, но всё равно учитывается при оценке вотермарка. Это позволяет оценивать вотермарк с использованием полного потока даже в случаях, когда значимая часть потока отфильтровывается. system_timestampназначается в момент регистрации сообщения вoutput.- Надёжно сохраняет часть данных в YT для гарантированного восстановления всей метаинформации. Сами сообщения в YT не сохраняет.
- Может писать в YT «вхолостую», то есть без реальных изменений. Предполагается, что такой поток должен создавать минимальную нагрузку.
TSwiftPassthroughOrderedSourceComputation
Наследник TSwiftOrderedSourceComputation. Аналогичен TPassthroughComputation: преобразует source в output путём приведения сообщений к новой схеме.
FAQ
Как настроить Source и Sink?
Source и Sink конфигурируются в спеке Computation через секции sources и sinks соответственно. Каждый Source/Sink задаётся отдельной подсекцией с указанием типа (например, TQueueSource) и параметров подключения.
Подробнее о доступных коннекторах см. раздел Коннекторы.
Как работает батчинг и партиционирование?
Каждая партиция обрабатывается строго однопоточно. Многопоточность достигается за счёт увеличения числа партиций (partition_count в спеке). IBatchProcessFunction::Process получает все сообщения и таймеры за текущую эпоху, что позволяет оптимизировать обработку.
Process function API намеренно не предоставляет PoolInvoker; распараллеливайте обработку числом партиций.
Как оценить нагрузку на внутренние таблицы?
Нагрузка на внутренние таблицы YTsaurus зависит от типа Computation и количества партиций. Ниже приведена приблизительная оценка:
|
Тип Computation |
Записей в эпоху на партицию |
Комментарий |
|
|
~2-4 |
Запись стейтов + коммит |
|
|
~2-4 |
Материализация выхода + оффсеты + стейты |
|
|
0 |
Не пишет в YT |
|
|
~1-2 |
Метаинформация для восстановления |
Общая нагрузка = записей на партицию * количество партиций * частота эпох. Для пайплайна с 1000 партиций и эпохой в 1 секунду TTransformComputation создаст ~2000-4000 записей/с.
Состояния пайплайна
Возможные состояния пайплайна (тип EPipelineState):
|
Возможные значения |
Описание |
|
|
Дефолтное значение состояния для еще не запущенного пайплайна. |
|
|
Пайплайн остановлен. Выполнен |
|
|
Пайплайн приостановлен. Все джобы остановлены, но промежуточные сообщения в пайплайне могут быть не обработаны. |
|
|
Пайплайн работает. Сообщения обрабатываются. |
|
|
Промежуточное состояние. Пайплайн в процессе остановки (переход в состояние |
|
|
Промежуточное состояние. Пайплайн в процессе приостановки. Все джобы останавливаются. |
|
|
Финальное состояние. Пайплайн завершен. Все источники были конечными ( |