ストーリー
高橋アーキテクトが新たなミッションを提示した。
ミッション概要
| ミッション | テーマ | 難易度 |
|---|---|---|
| Mission 1 | 現状のボトルネック分析 | 初級 |
| Mission 2 | ステートレス化の設計 | 中級 |
| Mission 3 | データベーススケーリング戦略 | 中級 |
| Mission 4 | 非同期処理の導入 | 中級 |
| Mission 5 | オートスケーリング設計 | 上級 |
| Mission 6 | 総合アーキテクチャ図 | 上級 |
Mission 1: 現状のボトルネック分析(10分)
以下の現状アーキテクチャのボトルネックを洗い出してください。
現状アーキテクチャ:
- App Server: 1台(Node.js、セッションをメモリに保存)
- Database: PostgreSQL 1台(読み書き両方)
- ファイル保存: ローカルディスク
- メール送信: API内で同期実行
- キャッシュ: なし
解答例
| ボトルネック | 問題 | 影響 |
|---|---|---|
| セッションのメモリ保存 | 水平スケーリング不可 | スケールアウトできない |
| DB 1台 | 読み書き集中 | 負荷増で応答遅延 |
| ローカルファイル保存 | サーバー固有の状態 | スケールアウト不可 |
| 同期メール送信 | API応答遅延 | ユーザー体験の悪化 |
| キャッシュなし | 全リクエストがDBへ | 不要なDB負荷 |
| 単一サーバー | SPOF | 障害で全サービス停止 |
改善の優先順位:
- キャッシュ導入(最も効果が大きい)
- セッション外部化(スケーリングの前提条件)
- メール送信の非同期化(簡単に実装可能)
- Read Replica導入
- ファイルストレージ外部化
- 水平スケーリング + ロードバランサー
Mission 2: ステートレス化の設計(15分)
現状のステートフルなアプリケーションをステートレスに変換する設計を行ってください。
対象
- セッション管理(メモリ → ?)
- ファイルアップロード(ローカルディスク → ?)
- 定期バッチ処理(各サーバーのcron → ?)
解答例
// 1. セッション管理: Redis に外部化
// Before
app.use(session({ store: new MemoryStore() }));
// After
app.use(session({
store: new RedisStore({
client: redisClient,
prefix: 'session:',
ttl: 1800, // 30分
}),
secret: process.env.SESSION_SECRET,
resave: false,
saveUninitialized: false,
}));
// 2. ファイルアップロード: S3 に外部化
// Before
const storage = multer.diskStorage({
destination: '/uploads/',
});
// After
const storage = multerS3({
s3: s3Client,
bucket: 'speedshop-uploads',
key: (req, file, cb) => {
cb(null, `uploads/${Date.now()}-${file.originalname}`);
},
});
// 3. 定期バッチ処理: 専用ワーカーサービスに分離
// Before: 各サーバーの crontab
// * * * * * node /app/scripts/cleanup.js
// After: 専用ワーカー(1台のみ)
// docker-compose.yml
// services:
// worker:
// image: speedshop-worker
// deploy:
// replicas: 1 # バッチ処理は1台のみ
// command: node worker.js
Mission 3: データベーススケーリング戦略(20分)
SpeedShopのデータベース構成を設計してください。
前提情報
- 現在のDB負荷: 読み取り80%、書き込み20%
- テーブル: users(50万件)、products(5万件)、orders(200万件)、order_items(500万件)
- 最も重いクエリ: 注文履歴検索、商品ランキング集計
解答例
// Phase 1: Read Replica 導入(即効性あり)
const dbConfig = {
primary: {
host: 'db-primary.internal',
role: 'write + critical reads',
},
replicas: [
{ host: 'db-replica-1.internal', role: 'general reads' },
{ host: 'db-replica-2.internal', role: 'general reads' },
],
};
// ルーティングルール
const routingRules = {
// Primary(書き込み + 整合性が必要な読み取り)
primary: [
'INSERT/UPDATE/DELETE (全テーブル)',
'注文確定直後の注文情報取得',
'在庫数の確認(正確な値が必要)',
],
// Replica(読み取り)
replica: [
'商品一覧/詳細の取得',
'注文履歴の参照',
'検索クエリ',
'ランキング集計',
],
};
// Phase 2: 重いクエリの分離
const queryOptimization = {
// ランキング集計 → Materialized View + 定期更新
ranking: {
strategy: 'Materialized View',
refreshInterval: '10分ごと',
query: 'REFRESH MATERIALIZED VIEW CONCURRENTLY product_rankings',
},
// 注文履歴検索 → 適切なインデックス
orderHistory: {
index: 'CREATE INDEX idx_orders_user_date ON orders(user_id, created_at DESC)',
},
};
// Phase 3: 将来的なシャーディング計画(必要時のみ)
const shardingPlan = {
target: 'orders, order_items',
shardKey: 'user_id',
strategy: 'hash-based',
timing: '注文数が1000万件を超えた時点で検討',
};
Mission 4: 非同期処理の導入(15分)
注文処理フローを同期/非同期に分離してください。
現在の注文処理フロー(全て同期)
- 注文データ作成(DB)
- 決済処理(外部API)
- 在庫引き当て(DB)
- 確認メール送信(外部API)
- レシートPDF生成(CPU負荷)
- 在庫アラート確認(条件付き外部API)
- ポイント付与(DB)
- 分析イベント送信(外部API)
解答例
// 同期(ユーザーが待つ必要がある処理)
async function placeOrderSync(data: OrderData): Promise<OrderResult> {
// 1. 注文データ作成
const order = await orderRepo.create(data);
// 2. 決済処理(失敗したら注文キャンセル)
try {
await paymentService.charge(order.totalAmount, data.paymentMethod);
} catch (error) {
await orderRepo.cancel(order.id);
throw new PaymentFailedError(error.message);
}
// 3. 在庫引き当て(失敗したら返金 + キャンセル)
try {
await inventoryService.reserve(order.items);
} catch (error) {
await paymentService.refund(order.id);
await orderRepo.cancel(order.id);
throw new OutOfStockError(error.message);
}
return { orderId: order.id, status: 'confirmed' };
// ここまで約700ms
}
// 非同期(バックグラウンドで処理)
async function publishOrderEvents(orderId: string): Promise<void> {
await messageQueue.publish('order.confirmed', { orderId });
}
// ワーカー側の処理
class OrderConfirmedWorker {
async handle(event: OrderConfirmedEvent): Promise<void> {
const order = await orderRepo.findById(event.orderId);
// 4. 確認メール送信
await emailService.sendConfirmation(order);
// 5. レシートPDF生成
await receiptService.generate(order);
// 6. 在庫アラート確認
for (const item of order.items) {
const stock = await inventoryService.getStock(item.productId);
if (stock < 10) {
await alertService.lowStock(item.productId, stock);
}
}
// 7. ポイント付与
await pointService.award(order.userId, order.totalAmount);
// 8. 分析イベント
await analyticsService.trackPurchase(order);
}
}
効果: レスポンスタイム 2000ms以上 → 約700ms(65%改善)
Mission 5: オートスケーリング設計(15分)
SpeedShopのオートスケーリング設定を設計してください。
要件
- 通常時: 月間100万PV(約4 RPS平均)
- ピーク時: セール時に通常の20倍(約80 RPS平均、バースト500 RPS)
- コスト最適化: 閑散期にスケールダウン
解答例
const autoScalingConfig = {
appServer: {
minInstances: 2, // 最小2台(冗長性確保)
maxInstances: 20, // 最大20台
desiredInstances: 3, // 通常時3台
scaleOutPolicy: {
metric: 'CPUUtilization',
threshold: 60, // CPU 60%超えで追加
cooldown: 180, // 3分のクールダウン
step: 2, // 2台ずつ追加
},
scaleInPolicy: {
metric: 'CPUUtilization',
threshold: 25, // CPU 25%以下で削減
cooldown: 600, // 10分のクールダウン(慎重に)
step: 1, // 1台ずつ削減
},
// セール時の事前スケーリング
scheduledScaling: [
{
name: 'sale-preparation',
schedule: 'cron(55 23 * * *)' , // セール前日23:55
desiredCapacity: 15,
},
{
name: 'post-sale-normalize',
schedule: 'cron(0 6 * * *)', // 翌朝6:00
desiredCapacity: 3,
},
],
},
workerServer: {
minInstances: 1,
maxInstances: 10,
// キューの深さに基づくスケーリング
scaleOutPolicy: {
metric: 'QueueDepth',
threshold: 1000, // キューに1000メッセージ以上
step: 2,
},
},
};
Mission 6: 総合アーキテクチャ図(15分)
10倍トラフィックに耐えるSpeedShopの最終アーキテクチャをテキストで描いてください。
解答例
=== SpeedShop スケーラブルアーキテクチャ ===
[CloudFront CDN]
│ 静的アセット配信、APIレスポンスキャッシュ
▼
[Application Load Balancer]
│ ラウンドロビン + ヘルスチェック
▼
[App Server × 2-20台] (Auto Scaling Group)
│ ステートレス設計
│ JWT認証 or Redis Session
├──→ [Redis Cluster]
│ セッション、キャッシュ、ジョブキュー
│
├──→ [Message Queue (SQS)]
│ │ 注文イベント、メール送信、分析
│ ▼
│ [Worker × 1-10台] (Auto Scaling Group)
│ メール、PDF生成、ポイント付与
│
└──→ [Database]
├─ Primary (Write + Critical Read)
├─ Replica 1 (Read)
└─ Replica 2 (Read)
[S3]
ユーザーアップロード、レシートPDF
[CloudWatch]
メトリクス監視、アラート、Auto Scalingトリガー
キャパシティ:
通常時: App 3台 + Worker 1台 = 約200 RPS
ピーク時: App 15台 + Worker 5台 = 約3000 RPS
最大: App 20台 + Worker 10台 = 約5000 RPS
達成度チェック
| ミッション | テーマ | 完了 |
|---|---|---|
| Mission 1 | ボトルネック分析 | [ ] |
| Mission 2 | ステートレス化 | [ ] |
| Mission 3 | DBスケーリング | [ ] |
| Mission 4 | 非同期処理導入 | [ ] |
| Mission 5 | オートスケーリング | [ ] |
| Mission 6 | 総合アーキテクチャ | [ ] |
チェックリスト
- 現状のボトルネックを体系的に分析できる
- ステートレス化の具体的な手法を実装できる
- Read Replicaを含むDB構成を設計できる
- 同期/非同期の処理分離を判断・設計できる
- オートスケーリングポリシーを設計できる
次のステップへ
お疲れさまでした。スケーラブルなアーキテクチャ設計の実践力が身についたはずです。
次のセクションでは、Step 4の理解度チェックです。
推定所要時間: 90分