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
720
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
6.1k
RBS meets LLMs - Type inference using LLM
kokuyouwind
0
790
オンラインボードゲームを作りたい人生だった
kokuyouwind
0
450
1年間本番運用してわかった、スタートアップこそAWS Copilot CLIを使うべきNつの理由
kokuyouwind
2
11k
なるべく楽したいAWSセキュリティ
kokuyouwind
1
59
Railsパフォーマンス・チューニング入門
kokuyouwind
0
260
Rubyパターンマッチに闇の力が備わり最強に見える
kokuyouwind
0
86
Slackワークフロー活用術
kokuyouwind
0
89
10分で作る勉強会アプリ
kokuyouwind
0
55
Other Decks in Programming
See All in Programming
責務と認知負荷を整える! 抽象レベルを意識した関心の分離
yahiru
2
440
法律の脱レガシーに学ぶフロントエンド刷新
oguemon
5
740
パスキーのすべて ── 導入・UX設計・実装の紹介 / 20250213 パスキー開発者の集い
kuralab
3
780
Software Architecture
hschwentner
6
2.1k
一休.com のログイン体験を支える技術 〜Web Components x Vue.js 活用事例と最適化について〜
atsumim
0
470
Boost Performance and Developer Productivity with Jakarta EE 11
ivargrimstad
0
270
Immutable ActiveRecord
megane42
0
140
1年目の私に伝えたい!テストコードを怖がらなくなるためのヒント/Tips for not being afraid of test code
push_gawa
0
140
Amazon Q Developer Proで効率化するAPI開発入門
seike460
PRO
0
110
Honoをフロントエンドで使う 3つのやり方
yusukebe
7
3.3k
Pulsar2 を雰囲気で使ってみよう
anoken
0
240
WebDriver BiDiとは何なのか
yotahada3
1
140
Featured
See All Featured
What's in a price? How to price your products and services
michaelherold
244
12k
Principles of Awesome APIs and How to Build Them.
keavy
126
17k
The Cult of Friendly URLs
andyhume
78
6.2k
Raft: Consensus for Rubyists
vanstee
137
6.8k
Bash Introduction
62gerente
611
210k
Practical Tips for Bootstrapping Information Extraction Pipelines
honnibal
PRO
12
960
VelocityConf: Rendering Performance Case Studies
addyosmani
328
24k
Visualizing Your Data: Incorporating Mongo into Loggly Infrastructure
mongodb
45
9.4k
Evolution of real-time – Irina Nazarova, EuRuKo, 2024
irinanazarova
6
550
ReactJS: Keep Simple. Everything can be a component!
pedronauck
666
120k
Helping Users Find Their Own Way: Creating Modern Search Experiences
danielanewman
29
2.4k
CoffeeScript is Beautiful & I Never Want to Write Plain JavaScript Again
sstephenson
160
15k
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