Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Sign up for free
Menu
Search
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Pricing
Search
Sign in
Sign up for free
総会員数1,500万人のレストランWeb予約サービスにおけるRustの活用
Search
Kōhei Yamamoto (山本浩平)
November 30, 2024
Technology
3.6k
3
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
総会員数1,500万人のレストランWeb予約サービスにおけるRustの活用
Rust.Tokyo 2024 一休スポンサーセッションでの発表資料です
Kōhei Yamamoto (山本浩平)
November 30, 2024
More Decks by Kōhei Yamamoto (山本浩平)
See All by Kōhei Yamamoto (山本浩平)
一休.comレストランにおけるRustの活用
kymmt90
3
1.2k
一休.comレストランのRustバックエンド開発の様子
kymmt90
15
12k
レガシーWebアプリケーションの性能とコードの健全性をインクリメンタルに改善する / pepabotech-20211209
kymmt90
1
3.1k
コードレビュー座学 / About code reviews
kymmt90
0
6.9k
ペパボのWebサービス 開発スタイル / Web services development at GMO Pepabo
kymmt90
2
640
GraphQL and Schema-First Development
kymmt90
4
4.2k
EC新サービスにおけるスキーマファースト開発 / Schema First Development in the New EC Service
kymmt90
1
2.5k
rails new --api してからやったこと 〜2017年・夏〜 / EC Tech MTG 3
kymmt90
0
940
カテゴリ階層の拡張を目的とした階層的トピックモデル / A hierarchical topic model for expanding category hierarchies
kymmt90
0
660
Other Decks in Technology
See All in Technology
全社共通データ基盤をつくる。ソニーのDatabricks活用とデータガバナンス設計の裏側
sony
0
430
OSC2026on_the-world-is-waiting-for-your-voice.pdf
naruoga
0
350
ファミコンでPHPを動かす / PHP on the Famicom side b
tomzoh
0
140
Snowflake Horizon Catalog と Apache Iceberg で作る オープンなデータ基盤
kitagawaz
0
440
形式手法を使って仕様をコーディングしよう
mikanichinose
0
180
[2026 Oracle Technical Deep Dive] オンプレミスDBのCloud移行アプローチ:移行計画に基づくメソッドとツールの選択 (2026年9月17日開催)
oracle4engineer
PRO
0
150
自主式軟體工廠
philipz
0
230
2026-10-01_MagicPod_QAハーネスエンジニアリングとQA組織の未来像
ynisqa1988
1
540
実体験から学ぶ分析エージェントの開発と運⽤
recruitengineers
PRO
1
280
自律型 AI をセキュアに実装!Gemini と MIG で作る動的コード実行環境
recruitengineers
PRO
1
210
Datadog の学び方 - あるいは、オブザーバビリティを学ぶとは何か
mananyuki
1
490
MCPゲートウェイを作って運用してわかったこと — Agent時代の権限管理の現在地
mtpooh
10
3.1k
Featured
See All Featured
What's in a price? How to price your products and services
michaelherold
247
13k
Navigating Team Friction
lara
192
16k
How to Ace a Technical Interview
jacobian
280
24k
State of Search Keynote: SEO is Dead Long Live SEO
ryanjones
0
310
Why Our Code Smells
bkeepers
PRO
340
58k
Fantastic passwords and where to find them - at NoRuKo
philnash
52
4k
Fashionably flexible responsive web design (full day workshop)
malarkey
409
67k
Deep Space Network (abreviated)
tonyrice
0
350
Tell your own story through comics
letsgokoyo
1
1.1k
The World Runs on Bad Software
bkeepers
PRO
72
12k
More Than Pixels: Becoming A User Experience Designer
marktimemedia
3
550
Skip the Path - Find Your Career Trail
mkilby
1
240
Transcript
0 総会員数1,500万人のレストラン Web予約サービスにおけるRustの活用 山本浩平 | 2024-11-30 Rust.Tokyo 2024
1 自己紹介 • 山本浩平 @kymmt90 • 2023年に一休に入社し、一休.comレストランの Rustでの開発に途中から参加
2 本発表で話すこと • 一休.comレストランについて • Rust活用の詳細 ◦ バックエンド ◦ エッジコンピューティング
• 今後のチャレンジ
3 一休.comレストラン | サービス概要 • 上質なレストランの Web予約サービスを提供 • 2006年ローンチの長い 歴史を持つサービス
4 一休.comレストラン | サービス概要 • 一休.com全体の会員規模1,500万人 • バックエンドへのトラフィックは最大1,000req/sほど (夕方〜夜が多い)
5 一休.comレストラン | Rustを利用中 • システムのさまざまな箇所をRustに移行中 ◦ Web UI バックエンドのGraphQL
API ◦ 社内の他システムとの連携用REST API ◦ Fastly Computeでのエッジコンピューティング ◦ [WIP] Solr (全文検索エンジン)のインデクサー
6 本発表で話すこと • 一休.comレストランについて • Rust活用の詳細 ◦ バックエンド ▪ 概要と設計
▪ 技術トピック ◦ エッジコンピューティング • 今後のチャレンジ
7 Rustバックエンド | 概要 GraphQL API REST API frontend app
internal systems SQL Server フレームワークはAxum GraphQL実装にはasync-graphqlを利用 Python, C#などからの移行 Solr Fastly (後述) internal systems backend
8 バックエンド | Rustの選定理由 • 「シンプル、かつすばやく、それでいて堅牢に作れる」 という社内の技術選定方針に基づく ◦ 表現力の高い型システムでドメインモデルをコード化 ◦
高速/省リソース: 運用コストを中長期的に改善 ◦ crateによるモジュール依存関係の制御 ◦ 社内で利用する技術のバランス
9 バックエンド | 設計 • クエリモデル ◦ C向け予約サービスの 特性上、表示用の 非正規化されたデータが
ほしい場面が多いので、 クエリモデルを導入 DB, フレームワーク, 環境の設定 dataloaderのI/Fやユースケースなど エンティティ (クエリモデル) データアクセス層など
10 バックエンド | 設計 [例:データアクセス層] async fn fetch_restaurants<C: Config>( &self,
database: &crate::Database<C>, keys: &[RestaurantId], ) -> Result<HashMap<RestaurantId, Result<Restaurant>>> { let query = format!( // クエリ ); // レコードフェッチ、 DTOからクエリモデルへ let restaurant_models = database .query_as::<dto::Restaurant>(&query, params) .await .context("failed to query restaurants")? .into_iter() .map(|d| (d.id, Restaurant::try_from(d))) .collect(); Ok(restaurant_models) } 具体的なDBや設定は外から注入 上位層→モデルの依存方向になり モデルは外界に依存しない
11 バックエンド | モジュールの設計 • Cargo Workspaceでリポジトリ内をモジュラーに ◦ 各層(実際はもう少し細粒度)をworkspace crateで表現
◦ 各crateのCargo.tomlでcrate間の依存の向きを強制 # ルートディレクトリのCargo.toml [workspace] resolver = "2" members = [ "backend/*", ] [workspace.dependencies] backend-query-model = { path = "./backend/query-model" } # ...
12 バックエンド | モジュールの設計 • workspace.dependenciesで設定した特定のcrateへの 依存をCargo.tomlに明記 # データアクセス層のCargo.toml [package]
name = "backend-data-access" version.workspace = true authors.workspace = true edition.workspace = true publish.workspace = true [dependencies] backend-query-model = { workspace = true }
13 本発表で話すこと • 一休.comレストランについて • Rust活用の詳細 ◦ バックエンド ▪ 設計
▪ 技術トピック ◦ エッジコンピューティング • 今後のチャレンジ
14 バックエンド | utoipaでOpenAPI記述 • 社内用REST APIはutoipaでOpenAPIドキュメントを記述 • Rust内からもドキュメントにアクセスできるので、リクエストボディ のバリデーションにOpenAPI上のJSON
Schemaを使う ◦ Axumのextractorを定義 ◦ ハンドラでリクエストボディを使うときに自動バリデーション
15 バックエンド | utoipaでOpenAPI記述 pub struct ConformantJson<T>(pub T); #[async_trait] impl<S,
T> FromRequest<S, Body> for ConformantJson<T> where // ... { type Rejection = ApiError; // IntoResponseである必要あり async fn from_request(req: Request<Body>, state: &S) -> Result<Self, Self::Rejection> { // utoipa::openapi::OpenApiの参照から特定のレスポンスボディのJSONスキーマを取得できるようにしてある let schema = OPENAPI_DOC .get_request_body_schema(path, &req.method().clone().into()) .expect("..."); match Json::from_request(req, state).await { Ok(Json(body)) => match validate_json_schema_conformance(&schema, &body) { Ok(_) => { let deserialized = serde_json::from_value(body).expect("..."); Ok(ConformantJson(deserialized)) } // 400エラーを返すApiError Err(err) => Err(err), }, Err(rejection) => // ... } } } ConformantJsonという構造体にAxumの FromRequestを実装することで、 JSON Schemaでリクエストボディを バリデーションしてから取り出すextractorを 作る
16 バックエンド | utoipaでOpenAPI記述 #[utoipa::path( post, path = "/internal/coupon-settings", request_body(
content = CouponSetting, content_type = "application/json", ), responses(/* ... */), )] pub async fn create_coupon_setting( State(state): State<AppState>, ConformantJson(new_coupon_setting): ConformantJson<CouponSetting>, ) -> Result<impl IntoResponse, ApiError> { // ... } extractorであるConformantJsonで CouponSettingを取り出すとき、 そのJSONスキーマでバリデーションされ、 エラーなら自動で400 Invalid Requestを返す utoipaのマクロ (utopia::path , utoipa::ToSchema な ど)でPOST /internal/coupon-settings の スキーマを定義する
17 バックエンド | CPUバウンドな処理の扱い • 一休では検索時の席在庫計算処理などがCPUバウンド ◦ Tokioなど非同期ランタイムはawaitで他タスクに切替 ◦ CPUバウンドな処理でブロックしてawaitまでに時間がかかると…
▪ 他タスクに切り替わらずスレッド数上限以上には並行できない ▪ 結果、他のリクエストが詰まったりする • Rayonのスレッドプールに処理を委譲し、それをawaitすることで 問題を解決 (cf. https://ryhl.io/blog/async-what-is-blocking/#the-rayon-crate)
18 バックエンド | CPUバウンドな処理の扱い pub async fn spawn_rayon<F, R>(f: F)
-> R where //... { let (tx, rx) = tokio::sync::oneshot::channel(); rayon::spawn(move || { let _ = tx.send(f()); }); rx.await.expect("...") } // CPUバウンドな処理を実行する let res = spawn_rayon(move || /* 席在庫計算などの処理 */).await?; CPUバウンドな処理はRayonに任せて 結果だけチャンネル経由で受け取る CPUバウンドな処理が動くタスクを awaitできるので非同期ランタイムが タスク切り替えできる
19 本発表で話すこと • 一休.comレストランについて • Rust活用の詳細 ◦ バックエンド ◦ エッジコンピューティング
• 今後のチャレンジ
20 エッジコンピューティング | CDNキャッシュ • 店舗探し体験の改善は顧客価値につながる ◦ 高速にレスポンスするためにCDNからキャッシュ配信 ◦ 一休ではFastlyを利用
• キャッシュロジックを少し工夫したい ◦ パーソナライズされないページでキャッシュ ◦ 会員のランクに応じたコンテンツをキャッシュ ◦ etc.
21 エッジコンピューティング | Fastly Compute • Fastly Computeを導入 ◦ エッジ上でWASMを実行。ロジックをRustで記述可能
◦ 一休.comレストランではVCLでの設定から移行中 ▪ コードが読みやすい、テストが書きやすい
22 エッジコンピューティング | Fastly Compute #[fastly::main] fn main(mut req: Request)
-> Result<Response, Error> { // ... let resp = handle(req.clone_with_body())?; // ... Ok(resp) } fn handle(mut req: Request) -> Result<Response, Error> { let backend = select_backend(&req); let resp = match &backend { Backend::RustBackend(cache_type) => { if !req.get_method().is_safe() { return Ok(req.with_pass(true).send(backend.name())?); } match cache_type { CacheType::Cache(options) => { // キャッシュ制御ロジック }, CacheType::CacheWithCacheGroup(options) => { // 会員ランクごとのキャッシュ制御ロジック }, // ... #[fastly::main]を付与したmain でリクエストをハンドリングする RustのコードとしてCDN上での キャッシュ制御やリクエスト振り分け を実装できる
23 本発表で話すこと • 一休.comレストランについて • Rust活用の詳細 ◦ バックエンド ◦ エッジコンピューティング
• 今後のチャレンジ
24 今後のチャレンジ | 予約コアロジックの移行 • VBScriptで書かれWindows Server上で動く、予約作成、 変更、キャンセルなどを担うシステム (サービス開始当初から存在) •
VBScriptは2027年ごろWindowsからデフォルト無効化 https://techcommunity.microsoft.com/t5/windows-it-pro-blog/vbscript-deprecation-timelines-and-next-steps/ba-p/4148301
25 今後のチャレンジ | 予約コアロジックの移行 • 新規予約、変更、キャンセル、割引やポイント付与を含む 既存のVBScriptからRustへの移行 • Rustのコードとして、どうデータモデルを表現するかから 取り組むことが求められる
26 エンジニアを募集しています https://www.ikyu.co.jp/recruit/engineer