LESSON 25分

ストーリー

高橋アーキテクト
マイクロサービスにすれば全部解決!
高橋アーキテクト
分散システムに入る前に、絶対に知っておくべき理論がある。CAP定理と、分散コンピューティングの8つの落とし穴だ
高橋アーキテクト
理論より早く実装したいんですが…
高橋アーキテクト
これを知らずに分散システムを作ると、本番で地獄を見る。信じてくれ、僕は見てきた

CAP定理とは

2000年にEric Brewerが提唱した定理で、分散データストアは以下の3つのうち、同時に2つしか保証できないというものです。

特性説明
Consistency(一貫性)すべてのノードが同じデータを返す
Availability(可用性)すべてのリクエストがレスポンスを返す
Partition Tolerance(分断耐性)ネットワーク分断が起きても動作し続ける
        Consistency
           /\
          /  \
         / CP \
        /______\
       /\      /\
      /  \ ?? /  \
     / AP \  / CA \
    /______\/______\
Availability    Partition
                Tolerance

現実的な選択

ネットワーク分断は避けられないため、実質的にはCPかAPの選択になります。

// CP(一貫性 + 分断耐性)の例
// ネットワーク分断時、レスポンスを返さない(可用性を犠牲)
interface CPSystem {
  // 分断時はエラーを返すが、データの一貫性は保証
  read(): Promise<Data | NetworkError>;
  write(data: Data): Promise<void | NetworkError>;
}

// AP(可用性 + 分断耐性)の例
// ネットワーク分断時でもレスポンスを返す(一貫性を犠牲)
interface APSystem {
  // 分断時も古いデータを返す(結果整合性)
  read(): Promise<Data>; // 常にレスポンスを返す
  write(data: Data): Promise<void>; // 常に受け付ける
}

代表的なシステムの分類

分類システム例ユースケース
CPZooKeeper, etcd, HBase設定管理、リーダー選出
APCassandra, DynamoDB, CouchDBSNS、IoTデータ収集
CA単一ノードRDBMS(分断が起きない前提)

Fallacies of Distributed Computing

1994年にSun Microsystemsのエンジニアたちがまとめた「分散コンピューティングの8つの誤解」です。

// 8つの誤解(Fallacies)
const fallacies = [
  "1. ネットワークは信頼できる",        // → タイムアウト、リトライが必要
  "2. レイテンシはゼロである",           // → リモート呼び出しはローカルの100〜1000倍遅い
  "3. 帯域幅は無限である",              // → ペイロードサイズを最適化すべき
  "4. ネットワークは安全である",          // → 常に認証・暗号化が必要
  "5. トポロジは変化しない",            // → サービスディスカバリが必要
  "6. 管理者は1人である",              // → 複数チームの責任分界が必要
  "7. トランスポートコストはゼロ",        // → シリアライズ/デシリアライズのコスト
  "8. ネットワークは均質である",          // → 異なるプロトコル、バージョンの混在
];

誤解がコードに与える影響

Fallacy 1: ネットワークは信頼できる

// BAD: ネットワーク障害を考慮していない
async function getUser(id: string): Promise<User> {
  const response = await fetch(`http://user-service/users/${id}`);
  return response.json();
}

// GOOD: リトライ、タイムアウト、サーキットブレーカーを考慮
async function getUser(id: string): Promise<User> {
  return await retry(
    async () => {
      const controller = new AbortController();
      const timeout = setTimeout(() => controller.abort(), 3000);

      try {
        const response = await fetch(`http://user-service/users/${id}`, {
          signal: controller.signal,
        });
        if (!response.ok) throw new Error(`HTTP ${response.status}`);
        return response.json();
      } finally {
        clearTimeout(timeout);
      }
    },
    { maxAttempts: 3, backoff: "exponential" }
  );
}

Fallacy 2: レイテンシはゼロである

// BAD: N+1問題をリモート呼び出しで再現
async function getOrderDetails(orderId: string) {
  const order = await orderService.getOrder(orderId);       // 1回のリモート呼び出し
  const user = await userService.getUser(order.userId);      // 2回目
  const products = await Promise.all(
    order.items.map(item => productService.getProduct(item.productId)) // N回!
  );
  return { order, user, products };
}

// GOOD: バッチ取得でリモート呼び出しを最小化
async function getOrderDetails(orderId: string) {
  const order = await orderService.getOrder(orderId);
  const [user, products] = await Promise.all([
    userService.getUser(order.userId),
    productService.getProductsBatch(order.items.map(i => i.productId)),
  ]);
  return { order, user, products };
}

PACELC定理

CAP定理を拡張した理論で、分断がない通常時のトレードオフも考慮します。

P(分断時): A(可用性)か C(一貫性)を選ぶ
E(通常時): L(レイテンシ)か C(一貫性)を選ぶ
分類分断時通常時
PA/EL可用性優先レイテンシ優先DynamoDB, Cassandra
PC/EC一貫性優先一貫性優先MongoDB(デフォルト設定)
PA/EC可用性優先一貫性優先設定次第で多くのDBが該当

まとめ

ポイント内容
CAP定理C, A, P のうち同時に2つまで
現実的な選択Pは避けられないのでCPかAP
8つの誤解ネットワークの信頼性を過信しない
PACELC通常時のレイテンシ vs 一貫性も考慮

チェックリスト

  • CAP定理の3つの特性を説明できる
  • CPとAPの違いを具体例で説明できる
  • 8つの誤解のうち主要なものを3つ以上挙げられる
  • PACELC定理の概要を理解した

次のステップへ

理論を学んだところで、次はマイクロサービスの具体的なメリットとリスクを見ていきます。どんな場面で採用し、どんな場面で避けるべきかを判断する力をつけましょう。


推定読了時間: 25分