From d68e9999574c9908e0e65f4cc24fbba1593fa81e Mon Sep 17 00:00:00 2001 From: mpd Date: Wed, 8 Jun 2011 11:35:00 -0700 Subject: [PATCH] Update Worker.working to handle being used in a distributed ring. Update test suite to run either with a single server or distributed (RESQUE_DISTRIBUTED=1 rake test to run distributed) Make sure all tests pass when running both single server and distributed. --- lib/resque/worker.rb | 14 ++++- test/redis-test-cluster.conf | 115 +++++++++++++++++++++++++++++++++++ test/resque_test.rb | 13 +++- test/test_helper.rb | 22 +++++-- 4 files changed, 154 insertions(+), 10 deletions(-) create mode 100644 test/redis-test-cluster.conf diff --git a/lib/resque/worker.rb b/lib/resque/worker.rb index b5f6e6529..2ed7614d2 100644 --- a/lib/resque/worker.rb +++ b/lib/resque/worker.rb @@ -35,9 +35,19 @@ def self.working names.map! { |name| "worker:#{name}" } - reportedly_working = redis.mapped_mget(*names).reject do |key, value| - value.nil? + reportedly_working = begin + redis.mapped_mget(*names).reject do |key, value| + value.nil? + end + rescue Redis::Distributed::CannotDistribute + result = {} + names.each do |name| + value = redis.get name + result[name] = value unless value.nil? + end + result end + reportedly_working.keys.map do |key| find key.sub("worker:", '') end.compact diff --git a/test/redis-test-cluster.conf b/test/redis-test-cluster.conf new file mode 100644 index 000000000..51acbac05 --- /dev/null +++ b/test/redis-test-cluster.conf @@ -0,0 +1,115 @@ +# Redis configuration file example + +# By default Redis does not run as a daemon. Use 'yes' if you need it. +# Note that Redis will write a pid file in /var/run/redis.pid when daemonized. +daemonize yes + +# When run as a daemon, Redis write a pid file in /var/run/redis.pid by default. +# You can specify a custom pid file location here. +pidfile ./test/redis-test-cluster.pid + +# Accept connections on the specified port, default is 6379 +port 9737 + +# If you want you can bind a single interface, if the bind option is not +# specified all the interfaces will listen for connections. +# +# bind 127.0.0.1 + +# Close the connection after a client is idle for N seconds (0 to disable) +timeout 300 + +# Save the DB on disk: +# +# save +# +# Will save the DB if both the given number of seconds and the given +# number of write operations against the DB occurred. +# +# In the example below the behaviour will be to save: +# after 900 sec (15 min) if at least 1 key changed +# after 300 sec (5 min) if at least 10 keys changed +# after 60 sec if at least 10000 keys changed +save 900 1 +save 300 10 +save 60 10000 + +# The filename where to dump the DB +dbfilename dump-cluster.rdb + +# For default save/load DB in/from the working directory +# Note that you must specify a directory not a file name. +dir ./test/ + +# Set server verbosity to 'debug' +# it can be one of: +# debug (a lot of information, useful for development/testing) +# notice (moderately verbose, what you want in production probably) +# warning (only very important / critical messages are logged) +loglevel debug + +# Specify the log file name. Also 'stdout' can be used to force +# the demon to log on the standard output. Note that if you use standard +# output for logging but daemonize, logs will be sent to /dev/null +logfile stdout + +# Set the number of databases. The default database is DB 0, you can select +# a different one on a per-connection basis using SELECT where +# dbid is a number between 0 and 'databases'-1 +databases 16 + +################################# REPLICATION ################################# + +# Master-Slave replication. Use slaveof to make a Redis instance a copy of +# another Redis server. Note that the configuration is local to the slave +# so for example it is possible to configure the slave to save the DB with a +# different interval, or to listen to another port, and so on. + +# slaveof + +################################## SECURITY ################################### + +# Require clients to issue AUTH before processing any other +# commands. This might be useful in environments in which you do not trust +# others with access to the host running redis-server. +# +# This should stay commented out for backward compatibility and because most +# people do not need auth (e.g. they run their own servers). + +# requirepass foobared + +################################### LIMITS #################################### + +# Set the max number of connected clients at the same time. By default there +# is no limit, and it's up to the number of file descriptors the Redis process +# is able to open. The special value '0' means no limts. +# Once the limit is reached Redis will close all the new connections sending +# an error 'max number of clients reached'. + +# maxclients 128 + +# Don't use more memory than the specified amount of bytes. +# When the memory limit is reached Redis will try to remove keys with an +# EXPIRE set. It will try to start freeing keys that are going to expire +# in little time and preserve keys with a longer time to live. +# Redis will also try to remove objects from free lists if possible. +# +# If all this fails, Redis will start to reply with errors to commands +# that will use more memory, like SET, LPUSH, and so on, and will continue +# to reply to most read-only commands like GET. +# +# WARNING: maxmemory can be a good idea mainly if you want to use Redis as a +# 'state' server or cache, not as a real DB. When Redis is used as a real +# database the memory usage will grow over the weeks, it will be obvious if +# it is going to use too much memory in the long run, and you'll have the time +# to upgrade. With maxmemory after the limit is reached you'll start to get +# errors for write operations, and this may even lead to DB inconsistency. + +# maxmemory + +############################### ADVANCED CONFIG ############################### + +# Glue small output buffers together in order to send small replies in a +# single TCP packet. Uses a bit more CPU but most of the times it is a win +# in terms of number of queries per second. Use 'yes' if unsure. +glueoutputbuf yes diff --git a/test/resque_test.rb b/test/resque_test.rb index 7b95be225..d1cb9b357 100644 --- a/test/resque_test.rb +++ b/test/resque_test.rb @@ -7,6 +7,11 @@ Resque.push(:people, { 'name' => 'chris' }) Resque.push(:people, { 'name' => 'bob' }) Resque.push(:people, { 'name' => 'mark' }) + @original_redis = Resque.redis + end + + teardown do + Resque.redis = @original_redis end test "can set a namespace through a url-like string" do @@ -201,7 +206,7 @@ end test "keeps track of resque keys" do - assert_equal ["queue:people", "queues"], Resque.keys + assert_equal ["queue:people", "queues"].sort, Resque.keys.sort end test "badly wants a class name, too" do @@ -238,7 +243,11 @@ assert_equal 3, stats[:queues] assert_equal 3, stats[:processed] assert_equal 1, stats[:failed] - assert_equal [Resque.redis.respond_to?(:server) ? 'localhost:9736' : 'redis://localhost:9736/0'], stats[:servers] + if ENV.key? 'RESQUE_DISTRIBUTED' + assert_equal [Resque.redis.respond_to?(:server) ? 'localhost:9736, localhost:9737' : 'redis://localhost:9736/0, redis://localhost:9737/0'], stats[:servers] + else + assert_equal [Resque.redis.respond_to?(:server) ? 'localhost:9736' : 'redis://localhost:9736/0'], stats[:servers] + end end test "decode bad json" do diff --git a/test/test_helper.rb b/test/test_helper.rb index 49d9d38da..e1fe75b57 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -39,16 +39,26 @@ exit_code = Test::Unit::AutoRunner.run end - pid = `ps -A -o pid,command | grep [r]edis-test`.split(" ")[0] + processes = `ps -A -o pid,command | grep [r]edis-test`.split("\n") + pids = processes.map { |process| process.split(" ")[0] } puts "Killing test redis server..." - `rm -f #{dir}/dump.rdb` - Process.kill("KILL", pid.to_i) + `rm -f #{dir}/dump.rdb #{dir}/dump-cluster.rdb` + pids.each { |pid| Process.kill("KILL", pid.to_i) } exit exit_code end -puts "Starting redis for testing at localhost:9736..." -`redis-server #{dir}/redis-test.conf` -Resque.redis = 'localhost:9736' +if ENV.key? 'RESQUE_DISTRIBUTED' + require 'redis/distributed' + puts "Starting redis for testing at localhost:9736 and localhost:9737..." + `redis-server #{dir}/redis-test.conf` + `redis-server #{dir}/redis-test-cluster.conf` + r = Redis::Distributed.new(['redis://localhost:9736', 'redis://localhost:9737']) + Resque.redis = Redis::Namespace.new :resque, :redis => r +else + puts "Starting redis for testing at localhost:9736..." + `redis-server #{dir}/redis-test.conf` + Resque.redis = 'localhost:9736' +end ##