Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
推し活の ハイトラフィックに立ち向かう Railsとアーキテクチャ - Kaigi on Ra...
Search
Hayato OKUMOTO
October 26, 2024
Programming
7
4.3k
推し活の ハイトラフィックに立ち向かう Railsとアーキテクチャ - Kaigi on Rails 2024
Hayato OKUMOTO
October 26, 2024
Tweet
Share
More Decks by Hayato OKUMOTO
See All by Hayato OKUMOTO
Angular x Auth0 複数サービス展開での認証基盤を考える
falcon8823
0
490
Angular Schematicsを利用した アプリ量産体制
falcon8823
0
98
iOSとIonicとHEIF画像
falcon8823
0
340
Ionicアプリのビルド自動化
falcon8823
0
23
Firebase Authentication - Ionic Meetup #12 Tokyo
falcon8823
0
270
IonicアプリをAuth0で認証する - Ionic Meetup #16 in Online
falcon8823
0
390
Other Decks in Programming
See All in Programming
PHPUnitしか使ってこなかった 一般PHPerがPestに乗り換えた実録
mashirou1234
0
420
非ブラウザランタイムとWeb標準 / Non-Browser Runtimes and Web Standards
petamoriken
0
430
Androidアプリの One Experience リリース
nein37
0
1.1k
週次リリースを実現するための グローバルアプリ開発
tera_ny
1
1.1k
Simple組み合わせ村から大都会Railsにやってきた俺は / Coming to Rails from the Simple
moznion
3
2k
いりゃあせ、PHPカンファレンス名古屋2025 / Welcome to PHP Conference Nagoya 2025
ttskch
1
150
Amazon Nova Reelの可能性
hideg
0
180
20241217 競争力強化とビジネス価値創出への挑戦:モノタロウのシステムモダナイズ、開発組織の進化と今後の展望
monotaro
PRO
0
270
Fibonacci Function Gallery - Part 2
philipschwarz
PRO
0
210
Swiftコンパイラ超入門+async関数の仕組み
shiz
0
170
ErdMap: Thinking about a map for Rails applications
makicamel
1
540
技術的負債と向き合うカイゼン活動を1年続けて分かった "持続可能" なプロダクト開発
yuichiro_serita
0
290
Featured
See All Featured
Fantastic passwords and where to find them - at NoRuKo
philnash
50
2.9k
JavaScript: Past, Present, and Future - NDC Porto 2020
reverentgeek
47
5.1k
Speed Design
sergeychernyshev
25
730
How to Think Like a Performance Engineer
csswizardry
22
1.3k
Building an army of robots
kneath
302
45k
Documentation Writing (for coders)
carmenintech
67
4.5k
The MySQL Ecosystem @ GitHub 2015
samlambert
250
12k
Easily Structure & Communicate Ideas using Wireframe
afnizarnur
192
16k
A Modern Web Designer's Workflow
chriscoyier
693
190k
Adopting Sorbet at Scale
ufuk
74
9.2k
Side Projects
sachag
452
42k
The Art of Programming - Codeland 2020
erikaheidi
53
13k
Transcript
2024年10月26日 推 し 活 の ハ イ ト ラ フ
ィ ッ ク に 立 ち 向 か う R a i l s と ア ー キ テ ク チ ャ 株式会社TwoGate 取締役CTO 奥本 隼
奥本 隼 (Hayato OKUMOTO) @falcon_8823 株式会社TwoGate 取締役CTO チーム組成から11年 / 創業8年
長野高専出身 Rails歴10年以上 自己紹介
Ruby Sponsor
TwoGateの主要なソリューション ライブイベント向け OEM型モバイルオーダーアプリ オンラインくじ ファンクラブアプリ チケットサイト 共通会員ID基盤 Shopify EC構築支援 多数のサービス展開
ライブエンタメ領域に対して、 コンパウンド戦略でサービスを複数展開 しています。
Caravan - イベント物販に特化したアプリ
特徴 サーバサイドはマルチテナント アプリはOEM型 1アーティスト=1アプリ 短納期での提供 内製向けローコード化 負荷対策に強い
事例 TwoGate inc. のApp Storeで一部配信されています。 累計220万DL / 100万ユーザ登録 エンタメ業界、事例を公開しにくい… TwoGateのブースでこっそりお教えします。
累計50アプリほど提供
きっと会場内にも ユーザがいるはず
技術スタック サーバサイド フロントエンド インフラ
Fastly インフラアーキテクチャ 実はシンプル+モノリシックなRailsアプリ ALB nginx nginx Rails App Aurora PostgreSQL
ElastiCache Redis (Cache) ElastiCache Redis (Queue) Sidekiq API Request Amazon ECS
ピークトラフィック CDN 50,000 RPS ALB / Rails 8,000 RPS 決済エンドポイント
400 RPS 先日660 RPSに記録更新
トラフィックに 耐えきれず 障害の経験も
Rails × ハイトラフィック 本発表のテーマ
推し活 × ハイトラフィック
ハイトラフィックに挑む設計 スロークエリをおこさないように実装 CDNでのキャッシュを活用する 後から導入ではなく最初から / CDNでキャッシュするエ ンドポイントは名前空間を切る アプリケーションキャッシュ(Redis)の活用 CDNでのキャッシュがどうしても難しいとき
この規模で起きる問題 DBレイヤーでのボトルネックから始まる インデックスの不足 / クエリが悪い / テーブル設計が悪い 実行計画が変わって急に遅くなることも 各種外部APIのレートリミットに引っかかる ピーク性能を出すパラメータチューニングが必要
ALBのスケーリングが間に合わずにエラーが発生する
どのように対処しているか Performance Insight / APMによる分析 アプリケーションログの分析 特定条件だけ遅いケース:ある商品だけ在庫が非常に多い 実行計画の解析 ChatGPTに投げ込むと便利 負荷試験
ChatGPTで実行計画を解析
先着販売 予告した日時に販売が開始する 在庫限りでの販売 受け取り時間枠選択→商品選択→カート→決済の流れ 本発表で扱うプロダクトの機能 ユーザが一同にアクセスし、在庫の奪い合いが始まる。
ライブエンタメでの販売の難しさ 在庫を綺麗に売り切りたい 在庫数を大幅に超過してはいけない 決済エラー等によって在庫が浮いたままではいけない ピークトラフィックでサーバダウンできない レスポンスタイムの悪化はユーザの不満になる
本発表で扱うテーマ 本発表では、次の2つのテーマについて取り扱います。 高スループットかつ正確な在庫確保のアーキテクチャ 外部決済APIのレートリミットとの向き合い
在庫確保 × パフォーマンス Part.1
在庫確保の簡易な実装 商品ID 在庫数 販売数 P1 100 20 P2 150 10
P3 50 30 在庫テーブル 商品P1を10購入 UPDATE 在庫テーブル SET 販売数 = 販売数 - 10 WHERE 商品ID = P1;
在庫確保の簡易な実装 商品ID 在庫数 販売数 P1 100 10 P2 150 10
P3 50 30 在庫テーブル 商品P1を10購入 UPDATE 在庫テーブル SET 販売数 = 販売数 - 10 WHERE 商品ID = P1; SQLでトランザクションを 学ぶ題材でよく出てくる実装
在庫確保の簡易な実装 商品ID 在庫数 販売数 P1 100 20 P2 150 10
P3 50 30 在庫テーブル 商品P1を10購入 UPDATE 在庫テーブル SET 販売数 = 販売数 - 10 WHERE 商品ID = P1; 同時に購入者がいると… 商品P2を20購入 UPDATE 在庫テーブル SET 販売数 = 販売数 - 20 WHERE 商品ID = P2;
在庫確保の簡易な実装 商品ID 在庫数 販売数 P1 100 -10 P2 150 10
P3 50 30 在庫テーブル 同時に購入者がいると… 在庫数を確認したタイミングと 更新するまでの間に更新が走れ ば、在庫数を超過する。 解決策 行ロックを導入する
在庫確保の安全な実装 商品ID 在庫数 販売数 P1 100 20 P2 150 10
P3 50 30 在庫テーブル 商品P1を10購入 BEGIN; SELECT * FROM 在庫テーブル WHERE 商品ID = P1 FOR UPDATE; -- ここで在庫数 > 販売数を確認 UPDATE 在庫テーブル SET 販売数 = 販売数 - 10 WHERE 商品ID = P1; COMMIT;
行ロックを掛けることで正確な数量の販売ができるように しかし、 数百RPSでの在庫確保のワークロードでは… 同じ行を更新するユーザが多数いる ロックの奪い合いでロック待ち, デッドロックが頻発する 在庫確保の安全な実装
ロック競合を回避するには =同じデータを同時に書き換えないような構造に変更する 次の疑問 どうやって行を奪い合わないようにすればよい?? 同じ行を同時に書き換えるから競合する
在庫テーブルの設計を変える 在庫ID 商品ID ユーザID Z1 P1 Z2 P1 Z3 P1
Z4 P1 在庫テーブル 1行1在庫のテーブルに変える 順番に排出できれば、競合を回避 できるはず。 どのように実装する??
FOR UPDATE SKIP LOCKED これを対処する実装は非常に シンプル 行ロックされている行をスキ ップした結果を応答してくれ る PostgreSQL
9.5~ MySQL 8.0~ BEGIN; SELECT * FROM 在庫テーブル WHERE 商品ID = P1 LIMIT 4 FOR UPDATE SKIP LOCKED; -- ここで行数 = 購入数を確認 UPDATE 在庫テーブル SET ユーザID = Ua WHERE 商品ID = P1; COMMIT;
在庫テーブルの設計を変える 在庫ID 商品ID ユーザID Z1 P1 Z2 P1 Z3 P1
Z4 P1 在庫テーブル ユーザUaがP1を2行取得&ロック 1. ロックが無い2行取得 SELECT * FROM 在庫テーブル WHERE 商品ID = P1 LIMIT 2 FOR UPDATE SKIP LOCKED;
在庫テーブルの設計を変える 在庫ID 商品ID ユーザID Z1 P1 Z2 P1 Z3 P1
Z4 P1 在庫テーブル 2. ユーザUbがP1を3行取得 SELECT * FROM 在庫テーブル WHERE 商品ID = P1 LIMIT 3 FOR UPDATE SKIP LOCKED; Uaがロックしている行をスキップ Z3,Z4の2行だけを返す 足りないので在庫不足で扱う
高スループットでの在庫確保が可能に 行ロックされていない行を必ず返答する保証がある 競合することがなくなり、実測で数百TPSでの処理が可 能に 【余談】Solid QueueはDBをバックエンドにしているが、 同様にFOR UPDATE SKIP LOCKEDを使って実現してい
る
在庫処理まとめ 1在庫1行のデータ構造で管理する 欠点としてテーブルが肥大化するが、販売が終わったら 消すことで解消 FOR UPDATE SKIP LOCKEDで競合を回避する 数百TPSの注文処理を捌ける 先日は200万在庫のテーブルで運用し問題無く捌いた
決済 × パフォーマンス Part.2
前提 クレジットカード決済には外部のペイメントプロバイダを利用 非通過、非保持化のため 決済関連のAPIレートリミットは20-30 rps程度 stripeでもデフォルトは100 rps 決済会社との交渉や料率の交渉が必要 そもそも400 rps近くまでの緩和は厳しい
与信確保 (オーソリ) 決済と在庫確保の流れ 決済できずに宙に浮く在庫を無くすため、この流れで実装。 在庫確保 売上確定 与信開放 (キャンセル) 在庫OK 在庫NG
売り切れた在庫が復活すると、タイミングと件数 によってはクレームが発生するため。
本当にあった怖い話 与信確保→在庫確保→売上確定 売上確定時にレートリミットエラー→ロールバック 奇跡的に与信確保できた人でも、 売上確定時にレートリミットエラー 結局、ほとんど誰も買えない事態に。
じゃあどうする? そもそも、決済プロバイダのレートを上げることができない 非同期にして後から決済を通知する? 待機してもらう?
諦めて待機してもらう いずれにせよ、 解決しない問題なので 諦めました
正しく諦める 決済システムの限界よりもリクエストを受け付けな いようにする これ以上捌けないのに処理を発行しない レートリミットを導入
レートリミットの実装 CDN/WAFで対応する? 正確性や条件制御が単純なため不採用 お高いプランにするとできたりする 正確なレートリミットのためにRedisで自作
決済×パフォーマンスまとめ コロンブスの卵的な対応だが… 限界に対して正しく向き合うことは大事 負荷試験で外部APIをモックするときはレートリミ ット時の挙動も考慮すること
最後に… Railsでハイトラフィックを捌いている事例をお伝 えしました TwoGateはライブエンタメ業界に全方位プロダク トを展開していきます
ASK THE SPEAKER 以下の場所で質疑をお受け付けします! TwoGate企業ブース FREE AFTER PARTY by TWOGATE
& KOMOJU 本日19時開催 『推し活のハイトラフィックに立 ち向かうRailsとアーキテクチャ』 【登壇者】取締役CTO 奥本 『Type on Rails: Railsアプリ ケーションの安全性と開発体験を 型で革新する』 【登壇者】エンジニア 村田 Speakers
None