Software Architect · Модуль 10
Как только система выходит за пределы одного процесса, появляются задержки, повторы, частичные отказы и неоднозначная правда.
Partial failure · timeout · retry · consistency · CAP
В распределённой системе отказ может быть частичным: один компонент уверен, что операция прошла, другой этого ещё не знает.
Сеть не является функцией
Отправить письмо не значит, что адресат его получил, прочитал и понял. С сетью так же.
Вызов соседнего сервиса может завершиться success, timeout, network error или ответом после того, как клиент уже сдался. Сервер мог выполнить операцию, но клиент не получил ответ. Клиент мог сделать retry, а сервер получил дубль.
Поэтому distributed design требует timeouts, retries с backoff, idempotency, circuit breakers, correlation ids и понятных границ ответственности. Без этого система иногда работает только потому, что нагрузка мала.
Consistency стоит денег
Синхронизировать часы во всех комнатах можно, но чем больше здание, тем дороже идеальная точность.
Strong consistency удобна для пользователя и разработчика, но ограничивает availability и latency. Eventual consistency лучше масштабируется и переживает отказы, но требует компенсаций, статусов ожидания и UX, который честно показывает промежуточное состояние.
Архитектор не должен бросаться словами CAP theorem как лозунгом. Важнее конкретный вопрос: где бизнес требует строгой консистентности, а где допустима задержка распространения?
Сложность распределённой системы проявляется на границах: деньги, inventory, уведомления, отчётность.
Пример: order и email как разные гарантии
Чек важнее рекламного письма. Потеря чека — инцидент, потеря письма — неприятность.
Создание заказа и списание денег должны быть строго контролируемыми. Email-подтверждение может идти через outbox и retry worker. Если email задержался, заказ всё равно валиден.
Такое разделение гарантий помогает не тащить distributed transaction туда, где достаточно eventual consistency.
Антипример: синхронная цепочка из семи сервисов
Если поездка на работу зависит от семи лифтов подряд, один застрявший лифт ломает весь день.
Frontend вызывает API, API вызывает checkout, checkout вызывает pricing, pricing вызывает catalog, потом identity, payment, notification. У каждого timeout 30 секунд. При проблеме пользователь ждёт, а инженеры ищут, где именно сломалось.
Синхронные цепочки умножают latency и вероятность отказа. Их нужно сокращать, кэшировать, переводить в async или проектировать с явными degradation paths.
- Что произойдёт при timeout после успешного выполнения? - Какие операции должны быть идемпотентными? - Где нужна strong consistency? - Какой partial failure пользователь увидит и как его объяснит UI?