Resque is not picking up Redis configuration settings
Asked Answered
N

5

7

I am having unexpected and significant problems trying to get a Rails app, running under Unicorn, to connect to a password-protected Redis server.

Using bundle exec rails c production on the command line, I can issue commands through Resque.redis. However, it seems that my configuration is being lost when it's forked under Unicorn.

Using a non-password-protected Redis server Just Works. However, I intend to run workers on other servers than where the Redis server lives, so I need this to be password protected.

I have also had success in using a password protected (using the same technique) but using Passenger rather than Unicorn.

I have the following setup:

# config/resque.yml

development: localhost:6379
test: localhost:6379
production: redis://user:[email protected]:6379

.

# config/initializers/redis.rb

rails_root = ENV['RAILS_ROOT'] || File.dirname(__FILE__) + '/../..'
rails_env = ENV['RAILS_ENV'] || 'development'

$resque_config = YAML.load_file(rails_root + '/config/resque.yml')
uri = URI.parse($resque_config[rails_env])
Resque.redis = Redis.new(host: uri.host, port: uri.port, password: uri.password)

.

# unicorn.rb bootup file

preload_app true

before_fork do |server, worker|
  Redis.current.quit
end

after_fork do |server, worker|
  Redis.current.quit
end

.

Nepean answered 6/6, 2012 at 20:59 Comment(0)
W
5

UPDATED Totally different idea based on @lmarlow's comment to a resque issue.

I bet it breaks wherever you have Redis ~>3 (I mean the ruby client version, not the server version).

As of this writing, Resque needs Redis ~>2 but doesn't specify that in its gemspec. Therefore you have to help it out by adding this to your Gemfile:

gem 'redis', '~>2' # until a new version of resque comes out
gem 'resque'

Also, make sure that bundler is being used everywhere. Otherwise, if your system has a new version of the Redis gem, it will get used and Resque will fail like before.

Finally, a cosmetic note... you could simplify the config to:

# config/initializers/redis.rb
$resque_redis_url = uris_per_environment[rails_env] # note no URI.parse
Resque.redis = $resque_redis_url

and then

# unicorn.rb bootup file
after_fork do |server, worker|
  Resque.redis = $resque_redis_url
end
Wyatan answered 6/6, 2012 at 22:19 Comment(4)
I have tried this (simplifying, storing the config in a global) and even hard-coded the connect string in the after_fork hook as Resque.redis = "redis://user:[email protected]:6379" but alas, no success. Resque workers work, Rails web app fails to use it. If I change the port, workers track this, app does not.Nepean
If you start a rails console on production, what's the output of Resque.redis ?Wyatan
From the console, everything works exactly as expected, and Resque.redis.info (for instance) returns info from the server. If I use an invalid password, I get a "password is wrong" style error, so I know it's properly connecting on the console. Even running Resque.redis.quit followed by the same Resque.redis = "url" command works from the console.Nepean
I also had to fix some examples which used ENV['RAILS_ENV'] -- but for some reason, that was not working. So, Rails.env fixed that. Also, I could not do a fast reload of Unicorn, but a full restart fixed that too. Same problem as when you add a gem, I've noticed.Nepean
K
6

Ok, for the sake of other people who might be googling this problem, I've solved this for myself at least

Basic problem is calling Redis.new other places in the code ,e.g. in your geocoder setup or unicorn config file.

just make sure that every time you call initialize Redis you pass in the appropriate values e.g. something like

REDIS = Redis.connect(:url =>  ENV['REDISTOGO_URL'])

everywhere and you should never have

Redis.new 

as it will default to localhost and the default port

Krahmer answered 2/9, 2012 at 0:33 Comment(0)
W
5

UPDATED Totally different idea based on @lmarlow's comment to a resque issue.

I bet it breaks wherever you have Redis ~>3 (I mean the ruby client version, not the server version).

As of this writing, Resque needs Redis ~>2 but doesn't specify that in its gemspec. Therefore you have to help it out by adding this to your Gemfile:

gem 'redis', '~>2' # until a new version of resque comes out
gem 'resque'

Also, make sure that bundler is being used everywhere. Otherwise, if your system has a new version of the Redis gem, it will get used and Resque will fail like before.

Finally, a cosmetic note... you could simplify the config to:

# config/initializers/redis.rb
$resque_redis_url = uris_per_environment[rails_env] # note no URI.parse
Resque.redis = $resque_redis_url

and then

# unicorn.rb bootup file
after_fork do |server, worker|
  Resque.redis = $resque_redis_url
end
Wyatan answered 6/6, 2012 at 22:19 Comment(4)
I have tried this (simplifying, storing the config in a global) and even hard-coded the connect string in the after_fork hook as Resque.redis = "redis://user:[email protected]:6379" but alas, no success. Resque workers work, Rails web app fails to use it. If I change the port, workers track this, app does not.Nepean
If you start a rails console on production, what's the output of Resque.redis ?Wyatan
From the console, everything works exactly as expected, and Resque.redis.info (for instance) returns info from the server. If I use an invalid password, I get a "password is wrong" style error, so I know it's properly connecting on the console. Even running Resque.redis.quit followed by the same Resque.redis = "url" command works from the console.Nepean
I also had to fix some examples which used ENV['RAILS_ENV'] -- but for some reason, that was not working. So, Rails.env fixed that. Also, I could not do a fast reload of Unicorn, but a full restart fixed that too. Same problem as when you add a gem, I've noticed.Nepean
C
2

This was helpful to me:

source: https://github.com/redis/redis-rb/blob/master/examples/unicorn/unicorn.rb

require "redis"

worker_processes 3

# If you set the connection to Redis *before* forking,
# you will cause forks to share a file descriptor.
#
# This causes a concurrency problem by which one fork
# can read or write to the socket while others are
# performing other operations.
#
# Most likely you'll be getting ProtocolError exceptions
# mentioning a wrong initial byte in the reply.
#
# Thus we need to connect to Redis after forking the
# worker processes.

after_fork do |server, worker|
  Redis.current.quit
end
Cookstove answered 5/12, 2012 at 21:26 Comment(0)
D
2

What worked for me was the unicorn config here: https://mcmap.net/q/1333237/-resque-resque-server-on-redistogo-with-heroku

before_fork do |server, worker|
  if defined?(Resque)
    Resque.redis.quit
    Rails.logger.info("Disconnected from Redis")
  end
end

after_fork do |server, worker|
  if defined?(Resque)
    Resque.redis = REDIS_WORKER
    Rails.logger.info("Connected to Redis")
  end
end
Dorladorlisa answered 19/7, 2013 at 17:39 Comment(0)
C
0

I think the issue was with Resque-web. Its config file, they fixed it now. In version 0.0.11, they mention it in a comment as well : https://github.com/resque/resque-web/blob/master/config/initializers/resque_config.rb#L3

Earlier, their file looked like this : https://github.com/resque/resque-web/blob/v0.0.9/config/initializers/resque_config.rb

And, if due to any reasons, you cannot upgrade, then rather try to set the env variable RAILS_RESQUE_REDIS=<host>:<port> instead, as the Initializers are loading after the it tries connect redis(and fails).

Cuneal answered 11/9, 2017 at 6:34 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.