Skip to content

Commit

Permalink
Merge pull request #687 from 37signals/retry_reconnection
Browse files Browse the repository at this point in the history
Worker tries to reconnect to Redis up to 3 times with increasing delay before giving up
  • Loading branch information
steveklabnik committed Sep 25, 2012
2 parents 44ad395 + 2bfc49e commit d39046f
Show file tree
Hide file tree
Showing 2 changed files with 53 additions and 1 deletion.
20 changes: 19 additions & 1 deletion lib/resque/worker.rb
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ def work(interval = 5.0, &block)
else
unregister_signal_handlers if !@cant_fork
procline "Processing #{job.queue} since #{Time.now.to_i}"
redis.client.reconnect # Don't share connection with parent
reconnect
perform(job, &block)
exit! unless @cant_fork
end
Expand Down Expand Up @@ -224,6 +224,24 @@ def reserve(interval = 5.0)
Job.new(queue.name, job) if queue && job
end

# Reconnect to Redis to avoid sharing a connection with the parent,
# retry up to 3 times with increasing delay before giving up.
def reconnect
tries = 0
begin
redis.client.reconnect
rescue Redis::BaseConnectionError
if (tries += 1) <= 3
log "Error reconnecting to Redis; retrying"
sleep(tries)
retry
else
log "Error reconnecting to Redis; quitting"
raise
end
end
end

# Returns a list of queues to use when searching for a job.
# A splat ("*") means you want every queue (in alpha order) - this
# can be useful for dynamically adding new queues. Low priority queues
Expand Down
34 changes: 34 additions & 0 deletions test/worker_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -504,6 +504,40 @@ def self.perform
refute_equal original_connection, Resque.redis.client.connection.instance_variable_get("@sock")
end

it "tries to reconnect three times before giving up" do
begin
class Redis::Client
alias_method :original_reconnect, :reconnect

def reconnect
raise Redis::BaseConnectionError
end
end

class Resque::Worker
alias_method :original_sleep, :sleep

def sleep(duration = nil)
# noop
end
end

@worker.very_verbose = true
stdout, stderr = capture_io { @worker.work(0) }

assert_equal 3, stdout.scan(/retrying/).count
assert_equal 1, stdout.scan(/quitting/).count
ensure
class Redis::Client
alias_method :reconnect, :original_reconnect
end

class Resque::Worker
alias_method :sleep, :original_sleep
end
end
end

it "will call before_pause before it is paused" do
before_pause_called = false
captured_worker = nil
Expand Down

3 comments on commit d39046f

@claco
Copy link

@claco claco commented on d39046f Nov 1, 2012

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like this is in the changelog and should be in the 1.23 release, but I'm not seeing this behavior at at locally.

If I manually fire up a worker using rake resque:work, then kill/restart my local redis-server, I get an immediate exception about ECONNREFUSED an the worker dies. I see no output related to "Error reconnecting to Redis; retrying".

*** Starting worker hank:5211:cache
*** Error reserving job: #<Redis::CannotConnectError: Error connecting to Redis on localhost:6379 (ECONNREFUSED)>
*** /Users/claco/.rvm/gems/ruby-1.9.3-p286@w3/gems/redis-3.0.2/lib/redis/client.rb:268:in rescue in establish_connection' /Users/claco/.rvm/gems/ruby-1.9.3-p286@w3/gems/redis-3.0.2/lib/redis/client.rb:263:inestablish_connection'
/Users/claco/.rvm/gems/ruby-1.9.3-p286@w3/gems/redis-3.0.2/lib/redis/client.rb:69:in connect' /Users/claco/.rvm/gems/ruby-1.9.3-p286@w3/gems/redis-3.0.2/lib/redis/client.rb:282:inensure_connected'
/Users/claco/.rvm/gems/ruby-1.9.3-p286@w3/gems/redis-3.0.2/lib/redis/client.rb:173:in block in process' /Users/claco/.rvm/gems/ruby-1.9.3-p286@w3/gems/redis-3.0.2/lib/redis/client.rb:248:inlogging'
/Users/claco/.rvm/gems/ruby-1.9.3-p286@w3/gems/redis-3.0.2/lib/redis/client.rb:172:in process' /Users/claco/.rvm/gems/ruby-1.9.3-p286@w3/gems/redis-3.0.2/lib/redis/client.rb:84:incall'
/Users/claco/.rvm/gems/ruby-1.9.3-p286@w3/gems/redis-3.0.2/lib/redis.rb:899:in block in lpop' /Users/claco/.rvm/gems/ruby-1.9.3-p286@w3/gems/redis-3.0.2/lib/redis.rb:36:inblock in synchronize'
/Users/claco/.rvm/rubies/ruby-1.9.3-p286/lib/ruby/1.9.1/monitor.rb:211:in mon_synchronize' /Users/claco/.rvm/gems/ruby-1.9.3-p286@w3/gems/redis-3.0.2/lib/redis.rb:36:insynchronize'
/Users/claco/.rvm/gems/ruby-1.9.3-p286@w3/gems/redis-3.0.2/lib/redis.rb:898:in lpop' /Users/claco/.rvm/gems/ruby-1.9.3-p286@w3/gems/redis-namespace-1.2.1/lib/redis/namespace.rb:257:inmethod_missing'
/Users/claco/.rvm/gems/ruby-1.9.3-p286@w3/gems/resque-1.23.0/lib/resque.rb:149:in pop' /Users/claco/.rvm/gems/ruby-1.9.3-p286@w3/gems/resque-1.23.0/lib/resque/job.rb:100:inreserve'
/Users/claco/.rvm/gems/ruby-1.9.3-p286@w3/gems/resque-1.23.0/lib/resque.rb:303:in reserve' /Users/claco/.rvm/gems/ruby-1.9.3-p286@w3/gems/resque-1.23.0/lib/resque/worker.rb:209:inblock in reserve'
/Users/claco/.rvm/gems/ruby-1.9.3-p286@w3/gems/resque-1.23.0/lib/resque/worker.rb:207:in each' /Users/claco/.rvm/gems/ruby-1.9.3-p286@w3/gems/resque-1.23.0/lib/resque/worker.rb:207:inreserve'
/Users/claco/.rvm/gems/ruby-1.9.3-p286@w3/gems/resque-1.23.0/lib/resque/worker.rb:136:in block in work' /Users/claco/.rvm/gems/ruby-1.9.3-p286@w3/gems/resque-1.23.0/lib/resque/worker.rb:133:inloop'
/Users/claco/.rvm/gems/ruby-1.9.3-p286@w3/gems/resque-1.23.0/lib/resque/worker.rb:133:in work' /Users/claco/.rvm/gems/ruby-1.9.3-p286@w3/gems/resque-1.23.0/lib/resque/tasks.rb:36:inblock (2 levels) in <top (required)>'
/Users/claco/.rvm/gems/ruby-1.9.3-p286@w3/gems/rake-0.9.2/lib/rake/task.rb:205:in call' /Users/claco/.rvm/gems/ruby-1.9.3-p286@w3/gems/rake-0.9.2/lib/rake/task.rb:205:inblock in execute'
/Users/claco/.rvm/gems/ruby-1.9.3-p286@w3/gems/rake-0.9.2/lib/rake/task.rb:200:in each' /Users/claco/.rvm/gems/ruby-1.9.3-p286@w3/gems/rake-0.9.2/lib/rake/task.rb:200:inexecute'
/Users/claco/.rvm/gems/ruby-1.9.3-p286@w3/gems/rake-0.9.2/lib/rake/task.rb:158:in block in invoke_with_call_chain' /Users/claco/.rvm/rubies/ruby-1.9.3-p286/lib/ruby/1.9.1/monitor.rb:211:inmon_synchronize'
/Users/claco/.rvm/gems/ruby-1.9.3-p286@w3/gems/rake-0.9.2/lib/rake/task.rb:151:in invoke_with_call_chain' /Users/claco/.rvm/gems/ruby-1.9.3-p286@w3/gems/rake-0.9.2/lib/rake/task.rb:144:ininvoke'
/Users/claco/.rvm/gems/ruby-1.9.3-p286@w3/gems/rake-0.9.2/lib/rake/application.rb:112:in invoke_task' /Users/claco/.rvm/gems/ruby-1.9.3-p286@w3/gems/rake-0.9.2/lib/rake/application.rb:90:inblock (2 levels) in top_level'
/Users/claco/.rvm/gems/ruby-1.9.3-p286@w3/gems/rake-0.9.2/lib/rake/application.rb:90:in each' /Users/claco/.rvm/gems/ruby-1.9.3-p286@w3/gems/rake-0.9.2/lib/rake/application.rb:90:inblock in top_level'
/Users/claco/.rvm/gems/ruby-1.9.3-p286@w3/gems/rake-0.9.2/lib/rake/application.rb:129:in standard_exception_handling' /Users/claco/.rvm/gems/ruby-1.9.3-p286@w3/gems/rake-0.9.2/lib/rake/application.rb:84:intop_level'
/Users/claco/.rvm/gems/ruby-1.9.3-p286@w3/gems/rake-0.9.2/lib/rake/application.rb:62:in block in run' /Users/claco/.rvm/gems/ruby-1.9.3-p286@w3/gems/rake-0.9.2/lib/rake/application.rb:129:instandard_exception_handling'
/Users/claco/.rvm/gems/ruby-1.9.3-p286@w3/gems/rake-0.9.2/lib/rake/application.rb:59:in run' /Users/claco/.rvm/gems/ruby-1.9.3-p286@w3/gems/rake-0.9.2/bin/rake:32:in<top (required)>'
/Users/claco/.rvm/gems/ruby-1.9.3-p286@w3/bin/rake:19:in load' /Users/claco/.rvm/gems/ruby-1.9.3-p286@w3/bin/rake:19:in

'
rake aborted!
Error connecting to Redis on localhost:6379 (ECONNREFUSED)

@steveklabnik
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you file an Issue, please? and cc @trevorturk

@claco
Copy link

@claco claco commented on d39046f Nov 1, 2012

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.