本章では、単一サーバではなく、複数のノードが協調して動作する 分散システム(Distributed System)の内部構造と設計原理を学ぶ。
CAP定理、Consistency Model、Quorum、Consensus、Leader Election、 Replication、Partitioning、Distributed Lock、Idempotency、 Circuit Breakerなどを体系的に理解し、 「一部のサーバが壊れてもシステム全体を継続させる」ための設計能力を身につける。
単純にサーバ台数を増やすだけでは、高可用な分散システムにはならない。 ネットワーク遅延、部分障害、データ不整合、二重実行、Split Brainなど、 分散環境特有の問題を前提として設計することが本章のテーマである。
1. 分散システムとは
分散システムとは、複数の独立したコンピュータがネットワークを介して協調し、 全体として一つのシステムを構成するアーキテクチャである。
Client
|
Load Balancer
/ | \
Node A Node B Node C
\ | /
Distributed DB
分散化することで、以下の特性を実現しやすくなる。
- 水平スケーリング
- 高可用性
- 負荷分散
- 地理的分散
- 障害耐性
しかし、ノード数が増えるほどシステムの状態管理は難しくなる。
特に重要なのが、 「別々のマシンは完全に同じ状態を同じ瞬間に観測できるとは限らない」 という事実である。
2. 分散システムでは部分障害が発生する
単一プロセスでは、正常動作しているか停止しているかを比較的判断しやすい。
しかし分散システムでは、 システムの一部分だけが故障するPartial Failure(部分障害)が発生する。
Node A ───── Node B
|
X
|
Node C
例えばNode AからNode Cへ通信できなくても、 Node BからNode Cへは通信できる可能性がある。
さらに通信相手から応答がない場合でも、 以下を完全に区別できない場合がある。
- 相手ノードが停止した
- ネットワークが切断された
- 処理が非常に遅い
- リクエストは届いたがレスポンスだけ消失した
- GCやCPU飽和によって一時停止している
この不確実性こそが分散システムを難しくする根本原因の一つである。
3. CAP定理
分散データシステムを理解する上で重要な理論がCAP定理である。
- Consistency:整合性
- Availability:可用性
- Partition Tolerance:ネットワーク分断耐性
■ Consistency
CAPにおけるConsistencyは、 正常に完了した書き込み後の読み取りが単一コピーのように整合した値を観測する性質として考える。
■ Availability
障害のないノードへの各リクエストが、 最終的に正常なレスポンスを返す性質である。
■ Partition Tolerance
ノード間通信が分断されても、 システムが定義された動作を継続できる性質である。
Consistency
/\
/ \
/ \
/ \
/ \
Availability --- Partition Tolerance
CAP定理は単純に 「C・A・Pから好きな2つを選択する」 という意味ではない。
ネットワークPartitionが実際に発生した状況では、 ConsistencyとAvailabilityの両方を完全には維持できないため、 どちらを優先するかという選択が発生する。
4. CPとAPの設計思想
■ CP型
Partition発生時にAvailabilityを犠牲にしてでも、 整合性を優先する設計である。
Node A X Node B
Write
↓
整合性を保証できない
↓
一部リクエストを拒否
例えば、誤った状態で処理を継続することが危険なシステムでは、 整合性を優先する設計が適する場合がある。
■ AP型
Partition発生時でも可能な限りリクエストを受け付け、 後から状態を収束させる設計である。
Node A Node B
Value = 10 Value = 11
↓
Network Recovery
↓
Conflict Resolution
↓
Convergence
可用性を優先する代わりに、 一時的な不整合を許容する場合がある。
5. Consistency Model
「整合性」は一種類ではない。 分散システムでは、どの程度の整合性を保証するかによって複数のモデルが存在する。
■ Strong Consistency
書き込み完了後の読み取りで、 最新状態を観測できることを要求する強い整合性モデルである。
■ Eventual Consistency
一時的な不整合を許容するが、 新しい更新がなければ最終的にReplica間の状態が収束するモデルである。
Write
Node A = 100
↓ Replication
Node B = 90
Node C = 90
↓
Node B = 100
Node C = 100
■ Read-Your-Writes Consistency
自分が書き込んだデータについては、 その後の自分自身の読み取りで反映済みの状態を確認できる性質である。
■ Monotonic Reads
一度新しい状態を観測したクライアントが、 その後それより古い状態へ戻らないことを保証する。
サービス要件によって必要なConsistency Modelは異なるため、 「常にStrong Consistencyが最良」とは限らない。
6. Linearizability
分散システムにおける強い整合性を考える上で重要なのが Linearizability(線形化可能性)である。
各操作が、呼び出しから応答までのどこか一点で 原子的に実行されたかのように観測できる性質である。
Client A
WRITE X = 100
|-------------|
完了
Client B
READ X
↓
100
リアルタイム上で先に完了した操作の順序を維持する必要があるため、 非常に強い保証となる。
一方で、強い整合性を実現するためにはノード間通信や合意処理が必要となり、 LatencyやAvailabilityとのトレードオフが発生する。
7. Replication
Replicationは、同じデータを複数ノードへ複製する技術である。
Leader
|
+------+------+
| |
v v
Replica B Replica C
Replicationによって以下を実現できる。
- 高可用性
- 障害耐性
- 読み取り負荷分散
- 地理的冗長化
しかしReplica間で更新タイミングが異なるため、 Replication Lagによるデータ不整合が発生する可能性がある。
8. Synchronous ReplicationとAsynchronous Replication
■ Synchronous Replication
Replicaへの反映を確認してから書き込み成功を返す方式である。
Client
|
WRITE
↓
Leader
|
+------> Replica
| |
| ACK
|<---------+
|
SUCCESS
メリット
- データ損失リスクを抑えやすい
- 強い整合性を設計しやすい
デメリット
- 書き込みLatencyが増加する
- Replica障害の影響を受ける可能性がある
■ Asynchronous Replication
Leaderへの書き込み完了後、 Replicaへの反映を非同期で実行する方式である。
Client
|
WRITE
↓
Leader
|
SUCCESS
|
+------> Replica
メリット
- 書き込みLatencyを低くしやすい
- Replica遅延の影響を受けにくい
デメリット
- Replication Lagが発生する
- 障害タイミングによって未複製データを失う可能性がある
9. Leader / Follower Architecture
代表的なReplication方式の一つがLeader / Follower構成である。
WRITE
|
v
Leader
/ \
v v
Follower A Follower B
↑ ↑
READ READ
通常、書き込みをLeaderへ集中させ、 FollowerへReplicationする。
読み取りをFollowerへ分散することで、 Read Scalabilityを向上できる。
ただしReplication Lagが存在すると、 書き込み直後にFollowerから読み込んだ場合、 古い値が返る可能性がある。
10. Leader Election
Leaderが障害によって停止した場合、 新しいLeaderを決定する必要がある。
Before Failure
Node A = Leader
Node B = Follower
Node C = Follower
↓
Node A Failure
↓
Leader Election
↓
Node B = Leader
Node C = Follower
この処理をLeader Electionという。
単純に「最初に名乗ったノードをLeaderにする」だけでは、 ネットワーク分断時に複数Leaderが発生する可能性がある。
そのため実際の分散システムでは、 Consensus Algorithmなどを利用してLeaderを決定する。
11. Split Brain
Split Brainは、 ネットワーク分断などによって複数ノードが自分をLeaderだと判断する状態である。
Network Partition
Leader A X Leader B
WRITE X=100 WRITE X=200
両方のLeaderが書き込みを受け付けると、 ネットワーク復旧後にデータ競合が発生する。
Split Brain対策には以下のような設計が利用される。
- Quorum
- Consensus Algorithm
- Fencing
- Lease
- Generation / Term管理
12. Quorum
Quorumは、複数ノードのうち一定数以上の合意を必要とする設計である。
Replica数をN、書き込み確認数をW、読み取り確認数をRとする。
N = Replica Count
W = Write Quorum
R = Read Quorum
代表的なQuorum設計では、 以下の関係を利用して読み書きの集合が重なるようにする。
W + R > N
例えば以下を考える。
N = 3
W = 2
R = 2
W + R = 4 > 3
ただし、この条件だけであらゆる実装にStrong Consistencyが自動的に保証されるわけではない。 バージョン管理、競合解決、障害モデル、読み書きアルゴリズムなども重要となる。
13. Consensusとは
Consensus(合意形成)は、 複数ノードが一つの値や状態について合意するための仕組みである。
分散システムでは以下のような場面で必要となる。
- Leader Election
- 構成情報管理
- 分散ログ
- クラスタメンバー管理
- 状態機械の複製
代表的なConsensus Algorithmには以下がある。
- Paxos
- Raft
14. Raft Consensus Algorithm
Raftではノードが主に以下の状態を持つ。
- Leader
- Follower
- Candidate
Follower
|
| Election Timeout
v
Candidate
|
| Majority Vote
v
Leader
Leaderはクライアントから更新を受け取り、 Log EntryとしてFollowerへ複製する。
Client
|
v
Leader
|
+------- Log Entry -------> Follower A
|
+------- Log Entry -------> Follower B
Majority ACK
↓
Commit
過半数へLog Entryが複製されることで、 クラスタとしてCommit可能な状態を判断する。
15. TermとLog Replication
Raftでは時間をTermと呼ばれる論理的な期間へ分割する。
Term 1
↓
Leader A
Term 2
↓
Leader B
Term 3
↓
Leader C
Term番号を利用することで、 古いLeaderからの情報を識別できる。
Leaderが受け取った更新はLogとしてFollowerへ複製され、 合意されたLogを順番に状態機械へ適用する。
このState Machine Replicationの考え方は、 多くの分散システムで重要となる。
16. HeartbeatとFailure Detection
分散システムでは、 他ノードが正常に動作しているか確認するためHeartbeatが利用される。
Node A ---- heartbeat ----> Node B
Node A ---- heartbeat ----> Node C
一定時間Heartbeatを確認できなければ、 障害の可能性があると判断する。
しかし分散環境では、 「応答がない = ノードが停止した」と断定することはできない。
- ネットワーク遅延
- Packet Loss
- CPU Saturation
- GC Pause
- 一時的なI/O停止
などでもHeartbeatが遅延する可能性がある。
したがってFailure Detectorは、 厳密な故障判定ではなく、タイムアウトなどを利用した推測として設計されることが多い。
17. Network Partition
Network Partitionとは、 クラスタ内の一部ノード間で通信できなくなる状態である。
Node A ----- Node B
XXXXXXXXXXXXX
Node C ----- Node D
重要なのは、 各グループから見ると「相手側が壊れたように見える」という点である。
この状態で双方が独立して更新を続けると、 復旧後に状態競合が発生する。
分散システムではNetwork Partitionを例外ではなく、 発生し得る前提条件として設計する必要がある。
18. Partitioning / Sharding
データ量が単一ノードで処理できない場合、 データを複数ノードへ分割するPartitioningが利用される。
User ID
1 - 1000000
↓
Shard A
1000001 - 2000000
↓
Shard B
2000001 - 3000000
↓
Shard C
Partitioningによって、 ストレージ容量や処理負荷を水平分散できる。
19. Hash Partitioning
キーのHash値を利用して配置先を決定する方式である。
hash(key) % N
単純なModulo方式では、 ノード数Nが変更されたときに大量のキー配置が変化する可能性がある。
大規模分散システムでは、 ノード追加・削除時のデータ移動量を抑える仕組みが重要となる。
20. Consistent Hashing
Consistent Hashingは、 ノード追加・削除時のキー再配置を抑えるために利用される方式である。
Node A
|
Key1 | Key2
\ | /
Hash Ring
/ \
Node D Node B
\ /
Node C
Hash空間をリング状に扱い、 キーを一定ルールでノードへ割り当てる。
ノードが追加・削除されても、 全データではなく一部のキーだけを再配置すればよいという利点がある。
実際のシステムでは負荷分散を改善するため、 Virtual Nodeなどの仕組みと組み合わせることもある。
21. Hot Partition問題
Partitioningしていても、 アクセスが特定キーへ集中すると一部ノードだけが高負荷になる。
Shard A ████████████████████
Shard B ███
Shard C ██
これをHot PartitionまたはHotspot問題と呼ぶ。
■ 原因
- 特定ユーザーへのアクセス集中
- 時系列キーの偏り
- 人気コンテンツへの集中
- 不適切なPartition Key
単にShard数を増やすだけでは解決しない場合があり、 Partition Keyの設計そのものが重要となる。
22. Distributed Lock
複数ノードから同じリソースを同時更新したくない場合、 Distributed Lock(分散ロック)が必要になる場合がある。
Node A ----+
|
Node B ----+---- Distributed Lock ---- Resource
|
Node C ----+
しかし分散ロックは、 単一プロセス内のMutexよりはるかに難しい。
例えばLockを取得したNode Aが長時間停止した場合を考える。
Node A
|
LOCK ACQUIRED
|
Long Pause
|
| Lock Expired
|
| Node B
| |
| LOCK ACQUIRED
| |
Resume |
| |
WRITE WRITE
Node Aは自分がまだLock所有者だと思っている可能性がある。
そのためTTLだけで完全な安全性を保証できるとは限らない。
23. Fencing Token
古いLock所有者による操作を防ぐ方法の一つがFencing Tokenである。
Node A
Token = 41
Node B
Token = 42
ストレージ側では、 より新しいTokenだけを受け付ける。
WRITE Token 42 → ACCEPT
WRITE Token 41 → REJECT
これにより、一時停止していた古いLock所有者が復帰しても、 古い権限による書き込みを拒否できる。
24. Idempotency
分散システムではネットワークエラーによって、 クライアントが同じリクエストを再送することがある。
Client
POST /payment
|
v
Server
Payment Completed
X
Response Lost
クライアントから見ると成功したか分からないため、 同じリクエストを再送する可能性がある。
POST /payment
このとき二重決済が発生してはいけない。
そこで重要になるのがIdempotency(冪等性)である。
■ Idempotency Key
Idempotency-Key: 7f2c8a...
同じキーのリクエストについて、 一度だけ業務処理を実行するよう設計する。
決済・注文・予約などでは特に重要な設計となる。
25. At-Most-Once / At-Least-Once / Exactly-Once
分散処理では、メッセージや処理が何回実行される可能性があるかを考える必要がある。
■ At-Most-Once
最大1回処理する。
- 重複処理を避けられる
- 障害時に処理が失われる可能性がある
■ At-Least-Once
最低1回は処理する。
- 処理を失いにくい
- 重複実行される可能性がある
■ Exactly-Once
論理的に一度だけ処理された結果を実現することを目指す。
実際の分散システムでは、 単純にネットワーク配送そのものを「物理的に絶対1回」にするのではなく、 Idempotency、Transaction、Deduplicationなどを組み合わせて Exactly-Once相当の処理結果を設計することが重要となる。
26. Message Queueによる非同期化
分散システムでは、 サービス間を同期通信だけで接続すると障害が連鎖しやすくなる。
Service A
|
v
Message Queue
|
+------> Service B
|
+------> Service C
Message Queueを利用すると、 ProducerとConsumerを時間的に分離できる。
■ メリット
- サービス間の疎結合化
- 負荷平準化
- 非同期処理
- リトライ制御
- 一時的なConsumer障害への耐性
一方で、重複配信・順序保証・再処理・メッセージ滞留など、 新しい設計課題も発生する。
27. Retry設計
分散システムでは一時的な障害に対してRetryが有効である。
しかし、単純な即時Retryは障害を悪化させる可能性がある。
Request Failed
↓
Retry
↓
Retry
↓
Retry
↓
Overload
大量クライアントが同時にRetryすると、 障害中のサービスへさらに負荷を集中させるRetry Stormが発生する。
28. Exponential BackoffとJitter
Retry間隔を徐々に長くする方法がExponential Backoffである。
1st Retry → 1 sec
2nd Retry → 2 sec
3rd Retry → 4 sec
4th Retry → 8 sec
しかし全クライアントが同じタイミングでRetryすると、 再びアクセスが集中する。
そこでランダムな待機時間であるJitterを追加する。
Retry Delay
=
Exponential Backoff
+
Random Jitter
これによりRetryタイミングを分散できる。
29. Timeout設計
分散システムでは、 外部サービスへのリクエストを無期限に待つ設計を避ける必要がある。
Service A
|
| Request
v
Service B
|
|
|
|
No Response
Timeoutが存在しない場合、 スレッド・コネクション・メモリなどのリソースが長時間占有される可能性がある。
ただしTimeoutを短くしすぎると、 正常に完了できる処理まで失敗させてしまう。
Latency分布やSLOを基に適切なTimeoutを設計する必要がある。
30. Circuit Breaker
障害中のサービスへリクエストを送り続けると、 障害がシステム全体へ連鎖する可能性がある。
これを防ぐ代表的なパターンがCircuit Breakerである。
■ CLOSED
通常状態。リクエストを送信する。
■ OPEN
障害率が閾値を超えた場合、 一定期間リクエストを遮断する。
■ HALF-OPEN
少数のリクエストを試験的に送信し、 復旧を確認する。
CLOSED
|
Failures
v
OPEN
|
Timeout
v
HALF-OPEN
|
+---- Success ----> CLOSED
|
+---- Failure ----> OPEN
Circuit Breakerによって、 障害サービスへの不要なアクセスを抑制できる。
31. Bulkhead Pattern
船舶の隔壁(Bulkhead)のように、 障害範囲を分離する設計パターンである。
Application
├── Thread Pool A → Service A
├── Thread Pool B → Service B
└── Thread Pool C → Service C
例えばService Aが停止しても、 Service A向けのリソースだけが枯渇するように分離する。
すべての外部サービスで同じThread Poolを共有している場合、 一つの依存先障害によって全処理が停止する可能性がある。
32. Cascading Failure
一つの障害が別サービスへ連鎖し、 最終的にシステム全体が停止する現象をCascading Failureという。
Database Slow
↓
API Slow
↓
Thread Pool Exhaustion
↓
Gateway Timeout
↓
Client Retry
↓
Traffic Increase
↓
Database More Overloaded
非常に危険なのは、 障害対応機能であるRetryそのものが障害を増幅する場合があることである。
以下を組み合わせて障害伝播を抑制する。
- Timeout
- Retry Budget
- Exponential Backoff
- Jitter
- Circuit Breaker
- Bulkhead
- Rate Limiting
- Load Shedding
33. Backpressure
ConsumerがProducerの処理速度に追いつかない場合、 処理待ちデータが増加する。
Producer
██████████████████
↓
Queue
████████████████████████
↓
Consumer
████
この状態を放置すると、 Queue・メモリ・ストレージなどが限界へ到達する。
下流の処理能力に応じて上流の流量を制御する考え方がBackpressureである。
分散システムでは、 「受け取れるから受け取る」のではなく、 システム全体の処理能力に合わせて流量を制御する必要がある。
34. Load Shedding
システム容量を超えるトラフィックが到達した場合、 すべてのリクエストを処理しようとすると全体が停止する可能性がある。
そこで一部の処理を意図的に拒否し、 重要な処理を守るLoad Sheddingが利用される。
Incoming Requests
████████████████████
↓
Capacity
██████████
↓
Accept
██████████
Reject
██████████
「100%のリクエストを処理しようとして全滅する」より、 「一部を拒否してサービス全体を維持する」方が適切な場合がある。
35. Clockは信用できるとは限らない
分散システムでは、 複数サーバの物理時計が完全に一致しているとは限らない。
Node A 10:00:00.100
Node B 10:00:00.085
Node C 10:00:00.120
時計のずれをClock Skewという。
NTPなどによって同期していても、 完全に同一時刻になることを前提としてはいけない。
そのためイベント順序を単純なWall Clockだけで決定すると、 分散環境では問題が発生する可能性がある。
36. Logical Clock
イベントの因果関係を扱うため、 物理時計ではなくLogical Clock(論理時計)を利用する考え方がある。
■ Lamport Clock
イベントごとに論理カウンタを進め、 イベント間の順序関係を表現する。
Node A
Event A1 → 1
Event A2 → 2
\
Message
\
Node B v
Event B1 → 3
Event B2 → 4
Lamport Clockでは因果関係を反映した順序付けが可能になるが、 タイムスタンプの大小だけから逆方向の因果関係を完全に推論できるわけではない。
37. Vector Clock
Vector Clockでは、 各ノードの論理時刻をベクトルとして管理する。
Node A [3,1,0]
Node B [2,4,0]
Node C [2,1,5]
これにより、 二つのイベントに因果関係があるのか、 それとも並行して発生したのかを判定できる。
Eventual Consistencyを採用する分散データシステムでは、 競合検出を考える上で重要な概念となる。
38. Saga Pattern
複数サービスをまたぐ処理では、 単一データベースのACID Transactionをそのまま利用できない場合がある。
例えばECサイトの注文処理を考える。
Order
↓
Payment
↓
Inventory
↓
Shipping
途中のInventory処理が失敗した場合、 既に完了したPaymentをどう扱うかという問題が発生する。
Saga Patternでは、 複数のLocal Transactionと補償処理を組み合わせる。
Create Order
↓
Payment
↓
Inventory Failed
↓
Refund Payment
↓
Cancel Order
このような逆操作をCompensating Transactionと呼ぶ。
39. Two-Phase Commit
複数リソース間でTransactionを調整する代表的な方式として Two-Phase Commit(2PC)がある。
Phase 1:Prepare
Coordinator
|
+---- PREPARE ----> Participant A
|
+---- PREPARE ----> Participant B
Phase 2:Commit
Coordinator
|
+---- COMMIT ----> Participant A
|
+---- COMMIT ----> Participant B
複数参加者でTransactionを調整できる一方、 Coordinator障害や待機時間、ロック保持などが可用性・性能上の課題となる。
マイクロサービスでは、 すべてを分散Transactionで解決するのではなく、 SagaやEventual Consistencyを選択するケースも多い。
40. 分散システムのObservability
分散システムでは、 単一サーバのログだけを見てもリクエスト全体を追跡できない。
Client
↓
Gateway
↓
Service A
↓
Service B
↓
Database
そのため以下を組み合わせて観測する。
- Metrics
- Logs
- Distributed Traces
特にTrace IDをサービス間で伝播させることで、 一つのリクエストがどのサービスを通過したのか追跡できる。
Trace ID: abc123
Gateway
↓ abc123
Service A
↓ abc123
Service B
↓ abc123
Database
分散システムでは、 障害耐性だけではなく「障害を観測できる設計」も不可欠である。
41. 実践障害解析:Leaderが突然停止した
3ノード構成の分散システムを考える。
Node A = Leader
Node B = Follower
Node C = Follower
Node Aが突然停止した。
Step 1:Heartbeat Timeout
FollowerがLeaderからのHeartbeatを確認できなくなる。
Step 2:Election開始
Followerの一つがCandidateとなり、 他ノードへVoteを要求する。
Step 3:Majority取得
過半数のVoteを取得したNodeが新しいLeaderとなる。
Node A = DOWN
Node B = Candidate
Node C = Vote
↓
Node B = Leader
Step 4:Log整合性確認
新LeaderとFollower間でLog状態を整合させる。
Step 5:サービス再開
新Leaderが書き込みを受け付ける。
ここで重要なのは、 単純に「予備サーバへ切り替わる」のではなく、 Leaderの正当性とデータ状態をクラスタ全体で判断する必要があることである。
42. 実践障害解析:サービス障害が全体へ連鎖した
次のマイクロサービス構成を考える。
Client
↓
Gateway
↓
Order Service
↓
Payment Service
↓
Database
DatabaseのLatencyが急激に増加したとする。
DB Latency Increase
↓
Payment Response Slow
↓
Order Thread Waiting
↓
Thread Pool Exhaustion
↓
Gateway Timeout
↓
Client Retry
↓
Traffic Increase
↓
System Collapse
この障害では、最初の原因はDatabaseであっても、 最終的にはシステム全体へ障害が伝播している。
■ 防御設計
- 適切なTimeout
- Retry回数制限
- Exponential Backoff + Jitter
- Circuit Breaker
- Bulkhead
- Rate Limiting
- Load Shedding
- Queueによる負荷平準化
応用レベルでは、 「障害をゼロにする」のではなく、 「障害が発生しても爆発半径を限定する」という考え方が重要となる。
43. 分散システム設計で避けるべき思考
分散システムでは、以下の前提を無条件に信用してはいけない。
- ネットワークは常に正常である
- Latencyは一定である
- 帯域は無限にある
- 通信は必ず一度だけ届く
- すべてのノードが同じ状態を見ている
- サーバの時計は完全に同期している
- 依存サービスは必ず応答する
- Retryすれば必ず安全である
- ノードを増やせば性能が比例して向上する
分散システムでは、 「失敗すること」を異常系として後付けするのではなく、 失敗を通常の設計条件として扱う必要がある。
44. 応用エンジニアに求められる分散システム思考
応用レベルでは、 「サーバを冗長化しました」という説明だけでは不十分である。
以下のような問いに答えられる必要がある。
- ネットワークPartition時に書き込みを継続するのか
- Leader障害時に誰が次のLeaderを決めるのか
- 古いLeaderが復帰した場合に書き込みを防げるか
- Replication Lagをどこまで許容できるか
- 同じリクエストが2回来ても安全か
- Retryによって負荷を増幅しないか
- Consumerが遅れた場合にQueueが無限増加しないか
- 特定ShardだけがHotspotにならないか
- 依存サービス障害をどこで遮断するか
- 過負荷時に何を捨てて何を守るか
- 時計がずれても処理順序を保証できるか
- 障害発生時にリクエストをEnd-to-Endで追跡できるか
この問いに設計上の根拠を持って回答できることが、 分散システムを扱うエンジニアに求められる能力である。
本章のゴール
本章のゴールは、 複数ノードから構成されるシステムを 「サーバを増やしたもの」ではなく、 不確実性・部分障害・整合性・合意形成を扱う独立した設計領域として理解することである。
- 分散システム特有のPartial Failureを理解できる
- CAP定理を正確に説明できる
- Strong / Eventual Consistencyを使い分けられる
- Linearizabilityの意味を理解できる
- Synchronous / Asynchronous Replicationを設計できる
- Leader ElectionとSplit Brainを理解できる
- Quorumの考え方を説明できる
- RaftによるConsensusの基本構造を理解できる
- PartitioningとConsistent Hashingを理解できる
- Hot Partitionを考慮したShard設計ができる
- Distributed LockとFencing Tokenを理解できる
- Idempotencyによって重複処理を防止できる
- At-Least-OnceなどのDelivery Semanticsを説明できる
- Retry / Backoff / Jitterを安全に設計できる
- TimeoutとCircuit Breakerを設計できる
- Bulkheadによって障害範囲を分離できる
- BackpressureとLoad Sheddingを設計できる
- Logical Clockによるイベント順序を理解できる
- Sagaと2PCのトレードオフを理解できる
- Distributed Tracingによって障害を追跡できる
- Cascading Failureを防ぐアーキテクチャを設計できる
これにより、 「正常時に動くシステム」ではなく、 ネットワーク分断、ノード障害、遅延、重複、過負荷が発生しても 制御可能な分散システムを設計するための基盤が完成する。