この記事は 一休.com Advent Calendar 2023 6日目の記事です。 一休レストランの開発チームでエンジニアをしている香西です。 今回は Solr クエリの速度改善についてお話します。 背景 2023年10月、一休レストランのスマートフォン用 レストラン詳細ページをリニューアルしました! UI/UX の見直しとともに、使用技術も一新しました。 バックエンド言語:Python から Rustへ フロントエンドフレームワーク:Nuxt.js から Next.jsへ *1 スマートフォン用 レストラン詳細ページ 課題 「日付を選ぶカレンダーの表示が遅い」 社内限定リリースの直後、多方面からこの声が聞こえてきました... レストランへ行く日付を選ぶカレンダーは予約フローの第一ステップなので、表示速度が遅いことは致命的です。 特に、設定データ(料理のコース種類・席の種類など)が多いレストランでは、カレンダーの空席状況を取得するのに15秒以上かかることがあり、このままでは正式リリースできない状況でした。 空席カレンダーの UI 一休レストランでは、各レストランの空席情報を全文検索システム Solr にインデックスし、予約できる日を Solr で検索して空席状況を取得しています。 Solr には、レストランの設定データごとにドキュメントを作成しインデックスしています。そのため設定データが多いレストランは検索対象のドキュメント数が膨大になり、検索に時間がかかっていました。 やったこと 検索マイクロサービスを経由するのをやめた Solr へのアクセスは「フロントエンド → バックエンド → 検索マイクロサービス → Solr」という流れで行われます。 従来からあった検索マイクロサービスのオーバーヘッドが大きかったため、検索マイクロサービスを経由するのをやめて、「バックエンド → Solr」に直接アクセスするようにしました。 検索マイクロサービス(C#)で行われていた Solr クエリの組み立てや、Solr からのレスポンスをオブジェクトに変換し在庫計算を行う処理を、バックエンド(Rust)に移行しました。 ワイルドカードを使うようにした Solr にインデックスされているデータのなかには、日付ごとに異なる情報が含まれています。これらの情報は、それぞれ特定の日付(例:231025)を含むフィールド名で表現されています。 "231025Close_tdt": "2023-10-21T00:00:00Z", "231025VisitTimeFrom_tdt": "2023-10-25T18:00:00Z", "231025VisitTimeTo_tdt": "2023-10-25T18:30:00Z", "231025HasInventory_b": true, "231025HasRotationOrBlockTime_b": false, "231025Inventory_ti": 1, "231025SalesUpperLimitOver_b": false, 例えば、先1か月分の各日付の情報を取得する場合、以下のような Solr クエリを生成していました。 // 変更前 fl=231025Close_tdt,231025VisitTimeFrom_tdt,231025VisitTimeTo_tdt,231025HasInventory_b,231025HasRotationOrBlockTime_b,231025Inventory_ti,231025SalesUpperLimitOver_b,231026Close_tdt,231026VisitTimeFrom_tdt,231026VisitTimeTo_tdt,231026HasInventory_b,231026HasRotationOrBlockTime_b,231026Inventory_ti,231026SalesUpperLimitOver_b,231027Close_tdt,231027VisitTimeFrom_tdt,231027VisitTimeTo_tdt,231027HasInventory_b,231027HasRotationOrBlockTime_b,231027Inventory_ti,231027SalesUpperLimitOver_b,231028Close_tdt,231028VisitTimeFrom_tdt,231028VisitTimeTo_tdt,231028HasInventory_b,231028HasRotationOrBlockTime_b,231028Inventory_ti,231028SalesUpperLimitOver_b,231029Close_tdt,231029VisitTimeFrom_tdt,231029VisitTimeTo_tdt,231029HasInventory_b,231029HasRotationOrBlockTime_b,231029Inventory_ti,231029SalesUpperLimitOver_b,231030Close_tdt,231030VisitTimeFrom_tdt,231030VisitTimeTo_tdt,231030HasInventory_b,231030HasRotationOrBlockTime_b,231030Inventory_ti,231030SalesUpperLimitOver_b...つづく field list で「231025のフィールド群,231026のフィールド群,231027のフィールド群 ...」のように、特定の日付が含まれるフィールド群を個別に指定していましたが、日付部分(231025)をワイルドカード(??????)に置き換えて「??????のフィールド群」という書き方に変更しました。 // 変更後 fl=??????Close_tdt,??????VisitTimeFrom_tdt,??????VisitTimeTo_tdt,??????HasInventory_b,??????HasRotationOrBlockTime_b,??????Inventory_ti,??????SalesUpperLimitOver_b この変更により、設定データが多いレストランではレスポンスタイムが約 1/5 に短縮され、大きな改善効果が得られました! 100件ずつ並列で取得するようにした 最初に、検索結果の総件数のみを取得し、総件数を100で割って何回取得すればよいか判断し、100件ずつ並列で Solr にリクエストを送るようにしました。 Rust で Solr から結果を取得するサンプルコードです。 search_calendar にカレンダーの検索条件を渡すと、まず Solr から総件数を取得し、そのあと100件ずつ検索結果を取得します。 pub async fn search_calendar ( & self , input: & model :: CalendarInput, ) -> anyhow :: Result < Vec < model :: Date >> { let rows = 100 ; let query = CalendarQuery (input. clone ()); // 先に総件数のみを取得する let total_count = self . get_solr_data ( & query, 0 , 0 ).await ? .response.total_count; let query = & query; // 100 件ずつ取得する let futures = ( 0 ..total_count. div_ceil (rows)). map ( | n | async move { self . get_solr_data (query, n * rows, rows).await }); let res = futures_util :: future :: try_join_all (futures).await ? ; // 以下略(Solr の結果をもとに返り値を作る) } 不要な Solr クエリを削る 改めて Solr クエリに削除できる部分がないか見直しました。 不要なフィールドを取得していないか ユーザーの指定条件によって削除できるフィールドはないか 無駄に group , sort の機能を使用していないか といった観点でチェックを行いました。 成果 この改善により、カレンダーの空席状況を取得するのに15秒程かかっていたのが2~3秒程度に短縮され、スムーズな UX をユーザーに提供することができました! また、システム観点でも大きな効果がありました。 今回の速度改善対象は、スマートフォン用 レストラン詳細ページのカレンダーの検索処理でしたが、Solr 全体のパフォーマンスが向上しました。 Solr の CPU コア使用率が半分以上減少 Solr 全体のレスポンスタイムが450ミリ秒から200ミリ秒程度に短縮 Solr の CPU コア使用率が半分以上減少 Solr 全体のレスポンスタイムが450ミリ秒から200ミリ秒程度に短縮 一休レストランの Solr に関する改善点はまだ多くありますが、少しずつ着実に取り組んでいきたいと思います。 さいごに 一休では、ともに良いサービスをつくっていく仲間を募集中です! www.ikyu.co.jp カジュアル面談も実施しているので、お気軽にご応募ください。 hrmos.co *1 : Next.js で起きた課題については 一休.com Advent Calendar 2023 15日目の記事で解説予定です。