cloudflare-workers-performance について
Cloudflare Workers の最適化に取り組む開発者向けのリファレンススキルです。CPU、メモリ、レイテンシ、キャッシュ、バンドルサイズの手法に加え、関連資料、テンプレート、プロファイリングスクリプトを扱います。
主な機能
- 01CPUとホットパスの最適化
- 02ストリーミングとメモリ管理
- 03Cache APIとバッチ処理
- 04バンドルサイズとコールドスタートの指針
- 05テンプレートとプロファイリングスクリプト
活用シーン
- 01WorkerのCPU使用量とレイテンシの削減
- 02大きなペイロードをバッファリングせず処理
- 03メモリやコールドスタートの問題を調査
- 04キャッシュやパフォーマンス監視を追加
インストール
コマンドをコピーしてターミナルで実行します。
$
npx skills add https:/ / github.com/ secondsky/ claude-skills --skill cloudflare-workers-performanceインストール後、Claude Code で/cloudflare-workers-performanceを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
references/bundle-optimization.mdcaching-strategies.mdcold-starts.mdcpu-optimization.mdmemory-optimization.mdscripts/benchmark.shprofile-worker.shtemplates/caching-layer.tsoptimized-worker.tsperformance-middleware.ts詳細
Cloudflare Workers のパフォーマンス最適化
Worker のパフォーマンスを最大化し、レイテンシを最小限に抑えるための手法です。
すぐにできる改善
// 1. Avoid unnecessary cloning
// ❌ Bad: Clones entire request
const body = await request.clone().json();
// ✅ Good: Parse directly when not re-using body
const body = await request.json();
// 2. Use streaming instead of buffering
// ❌ Bad: Buffers entire response
const text = await response.text();
return new Response(transform(text));
// ✅ Good: Stream transformation
return new Response(response.body.pipeThrough(new TransformStream({
transform(chunk, controller) {
controller.enqueue(process(chunk));
}
})));
// 3. Cache expensive operations
const cache = caches.default;
const cached = await cache.match(request);
if (cached) return cached;
重要なルール
- CPU 制限内に収める - 10ms(無料プラン)、30ms(有料プラン)、50ms(無制限プラン)
- コールドスタートを最小限に抑える - バンドルサイズを 1MB 未満にし、動的インポートを避けます
- Cache API を使用する - エッジでレスポンスをキャッシュします
- 大きなペイロードはストリーミングする - レスポンス全体をバッファリングしないでください
- 操作をまとめる - 複数の KV/D1 呼び出しをまとめます
パフォーマンスに関する主なエラー 10 件
| エラー | 症状 | 対処法 |
|---|---|---|
| CPU 制限の超過 | Worker が終了する | ホットパスを最適化し、ストリーミングを使用します |
| コールドスタートのレイテンシ | 最初のリクエストが遅い | バンドルサイズを小さくし、トップレベルの await を避けます |
| メモリ負荷 | GC の遅延、タイムアウト | データをストリーミングし、大きな配列を避けます |
| KV のレイテンシ | 読み取りが遅い | Cache API を使用し、読み取りをまとめます |
| D1 のクエリが遅い | レイテンシが高い | インデックスを追加し、SQL を最適化します |
| バンドルサイズが大きい | コールドスタートが遅い | ツリーシェイキングを行い、コードを分割します |
| ブロッキング操作 | リクエストのタイムアウト | Promise.all とストリーミングを使用します |
| 不要なクローン作成 | メモリの急増 | 必要な場合にのみクローンを作成します |
| キャッシュがない | 計算の繰り返し | キャッシュ層を実装します |
| 同期操作 | CPU の急増 | 非同期の代替手段を使用します |
CPU の最適化
ホットパスのプロファイリング
async function profiledHandler(request: Request): Promise<Response> {
const timing: Record<string, number> = {};
const time = async <T>(name: string, fn: () => Promise<T>): Promise<T> => {
const start = Date.now();
const result = await fn();
timing[name] = Date.now() - start;
return result;
};
const data = await time('fetch', () => fetchData());
const processed = await time('process', () => processData(data));
const response = await time('serialize', () => serialize(processed));
console.log('Timing:', timing);
return new Response(response);
}
JSON 操作の最適化
// For large JSON, use streaming parser
import { JSONParser } from '@streamparser/json';
async function parseStreamingJSON(stream: ReadableStream): Promise<unknown[]> {
const parser = new JSONParser();
const results: unknown[] = [];
parser.onValue = (value) => results.push(value);
for await (const chunk of stream) {
parser.write(chunk);
}
return results;
}
メモリの最適化
大きな配列を避ける
// ❌ Bad: Loads all into memory
const items = await db.prepare('SELECT * FROM items').all();
const processed = items.results.map(transform);
// ✅ Good: Process in batches
async function* batchProcess(db: D1Database, batchSize = 100) {
let offset = 0;
while (true) {
const { results } = await db
.prepare('SELECT * FROM items LIMIT ? OFFSET ?')
.bind(batchSize, offset)
.all();
if (results.length === 0) break;
for (const item of results) {
yield transform(item);
}
offset += batchSize;
}
}
キャッシュ戦略
マルチレイヤーキャッシュ
interface CacheLayer {
get(key: string): Promise<unknown | null>;
set(key: string, value: unknown, ttl?: number): Promise<void>;
}
// Layer 1: In-memory (request-scoped)
const memoryCache = new Map<string, unknown>();
// Layer 2: Cache API (edge-local)
const edgeCache: CacheLayer = {
async get(key) {
const response = await caches.default.match(new Request(`https://cache/${key}`));
return response ? response.json() : null;
},
async set(key, value, ttl = 60) {
await caches.default.put(
new Request(`https://cache/${key}`),
new Response(JSON.stringify(value), {
headers: { 'Cache-Control': `max-age=${ttl}` }
})
);
}
};
// Layer 3: KV (global)
// Use env.KV.get/put
バンドルの最適化
// 1. Tree-shake imports
// ❌ Bad
import * as lodash from 'lodash';
// ✅ Good
import { debounce } from 'lodash-es';
// 2. Lazy load heavy dependencies
let heavyLib: typeof import('heavy-lib') | undefined;
async function getHeavyLib() {
if (!heavyLib) {
heavyLib = await import('heavy-lib');
}
return heavyLib;
}
リファレンスを読み込むタイミング
タスクに応じて、必要なリファレンスを読み込みます。
- CPU 使用率を最適化しますか? →
references/cpu-optimization.mdを読み込みます - メモリに関する問題ですか? →
references/memory-optimization.mdを読み込みます - キャッシュを実装しますか? →
references/caching-strategies.mdを読み込みます - バンドルサイズを小さくしますか? →
references/bundle-optimization.mdを読み込みます - コールドスタートの問題ですか? →
references/cold-starts.mdを読み込みます
テンプレート
| テンプレート | 用途 | 使用するタイミング |
|---|---|---|
templates/performance-middleware.ts | パフォーマンスの監視 | タイミング測定/プロファイリングの追加 |
templates/caching-layer.ts | マルチレイヤーキャッシュ | キャッシュの実装 |
templates/optimized-worker.ts | パフォーマンスパターン | 最適化された Worker の開始 |
スクリプト
| スクリプト | 用途 | コマンド |
|---|---|---|
scripts/benchmark.sh | 負荷テスト | ./benchmark.sh <url> |
scripts/profile-worker.sh | CPU プロファイリング | ./profile-worker.sh |
リソース
コメント
Markdown 対応