Sidekiq v6.0.6 + sidekiq-cron + fakeredis で Redis に接続しようとして CI のテストが失敗する
2020/04/06 追記: この問題は sidekiq-cron v1.2.0 で解消されました 👏
2020/04/01 追記: はじめは Sidekiq 単体の問題かと思っていましたが、sidekiq-cron と組み合わせている場合に発生するようなので、全体的に更新しました。
Sidekiq v6.0.6 がリリースされました。
Sidekiq v6.0.6 + sidekiq-cron v1.1.0 + fakeredis の組み合わせで、CircleCI で DB のマイグレーションを行う際にテストが失敗するようになり、対応したメモです。
僕が参加しているプロジェクトでは、RSpec を実行するときは、fakeredis という Redis をシミュレートする Gem を使用しています。
以下が CI のテストが失敗したときのログです。
DB のマイグレーション後、本来は Redis に接続しないはずの処理なのに、127.0.0.1:6379
の Redis に接続できないとエラーが発生しています。
bin/rails db:create db:migrate
# ...
Traceback (most recent call last):
# ...
26: from /home/circleci/repo/vendor/bundle/ruby/2.6.0/gems/sidekiq-6.0.6/lib/sidekiq/launcher.rb:103:in `flush_stats'
25: from /home/circleci/repo/vendor/bundle/ruby/2.6.0/gems/sidekiq-6.0.6/lib/sidekiq.rb:94:in `redis'
24: from /home/circleci/repo/vendor/bundle/ruby/2.6.0/gems/connection_pool-2.2.2/lib/connection_pool.rb:61:in `with'
23: from /home/circleci/repo/vendor/bundle/ruby/2.6.0/gems/connection_pool-2.2.2/lib/connection_pool.rb:61:in `handle_interrupt'
22: from /home/circleci/repo/vendor/bundle/ruby/2.6.0/gems/connection_pool-2.2.2/lib/connection_pool.rb:64:in `block in with'
21: from /home/circleci/repo/vendor/bundle/ruby/2.6.0/gems/connection_pool-2.2.2/lib/connection_pool.rb:64:in `handle_interrupt'
20: from /home/circleci/repo/vendor/bundle/ruby/2.6.0/gems/connection_pool-2.2.2/lib/connection_pool.rb:65:in `block (2 levels) in with'
19: from /home/circleci/repo/vendor/bundle/ruby/2.6.0/gems/sidekiq-6.0.6/lib/sidekiq.rb:97:in `block in redis'
18: from /home/circleci/repo/vendor/bundle/ruby/2.6.0/gems/sidekiq-6.0.6/lib/sidekiq/launcher.rb:104:in `block in flush_stats'
17: from /home/circleci/repo/vendor/bundle/ruby/2.6.0/gems/redis-4.1.3/lib/redis.rb:2411:in `pipelined'
16: from /home/circleci/repo/vendor/bundle/ruby/2.6.0/gems/redis-4.1.3/lib/redis.rb:52:in `synchronize'
15: from /usr/local/lib/ruby/2.6.0/monitor.rb:235:in `mon_synchronize'
14: from /home/circleci/repo/vendor/bundle/ruby/2.6.0/gems/redis-4.1.3/lib/redis.rb:52:in `block in synchronize'
13: from /home/circleci/repo/vendor/bundle/ruby/2.6.0/gems/redis-4.1.3/lib/redis.rb:2416:in `block in pipelined'
12: from /home/circleci/repo/vendor/bundle/ruby/2.6.0/gems/redis-4.1.3/lib/redis/client.rb:162:in `call_pipeline'
11: from /home/circleci/repo/vendor/bundle/ruby/2.6.0/gems/redis-4.1.3/lib/redis/client.rb:306:in `with_reconnect'
10: from /home/circleci/repo/vendor/bundle/ruby/2.6.0/gems/redis-4.1.3/lib/redis/client.rb:164:in `block in call_pipeline'
9: from /home/circleci/repo/vendor/bundle/ruby/2.6.0/gems/redis-4.1.3/lib/redis/client.rb:196:in `call_pipelined'
8: from /home/circleci/repo/vendor/bundle/ruby/2.6.0/gems/redis-4.1.3/lib/redis/client.rb:230:in `process'
7: from /home/circleci/repo/vendor/bundle/ruby/2.6.0/gems/redis-4.1.3/lib/redis/client.rb:319:in `logging'
6: from /home/circleci/repo/vendor/bundle/ruby/2.6.0/gems/redis-4.1.3/lib/redis/client.rb:231:in `block in process'
5: from /home/circleci/repo/vendor/bundle/ruby/2.6.0/gems/redis-4.1.3/lib/redis/client.rb:381:in `ensure_connected'
4: from /home/circleci/repo/vendor/bundle/ruby/2.6.0/gems/redis-4.1.3/lib/redis/client.rb:105:in `connect'
3: from /home/circleci/repo/vendor/bundle/ruby/2.6.0/gems/redis-4.1.3/lib/redis/client.rb:306:in `with_reconnect'
2: from /home/circleci/repo/vendor/bundle/ruby/2.6.0/gems/redis-4.1.3/lib/redis/client.rb:106:in `block in connect'
1: from /home/circleci/repo/vendor/bundle/ruby/2.6.0/gems/redis-4.1.3/lib/redis/client.rb:343:in `establish_connection'
/home/circleci/repo/vendor/bundle/ruby/2.6.0/gems/redis-4.1.3/lib/redis/client.rb:362:in `rescue in establish_connection': Error connecting to Redis on 127.0.0.1:6379 (Errno::ECONNREFUSED) (Redis::CannotConnectError)
対応方法
- Redis connect issue after upgrading to 6.0.6 · Issue #4505 · mperham/sidekiq · GitHub
- Sidekiq launcher shouldn't be required except when inside a Sidekiq process · Issue #278 · ondrejbartas/sidekiq-cron · GitHub
上記の Issue によると、Sidekiq の問題ではなく、sidekiq-cron が sidekiq/launcher
を require
しているためだ、とあります。
エラーログのバックトレースに sidekiq-cron が出てこなかったので疑っていませんでしたが、試しに Gemfile の sidekiq-cron をコメントアウトすると、問題が発生しませんでした。
従って、暫定対応になりますが、Gemfile では sidekiq-cron を require: false
にして、Sidekiq の initializer で sidekiq-cron を require
するようにしました。
# Gemfile
gem "sidekiq-cron", require: false
# config/initializers/sidekiq.rb
Sidekiq.configure_server do |config|
# ...
require "sidekiq-cron"
schedule_file = "#{Rails.root}/config/schedule.yml"
Sidekiq::Cron::Job.load_from_hash YAML.load_file(schedule_file)
end
なぜなのか
Ruby インタプリタを終了する際に Sidekiq の統計情報を Redis に書き込む変更が、v6.0.6 で追加されました。
sidekiq-cron を require
すると、内部で sidekiq/launcher
も require
されるので、インタプリタを終了する際に上記の処理が実行され、Redis にアクセスできずにエラーになるようです。
fakeredis を使用しておらず、Redis にアクセスできる状態なら、今回の問題は発生しないものと考えられます。
最後に
今回の問題に対する Pull Request も送られているようなので、今後のアップデートで解消されるかもしれません。 その際は追記します。
有償ですが Sidekiq Enterprise に cron 機能があるので、可能ならそれを使う方が安心できるんじゃないかなぁと思う、今日このごろです。
fakeredis は今回あまり関係がありませんでしたが、テストのパフォーマンスが大きく劣化することがなければ、fake ではない本物の Redis を使った方がいいな、と感じました。