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
APMをちゃんと使おうとしたら、いつのまにか独自gemを作っていた話
Search
kokuyouwind
October 28, 2023
Programming
0
710
APMをちゃんと使おうとしたら、いつのまにか独自gemを作っていた話
Kaigi on Rails 2023 セッションの発表資料です
https://kaigionrails.org/2023/talks/kokuyouwind/
kokuyouwind
October 28, 2023
Tweet
Share
More Decks by kokuyouwind
See All by kokuyouwind
Let's use LLMs from Ruby 〜 Refine RBS types using LLM 〜
kokuyouwind
0
6k
RBS meets LLMs - Type inference using LLM
kokuyouwind
0
790
オンラインボードゲームを作りたい人生だった
kokuyouwind
0
440
1年間本番運用してわかった、スタートアップこそAWS Copilot CLIを使うべきNつの理由
kokuyouwind
2
11k
なるべく楽したいAWSセキュリティ
kokuyouwind
1
57
Railsパフォーマンス・チューニング入門
kokuyouwind
0
250
Rubyパターンマッチに闇の力が備わり最強に見える
kokuyouwind
0
81
Slackワークフロー活用術
kokuyouwind
0
87
10分で作る勉強会アプリ
kokuyouwind
0
55
Other Decks in Programming
See All in Programming
自分ひとりから始められる生産性向上の取り組み #でぃーぷらすオオサカ
irof
8
2.2k
SwiftUIで単方向アーキテクチャを導入して得られた成果
takuyaosawa
0
140
AWS re:Invent 2024個人的まとめ
satoshi256kbyte
0
150
自動で //nolint を挿入する取り組み / Gopher's Gathering
utgwkk
1
170
法律の脱レガシーに学ぶフロントエンド刷新
oguemon
4
610
asdf-ecspresso作って 友達が増えた話 / Fujiwara Tech Conference 2025
koluku
0
1.6k
2024年のkintone API振り返りと2025年 / kintone API look back in 2024
tasshi
0
170
個人アプリを2年ぶりにアプデしたから褒めて / I just updated my personal app, praise me!
lovee
0
300
バックエンドのためのアプリ内課金入門 (サブスク編)
qnighy
7
1.5k
ペアーズでの、Langfuseを中心とした評価ドリブンなリリースサイクルのご紹介
fukubaka0825
1
200
動作確認やテストで漏れがちな観点3選
starfish719
5
860
令和7年版 あなたが使ってよいフロントエンド機能とは
mugi_uno
12
6k
Featured
See All Featured
jQuery: Nuts, Bolts and Bling
dougneiner
63
7.6k
JavaScript: Past, Present, and Future - NDC Porto 2020
reverentgeek
47
5.1k
A Modern Web Designer's Workflow
chriscoyier
693
190k
Refactoring Trust on Your Teams (GOTO; Chicago 2020)
rmw
33
2.8k
CoffeeScript is Beautiful & I Never Want to Write Plain JavaScript Again
sstephenson
160
15k
How To Stay Up To Date on Web Technology
chriscoyier
790
250k
Site-Speed That Sticks
csswizardry
3
310
10 Git Anti Patterns You Should be Aware of
lemiorhan
PRO
656
59k
Building Your Own Lightsaber
phodgson
104
6.2k
Raft: Consensus for Rubyists
vanstee
137
6.7k
Build your cross-platform service in a week with App Engine
jlugia
229
18k
The Invisible Side of Design
smashingmag
299
50k
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