Основы
Когда одного события недостаточно
Почему некоторые результаты зависят от предыдущих событий и что обработчик хранит между ними.
Изучать с ИИ-ассистентом 🤖
Скопируйте промпт и вставьте его в чат с ИИ-ассистентом.
Ты — преподаватель курса «Потоковая обработка».Тема: «Когда одного события недостаточно»О чём: Почему некоторые результаты зависят от предыдущих событий и что обработчик хранит между ними.Текст статьи в 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:00 | order_placed | Нет |
| 10:02 | picking_started | Нет |
| 10:04 | replacement_proposed | Да |
| 10:06 | replacement_accepted | Нет |
| 10:09 | order_packed | Нет |
| 10:12 | courier_collected | Нет |
| 10:25 | order_delivered | Нет |
Когда ответ меняется, обработчик отправляет списку обновление: в 10:04 добавить
ord-1042, в 10:06 убрать. Когда событие оставляет ответ прежним, как упаковка
в 10:09, в списке менять нечего. Чтобы различить эти случаи, обработчик
сравнивает новый ответ с сохранённым.
По своей природе этот результат отличается от ленты стойки выдачи. Лента сообщает, что произошло. Список ожидающих показывает текущее состояние (current state): какие заказы ждут ответа прямо сейчас. Обработчик держит у себя свою копию этого ответа, чтобы вычислить следующее изменение. Эта память может быть переменной в работающей программе или строкой в базе данных.
Сколько помнить
Заголовок раздела «Сколько помнить»Обработчик мог бы хранить все события заказа, но этому списку нужны только «да» или «нет». Другим вопросам нужно другое состояние:
| Вопрос | Что хранит обработчик |
|---|---|
| Ждёт ли заказ ответа? | «Да» или «нет» |
| Что предложил сборщик? | Предложенный товар, пока не придёт ответ |
| Какие замены были в заказе? | Сами события о заменах |
Если хранить больше, чем нужно для вопроса, понадобится больше места, а обработка каждого события станет дороже. Если хранить меньше, на некоторые вопросы не ответить: по одному «да» или «нет» обработчик не сможет сказать сотрудникам, что предлагали зелёные яблоки.
«Да» или «нет» хватает ещё и потому, что так устроена работа нашего магазина: сборщик предлагает одну замену за раз и ждёт ответа. Если бы он мог предложить две сразу, например яблоки и хлеб, ваш ответ про яблоки поменял бы значение на «нет», хотя хлеб всё ещё ждёт ответа. Тогда обработчику пришлось бы помнить каждое открытое предложение.
До сих пор мы следили за одним заказом. Но магазин собирает много заказов
одновременно, и их события приходят вперемешку. Пока вы решаете насчёт яблок,
сборщик другого заказа, ord-1337, в 10:05 предлагает те же зелёные яблоки. Это
событие приходит между предложением по вашему заказу и вашим согласием. Ваше
согласие должно убрать из списка ваш заказ и оставить в нём ord-1337. Как
обработчику не смешать память о разных заказах?
Что запомнить
Заголовок раздела «Что запомнить»- Некоторые результаты зависят от предыдущих событий. Правило, которое видит только текущее событие и ничего не помнит между событиями, их не построит.
- Состояние — это то, что обработчик помнит между событиями. Обработчик, который хранит состояние, называется обработчиком с состоянием.
- Список ожидающих показывает текущее состояние: какие заказы ждут ответа прямо сейчас.
- Храним то состояние, которое нужно для вопроса: здесь «да» или «нет», для других вопросов больше.
Источники
Заголовок раздела «Источники»© 2026 Grig Alex · CC BY-SA 4.0
