civicship-api パフォーマンス比較
コード変更前後のパフォーマンスを比較し、リグレッション(性能劣化)を検出します。ベンチマーク結果、クエリ数、レスポンスタイムを分析します。
使用方法
bash
1# PRのパフォーマンス比較(自動)
2/performance-diff --pr 123
3
4# ブランチ間の比較
5/performance-diff --before main --after feature/point-expiration
6
7# 特定ドメインの比較
8/performance-diff wallet --before HEAD~1 --after HEAD
引数:
$ARGUMENTS: ドメイン名、PRでは番号、または --before/--after オプション
パフォーマンス比較プロセス
ステップ1: 変更内容の取得
GitでのDiffを取得:
bash
1# PRの差分取得
2gh pr diff 123
3
4# ブランチ間の差分
5git diff main..feature/point-expiration
6
7# 変更されたファイルのみ
8git diff --name-only main..feature/point-expiration
変更サマリー:
markdown
1## 変更概要
2
3### 変更されたファイル(10ファイル)
4
5**Application Layer:**
6- `src/application/domain/account/wallet/service.ts` (+50, -10)
7- `src/application/domain/account/wallet/usecase.ts` (+20, -5)
8- `src/application/domain/account/wallet/data/repository.ts` (+30, -8)
9
10**Infrastructure Layer:**
11- `prisma/schema.prisma` (+5, -0)
12
13**Presentation Layer:**
14- `src/application/domain/account/wallet/schema/type.graphql` (+10, -0)
15
16---
17
18### 変更の種類
19
20- [ ] データベーススキーマ変更(カラム追加)
21- [ ] 新しいクエリ追加
22- [ ] ビジネスロジック変更
23- [ ] GraphQL API拡張
ステップ2: パフォーマンステストの実行
変更前後でベンチマークを実行:
bash
1# 変更前(main)のパフォーマンステスト
2git checkout main
3pnpm test:performance > performance-before.txt
4
5# 変更後(feature branch)のパフォーマンステスト
6git checkout feature/point-expiration
7pnpm test:performance > performance-after.txt
8
9# 差分比較
10diff performance-before.txt performance-after.txt
パフォーマンステスト結果:
markdown
1## パフォーマンステスト結果
2
3### Test: GraphQL Query `wallet(id: "...")`
4
5#### 変更前(main)
6\`\`\`
7Requests: 1000
8Success: 1000 (100%)
9Duration: 5.2s
10Avg: 5.2ms
11P50: 4.8ms
12P95: 8.5ms
13P99: 12.3ms
14\`\`\`
15
16#### 変更後(feature/point-expiration)
17\`\`\`
18Requests: 1000
19Success: 1000 (100%)
20Duration: 5.5s
21Avg: 5.5ms (+5.8%)
22P50: 5.0ms (+4.2%)
23P95: 9.0ms (+5.9%)
24P99: 13.1ms (+6.5%)
25\`\`\`
26
27**評価:** 🟡 軽微な劣化(+5.8%)
28**原因:** 有効期限チェックの追加(日付比較)
29**許容範囲:** ✅ Yes(+10%以内)
30
31---
32
33### Test: GraphQL Mutation `walletCreate`
34
35#### 変更前
36\`\`\`
37Requests: 100
38Duration: 2.1s
39Avg: 21ms
40\`\`\`
41
42#### 変更後
43\`\`\`
44Requests: 100
45Duration: 2.3s
46Avg: 23ms (+9.5%)
47\`\`\`
48
49**評価:** 🟡 軽微な劣化(+9.5%)
50**原因:** expiresAt カラムの書き込み追加
51**許容範囲:** ✅ Yes(+10%以内)
52
53---
54
55### Test: Batch Process `expirePoints`
56
57#### 変更前
58\`\`\`
59該当なし(新規処理)
60\`\`\`
61
62#### 変更後
63\`\`\`
64Wallets Processed: 1000
65Duration: 8.5s
66Avg per wallet: 8.5ms
67\`\`\`
68
69**評価:** 🟢 新規処理(ベースラインとして記録)
ステップ3: クエリ数の比較
SQLクエリ数の変化を分析:
markdown
1## クエリ数の比較
2
3### GraphQL Query `wallet(id: "...")`
4
5#### 変更前
6\`\`\`
7SELECT * FROM t_wallets WHERE id = '...' -- 1クエリ
8\`\`\`
9
10**Total:** 1クエリ
11
12---
13
14#### 変更後
15\`\`\`
16SELECT * FROM t_wallets WHERE id = '...' -- 1クエリ
17\`\`\`
18
19**Total:** 1クエリ(変更なし)
20
21**評価:** ✅ クエリ数増加なし
22
23---
24
25### GraphQL Mutation `pointTransfer`
26
27#### 変更前
28\`\`\`
291. SELECT * FROM t_wallets WHERE userId = 'from-user'
302. SELECT * FROM t_wallets WHERE userId = 'to-user'
313. UPDATE t_wallets SET balance = ... WHERE id = 'from-wallet'
324. UPDATE t_wallets SET balance = ... WHERE id = 'to-wallet'
335. INSERT INTO t_point_transactions ...
34\`\`\`
35
36**Total:** 5クエリ
37
38---
39
40#### 変更後
41\`\`\`
421. SELECT * FROM t_wallets WHERE userId = 'from-user'
432. SELECT * FROM t_wallets WHERE userId = 'to-user'
44 (有効期限チェック追加: メモリ内で実行、クエリ不要)
453. UPDATE t_wallets SET balance = ... WHERE id = 'from-wallet'
464. UPDATE t_wallets SET balance = ... WHERE id = 'to-wallet'
475. INSERT INTO t_point_transactions ...
48\`\`\`
49
50**Total:** 5クエリ(変更なし)
51
52**評価:** ✅ クエリ数増加なし
ステップ4: N+1問題の検出
新たにN+1問題が導入されていないか確認:
markdown
1## N+1問題の検出
2
3### 検証: User.wallets
4
5**変更内容:**
6\`\`\`diff
7User: {
8 wallet: (parent, _, ctx) => {
9- return prisma.t_wallets.findUnique({ where: { userId: parent.id } });
10+ return ctx.loaders.wallet.load(parent.id);
11 }
12}
13\`\`\`
14
15**評価:**
16- 変更前: N+1問題あり
17- 変更後: DataLoader使用、N+1問題解消 ✅
18
19**パフォーマンス改善:**
20- 100ユーザー取得時のクエリ数: 100回 → 1回
21- レスポンスタイム: 5秒 → 50ms(100倍改善)
22
23**評価:** 🟢 大幅改善
ステップ5: データベースインデックスの影響
インデックス追加によるパフォーマンス改善を確認:
markdown
1## インデックスの影響
2
3### 追加されたインデックス
4
5\`\`\`prisma
6model t_wallets {
7 // ...
8+ @@index([expiresAt])
9}
10\`\`\`
11
12---
13
14### クエリパフォーマンス比較
15
16#### Query: `SELECT * FROM t_wallets WHERE expiresAt < NOW()`
17
18**変更前(インデックスなし):**
19\`\`\`
20Seq Scan on t_wallets (cost=0.00..25.50 rows=10 width=100)
21Execution Time: 45.2 ms
22\`\`\`
23
24**変更後(インデックスあり):**
25\`\`\`
26Index Scan using idx_wallets_expires_at on t_wallets (cost=0.15..8.17 rows=10 width=100)
27Execution Time: 2.1 ms
28\`\`\`
29
30**評価:** 🟢 大幅改善(21倍高速化)
ステップ6: メモリ使用量の比較
メモリフットプリントの変化:
markdown
1## メモリ使用量
2
3### ヒープメモリ
4
5**変更前:**
6- Used Heap: 120 MB
7- Heap Limit: 512 MB
8- Usage: 23%
9
10**変更後:**
11- Used Heap: 125 MB (+4.2%)
12- Heap Limit: 512 MB
13- Usage: 24%
14
15**評価:** 🟢 許容範囲内(+5%以下)
16
17---
18
19### オブジェクト数
20
21**変更前:**
22- Total Objects: 150,000
23
24**変更後:**
25- Total Objects: 152,000 (+1.3%)
26
27**評価:** 🟢 微増(問題なし)
ステップ7: リグレッション判定
総合的なパフォーマンス評価:
markdown
1## リグレッション判定
2
3### 判定基準
4
56|-----------|------|--------|--------|------|------|
7| Avg Response Time | +10% | 5.2ms | 5.5ms | +5.8% | ✅ Pass |
8| P95 Response Time | +15% | 8.5ms | 9.0ms | +5.9% | ✅ Pass |
9| P99 Response Time | +20% | 12.3ms | 13.1ms | +6.5% | ✅ Pass |
10| Query Count | +0 | 5 | 5 | 0 | ✅ Pass |
11| Memory Usage | +10% | 120MB | 125MB | +4.2% | ✅ Pass |
12| Error Rate | +0% | 0% | 0% | 0% | ✅ Pass |
13
14---
15
16### 総合評価
17
18**スコア:** 95 / 100 🟢 Excellent
19
20**判定:** ✅ **パフォーマンスリグレッションなし**
21
22**コメント:**
23- 軽微な劣化はあるが、全て許容範囲内
24- 有効期限チェックの追加によるオーバーヘッドは予想通り
25- インデックス追加により一部クエリは大幅改善
26- N+1問題の解消により全体的なパフォーマンス向上
27
28**推奨アクション:**
29- マージ可能
30- 本番環境での監視継続
ステップ8: パフォーマンス改善の提案
さらなる最適化の提案:
markdown
1## パフォーマンス改善提案
2
3### 提案1: キャッシュ導入
4
5**現状:**
6- 有効期限チェックを毎回実行
7
8**提案:**
9\`\`\`typescript
10// Redis キャッシュ(TTL: 60秒)
11const cacheKey = \`wallet:expiration:\${walletId}\`;
12const cached = await redis.get(cacheKey);
13if (cached) return JSON.parse(cached);
14
15const wallet = await this.repo.findById(ctx, walletId, tx);
16await redis.setex(cacheKey, 60, JSON.stringify(wallet));
17return wallet;
18\`\`\`
19
20**効果:**
21- レスポンスタイム: 5.5ms → 0.5ms(11倍改善)
22- データベース負荷: 90%削減
23
24---
25
26### 提案2: SELECT句の最適化
27
28**現状:**
29\`\`\`typescript
30return tx.t_wallets.findUnique({ where: { id } });
31\`\`\`
32
33**提案:**
34\`\`\`typescript
35return tx.t_wallets.findUnique({
36 where: { id },
37 select: {
38 id: true,
39 balance: true,
40 expiresAt: true,
41 // 必要なフィールドのみ
42 }
43});
44\`\`\`
45
46**効果:**
47- データ転送量: 80%削減
48- メモリ使用量: 削減
ステップ9: 比較レポート生成
markdown
1# パフォーマンス比較レポート
2
3**PR:** #123
4**ブランチ:** feature/point-expiration
5**比較:** main vs feature/point-expiration
6**実施日:** 2026-01-15
7
8---
9
10## エグゼクティブサマリー
11
12### 総合評価
13
14**判定:** ✅ **マージ可能**
15
16**スコア:** 95 / 100
17
18**リグレッション:** なし(軽微な劣化は許容範囲内)
19
20---
21
22## 詳細比較
23
24### レスポンスタイム
25
2627|---------------|--------|--------|------|------|
28| wallet(id) | 5.2ms | 5.5ms | +5.8% | ✅ |
29| walletCreate | 21ms | 23ms | +9.5% | ✅ |
30| pointTransfer | 45ms | 46ms | +2.2% | ✅ |
31
32---
33
34### クエリ数
35
3637|---------------|--------|--------|------|------|
38| wallet(id) | 1 | 1 | 0 | ✅ |
39| pointTransfer | 5 | 5 | 0 | ✅ |
40
41---
42
43### パフォーマンス改善
44
45- **N+1問題解消:** User.wallets(100倍改善)
46- **インデックス追加:** expiresAt クエリ(21倍改善)
47
48---
49
50## 推奨アクション
51
52### 即座に実施
53- ✅ PR をマージ可能
54- 🔔 本番環境で監視継続(24時間)
55
56### 将来的に検討
57- キャッシュ導入(さらなる最適化)
58- SELECT句の最適化
59
60---
61
62## 承認
63
64- [ ] テックリード
65- [ ] DevOpsリード
活用例
例1: PRのパフォーマンス比較
bash
1/performance-diff --pr 123
出力:
- 変更前後の比較
- リグレッション判定
- マージ可否の判断
例2: リリース前の検証
bash
1/performance-diff --before v1.0.0 --after main
出力:
参考資料