Skip to content

perf: batch DB operations in ingest and plan list to cut CPU load - #5

Draft
dmnlk wants to merge 1 commit into
satetsu888:mainfrom
dmnlk:claude/investigate-cpu-usage-iBZr7
Draft

perf: batch DB operations in ingest and plan list to cut CPU load#5
dmnlk wants to merge 1 commit into
satetsu888:mainfrom
dmnlk:claude/investigate-cpu-usage-iBZr7

Conversation

@dmnlk

@dmnlk dmnlk commented Apr 21, 2026

Copy link
Copy Markdown

背景

CPU 高騰の原因調査で、以下 2 箇所のホットパスに深刻な N+1 / 直列 DB アクセスがあることが判明したため修正します。

1. /api/ingest の直列 INSERT

PostToolUse hook が頻繁に発火するため、ingest は最も叩かれるエンドポイントです。
従来は transcript の行数ぶん Event.Create を直列で呼んでおり、1 リクエストで N 回の DB ラウンドトリップが発生していました。

2. Plan 一覧の多段 N+1

1 Plan につき以下を個別クエリで発行していました:

  • GetCollaboratorUserIDs (1 回)
  • 共同作業者ごとに User.FindByID ← ネストループ
  • Project.FindByID
  • PlanCommentThread.FindByPlanDocumentID  件数しか使わないのに全スレッドを取得

100 件 × 3 共同作業者の前提だと、1 リクエストで 400+ クエリ 発行していました。

変更点

Repository 層 (interface + 5 バックエンド + testsuite)

新規メソッド:

メソッド | 用途 -- | -- EventRepository.CreateBatch | トランザクション / 多値 INSERT による一括挿入。duplicate UUID は静かに skip UserRepository.FindByIDs | 複数 ID をまとめて取得 (map[id]*User を返却) ProjectRepository.FindByIDs | 同上 PlanCommentThreadRepository.CountActiveByPlanDocumentID | SELECT COUNT(*) で件数のみ取得

テスト方法

  •  go build ./... / go vet ./... 成功
  •  go test ./... (memory / sqlite / api) 全パス
  •  go build -tags=integration ./... 成功
  •  TURSO_URL=file:... で turso の integration テスト全パス
  •  Postgres / DynamoDB の integration テストは Docker 環境が必要なためレビュアー側で検証お願いします

影響範囲

  • Repository インタフェースにメソッド追加のみ (既存シグネチャ変更なし)
  • 外部 API のレスポンス形状に変更なし
  • マイグレーション不要

The ingest endpoint previously issued one INSERT per transcript line, and
the plan list endpoint issued a nested sequence of User.FindByID,
Project.FindByID, and a full FindByPlanDocumentID scan per plan. Both
compounded into O(N) round-trips on hot paths, pegging DB CPU.

Repository additions:
- EventRepository.CreateBatch: single transaction / multi-row INSERT
- UserRepository.FindByIDs and ProjectRepository.FindByIDs
- PlanCommentThreadRepository.CountActiveByPlanDocumentID (COUNT only)

Handler changes:
- /api/ingest now builds all events once and persists them in one call.
  Duplicate UUIDs are still silently skipped.
- Plan list prefetches users and projects in two batch queries and
  counts comment threads via the new COUNT query instead of fetching
  full thread rows.

Implemented for memory, sqlite, postgres, turso, and dynamodb backends,
with matching testsuite coverage that runs across all implementations.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants