perf: DB 往復回数を削減して API レイテンシーを改善 (#124) - #160
Conversation
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>
|
Important Review skippedDraft detected. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Plus Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
📝 WalkthroughWalkthrough読み取り専用のDBエンジン・セッションファクトリ・依存注入関数(get_read_db_session)を新設し、認証層とルーター層(blocks/pages/trips)の読み取り系エンドポイントをそちらへ切替えた。また、cruds層のリレーション読込を selectinload から joinedload に変更し、結果取得処理に unique() を追加。テスト用オーバーライドも追加した。 Changes読み取りDBセッション導入とクエリロード戦略変更
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()
関連Issue: 提案ラベル: performance, backend, database 提案レビュアー: バックエンド担当のメンテナ Poem:
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
|
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 |
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
There was a problem hiding this comment.
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
📒 Files selected for processing (9)
server/app/auth.pyserver/app/cruds/blocks.pyserver/app/cruds/pages.pyserver/app/cruds/trips.pyserver/app/db_connection.pyserver/app/routers/blocks.pyserver/app/routers/pages.pyserver/app/routers/trips.pyserver/tests/conftest.py
書き込み系ルートで認可用(読み取りセッション)と更新用(書き込みセッション)の 2 コネクションを同時に消費していた CodeRabbit の指摘に対応。 認可 Depends をエンドポイント本体と同じセッション依存にすることで、 FastAPI の依存関係キャッシュにより同一セッションを共有する。 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
概要
#124 の対応。API レイテンシーの支配項が「DB (Neon us-east-1) との RTT (~150ms) × 1 リクエストあたりの逐次往復回数」であることを計装ログから特定し、往復回数を削減する。
関連 Issue: #124(マージ後の再計測が残るため自動クローズしない)
詳細
分析結果(staging 計装ログより)
GET /trips/url/{url_id}: クエリ 3〜5 本(selectinload のネスト分)で db_total 300〜460mstotal_ms − db_total_ms ≈ 300msが全リクエストで一定 → cursor イベントに現れない BEGIN / ROLLBACK / pre_ping の隠れ往復変更内容
selectinloadのネスト(リレーション階層ごとに SELECT が逐次発行)→joinedloadチェーン(JOIN で 1 クエリ)。コレクションの joinedload に伴いresult.unique()を追加し、一覧系は返却順が不定になるため ORDER BY を明示get_read_db_sessionに切り替え、BEGIN / ROLLBACK の 2 往復を省略。プールは書き込み用エンジンと共有され、返却時に isolation はデフォルトへリセットされる(SQLAlchemy の connection characteristic 機構)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 で緩和する期待効果
残課題(別 Issue / PR 候補)
動作確認
jsonPayload.type="request")で before/after の p50/p95 と 500 発生率を再計測する確認項目
🤖 Generated with Claude Code