trainRoute と座標検索の高速化を master へ反映 - #1652
Conversation
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 キーで行い、上限が鉄道駅 で埋まるテストでは件数も確かめる。
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
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 |
概要
devに入っている 3 件をmasterへ反映する。中身はほぼ性能改善で、本番で CPU 350〜480 ms を使っていたクエリが 30〜60 ms 台に落ちる。stationsNearbyの並び方針はmasterと変わらない (#1647 で距離順へ変えたものを #1649 で元へ戻したため、dev内での往復で終わっている)。変更の種類
変更内容
trainRouteのボトルネックを解消しstationsNearbyの並びを距離順に修正index.rsに座標検索用のグリッド索引 (Grid、0.05° セルの CSR) を入れ、nearest/within_radiusが駅テーブル全走査 (39,211 レコード) をやめた。近傍バス停の付与は駅グループごとに 1 回この検索を呼ぶので、費用が駅数に比例して積み上がっていた。get_train_routeが系統全体に付帯情報を付けてから区間を切り出していたのを、切り詰めてから付けるように反転した。付帯情報の付与は駅ごとに独立なので結果は変わらないが、3 駅の要求が 250 駅ぶんの費用を払わなくなった。CompanyRepository::find_by_id_vecの線形containsをHashSetに置き換えた。stationsNearbyの並びを鉄道駅が先・バス停が後に戻すORDER BY transport_type, distance) と同じ並びへ戻した。masterから見た並び方針の変化は無い。make benchで全Queryフィールドを本番・ステージングへ投げ、クライアント応答時間と Worker のcpuTime(wrangler tailの値をcf-rayで突き合わせ) を比較したレポートをbenchmarks/に出す。Makefileにbenchターゲット、.gitignoreにbenchmarks/.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 駅)lineGroupStations(サンライズ出雲 250 駅)trainRoute_long(全 250 駅)lineGroupListStationslineListStations(13 路線)lineStations(山手線 30 駅)stationsNearby_limit100stations(主要 20 駅)stationsByNamestationsNearbystationGroupStations(駅グループ 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生成画像