背景
railsでウェブサービスを作っています。ありがたいことにユーザー数も増えてきました。そこで起きたことについてのメモです。
結論
ActiveRecord.transactionを貼っていたことが原因でLockWaitTimeoutが発生していました。
before
ActiveRecord.transaction do
users.each do |user|
user.do_something
user.profile.do_something
end
end
after
users.each do |user|
ActiveRecord.transaction do
user.do_something
user.profile.do_something
end
end
「transactionを全体に貼るなよ」というのはあると思いますが、お金の関わるところだったので全体で処理が完了するように担保したかったという背景もあり。。
起きたこと
ユーザー数が増えてきたタイミングから ActiveRecord::LockWaitTimeout
が発生するようになりました。
調査1(パフォーマンス・チューニング)
まずはログを見ましたが、アクセスが増えてるわけでも重い処理が走っているわけでもなさそうでした。
ということでnginx / puma / mysql の設定を疑いました。
nginx
ワーカープロセス数は auto
になっていたので自動でコア数に合わせて作成されます
worker_processes auto;
puma
pumaのドキュメントより
ワーカー数→CPUコア数 ~ 1.5倍
スレッド数→16
が良いそうです。
RAILS_MAXTHREADS = 16
WEB_CONCURRENCY = CPU_NUM * 1.5 = xx
threads_count = ENV.fetch("RAILS_MAX_THREADS") { 5 }
threads threads_count, threads_count
workers ENV.fetch("WEB_CONCURRENCY") { 2 }
mysql
RAILS_MAXTHREADS = 16
WEB_CONCURRENCY = CPU_NUM * 1.5 = xx
pool: <%= ENV['RAILS_MAX_THREADS'] || 5 %>
結果
ユーザーの接続数などが原因かと思っていましたがそんなこともなくLockWaitTimeoutは引き続き発生していました。
調査2
ログ見ても特に問題点がなさそうだったので、ダメ元で社内の人に確認すると「運用のとある処理中」にLockWaitTimeoutが発生していることが判明。
ユーザーの動きとは別のものがトリガーで起きていたようです。
該当するソースを読んだらtransactionを貼っていて、修正したら発生しなくなりました。
最後に
ActiveRecord::LockWaitTimeout
まわりで調べても設定まわりの話が多くメモ的に残しておこうと思いました。どなたか同じように詰まっている方のお力に慣れれば幸いです。
ActiveRecord.transactionが何をしているか気になったので追ってみました。
ソースを読む。
多分データベースのトランザクションを開始してる。
mysqlのトランザクションのかかり方は以下のURLより
http://bluerabbit.hatenablog.com/entry/2013/12/07/075759
https://qiita.com/dorarep/items/d7d767e1bceb2d5649a7