2026 г.

Курс «Распределённые системы». Глава 1. Модель системы

Цели главы. Договориться о языке, на котором будет говорить весь курс: что такое процесс, сообщение и событие; какими бывают отказы и модели времени; почему асинхронная модель с отказами — честная модель интернета. В конце — строгий разбор «заблуждений распределённых вычислений» и первые следствия, которые можно вывести из одной только модели, не написав ни строчки кода: например, что семантика «ровно один раз» для удалённого вызова недостижима в принципе.

1.1. Процессы, каналы, события

Распределённая система в нашей модели — это набор процессов (узлов) p1, …, pn, каждый со своим локальным состоянием и памятью, взаимодействующих только обменом сообщениями по каналам. Общей памяти нет; общих часов нет; всё, что процесс знает о других, он узнал из сообщений. Исполнение системы — последовательность событий трёх видов: отправка сообщения, получение сообщения, внутреннее событие (локальное вычисление). Эта скупая модель покрывает всё: микросервисы, реплики СУБД, узлы Kafka, мобильный клиент с сервером — и потому выводы из неё универсальны.

Важное методологическое замечание. Модель — это контракт с реальностью: алгоритм, доказанный в модели, корректен ровно настолько, насколько реальность соответствует её допущениям. Половина инцидентов в распределённых системах — это реальность, нарушившая допущение, о котором проектировщик не знал, что он его сделал. Поэтому первую главу курса мы тратим на то, чтобы все допущения назвать по имени.

1.2. Модели каналов

Что канал может сделать с сообщением? Перечислим по нарастающей враждебности:

  • Надёжный канал с сохранением порядка (FIFO): всё доставляется, по одному разу, в порядке отправки. Такой канал даёт TCP-соединение — пока оно живо.
  • Надёжный без порядка: доставляется всё, но в произвольном порядке.
  • Канал с потерями (fair-loss): отдельное сообщение может потеряться, но сообщение, отправляемое бесконечно много раз, будет доставлено бесконечно много раз; канал не создаёт сообщения из ничего. Это удобная абстракция IP-сети при длительно сохраняющейся связности.
  • Произвольный канал: потери, дублирование, переупорядочение, задержка на неограниченный срок.

Ловушка, которую надо увидеть в упор: TCP даёт FIFO-надёжность внутри соединения, но соединение рвётся, и после переустановки отправитель не знает, что из «отправленного» было доставлено и обработано. На уровне запросов приложения даже поверх TCP канал — с потерями и дублями (повторы!). Именно поэтому канальная надёжность не отменяет ни идемпотентности, ни всего, что построено на ней (см. статью «Надёжность поверх ненадёжной сети»).

1.3. Виды отказов

Иерархия отказов процесса, от безобидного к худшему:

  • Отказ-остановка (crash-stop): процесс молча останавливается навсегда. Простейшая модель.
  • Отказ-восстановление (crash-recovery): процесс падает и возвращается, сохранив (или потеряв) часть состояния на диске. Модель реального рестарта; появляется различие энергозависимого и стабильного хранилища.
  • Пропуски (omission): процесс жив, но часть сообщений не отправляет или не принимает (переполненные буферы, паузы GC, перегрузка). Коварство в том, что снаружи неотличимо от медленной сети.
  • Византийские отказы: процесс ведёт себя произвольно, в том числе злонамеренно — лжёт, шлёт разным адресатам разное. Актуально при недоверенных участниках (блокчейны, межорганизационные системы); в пределах одного ЦОДа обычно принимают модель crash-recovery, и главы 5–6 живут в ней; византийскому случаю посвящена глава 7.

Ключевое отличие от одиночного компьютера — отказы частичны: в любой момент какое-то подмножество узлов и каналов отказало, а остальные работают, и ни один узел не имеет достоверного глобального знания, кто именно жив. При этом отказы вовсе не обязаны быть независимыми: одна авария питания, ошибка конфигурации или потеря зоны доступности может одновременно затронуть множество компонентов. Проектирование распределённой системы — это проектирование поведения при каждом реалистичном, в том числе коррелированном, наборе отказов.

1.4. Модели времени: синхронная, асинхронная, частично синхронная

Три модели предположений о времени:

  • Синхронная: известны верхние границы задержки сообщений и относительной скорости процессов. Роскошная модель: тайм-аут в ней — совершенный детектор отказа (не ответил за границу — точно мёртв). В интернете границы задержки не существует, поэтому модель применима разве что к специализированным шинам.
  • Асинхронная: никаких границ нет — сообщение может идти сколь угодно долго, процесс может думать сколь угодно медленно. Честная модель худшего случая: пауза сборщика мусора на 40 секунд, ретрансмиссии, перегруженный канал — всё это законные исполнения. Плата за честность — суровые теоремы невозможности (глава 3).
  • Частично синхронная: границы существуют, но неизвестны, либо начинают выполняться после неизвестного момента стабилизации. Модель «интернет обычно приличен, но иногда сходит с ума на неизвестный срок» — именно в ней доказывается живость (liveness) Raft и его родни.

Следствие, которое стоит выучить наизусть: в асинхронной модели тайм-аут не доказывает отказ. Истёкший тайм-аут означает «ответ не пришёл вовремя» — узел мог умереть, задуматься или быть отрезанным; все три случая неотличимы для наблюдателя. Половина алгоритмической изощрённости курса (кворумы, термы, эпохи) — это техника безопасного принятия решений на основе недоказуемых подозрений. Формализация этой идеи — детекторы отказов — появится в главе 3.

1.5. Заблуждения распределённых вычислений — построчно

Список Питера Дойча и коллег (Sun Microsystems, 1990-е) обычно цитируют как фольклор; пройдём по нему как по перечню нарушаемых допущений модели, с указанием, какая часть курса отвечает за последствия.

  1. «Сеть надёжна» — ложно: канал с потерями (1.2). Ответ: повторы, идемпотентность, подтверждения (статья о надёжности; глава 10).
  2. «Задержка равна нулю» — ложно: асинхронная модель (1.4). Ответ: проектирование под round-trip, батчинг, локальность данных (главы 5, 8).
  3. «Полоса бесконечна» — ложно. Ответ: дельта-синхронизация, сжатие, разумный размер сообщений.
  4. «Сеть безопасна» — ложно. Ответ: шифрование и аутентификация каналов — вне рамок курса (см. раздел «Безопасность»), но допущение назвать обязаны.
  5. «Топология неизменна» — ложно: узлы приходят и уходят (1.3). Ответ: обнаружение сервисов, членство в кластере (главы 6, 8).
  6. «Администратор один» — ложно: чужие сервисы, чужие сети, чужие регламенты. Ответ: тайм-ауты и предохранители на каждой границе доверия.
  7. «Стоимость передачи равна нулю» — ложно: и в деньгах (межзоновый трафик облаков тарифицируется), и во времени сериализации.
  8. «Сеть однородна» — ложно: от 10G внутри стойки до GPRS в лифте. Ответ: часть II статьи о надёжности.

1.6. Первые следствия: семантики удалённого вызова

Уже введённой модели достаточно для первого содержательного результата. Рассмотрим удалённый вызов (RPC) по каналу с потерями. Клиент послал запрос и не получил ответ за тайм-аут. Возможны три мира: потерялся запрос (операция не выполнялась), упал сервер после выполнения (выполнена), потерялся ответ (выполнена). Клиент их не различает — см. 1.4. На уровне попыток доставки и наблюдаемого эффекта обычно различают:

  • Не более одного раза (at-most-once): операция даёт эффект 0 или 1 раз. Простейший вариант — не повторять; практический вариант допускает повторы с идентификатором запроса, пока сервер надёжно помнит обработанные идентификаторы.
  • Хотя бы один раз (at-least-once): при доступном сервере и fair-loss-канале повторять до подтверждения. Операция даст эффект один или несколько раз, если получатель не подавляет дубли.

«Ровно один раз» как свойство самой ненадёжной доставки недостижимо: отправитель не может одновременно получить абсолютную уверенность в доставке и исключить повтор (формальная граница — задача двух генералов в главе 3). Но эффект «ровно один раз» в ограниченной системе реализуем: at-least-once-доставка сочетается с долговечной дедупликацией, а запись идентификатора и бизнес-операция выполняются атомарно. Гарантия действует ровно в пределах хранилища, срока дедупликации и транзакционной границы; внешний побочный эффект снова возвращает неопределённость. Этот механизм мы разберём на Kafka и outbox в главах 9–10.

Итоги главы

  • Система = процессы + каналы + события; никакой общей памяти и общих часов; всё знание — из сообщений.
  • Каналы теряют, дублируют и переупорядочивают; TCP спасает только внутри соединения, на уровне приложения допущения надёжности незаконны.
  • Отказы частичны и могут быть коррелированы. Crash-stop, crash-recovery, пропуски и византийское поведение — разные модели с разными допущениями, а не безусловная вероятностная шкала; основная часть курса живёт в crash-recovery, глава 7 — в византийской модели.
  • Интернет асинхронен: тайм-аут — подозрение, а не доказательство; алгоритмы обязаны быть корректными при ложных подозрениях.
  • Из модели немедленно следует: «ровно один раз» не является свойством ненадёжного канала; ограниченный эффект «ровно один раз» строится поверх повторов, долговечной дедупликации и атомарной записи результата.

Упражнения

  1. Клиент послал запрос «создать заказ» и получил сетевую ошибку. Перечислите все возможные состояния сервера и покажите, что никакая дополнительная информация, доступная клиенту в этот момент, их не различает. Какие два безопасных действия у него остаются и какой семантике соответствует каждое?
  2. Сервис A вызывает B, B вызывает C; доступность каждого — 99,9% (независимо), сетевых потерь нет. Какова доступность ответа A? Обобщите на цепочку из k сервисов и посчитайте, сколько «девяток» останется у цепочки из 30 микросервисов по 99,9%.
  3. Процесс завис в паузе сборщика мусора на 50 секунд, затем продолжил работу. К какому виду отказов из 1.3 это ближе всего с точки зрения остальных узлов? Придумайте сценарий, в котором «воскресший» узел делает опасное действие, полагая себя лидером (мы вернёмся к этому сценарию в главе 6 под именем fencing).
  4. Покажите, что в асинхронной модели никакой протокол «пинг раз в секунду, три пропуска — узел мёртв» не является корректным детектором отказов: постройте исполнение, где живой узел объявлен мёртвым, и исполнение, где мёртвый долго числится живым. Почему первый род ошибок опаснее для алгоритмов с одним лидером?
  5. Технолог предлагает добиться exactly-once так: сервер пишет результат в БД и отправляет ответ в одной локальной транзакции. Найдите изъян: какое допущение о канале «ответ → клиент» здесь молча сделано?
  6. Классифицируйте по моделям 1.2–1.4: (а) два контейнера на одном хосте через localhost; (б) сервисы в одном ЦОДе; (в) мобильный клиент и сервер; (г) узлы в разных юрисдикциях с недоверенными операторами. Для каждого — реалистичная модель канала, отказов и времени.

Ответы и указания. 1: запрос мог не дойти, выполняться, завершиться с потерянным ответом или завершиться с ответом, ещё находящимся в сети; локальное состояние клиента после сетевой ошибки одинаково. Не повторять — риск пропуска, повторять без защиты — риск дубля, повторять с тем же ключом идемпотентности — безопасный эффект при сохранной таблице ключей. 2: 0,9993 ≈ 99,7%; для 30 сервисов 0,99930 ≈ 97% — глубина синхронной цепочки является бюджетом доступности. 3: с точки зрения других это временное молчание живого процесса; без ограждения проснувшийся старый лидер может продолжить запись параллельно с новым. 4: задержите каждый ответ на четыре секунды — живой узел будет объявлен мёртвым; после настоящего отказа последнее успешное наблюдение ничего не сообщает о будущем, а при большом тайм-ауте подозрение возникнет поздно. Ложное подозрение опаснее тем, что может породить второго лидера, тогда как запоздалое обнаружение обычно лишь продлевает недоступность. 5: локальная транзакция не включает канал ответа. Если ответ потерян, клиент не отличит выполненную операцию от невыполненной и может повторить её; серверу нужна атомарная дедупликация. 6: localhost обычно моделируют надёжным FIFO-каналом до отказа процесса или хоста, но контейнеры имеют общий домен отказа; один ЦОД — частичная синхронность, crash/omission и коррелированный отказ стойки или сети; мобильная связь — длительные задержки, разрывы, повторы после переподключения и offline-first клиент; недоверенные операторы требуют византийской модели, аутентификации и криптографической целостности, при этом временная модель всё равно остаётся частично синхронной.

Литература к главе

  1. C. Cachin, R. Guerraoui, L. Rodrigues, "Introduction to Reliable and Secure Distributed Programming," 2nd ed., Springer, 2011 — гл. 2: модели процессов, каналов и отказов.
  2. M. Kleppmann, "Designing Data-Intensive Applications," O'Reilly, 2017 — гл. 8 «Проблемы распределённых систем».
  3. P. Deutsch et al., "The Eight Fallacies of Distributed Computing," Sun Microsystems, 1994–1997.
  4. M. van Steen, A. Tanenbaum, "Distributed Systems," 4th ed., 2023 (свободно распространяется авторами) — гл. 1–2.

Содержание курса || Следующая глава

404 Not Found

404 Not Found


nginx/1.24.0 (Ubuntu)

Связь с редакцией