Skip to content

[パフォーマンス] フレーム表示時の Buckets 取得を一括化し N+1 を解消する #2411

Description

@masaton0216

概要

フレーム表示時、プラグインインスタンス生成のたびに UserPluginBase::__construct()フレーム毎に1本 Buckets を SELECT している(N+1)。
ページ上のフレーム数に比例してクエリが増えるため、フレーム数が多いページでレイテンシ悪化の要因となる。

該当箇所

  • app/Plugins/User/UserPluginBase.php L152-157
    $this->buckets = Buckets::select('buckets.*')
        ->join('frames', function ($join) use ($frame) {
            $join->on('frames.bucket_id', '=', 'buckets.id')
                ->where('frames.id', '=', $frame->id);
        })
        ->first();
  • 呼び出し元: app/Http/Controllers/Core/DefaultController.php
    • createInstanceMain() L966-975(メインエリア)
    • createInstanceCommonArea() L1031-1049(共通エリア)
    • どちらも foreach ($frames as $frame) { new $class_name($page, $frame, $pages); } の形になっており、コンストラクタ内で1フレーム=1 SELECT 発行。

計測(ローカル開発DB / frames=176, buckets=165)

1ページあたり8フレームを想定し、各20回の平均値。

方式 平均時間 クエリ数
現行(フレーム毎に Buckets::join(frames)->first() 4.21ms 8
一括(Buckets::whereIn('id', $bucket_ids)->get()->keyBy('id') 0.58ms 1

フレーム数が増えるほど差は拡大する想定(フレーム20個なら ~10ms → ~1ms を見込み)。

改善案

DefaultController::getFramesMain() で取得済みの $frames->pluck('bucket_id') を用いて、インスタンス生成前に一括取得 → コンストラクタへ注入(または frame 側に attach)する。

// DefaultController 側(擬似コード)
$bucket_ids = $frames->pluck('bucket_id')->filter()->unique()->all();
$buckets_map = Buckets::whereIn('id', $bucket_ids)->get()->keyBy('id');

foreach ($frames as $frame) {
    $bucket = $frame->bucket_id ? ($buckets_map[$frame->bucket_id] ?? null) : null;
    $plugin_instances[$frame->frame_id] = new $class_name($page, $frame, $pages, $bucket);
}

UserPluginBase::__construct() 側では、第4引数に bucket が渡ってきたらそれを利用、無ければ現行どおりフォールバックでクエリ(互換性維持)。

もしくは、ConnectFrame ミドルウェア(app/Http/Middleware/ConnectFrame.php)で frame_configs と同様に request->attributes に bucket マップを積んでおく方針でも良い(FrameConfig と揃う)。

影響範囲

  • 共通エリア(layouts_info 経由)のフレームも同経路で N+1 しているため、両対応必要。
  • UserPluginBase を継承する全プラグインに影響するが、$this->buckets の値自体は変えない設計にすることで差分を最小化する。
  • テスト: 既存 Feature テスト(各プラグインの index 表示)でリグレッション確認。

チェックリスト

  • DefaultController::createInstanceMain() 修正
  • DefaultController::createInstanceCommonArea() 修正
  • UserPluginBase::__construct() のシグネチャ拡張(互換性維持)
  • クエリ本数の Before/After 計測ログ添付
  • 既存テストのリグレッションなし確認

DB変更の有無

なし

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions