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
APMをちゃんと使おうとしたら、いつのまにか独自gemを作っていた話
Search
Sponsored
·
Ship Features Fearlessly
Turn features on and off without deploys. Used by thousands of Ruby developers.
→
kokuyouwind
October 28, 2023
Programming
1k
0
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
APMをちゃんと使おうとしたら、いつのまにか独自gemを作っていた話
Kaigi on Rails 2023 セッションの発表資料です
https://kaigionrails.org/2023/talks/kokuyouwind/
kokuyouwind
October 28, 2023
More Decks by kokuyouwind
See All by kokuyouwind
疑似コードによるプロンプト記述、どのくらい正確に実行される?
kokuyouwind
0
620
謎解きサイトを Rails SPA で作って RubyKaigi で配布した話
kokuyouwind
0
100
ドメイン指定Cookieとサービス間共有Redisで作る認証基盤サービス
kokuyouwind
0
49
Do LLMs dream of Type Inference?
kokuyouwind
0
54
Let's use LLMs from Ruby 〜 Refine RBS types using LLM 〜
kokuyouwind
0
8.7k
RBS meets LLMs - Type inference using LLM
kokuyouwind
0
1.1k
オンラインボードゲームを作りたい人生だった
kokuyouwind
0
720
1年間本番運用してわかった、スタートアップこそAWS Copilot CLIを使うべきNつの理由
kokuyouwind
2
12k
なるべく楽したいAWSセキュリティ
kokuyouwind
1
130
Other Decks in Programming
See All in Programming
気持ちぃ〜角丸 Squircle
ryu1013
0
300
Simple Storage Service(S3) is not simple
iwatsukayura
0
280
wkhtmltopdfの次どうするか問題2026
willnet
2
1.9k
APNsからLive Activityを開始する話
yumnumm
0
170
ChromeでローカルLLM
ryu1013
0
130
Apple Intelligence を用いた個人情報誤送信防止、及びユーザーリクエスト体験の改善について
yukiny
0
340
個人開発者よ、Honoを使え(おじさんver)
tanahiro2010
2
250
contenteditable と日本語入力に向き合う
colorful12
0
190
外から家のPCの電源入れたい!をPHPで解決する
kawashimo0
1
130
なぜCTOを降りてFDEを選んだのか?〜なぜプロダクト企業がFDEで顧客の現場に踏み込むのか〜
gonta
1
200
複雑なUIコンポーネントとの ⻑い付き合い⽅:波及するバグからテスト戦略を経て、安全な再設計へ
mixplace
0
390
PumaからFalconに移行したRailsアプリケーションでの実践
falcon8823
0
130
Featured
See All Featured
Keith and Marios Guide to Fast Websites
keithpitt
413
23k
Between Models and Reality
mayunak
4
490
The Director’s Chair: Orchestrating AI for Truly Effective Learning
tmiket
1
330
New Earth Scene 8
popppiees
4
2.7k
Speed Design
sergeychernyshev
33
2.2k
個人開発の失敗を避けるイケてる考え方 / tips for indie hackers
panda_program
123
22k
Building Better People: How to give real-time feedback that sticks.
wjessup
370
20k
Sam Torres - BigQuery for SEOs
techseoconnect
PRO
0
580
Fireside Chat
paigeccino
43
4.1k
Fantastic passwords and where to find them - at NoRuKo
philnash
53
4k
The Pragmatic Product Professional
lauravandoore
37
7.5k
Ten Tips & Tricks for a 🌱 transition
stuffmc
1
260
Transcript
APM をちゃんと使おうとしたら、 いつのまにか独⾃gem を作っていた話 Leaner Technologies, Inc. 黒曜 (@kokuyouwind)
$ whoami 黒曜 / @kokuyouwind Leaner Technologies Inc. 所属 Rails
エンジニア インフラ・SRE 的なこともやっている APM など開発周辺ツールの管理
APM の話をします
APM とは Application Performance Monitoring の略 リクエスト数や応答速度を監視・管理できる 代表的な APM サービス
etc.
APM の導⼊⾃体は結構簡単 https://docs.datadoghq.com/ja/tracing/trace_collection/dd_libraries/ruby/ ⼤抵の APM は エージェントソフトウェアと gem を設定するだけ #
Gemfile gem 'ddtrace', require: 'ddtrace/auto_instrument' # config/initializers/datadog.rb Datadog.configure do |c| c.service = 'my-service' end 1 2 3 4 5 6 7
APM の画⾯例 (1) https://docs.newrelic.com/jp/docs/apm/new-relic-apm/getting-started/introduction-apm/
APM の画⾯例 (2) https://docs.datadoghq.com/ja/tracing/other_telemetry/connect_logs_and_traces/
🤔 すごいけど、何を⾒ればいいの… ?
APM 導⼊後のあるある 導⼊で満⾜して、その後画⾯を開かなくなった 応答速度の遅いエンドポイントを⾒て、 「やっぱりここは重いよね〜」と話して終わる ソースコード上でどこが重いのか、結局わからない 筆者は過去に全部⼼当たりがあります…( ⼩声)
ひとつの参考事例として、 弊社での APM の使い⽅を紹介します
アジェンダ 簡単な APM の使い⽅ トレースを使ったパフォーマンス・チューニング APM Traceable gem を作った話 具体的なチューニング事例紹介
まとめ
アジェンダ 簡単な APM の使い⽅ トレースを使ったパフォーマンス・チューニング APM Traceable gem を作った話 具体的なチューニング事例紹介
まとめ
簡単な APM の使い⽅ ⼤抵の APM は集計単位ごとにページに分かれている サイト全体 エンドポイントごと リクエストごと 最初は「サイト全体」の指標で⼤まかな傾向を⾒ると良い
細かく掘り下げていくときに「エンドポイントごと」 「リクエストごと」の順に⾒ていく
ここから先は Datadog APM を例に スクリーンショットを出します ( 他APM でも同様の画⾯があるはず)
APM の⾒⽅ - 全体の統計
APM の⾒⽅ - 全体の統計
APM の⾒⽅ - 全体の統計 リクエスト数も応答速度も、極端な変化がないか確認 曜⽇や時間帯で傾向が違うため、前週同曜⽇と⽐較する サービスによっては⽉末・年末なども考慮する 異常値があった場合はトリアージする ⼀時的要因であれば様⼦⾒、問題がありそうなら調査・対応
APM の⾒⽅ - 個別エンドポイント
APM の⾒⽅ - 個別エンドポイント
APM の⾒⽅ - 個別エンドポイント
APM の⾒⽅ - 確認タイミング 週1 で「APM を⾒る会」を実施している 悪化傾向を⾒つけられるよう定期的に⾒る機会を作る トリアージもこのタイミングで実施 開発・運⽤に関わるメンバー全員で⼀緒に⾒ると良い
悪化しているエンドポイントに機能追加などの ⼼当たりがあるか確認しやすい
アジェンダ 簡単な APM の使い⽅ トレースを使ったパフォーマンス・チューニング APM Traceable gem を作った話 具体的なチューニング事例紹介
まとめ
重いエンドポイントを改善する際、 リクエストごとのトレースが⾜がかりになる
リクエストごとのトレース 何に時間がかかっているか( スパン) を表示できる DB アクセスやWeb API 呼び出しのスパンを⾃動で収集
N+1 クエリへの対処 SQL クエリを確認し、適切に includes などを設定する 特定条件やデータセットがないと発⽣しないN+1 クエリは bullet gem
などで⾒つけづらいケースがある
重いクエリへの対処 まずは EXPLAIN で実⾏計画を確認する インデックス追加だけで改善することも多い
時間が⾜りないので 詳しいチューニング⼿法は割愛
https://slides.com/kokuyouwind/rails-performance-tuning 詳細は以前の Kaigi on Rails 2020 で話した 「Rails パフォーマンス・チューニング⼊⾨」をご覧ください!
パフォーマンス・チューニングについては 他の⽅も発表されていたのでおすすめです! Rails アプリの 5,000 件の N+1 問題と戦っている話 初めてのパフォーマンス改善〜君たちはどう計測す( はか)
るか〜
アジェンダ 簡単な APM の使い⽅ トレースを使ったパフォーマンス・チューニング APM Traceable gem を作った話 具体的なチューニング事例紹介
まとめ
⾃動トレースは便利だが、 標準のトレースだけだと困るときがある
困るとき 1. SQL の発⾏場所がわからない ActionController スパン直下に SQL クエリのスパンがある 単なるメソッドの呼び出しは標準だとスパンにならない クエリ構築がどのコードで⾏われているかわからないと
修正しようがない ( 重いクエリの例を再掲)
困るとき2 . Ruby での処理が重いケース Ruby 処理は⾃動でスパンに区切られない 原因が全くわからないので対処もできなくなってしまう
⼿動インストゥルメンテーション https://docs.datadoghq.com/ja/tracing/trace_collection/dd_libraries/ruby/ Datadog::Tracing.trace(name, **options) do |span, trace| # 計測したい処理をここに記⼊ end
1 2 3 Datadog では Datadog::Tracing.trace を使って ⼿動でスパンを追加できる 渡したブロック内がスパンとして区切られる
Datadog::Tracing の微妙な使いづらさ # ↓ 標準だとこう書く必要がある class User def awesome_method Datadog::Tracing.trace(
'awesome_method', service: 'my-service', resource: 'User') do # ... 処理 end end end 1 2 3 4 5 6 7 8 9 10 11 処理対象をブロックで囲むとインデントが変わる 差分が⼤きくなって気軽につけ外ししづらい 引数が多く、記述がやや冗⻑
APM Traceable gem # ↓ こう書けるようにした class User include ApmTraceable::Tracer
trace_methods :awesome_method def awesome_method # ... 処理 end end 1 2 3 4 5 6 7 8 9 trace_methods にメソッド名を指定するとスパンとして表示 UseCase やPresenter など、独⾃レイヤークラスの呼び出し 重いメソッドを切り分けたプライベートメソッド
APM Traceable gem 各Presenter#call をtrace_methods した結果
APM Traceable gem 元々は1 モジュールでlib 以下に置いていた 以前 に書いた話 複数プロダクトに必要だったのでgem に切り出した
APM Traceable gem 本体 Datadog への送信⽤アダプタ アダプタを作れば他の APM にも切り替えられるようにした ブログ記事 apm_traceable apm_traceable-datadog
APM Traceable gem - 仕組み module ApmTraceable::Tracer def self.trace_methods(*method_names) wrapper
= Module.new do method_names.each do |method_name| define_method method_name do |*args, **options, &block| trace_span(method_name.to_s) { super(*args, **options, &block) } end end end prepend(wrapper) end end 1 2 3 4 5 6 7 8 9 10 11 12 13 trace_methods で各メソッドを持つモジュールを作り、 prepend して呼び出しをラップするだけ
APM Traceable gem - 仕組み Class User awesome_method prepended Module
awesome_method メソッド呼び出し Datadog Adapter trace_span トレース送信
アジェンダ 簡単な APM の使い⽅ トレースを使ったパフォーマンス・チューニング APM Traceable gem を作った話 具体的なチューニング事例紹介
まとめ
事例 1. SQL クエリの速度悪化 MySQL 8.0 に更新した際、特定エンドポイントだけ遅くなった
事例 1. SQL クエリの速度悪化
事例 1. SQL クエリの速度悪化 テンポラリテーブルでの SELECT COUNT(*) の問題っぽい… https://zenn.dev/hmatsu47/articles/mysql80-count-slowdown
事例 1. SQL クエリの速度悪化 元のクエリが GROUP BY してから最初の⾏を取っており、 実質 DISTINCT
相当の処理がテンポラリテーブルで⾏われていた DISTINCT を使えばテンポラリテーブルを使わなくなるので直した
事例 1. SQL クエリの速度悪化 元々 1.0sec -> 悪化して 4.0sec ->
改善後 100ms 結果的に元の10 倍に⾼速化🚀
事例 2. 外部API 呼び出しのトレース 外部 API を並列で呼び出す箇所のトレースが取れていなかった ( 単独だと取れるが、2 箇所以上Parallel
だと取れない)
事例 2. 外部API 呼び出しのトレース Parallel をまたぐとスパンの親⼦関係が途切れるようなので、 ⼿動で親ダイジェストを引き渡すよう修正 def search_parallel(clients) +
trace_span('search_parallel') do |_span, trace| results = Parallel.map(clients, in_processes: clients.count) do |client| + trace_span(client.name, continue_from: trace.to_digest) do client.search + end end + end end 1 2 3 4 5 6 7 8 9
事例 2. 外部API 呼び出しのトレース この変更で、きれいにトレースが⾒られるようになった 🎉
事例 2. 外部API 呼び出しのトレース トレースを精査したところ、外部API 呼び出しよりも XML レスポンスのパース処理に時間がかかっていた
事例 2. 外部API 呼び出しのトレース ローカルで再現させ、 Stackprof でプロファイルを取ったところ DidYouMean::Jaro.distance が⼤半を占めている… ?
事例 2. 外部API 呼び出しのトレース XML で未知の attribute が出てきた際、 const_get でNameError
が起こる実装になっていた。 const_defined? で確認を挟むよう実装を修正 klass = "CXML::#{camelize(key)}" - send("#{key}=", Object.const_get(klass).new(val)) + send("#{key}=", Object.const_get(klass).new(val)) + if Object.const_defined?(klass) + send("#{key}=", Object.const_get(klass).new(val)) + else 1 2 3 4 5 6
事例 2. 外部API 呼び出しのトレース
事例 2. 外部API 呼び出しのトレース エンドポイントのレスポンスタイムも、 修正を境に⾶び上がった値が出ることがなくなった
アジェンダ 簡単な APM の使い⽅ トレースを使ったパフォーマンス・チューニング APM Traceable gem を作った話 具体的なチューニング事例紹介
まとめ
まとめ APM は本番で起こっている問題を⾒つけるのに便利 統計値を定期的に⾒て、変化に気づけるようにすると良い トレースを⾒ると重い原因を⾒つける⾜がかりになる 重要なメソッドにスパンを仕込んでおくと原因を掘り下げやすい これをやりやすいように独⾃gem を作った トレースで⽬星をつけて、掘り下げた調査を別で⾏うこともある APM
全然使いこなせてないので、いい使い⽅教えてください! 休憩時間や懇親会でお話しましょう
補⾜資料
各 APM での⼿動トレース機能 New Relic add_method_tracer が trace_method とほぼ同等機能 Scout
APM だけで⼗分かもしれない Open Telemetry Ruby カスタムインストゥルメンテーション Custom Instrumentation Auto Instruments Manual Instrumentation