Потому что всё, что проходит по сети - может потеряться. Сеть ненадежная штука, она отваливается: происходят сбои, задержки, пакеты могут не доходить или идти долго
Вот типичная картина: клиент отправил запрос, на сервер, по HTTP. Казалось бы, обычная штука
1️⃣ Сценарий первый - запрос вообще не дошёл. Клиент повис, подождал, устал и отвалился
2️⃣ Сценарий второй - запрос дошёл, сервер его даже обработал, но ответ не вернулся. Потому что где-то между твоим сервером и клиентом случилось
3️⃣ Сценарий третий - запрос обработан, ответ отправлен, но клиент уже ушёл, потому что превышен timeout ожидания
Что делает клиент? Он отправляет запрос снова. И это логично. Если у нас действительно надежная система, то нам нельзя терять сообщения и нужно их переотправить. Вдруг ничего не произошло и сервер не обработал? А вот тут начинается боль 😐
Собственно, здесь и появляется идемпотентность. Клиент присылает вместе с запросом уникальный requestId, а сервер запоминает, что такой запрос уже обрабатывался. Если он прилетает повторно, мы не списываем деньги ещё раз, а возвращаем результат первой попытки
❗️ Но и просто навесить retry на каждый вызов недостаточно, здесь уже рассказывал почему, если сеть моргнула на секунду - повтор поможет. Если соседний сервис лежит уже десять минут, тысяча клиентов с тремя ретраями превратит тысячу запросов в четыре тысячи и поможет сервису умереть чуть увереннее 💀
Поэтому нормальная работа с сетью - это не один волшебный паттерн, а несколько уровней защиты:
📌 timeout, чтобы не ждать ответ бесконечно
📌 ограниченный retry с backoff и jitter
📌 идемпотентность, чтобы повтор не создавал второй эффект (писал тут)
📌 Circuit Breaker, чтобы перестать ходить в сервис, которому уже плохо
📌 логи, метрики и tracing, чтобы узнать о проблеме раньше звонка клиента в пятницу вечером
А если связь пропала между целыми частями системы, можно временно перестать принимать операции и сохранить строгую консистентность или же продолжить работать, разрешить данным ненадолго разъехаться и синхронизировать их позже
Это и есть та часть CAP-теоремы, которую важно понимать на практике: когда между узлами случился network partition, нельзя одновременно гарантировать и всегда актуальные данные, и успешный ответ на каждый запрос. Чем-то придётся пожертвовать - всегда 🔫
Так что паттерны из прошлых постов придумали не ради красивых схем и вопросов на собеседовании. Они появились из одной довольно неприятной особенности распределённых систем: они распределенные
Мораль такова
Сеть - это не ласковый и приветливый мир, здесьубивают🔫падают запросы
Дизайн, в котором пакеты всегда доходят, красив только в теории, в реальности такого не случается. При работе с сетью надо быть предельно аккуратным и продумывать проблемы, которые могут возникнуть. Ваша система должна уметь решать эти проблемы, чтобы быть надежной
#хардовая_польза
П.с. Первую и вторую части про паттерны микросервисов оставлю для чтения здесь и здесь 👀