Перейти к содержимому

Основы

Когда одного события недостаточно

Почему некоторые результаты зависят от предыдущих событий и что обработчик хранит между ними.

Изучать с ИИ-ассистентом 🤖

Скопируйте промпт и вставьте его в чат с ИИ-ассистентом.

Ты — преподаватель курса «Потоковая обработка».
Тема: «Когда одного события недостаточно»
О чём: Почему некоторые результаты зависят от предыдущих событий и что обработчик хранит между ними.
Текст статьи в Markdown: https://stream-processing-systems.github.io/ru/course/fundamentals/when-one-event-is-not-enough.md
Опирайся на эту статью. Сначала спроси, как мне удобнее: кратко, пошагово или глубоко с вопросами на понимание. Объясняй на примере из статьи и в её терминах. Если добавляешь сведения, которых в статье нет, явно это отмечай.

В нашем сервисе доставки продуктов магазин ведёт список заказов, которые ждут ответа покупателя. Когда сборщик предлагает замену, заказ попадает в этот список, и сотрудники магазина видят, что сборщик ждёт. Когда покупатель отвечает, заказ из списка уходит. Для вашего заказа ord-1042 сборщик в 10:04 предложил зелёные яблоки вместо красных, а в 10:06 вы согласились.

Ленту стойки выдачи мы строили по одному событию (event) за раз. Со списком иначе. Ваше согласие в 10:06 должно убрать заказ из списка, но только потому, что предложение в 10:04 его туда добавило. Как обработчику (processor) построить результат, который зависит от уже увиденных событий?

Обработчик, который у нас уже есть, состоит из фильтра (filter) и преобразования (transformation): он отбирает часть событий и меняет их форму. Допустим, мы используем его здесь и оставляем все события replacement_proposed. Получится лента вопросов, которые задали сборщики. Предложение, сделанное в 10:04, останется в ней и после вашего ответа, поэтому по такой ленте не понять, кто ещё ждёт. Если оставить события replacement_accepted, получится лента ответов, и по ней тоже не понять, кто ждёт.

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

Значит, обработчик должен что-то сохранять от одного события до следующего. Такие сведения называются состоянием (state): это то, что обработчик помнит о предыдущих событиях и использует, когда приходит следующее. Обработчик, который хранит состояние, называется обработчиком с состоянием (stateful). Правило обработки событий об упаковке ничего не хранило между событиями, то есть было без состояния (stateless)1.

Для вашего заказа обработчик помнит одно: ждёт ли заказ ответа? Предложение замены меняет ответ на «да», ответ покупателя — на «нет», а остальные события оставляют его прежним.

Помнить между событиями Правило читает сохранённый ответ заказа, записывает новый и отправляет в список ожидающих добавления и удаления. ОБРАБОТЧИК СОБЫТИЯ ЗАКАЗА ЧТЕНИЕ ЗАПИСЬ ДОБАВИТЬ / УБРАТЬ Источник система магазина Состояние ждёт: да Правило обновляет ответ Приёмник список ожидающих

Вот как меняется сохранённый ответ по мере поступления событий ord-1042:

ВремяСобытиеЖдёт ответа?
10:00order_placedНет
10:02picking_startedНет
10:04replacement_proposedДа
10:06replacement_acceptedНет
10:09order_packedНет
10:12courier_collectedНет
10:25order_deliveredНет

Когда ответ меняется, обработчик отправляет списку обновление: в 10:04 добавить ord-1042, в 10:06 убрать. Когда событие оставляет ответ прежним, как упаковка в 10:09, в списке менять нечего. Чтобы различить эти случаи, обработчик сравнивает новый ответ с сохранённым.

По своей природе этот результат отличается от ленты стойки выдачи. Лента сообщает, что произошло. Список ожидающих показывает текущее состояние (current state): какие заказы ждут ответа прямо сейчас. Обработчик держит у себя свою копию этого ответа, чтобы вычислить следующее изменение. Эта память может быть переменной в работающей программе или строкой в базе данных.

Обработчик мог бы хранить все события заказа, но этому списку нужны только «да» или «нет». Другим вопросам нужно другое состояние:

ВопросЧто хранит обработчик
Ждёт ли заказ ответа?«Да» или «нет»
Что предложил сборщик?Предложенный товар, пока не придёт ответ
Какие замены были в заказе?Сами события о заменах

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

«Да» или «нет» хватает ещё и потому, что так устроена работа нашего магазина: сборщик предлагает одну замену за раз и ждёт ответа. Если бы он мог предложить две сразу, например яблоки и хлеб, ваш ответ про яблоки поменял бы значение на «нет», хотя хлеб всё ещё ждёт ответа. Тогда обработчику пришлось бы помнить каждое открытое предложение.

До сих пор мы следили за одним заказом. Но магазин собирает много заказов одновременно, и их события приходят вперемешку. Пока вы решаете насчёт яблок, сборщик другого заказа, ord-1337, в 10:05 предлагает те же зелёные яблоки. Это событие приходит между предложением по вашему заказу и вашим согласием. Ваше согласие должно убрать из списка ваш заказ и оставить в нём ord-1337. Как обработчику не смешать память о разных заказах?

  • Некоторые результаты зависят от предыдущих событий. Правило, которое видит только текущее событие и ничего не помнит между событиями, их не построит.
  • Состояние — это то, что обработчик помнит между событиями. Обработчик, который хранит состояние, называется обработчиком с состоянием.
  • Список ожидающих показывает текущее состояние: какие заказы ждут ответа прямо сейчас.
  • Храним то состояние, которое нужно для вопроса: здесь «да» или «нет», для других вопросов больше.
  1. Apache Flink 2.3: What is State?.

© 2026 Grig Alex · CC BY-SA 4.0