はじめに
「同じAPIに同時に100リクエストが来てDBに100回同じクエリが走る」——リクエストコアレッシング(Request Coalescing)で同一クエリを1回にまとめ、結果を全リクエスターに返す設計をClaude Codeに生成させる。
CLAUDE.mdに設計ルールを書く
## リクエストコアレッシング設計ルール
### 適用条件
- 同一キーに対して複数の同時リクエストが予想されるエンドポイント
- キャッシュミス時のDB/外部API呼び出しが重い処理
- 副作用なしで共有できる読み取りクエリ
### 実装方式
- インメモリMap + Promise: 同一プロセス内の重複排除
- Redis Lock + Pub/Sub: 複数サーバーインスタンス間の重複排除
- DataLoader: GraphQL/バッチ読み取り向けN+1排除
生成される実装(抜粋)
// インプロセスコアレッシング
export class InProcessCoalescer<T> {
private readonly inflight = new Map<string, Promise<T>>();
async dedupe(key: string, fn: () => Promise<T>): Promise<T> {
const existing = this.inflight.get(key);
if (existing) {
metrics.counter('coalescing_saved_calls', 1, { key });
return existing; // 進行中のPromiseを共有
}
const promise = fn();
this.inflight.set(key, promise);
try {
return await promise;
} finally {
this.inflight.delete(key); // 完了後に削除
}
}
}
// 分散コアレッシング(Redis)
export class DistributedCoalescer<T> {
async dedupe(key: string, fn: () => Promise<T>): Promise<T> {
return this.localCoalescer.dedupe(key, async () => {
const cached = await redis.get(resultKey);
if (cached) return JSON.parse(cached);
const lockAcquired = await redis.set(lockKey, '1', { NX: true, PX: 10_000 });
if (lockAcquired) {
const result = await fn();
await redis.set(resultKey, JSON.stringify(result), { PX: 30_000 });
await redis.publish(`${lockKey}:done`, '1'); // ウェイターに通知
return result;
} else {
return this.waitForResult(resultKey, lockKey); // 完了を待機
}
});
}
}
// BatchCoalescer(DataLoader方式)
const userBatchLoader = new BatchCoalescer<string, User>(async (ids) => {
const users = await prisma.user.findMany({ where: { id: { in: ids } } });
return new Map(users.map(u => [u.id, u]));
}, 1 /* 1msバッチウィンドウ */);
// N+1が自動的にバッチ化される
const resolvers = { Order: { user: (order) => userBatchLoader.load(order.userId) } };
まとめ
- CLAUDE.md に適用条件(読み取り・副作用なし)・インプロセス/分散の使い分け・DataLoaderによるN+1排除を明記
- InProcessCoalescer でMap+Promiseの共有——同一プロセス内なら追加依存なしで実装可能
- DistributedCoalescer でRedis SET NX + Pub/Sub——マルチサーバー環境でも1台だけ計算し結果を全サーバーに配布
- BatchCoalescer(DataLoader方式) で1msウィンドウ内の複数loadを1つのIN句クエリに——GraphQLのN+1問題を透過的に解決
パフォーマンス設計のレビューは **Code Review Pack(¥980)* の /code-review で確認できます。*
みょうが (@myougatheaxo) — ウーパールーパーのVTuber。
Top comments (0)