Если вам нравится читать статьи на Medium и вы заинтересованы в том, чтобы стать участником, я буду рад поделиться с вами своей реферальной ссылкой!
Сложность управления распределенными системами, особенно во время обработки транзакций, привела к появлению различных шаблонов и протоколов для управления этими сценариями. Хотя шаблон двухфазной фиксации (2PC) предлагает решение, у него есть свои недостатки. Одной из критических уязвимостей является его подверженность сбою в случае сетевого раздела. Для решения этой проблемы был введен шаблон трехфазной фиксации (3PC), который сделал протокол неблокирующим, тем самым устранив неопределенное ожидание ответов от других узлов. В этой статье представлен общий обзор шаблона трехфазной фиксации и пример кода Go, иллюстрирующий его работу.
Шаблон трехфазной фиксации (3PC)
Шаблон трехфазной фиксации является усовершенствованием шаблона двухфазной фиксации. Он вводит дополнительную фазу, фазу PreCommit, чтобы избежать проблемы блокировки в 2PC. Три этапа протокола следующие:
- Фаза 1 — Фаза голосования. Как и в 2PC, узел-координатор отправляет запрос на фиксацию транзакции всем узлам-участникам. Затем каждый участник проголосует «Да», чтобы зафиксировать, или «Нет», чтобы отменить транзакцию.
- Фаза 2 — Фаза PreCommit: если все участники проголосовали за, координатор отправляет сообщение PreCommit всем узлам, указывающее, что транзакция скоро будет зафиксирована. Если какой-либо узел проголосовал «Нет», координатор отправляет всем узлам сообщение Abort.
- Фаза 3 — Фаза фиксации/отмены: после фазы предварительной фиксации координатор отправляет сообщение фиксации всем узлам-участникам, подтверждая фиксацию транзакции. При возникновении каких-либо ошибок координатор отправляет сообщение о прерывании.
Реализация шаблона трехфазной фиксации (3PC) в Go
Go с его эффективной обработкой параллелизма через горутины и каналы — отличный выбор для иллюстрации алгоритмов распределенных систем. Упрощенный пример кода Go для демонстрации шаблона трехфазной фиксации выглядит следующим образом:
package main
import (
"fmt"
"sync"
)
type Transaction struct {
Data string
}
type Node struct {
ID int
}
type Coordinator struct {
Nodes []*Node
}
func (c *Coordinator) ThreePhaseCommit(transaction *Transaction) bool {
var wg sync.WaitGroup
results := make(chan bool, len(c.Nodes))
for _, node := range c.Nodes {
wg.Add(1)
go func(node *Node) {
defer wg.Done()
// Phase 1: Voting phase
fmt.Printf("Node %v: received VOTE-REQ for transaction\n", node.ID)
vote := vote(node, transaction)
// Sending vote to the coordinator
results <- vote
}(node)
}
go func() {
wg.Wait()
close(results)
}()
// The coordinator collects votes
for result := range results {
if !result {
fmt.Println("Transaction abort")
return false
}
}
// Phase 2: PreCommit phase
for _, node := range c.Nodes {
fmt.Printf("Node %v: received PRE-COMMIT for transaction\n", node.ID)
}
// Phase 3: Commit phase
for _, node := range c.Nodes {
fmt.Printf("Node %v: COMMIT transaction\n", node.ID)
}
fmt.Println("Transaction commit")
return true
}
func vote(node *Node, transaction *Transaction) bool {
// Here logic of voting for a transaction would be implemented
// e.g. checking constraints or locking resources
return true
}
func main() {
nodes := []*Node{{ID: 1}, {ID: 2}, {ID: 3}}
coordinator := &Coordinator{Nodes: nodes}
transaction := &Transaction{Data: "example transaction"}
coordinator.ThreePhaseCommit(transaction)
}
В этой функции main мы создаем несколько узлов и координатора, а затем запускаем транзакцию с помощью метода ThreePhaseCommit.

Заключение
Шаблон трехфазной фиксации (3PC), являющийся расширением шаблона двухфазной фиксации (2PC), вводит дополнительную фазу предварительной фиксации для решения проблемы блокировки, обнаруженной в 2PC. Протокол 3PC, хотя и более сложный, обеспечивает лучшую отказоустойчивость в распределенных системах. Однако важно отметить, что этот шаблон не является полностью отказоустойчивым. Он подвержен сбоям, когда сеть разделена на несколько сегментов, где каждый сегмент может взаимодействовать внутри себя, но не с другими сегментами. В таких случаях алгоритмы консенсуса, такие как Paxos или Raft, могут быть лучшим решением. Этот пример Go призван проиллюстрировать основную работу протокола 3PC в его простейшей форме, реальная реализация может включать в себя более сложные функции, такие как сетевые коммуникации, отказоустойчивость и постоянное хранилище.