# Подсказки к упражениям ## Двухфазный коммит протокол на основе ZooKeeper На основе материалов: https://zookeeper.apache.org/doc/r3.4.2/recipes.html#sc_recipes_twoPhasedCommit и https://stackoverflow.com/questions/24635777/how-to-implement-2pc-in-zookeeper-cluster [![IMAGE_ALT](https://img.youtube.com/vi/yu2TZF7S1Mg/3.jpg)](https://youtu.be/yu2TZF7S1Mg?t=964) Протокол двухфазной коммита — это алгоритм, позволяющий всем клиентам в распределенной системе договориться либо о коммите транзакции, либо о ее откате. Взаимодействующие компоненты: - создатель транзакции, - координатор транзакции, - исполнители транзакции, - zookeeper, - (опционально) прямой канал связи создателя транзакции и координатора, - (опционально) прямые каналы связи между координатором и исполнителями. ### Вариант 1. В ZooKeeper вы можете реализовать двухфазный коммит протокол, если координатор создаст узел транзакции, скажем "/app/Tx", и один дочерний узел для каждого исполнителя транзакции, скажем "/app/Tx/node_i". Когда *координатор* создает дочерний узел, он оставляет его содержимое неопределенным. Первая фаза: Как только каждый исполнитель, участвующий в транзакции, получает транзакцию от координатора, исполнитель читает каждый дочерний узел "node_i" и подписывается на события изменения транзакционного узла. Затем каждый исполнитель обрабатывает запрос и принимает решение "commit" или "abort", записывая данные в свой узел. Вторая фаза: Как только запись завершается, другие исполнители получают уведомление, и как только все исполнители получат все голоса, они могут принять решение либо "commit", либо "abort". Обратите внимание, что узел может принять решение абортировать транзакцию раньше, если какой-либо исполнитель проголосует за "abort". Интересным аспектом этой реализации является то, что единственная роль координатора заключается в определении группы исполнителей, создании узлов ZooKeeper и распространении транзакции на соответствующим исполнителям. Фактически, даже распространение транзакции может быть сделано через ZooKeeper путем записи в узле транзакции. У описанного выше подхода есть два важных недостатка: — это сложность сообщений, которая составляет O(n²). — невозможность обнаружения аварийного завершения исполнителей. Для решения первой проблемы можно сделать так, чтобы об изменениях в узлах транзакций уведомлялся только координатор, а затем уведомлять исполнителей, как только координатор примет решение. Обратите внимание, что этот подход остаётся масштабируемым, но он медленнее, поскольку требует, чтобы все коммуникации проходили через координатора. Для решения второй проблемы можно сделать так, чтобы координатор распространял транзакцию исполнителям, а каждый исполнитель создавал свой собственный эфемерный узел. ### Вариант 2. 1. Координатор C регистрирует транзакционный узел /app/tx Первая фаза: 2. Координатор уведомляет исполнителей о транзакции 3. Координатор подписывается на изменения транзакционного узла (устанавливает WATCH на /app/tx) 4. Каждый исполнитель создает эфемерных узел /app/tx/node_i с решением commit/abort 5. Исполнитель подписывается на события своего узла для получения решения от координатора ( вторая фаза ) Вторая фаза: 6. Координатор принимает решение о commit/abort после ожидания таймаута или после создания всех узлов исполнителей с решением commit 7. Координатор изменяет значение эфемерных узлов для каждого исполнителя на commit / abort 8. Исполнители применяют / прерывают транзакцию 9. Исполнители обновляют значение узла на committed