Skip to content

trainRoute と座標検索の高速化を master へ反映 - #1652

Merged
TinyKitten merged 3 commits into
masterfrom
dev
Aug 24, 2026
Merged

TinyKitten merged 3 commits into
masterfrom
dev

Conversation

@TinyKitten

@TinyKitten TinyKitten commented Aug 24, 2026

Copy link
Copy Markdown
Member

概要

dev に入っている 3 件を master へ反映する。中身はほぼ性能改善で、本番で CPU 350〜480 ms を使っていたクエリが 30〜60 ms 台に落ちる。stationsNearby の並び方針は master と変わらない (#1647 で距離順へ変えたものを #1649 で元へ戻したため、dev 内での往復で終わっている)。

変更の種類

  • バグ修正
  • 新機能
  • データの修正・追加
  • リファクタリング
  • ドキュメント
  • CI/CD
  • その他

変更内容

  • trainRoute のボトルネックを解消し stationsNearby の並びを距離順に修正 #1647 trainRoute のボトルネックを解消し stationsNearby の並びを距離順に修正
    • index.rs に座標検索用のグリッド索引 (Grid、0.05° セルの CSR) を入れ、nearest / within_radius が駅テーブル全走査 (39,211 レコード) をやめた。近傍バス停の付与は駅グループごとに 1 回この検索を呼ぶので、費用が駅数に比例して積み上がっていた。
    • get_train_route が系統全体に付帯情報を付けてから区間を切り出していたのを、切り詰めてから付けるように反転した。付帯情報の付与は駅ごとに独立なので結果は変わらないが、3 駅の要求が 250 駅ぶんの費用を払わなくなった。
    • 近傍バス停の取得を「最寄り 50 件を取ってから有効な路線を持つものに絞る」から「半径 300 m 以内を近い順に見て、有効な路線を持つものを 50 件まで採る」に変えた。前者は路線を引けないバス停が枠を埋めたぶんだけ採用数が減っていた。
    • 駅グループの代表座標と各駅の座標の隔たりぶんを検索半径に足し、代表からは半径の外だが同じグループの別の駅からは内側、というバス停の取りこぼしを直した。
    • 採用されないバス停の駅グループぶんまでバス路線を引いていたのを、採否判定と同じ式で先に絞るようにした。
    • CompanyRepository::find_by_id_vec の線形 containsHashSet に置き換えた。
  • stationsNearby の並びを鉄道駅が先・バス停が後に戻す #1649 stationsNearby の並びを鉄道駅が先・バス停が後に戻す
  • 本番とステージングの GraphQL 性能を比較するベンチマークスキルを追加 #1650 本番とステージングの GraphQL 性能を比較するベンチマークスキルを追加
    • make bench で全 Query フィールドを本番・ステージングへ投げ、クライアント応答時間と Worker の cpuTime (wrangler tail の値を cf-ray で突き合わせ) を比較したレポートを benchmarks/ に出す。
    • Makefilebench ターゲット、.gitignorebenchmarks/.logs/ を追加。

レスポンスの変化 (本番と stg の実レスポンス比較)

  • stationsNearby の並び方針は同じ。 渋谷駅前 limit: 20 で両環境とも 20 件すべて鉄道駅 (鉄道だけで上限が埋まるためバス停は入らない)。順序で違うのは 2 位と 6 位の入れ替わりだけで、これは同一距離のタイ。master は不安定ソートでタイの順が不定だったのを、station_cd 昇順で決定的にしたため。
  • 近傍バス路線も一致。 渋谷駅 (id: 1130205) の lines は両環境とも鉄道 10 件 / バス 35 件で完全一致。上の取りこぼし修正が効くのは、50 件の枠を使い切るバス停密集地か、駅の広がりが大きい駅グループに限られる。

実測 (本番 master 相当 → ステージング dev 相当、CPU Time の平均)

反復 15 回・全 23 ケース・両環境で同一データ (/__health で駅 39,211 / 路線 1,601 / 会社 178 が一致) を確認した上での計測。遅くなったケースは無い。

クエリ 本番 ステージング
trainRoute_short (250 駅系統の先頭 3 駅) 369.07 ms 4.47 ms -98.8%
lineGroupStations (サンライズ出雲 250 駅) 365.07 ms 31.07 ms -91.5%
trainRoute_long (全 250 駅) 389.67 ms 32.93 ms -91.5%
lineGroupListStations 481.07 ms 58.93 ms -87.7%
lineListStations (13 路線) 356.13 ms 62.73 ms -82.4%
lineStations (山手線 30 駅) 58.33 ms 14.27 ms -75.5%
stationsNearby_limit100 110.47 ms 35.67 ms -67.7%
stations (主要 20 駅) 32.60 ms 12.67 ms -61.1%
stationsByName 32.87 ms 21.40 ms -34.9%
stationsNearby 15.33 ms 11.00 ms -28.3%

stationGroupStations (駅グループ 1 つなので座標検索を 1 回しか呼ばない)、routes / connectedRoutes (バス停の付帯情報を通らない) は同等。判定が境界だったケースは --repeat 30 で再計測して向きを確かめてある。

テスト

  • make fmt が通ること
  • make clippy が通ること(wasm32 ターゲットを含む)
  • make test が通ること

src/index.rs のグリッド索引には全走査との差分検査 (grid_search_matches_full_scan / within_radius_matches_full_scan) と並び順の固定 (nearest_puts_rail_before_bus / nearest_fills_the_limit_with_rail_before_bus) を、get_train_route には区間の長さに費用が比例することを固定するテストを入れてある。データ (data/*.csv) の変更は無いので data_validator は流していない。

関連Issue

今回のAI生成画像

image

trainRoute は要求区間の長さに関係なく 1 リクエスト約 290ms かかっていた。
ボトルネックは 2 か所。

- 系統の全駅に付帯情報を付けてから要求区間へ切り出していた。3 駅を要求しても
  250 駅ぶんを組み立てるため、区間の長さに費用が比例しない。付帯情報の付与は
  駅ごとに独立しているので、切り詰めてから付けるよう順序を入れ替えた。
  O(系統の駅数) から O(要求区間の駅数) へ。
- 近傍バス停の検索が駅ごとの全件走査だった。種別ごとのグリッド索引
  (0.05 度のマスを CSR で保持) を入れ、半径を広げながら探して打ち切る。
  O(駅の総数) から O(半径内のマス) へ。stationsNearby にも効く。

本番相当のデータ (41,148 駅 / うちバス停 30,000) での計測:

  250 駅 (東京〜出雲市)  290.4ms -> 14.7ms
  13 駅                  293.9ms -> 2.59ms
  3 駅                   295.0ms -> 1.39ms

あわせて、索引化の過程で見つかった不具合を修正する。

- stationsNearby の並びが近い順という仕様に反し、transportType 未指定のとき
  鉄道駅が常にバス停より先に来ていた。移行前の SQL から続く挙動。
- 近傍バス路線が、無効な路線のバス停に上限の枠を埋められて欠落していた
  (#1648)。有効な路線を持つバス停だけを数えて打ち切る。
- 距離が同着の場合の並びを station_cd 昇順に決め切る。不安定ソート任せで
  どの路線の行が先に来るか不定だった。

索引は外部入力 (GTFS) の外れ値に対して頑健にしてある。NaN・無限大・WGS84 の
範囲外を索引から外し、マス数に上限を設けて超える場合はマスを粗くする。
nearest / within_radius は入口で無効な座標・半径を弾く (lat が NaN だと
探索半径を無限大まで広げても打ち切れず、1 リクエストで Worker が固まる)。

Worker crate の索引はネイティブでも動くため make test へ追加した。
種別を指定しない stationsNearby は距離だけで並べていたため、駅前で引くと
鉄道駅の間にバス停が挟まっていた。移行前の SQL と同じ
`ORDER BY transport_type, distance` に戻す。

- `index::nearest` は鉄道駅を距離順に上限まで詰め、残った枠にだけバス停を
  距離順で入れる。バス停側は残り枠ぶんしか引かないので、鉄道駅で埋まる地点
  では検索そのものが走らない。
- 件数の上限は種別ごとではなく並べた後の全体に掛かる。鉄道駅だけで limit 件
  そろう地点ではバス停は返らない。
- `StationRepository::get_by_coordinates` の契約として明文化し、モックの並びも
  合わせる。
- 全件走査との突き合わせを種別・距離・station_cd の 3 キーで行い、上限が鉄道駅
  で埋まるテストでは件数も確かめる。
@TinyKitten TinyKitten self-assigned this Aug 24, 2026
@coderabbitai

coderabbitai Bot commented Aug 24, 2026

Copy link
Copy Markdown
Contributor

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

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: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 3087aa34-02f4-4489-9ab7-95d5bd798b9c

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

Warning

Billing warning: we have not been able to collect payment for this subscription for more than 72 hours. Please update the payment method or pay any pending invoices in Billing to avoid service interruption.


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

@TinyKitten TinyKitten changed the title trainRoute と近傍探索の高速化・stationsNearby の並び修正を master へ反映 trainRoute と座標検索の高速化を master へ反映 Aug 24, 2026
@TinyKitten
TinyKitten merged commit 810f103 into master Aug 24, 2026
8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant