Распределённые системы
Все объяснения, задачи и лабораторная в одном месте.
Можно сохранить страницу в PDF через печать браузера. Для печати разборы ответов раскрываются автоматически.
1. Модели отказов, время и частичный порядок
Объяснение
Узлы видят только часть событий; timeout означает отсутствие своевременного ответа, а не доказанную смерть сервера. Часы разных машин могут расходиться. Причинный порядок не равен сортировке по локальному времени.
Задача для самостоятельного решения
Клиент не получил ответ на перевод денег. Можно ли считать, что перевод не выполнен?
Показать разбор ответа
Нет: сервер мог выполнить запись и потерять ответ. Клиент должен проверить результат по идентификатору операции или повторить запрос с тем же ключом идемпотентности.
2. RPC, очереди и семантика доставки
Объяснение
Доставка at-least-once допускает повторы. Exactly-once часто относится только к ограниченной транзакционной области. Подтверждение сообщения после фиксации результата предотвращает потерю, но требует дедупликации при повторе.
Задача для самостоятельного решения
Обработчик упал после записи в БД, но до ACK. Что произойдёт?
Показать разбор ответа
Брокер доставит сообщение снова. Уникальный event_id в той же транзакции, что бизнес-изменение, позволит распознать повтор и не начислить деньги дважды.
3. Репликация и модели согласованности
Объяснение
Сильная согласованность упрощает чтение после записи, но требует координации. Eventual consistency допускает временно разные ответы реплик. Конфликт разрешают правилом предметной области, а не произвольным выбором последней записи.
Задача для самостоятельного решения
Пользователь сменил имя, но реплика показывает старое. Это обязательно потеря данных?
Показать разбор ответа
Нет, это может быть задержка репликации. Для собственного изменения можно читать ведущий узел или использовать токен версии; отображение старых данных должно быть учтено в UX.
4. Партиционирование и балансировка
Объяснение
Партиционирование делит данные по ключу. Равное число ключей не гарантирует равную нагрузку: один популярный ключ может стать горячим. Перемещение партиций требует учёта записей во время миграции.
Задача для самостоятельного решения
Почему tenant_id как shard key опасен при одном очень крупном клиенте?
Показать разбор ответа
Все его запросы попадают на один shard. Возможны составной ключ, дополнительное разбиение или выделенный shard; решение зависит от запросов, которым нужна локальность.
5. Консенсус, leader election и leases
Объяснение
Консенсус согласует порядок изменений при отказах в заданной модели. Большинство из 2f+1 узлов выдерживает f недоступных участников. Lease зависит от времени и должен сочетаться с защитой от устаревшего владельца.
Задача для самостоятельного решения
Сколько недоступных узлов выдерживает кластер из пяти при quorum majority?
Показать разбор ответа
Два: остаётся большинство из трёх. При сетевом разделении 3/2 записывает сторона с тремя. Fencing token помогает хранилищу отвергнуть действия прежнего лидера.
6. Chaos testing и проектирование восстановления
Объяснение
Chaos-тест проверяет конкретную гипотезу об отказе под контролируемой нагрузкой. Нужны наблюдаемая норма, ограниченная область воздействия и условие остановки. Восстановление — часть проверки, а не необязательный финал.
Задача для самостоятельного решения
Спроектируйте тест падения одной реплики.
Показать разбор ответа
Зафиксировать p95 и долю ошибок, отключить одну учебную реплику, проверить переключение и отсутствие потери подтверждённых записей, вернуть реплику и дождаться синхронизации.
Лабораторная работа
Подготовка
Python 3. Модель демонстрирует идемпотентность в памяти и намеренно не является production-хранилищем.
Учебный пример
operations = {}
balance = 0
def credit(key, amount):
global balance
if key in operations:
old_amount, result = operations[key]
if old_amount != amount: raise ValueError("Конфликт ключа")
return result
balance += amount
operations[key] = (amount,balance)
return balance
assert credit("op-1",100) == 100
assert credit("op-1",100) == 100
assert balance == 100Как работает пример и что ожидать
Два одинаковых запроса меняют баланс один раз. Модель ломается при перезапуске и конкурентных вызовах: запись ключа и изменение должны быть в одной транзакции долговременной БД. Именно эти ограничения определяют следующую итерацию.
Итоговая работа
Перенесите состояние в SQLite/PostgreSQL с уникальным ключом операции, добавьте конкурентный тест и имитацию потери ответа после COMMIT. Затем сформулируйте поведение при недоступной реплике и порядок восстановления.
Проверка результата
1. Опишите исходные данные и условия запуска, чтобы другой человек мог повторить работу.
2. Приложите результат обычного сценария и сравните его с ожидаемым.
3. Проверьте неверный вход, граничный случай и отказ зависимости, если она есть.
4. Объясните выбранное решение и известное ограничение.
5. Сохраните исправления после самопроверки вместе с примером, который раньше не работал.
Как оценить работу
По каждому пункту поставьте 0 (не выполнено), 1 (выполнено с пробелами) или 2 (результат воспроизводим и объяснён). Если обязательный сценарий не работает, вернитесь к нему независимо от общей суммы. Это рубрика самопроверки: сайт не исполняет присланный код и не выдаёт автоматическую оценку проекта.