プロファイリング
パフォーマンスの問題を追跡しやすくするために、GitLabには一連のプロファイリングツールが用意されています。これらの中にはデフォルトで利用できるものもあれば、明示的に有効にする必要があるものもあります。
URLのプロファイリング
Gitlab::Profiler.profile
メソッドとそれに対応するbin/profile-url
スクリプトがあり、特定の URL に対する GET または POST リクエストを、匿名ユーザー (デフォルト) あるいは特定のユーザーとしてプロファイリングすることができます。
スクリプトを使用する場合、引数を指定しないことで、コマンドライン・ドキュメンテーションを利用できます。
インタラクティブなコンソール・セッションでこのメソッドを使用すると、そのコンソール・セッション内でのアプリケーション・コードの変更がプロファイラの出力に反映されます。
使用例:
Gitlab::Profiler.profile('/my-user')
# Returns a RubyProf::Profile for the regular operation of this request
class UsersController; def show; sleep 100; end; end
Gitlab::Profiler.profile('/my-user')
# Returns a RubyProf::Profile where 100 seconds is spent in UsersController#show
作成者を必要とするルートでは、Gitlab::Profiler
にユーザーを提供する必要があります。 このようにできます:
Gitlab::Profiler.profile('/gitlab-org/gitlab-test', user: User.first)
Gitlab::Profiler.profile
にlogger:
キーワード引数を渡すと、ActiveRecord と ActionController のログ出力がそのロガーに送られます。 その他のオプションはメソッドのソースに記載されています。
Gitlab::Profiler.profile('/gitlab-org/gitlab-test', user: User.first, logger: Logger.new(STDOUT))
RubyProfプリンタもあります:Gitlab::Profiler::TotalTimeFlatPrinter
. これはRubyProf::FlatPrinter
と同じように動作しますが、min_percent
オプションはメソッド自体の時間ではなく、メソッドの合計時間で動作します。 (これは、ライブラリコードでほとんどの時間を費やすことがよくありますが、これはアプリケーションの呼び出しから来るためです。) また、max_percent
オプションもあり、有用でない内部呼び出しをフィルタリングするのに役立ちます (ActionDispatch::Integration::Session#process
のようなものです)。
Gitlab::Profiler.print_by_total_time
という便利な方法があります:
result = Gitlab::Profiler.profile('/my-user')
Gitlab::Profiler.print_by_total_time(result, max_percent: 60, min_percent: 2)
# Measure Mode: wall_time
# Thread ID: 70005223698240
# Fiber ID: 70004894952580
# Total: 1.768912
# Sort by: total_time
#
# %self total self wait child calls name
# 0.00 1.017 0.000 0.000 1.017 14 *ActionView::Helpers::RenderingHelper#render
# 0.00 1.017 0.000 0.000 1.017 14 *ActionView::Renderer#render_partial
# 0.00 1.017 0.000 0.000 1.017 14 *ActionView::PartialRenderer#render
# 0.00 1.007 0.000 0.000 1.007 14 *ActionView::PartialRenderer#render_partial
# 0.00 0.930 0.000 0.000 0.930 14 Hamlit::TemplateHandler#call
# 0.00 0.928 0.000 0.000 0.928 14 Temple::Engine#call
# 0.02 0.865 0.000 0.000 0.864 638 *Enumerable#inject
プロフィールをHTML形式で印刷するには、次の例を使用します:
result = Gitlab::Profiler.profile('/my-user')
printer = RubyProf::CallStackPrinter.new(result)
printer.print(File.open('/tmp/profile.html', 'w'))
GitLab-Profilerはこれをベースに、複数の URL に対して一つの YAML ファイルで設定できるようにしたり、プロファイルやログ出力を S3 にアップロードできるようにしたりするなど、いくつかの機能を追加したプロジェクトです。
GitLab.comについては、こちらで最新の結果をご覧いただけます(GitLabチームメンバー限定):https://redash.gitlab.com/dashboard/gitlab-profiler-statistics
シャーロック
SherlockはGitLabに組み込まれたカスタムプロファイリングツールです。SherlockはGitLabを開発モードで実行_し、_環境変数ENABLE_SHERLOCK
を空でない値に設定した_場合のみ_利用できます。 例えば、以下のようになります:
ENABLE_SHERLOCK=1 bundle exec rails s
記録された取引は、/sherlock/transactions
にアクセスして確認できます。
弾丸
Bulletは、N+1クエリの問題を追跡するために使用できるGemです。 Bulletセクションはパフォーマンス・バーに表示されます。
Bulletはかなりの量のロギングノイズを追加するため、ロギングはデフォルトでは無効になっています。 ロギングを有効にするには、GitLabを起動する前に環境変数ENABLE_BULLET
に空でない値を設定します。 たとえば、次のようにします:
ENABLE_BULLET=true bundle exec rails s
Bulletはクエリの問題をRailsログとChromeコンソールの両方に記録します。
BulletでN+1
クエリを見つけたときのフォローアップとして、リグレッションを防ぐためにQueryRecoderテストを書くことを検討してください。
パフォーマンスに影響する設定
-
development
環境ではデフォルトでホットリロードが有効になっていますが、ホットリロードはシングルスレッドで行われるため、Railsはリクエストごとにファイルの変更をチェックし、競合ロックが発生する可能性があります。 -
development
環境では、リクエストが発生すると、コードを遅延してロードすることができ、その結果、最初のリクエストは常に遅くなります。
プロファイリング/ベンチマークのためにこれらの機能を無効にするには、GitLabを起動する前にRAILS_PROFILE
環境変数をtrue
に設定します。 例えばGDKを使用する場合:
- GDKのルートディレクトリにファイル
env.runit
を作成します。 -
env.runit
ファイルにexport RAILS_PROFILE=true
を追加してください。 - でGDKを再起動します。
gdk restart
この環境変数は開発モードにのみ適用されます。