Все хотят обновлений в реальном времени, пока не осознают, что «быстро» и «правильно» — это не одно и то же. В распределенной системе события могут перемещаться со скоростью света и все равно приходить в неверном порядке. WebSockets обрываются и переподключаются. Брокеры сообщений повторно доставляют пакеты. Фоновые воркеры борются с отменой по таймауту. Результат? Клиент может увидеть событие 42, затем событие 40, а затем снимок состояния, утверждающий, что система уже находится на событии 45. Если вы строите долгоживущие рабочие процессы агентов, этот хаос — не исключение, а норма. Исправьте порядок ваших событий, прежде чем беспокоиться о сокращении миллисекунд задержки.

Хаотичная реальность «реального времени»

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

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

Порядковые номера — это настоящие часы

Решение заключается в использовании строгих монотонных порядковых номеров, генерируемых продюсером. Каждая операция, изменяющая состояние, получает номер, который увеличивается ровно на единицу, без пропусков и откатов. Этот номер должен сохраняться в той же транзакции, что и само событие. Если строка в базе данных обновляется, а фиксация (commit) последовательности не удается, вы откатываете и то, и другое. Это делает логическую временную шкалу атомарной по отношению к изменению состояния.

ID событий по-прежнему полезны, но они решают другую проблему. ID события идентифицирует конкретную полезную нагрузку, чтобы вы могли дедуплицировать ее, если брокер доставит одно и то же сообщение дважды. Порядковый номер, с другой стороны, говорит вам, где эта нагрузка находится в причинно-следственной цепочке. Он выявляет пропуски. Он выявляет порядок. Временная метка (timestamp) не делает ни того, ни другого. Часы спешат или отстают, NTP может откатываться назад, а виртуальные машины — ставить процессы на паузу. Используйте временные метки только для отображения, например, «Начато 3 минуты назад», и никогда не используйте их в качестве ключа сортировки для бизнес-логики.

Как клиенту обрабатывать поток

Как только продюсер гарантирует монотонную последовательность, консьюмер получает простые и жесткие правила. Если входящий порядковый номер меньше или равен последнему примененному номеру, отбросьте его. Это либо дубликат, либо устаревшее сообщение, пришедшее с опозданием. Если порядковый номер ровно на единицу больше последнего примененного, примените его немедленно. Это «счастливый путь». Если порядковый номер перескакивает вперед — например, вы ожидали 12, но получили 15 — значит, что-то пропущено. Забуферизируйте новое событие и запросите у сервера повтор (replay), начиная со следующего ожидаемого номера. Не гадайте. Не перескакивайте вперед в надежде, что пропуск не имеет значения.

Терминальные состояния должны рассматриваться как необратимые. Как только задача помечена как завершенная, проваленная или отмененная, клиент должен отклонять любые последующие изменения состояния для этой операции. Это звучит очевидно, пока вы не столкнетесь с