Skip to content

perf: DB 往復回数を削減して API レイテンシーを改善 (#124) - #160

Draft
bobtaroh wants to merge 4 commits into
developfrom
feature/124-reduce-db-roundtrips
Draft

perf: DB 往復回数を削減して API レイテンシーを改善 (#124)#160
bobtaroh wants to merge 4 commits into
developfrom
feature/124-reduce-db-roundtrips

Conversation

@bobtaroh

@bobtaroh bobtaroh commented Jul 4, 2026

Copy link
Copy Markdown
Collaborator

概要

#124 の対応。API レイテンシーの支配項が「DB (Neon us-east-1) との RTT (~150ms) × 1 リクエストあたりの逐次往復回数」であることを計装ログから特定し、往復回数を削減する。

関連 Issue: #124(マージ後の再計測が残るため自動クローズしない)

詳細

分析結果(staging 計装ログより)

  • DB クエリ 1 本あたり 146〜234ms でほぼ一定 → クエリの重さではなくネットワーク RTT が支配
  • GET /trips/url/{url_id}: クエリ 3〜5 本(selectinload のネスト分)で db_total 300〜460ms
  • total_ms − db_total_ms ≈ 300ms が全リクエストで一定 → cursor イベントに現れない BEGIN / ROLLBACK / pre_ping の隠れ往復
  • 非同期処理(asyncpg + AsyncSession)自体は正しく動作している(403 応答は 1ms、イベントループのブロッキングなし)

変更内容

  1. 読み系クエリの 1 クエリ化: selectinload のネスト(リレーション階層ごとに SELECT が逐次発行)→ joinedload チェーン(JOIN で 1 クエリ)。コレクションの joinedload に伴い result.unique() を追加し、一覧系は返却順が不定になるため ORDER BY を明示
  2. 読み取り専用 AUTOCOMMIT セッションの導入: GET エンドポイントと認可 Depends を get_read_db_session に切り替え、BEGIN / ROLLBACK の 2 往復を省略。プールは書き込み用エンジンと共有され、返却時に isolation はデフォルトへリセットされる(SQLAlchemy の connection characteristic 機構)
  3. pool_pre_ping 廃止 → pool_recycle=300: asyncpg dialect の pre_ping は pgbouncer 対応(sqlalchemy#10226)のため BEGIN + SELECT + ROLLBACK に包まれており、checkout ごとに最大 3 往復を消費していた。接続先は Neon の pooler (pgbouncer) なのでコンピュート suspend 後もクライアント接続は原則有効であり、stale 接続は pool_recycle で緩和する
  4. 認可 Depends の読み取り/書き込み分離(レビュー指摘対応): エンドポイント本体と同じセッション依存を使い、FastAPI の依存関係キャッシュで 1 リクエスト 1 コネクションに統一

期待効果

パス Before After(見込み)
GET /trips/url/{url_id} 600〜770ms(5〜8 往復) 〜250ms(1〜2 往復)
GET /pages/{id}/blocks 690〜740ms 〜400ms(1 クエリ + 認可 1 往復)

残課題(別 Issue / PR 候補)

  • 書き込み系(特に PUT /blocks/{id} の location 置換)は flush / commit で依然 5〜6 往復
  • 認可 Depends の trip_id 解決クエリを本体クエリに JOIN 統合すればさらに 1 往復削減可
  • 根本対策としては DB の東京近傍への移設(RTT 150ms → 数 ms)が最大のレバー

動作確認

  • バックエンドのみの変更(フロントエンドの変更なし)のため画像なし
  • CI テストスイート 102 件パス(読み系・書き込み系・認可の回帰を網羅)
  • ruff / mypy で新規エラーなし
  • マージ後、staging の計装ログ(jsonPayload.type="request")で before/after の p50/p95 と 500 発生率を再計測する

確認項目

  • 動作確認を実施している
  • issueはPRページ右下のDevelopmentからissueが紐づいている

🤖 Generated with Claude Code

DB (Neon us-east-1) との RTT (~150ms) × 逐次往復回数がレイテンシーの支配項
であるため、1 リクエストあたりの往復回数を削減する。

- 読み系クエリを selectinload のネスト(3〜5 クエリ逐次発行)から
  joinedload チェーン(1 クエリ)に変更
- 読み取り専用エンドポイント用に AUTOCOMMIT セッションを追加し、
  BEGIN / ROLLBACK の往復を省略(プールは書き込み用エンジンと共有し、
  返却時に isolation はデフォルトへリセットされる)
- pool_pre_ping を廃止し pool_recycle=300 に変更
  (asyncpg の pre_ping は pgbouncer 対応のため BEGIN + SELECT + ROLLBACK
  の最大 3 往復を checkout ごとに消費していた)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@bobtaroh
bobtaroh requested a review from a team as a code owner July 4, 2026 00:28
@bobtaroh bobtaroh self-assigned this Jul 4, 2026
@bobtaroh
bobtaroh requested review from koki-fore and kuu13580 July 4, 2026 00:28
@coderabbitai

coderabbitai Bot commented Jul 4, 2026

Copy link
Copy Markdown

Review Change Stack

Important

Review skipped

Draft detected.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: a868c74c-ed69-4dd4-8177-b0dfeb69e40b

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

読み取り専用のDBエンジン・セッションファクトリ・依存注入関数(get_read_db_session)を新設し、認証層とルーター層(blocks/pages/trips)の読み取り系エンドポイントをそちらへ切替えた。また、cruds層のリレーション読込を selectinload から joinedload に変更し、結果取得処理に unique() を追加。テスト用オーバーライドも追加した。

Changes

読み取りDBセッション導入とクエリロード戦略変更

Layer / File(s) Summary
read専用DBエンジン・セッションファクトリ・依存注入関数の追加
server/app/db_connection.py
pool_pre_ping を削除し pool_recycle=300 を追加、AUTOCOMMIT な read_engineReadAsyncSessionLocalautoflush=False)を新設し、get_read_db_session() 依存関数を追加した。
cruds層のeager-loading戦略変更とunique()適用
server/app/cruds/blocks.py, server/app/cruds/pages.py, server/app/cruds/trips.py
_block_with_relations()/_page_with_relations()/_trip_with_relations()selectinload から joinedload に変更し、find_*/get_* 系の返却処理に result.unique() を追加、create_block の再取得も統一ヘルパー経由にした。
認証・ルーター層のread専用セッションへの切替
server/app/auth.py, server/app/routers/blocks.py, server/app/routers/pages.py, server/app/routers/trips.py
require_page_access/require_block_access および blocks/pages/trips の読み取り系エンドポイントの db 依存を get_db_session から get_read_db_session に変更した。
テスト用read専用セッションのオーバーライド追加
server/tests/conftest.py
get_read_db_session の import を追加し、AUTOCOMMIT な TestingReadAsyncSessionLocaloverride_get_read_db_session() を新設して dependency_overrides に登録した。

Estimated code review effort: 3 (Moderate) | ~25 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Router
  participant get_read_db_session
  participant ReadAsyncSessionLocal
  participant read_engine
  Router->>get_read_db_session: Depends呼び出し
  get_read_db_session->>ReadAsyncSessionLocal: セッション生成
  ReadAsyncSessionLocal->>read_engine: AUTOCOMMIT接続取得
  get_read_db_session-->>Router: AsyncSessionをyield
  Router->>get_read_db_session: 処理完了
  get_read_db_session->>ReadAsyncSessionLocal: session.close()
Loading

関連Issue: #124

提案ラベル: performance, backend, database

提案レビュアー: バックエンド担当のメンテナ

Poem:

うさぎぴょん、DBの道を二つに分け
読むだけの道はAUTOCOMMITで軽やかに
joinedloadでまとめて一気に取り込み
unique()で重なりきれいに整えて
レイテンシー縮まる春の朝

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Linked Issues check ✅ Passed #124のコード面の要件であるボトルネック特定と改善実装に沿っており、joinedload化やread-onlyセッション導入で目的に合致しています。
Out of Scope Changes check ✅ Passed 変更は読み取り経路の最適化、接続設定、関連テストに限定されており、明らかなスコープ外変更は見当たりません。
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed DBの往復回数削減とAPIレイテンシー改善というPRの主目的を正しく要約しています。
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/124-reduce-db-roundtrips

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions

github-actions Bot commented Jul 4, 2026

Copy link
Copy Markdown

Visit the preview URL for this PR (updated for commit 9cefea3):

https://tabi-share-8ef6b--pr160-feature-124-reduce-d-fpqgtqtn.web.app

(expires Sat, 11 Jul 2026 01:48:24 GMT)

🔥 via Firebase Hosting GitHub Action 🌎

Sign: 9f2a87ede127df7673322845e34cf22c1372d720

@bobtaroh
bobtaroh removed request for koki-fore and kuu13580 July 4, 2026 00:29
@bobtaroh
bobtaroh marked this pull request as draft July 4, 2026 00:29

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@server/app/auth.py`:
- Line 19: 書き込みルートで認可用と更新用に別々のDBセッションを開いているのが問題です。require_page_access と
require_block_access の実装を見直し、get_db_session
で取得した同一セッションを認可処理にも使うか、認可結果だけをルートへ返す形に変更して、重複したDBコネクション消費を避けてください。

In `@server/app/db_connection.py`:
- Around line 22-33: `create_async_engine` の `engine` 初期化では `pool_recycle`
だけでは切断済みコネクションを防げないため、`pool_pre_ping`
を有効に戻すか、少なくとも切断時の再試行処理を追加してください。`db_connection.py` の `engine`
設定を修正し、`read_engine` も同じプールを使う前提で読み取り系でも stale 接続が checkout されても回復できるようにしてください。
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: f4b47854-5571-45ba-98b6-110d31801194

📥 Commits

Reviewing files that changed from the base of the PR and between 05e825a and d48bab8.

📒 Files selected for processing (9)
  • server/app/auth.py
  • server/app/cruds/blocks.py
  • server/app/cruds/pages.py
  • server/app/cruds/trips.py
  • server/app/db_connection.py
  • server/app/routers/blocks.py
  • server/app/routers/pages.py
  • server/app/routers/trips.py
  • server/tests/conftest.py

Comment thread server/app/auth.py Outdated
Comment thread server/app/db_connection.py
書き込み系ルートで認可用(読み取りセッション)と更新用(書き込みセッション)の
2 コネクションを同時に消費していた CodeRabbit の指摘に対応。
認可 Depends をエンドポイント本体と同じセッション依存にすることで、
FastAPI の依存関係キャッシュにより同一セッションを共有する。

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
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.

1 participant