ActiveJobとSidekiqで実装されたとある非同期処理を高速化しようと試みています。 入力ファイルからデータを読み取ってDBへ保存する処理です。 事前処理がそれなりにあるのでCPUバウンドな処理とI/Oバウンドな処理が半々ぐらいです。
CPUバウンドな処理は並列化すればいいので、1つのジョブの複数のジョブに分割することにしました。 入力ファイルは複数のファイルを圧縮したものです。 処理の単位も解凍後のファイル単位で分割出来ます。 解凍後のファイル毎にジョブをわけることにしました。 解凍後は7万ファイルに分かれることもあるので、ファイル1つずつではなく100個ずつジョブをわけます。
結果、処理速度は約1/3に低下しました。 1秒に3ファイル程度処理できていたのが1ファイルになりました。 1つのジョブで100ファイルを処理します。 ジョブあたり30秒程度で終わることを期待していました。 100秒になりました。
試しにDBへの保存処理をスキップしてみたところ、高速化しています。 CPUバウンドな処理が並列化で高速化したのは予想通りです。 それを上回るレベルでDBへのアクセスが遅くなったのが予想外です。
DBへアクセスするクライアントが増えたので、DBのスイッチング回数が増えて速度が低下するのはありそうな話です。 ですが、動かしているワーカーは4つです。 CPUのコア数が4つしかないので、4つに制限しました。 たかだか4ワーカーからのアクセスでそんなに遅くなるものなのでしょうか? 30秒が35秒になることはあっても、100秒になる気はしません。 ワーカー数を減らしても速度は回復しません。
DBへのアクセスは検索もそれなりに含まれています。 ジョブを分割した分、ジョブが切り替わるたびにSQLキャッシュがリセットされます。 100秒に一回SQLキャッシュをリセットすることがそんなに影響あるでしょうか? 30秒が40秒になることはあっても、100秒になる気はしません。
というわけで謎です。 SQLの検索部分が速くなりそうな作戦はあります。 実施すれば解消されるかもしれません。 されないかもしれません。 仮にされても気持ち悪いです。 気になるところです。