テックカリキュラム

分散システム・整合性・障害耐性の高度設計

分散システム・整合性・障害耐性の高度設計

本章では、単一サーバではなく、複数のノードが協調して動作する 分散システム(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を防ぐアーキテクチャを設計できる

これにより、 「正常時に動くシステム」ではなく、 ネットワーク分断、ノード障害、遅延、重複、過負荷が発生しても 制御可能な分散システムを設計するための基盤が完成する。