resque 1.23.0 → 2.6.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/HISTORY.md +271 -0
- data/README.markdown +454 -484
- data/Rakefile +4 -17
- data/bin/resque-web +10 -22
- data/lib/resque/data_store.rb +335 -0
- data/lib/resque/errors.rb +15 -1
- data/lib/resque/failure/airbrake.rb +32 -4
- data/lib/resque/failure/base.rb +16 -7
- data/lib/resque/failure/multiple.rb +26 -8
- data/lib/resque/failure/redis.rb +92 -15
- data/lib/resque/failure/redis_multi_queue.rb +104 -0
- data/lib/resque/failure.rb +62 -32
- data/lib/resque/helpers.rb +11 -57
- data/lib/resque/job.rb +79 -12
- data/lib/resque/log_formatters/quiet_formatter.rb +7 -0
- data/lib/resque/log_formatters/verbose_formatter.rb +7 -0
- data/lib/resque/log_formatters/very_verbose_formatter.rb +8 -0
- data/lib/resque/logging.rb +18 -0
- data/lib/resque/plugin.rb +22 -10
- data/lib/resque/railtie.rb +10 -0
- data/lib/resque/server/public/jquery-3.6.0.min.js +2 -0
- data/lib/resque/server/public/jquery.relatize_date.js +4 -4
- data/lib/resque/server/public/main.js +3 -0
- data/lib/resque/server/public/ranger.js +16 -8
- data/lib/resque/server/public/style.css +13 -8
- data/lib/resque/server/views/error.erb +1 -1
- data/lib/resque/server/views/failed.erb +27 -59
- data/lib/resque/server/views/failed_job.erb +50 -0
- data/lib/resque/server/views/failed_queues_overview.erb +24 -0
- data/lib/resque/server/views/job_class.erb +8 -0
- data/lib/resque/server/views/key_sets.erb +2 -4
- data/lib/resque/server/views/key_string.erb +1 -1
- data/lib/resque/server/views/layout.erb +7 -6
- data/lib/resque/server/views/next_more.erb +22 -10
- data/lib/resque/server/views/processing.erb +2 -0
- data/lib/resque/server/views/queues.erb +22 -13
- data/lib/resque/server/views/stats.erb +5 -5
- data/lib/resque/server/views/workers.erb +4 -4
- data/lib/resque/server/views/working.erb +10 -11
- data/lib/resque/server.rb +51 -108
- data/lib/resque/server_helper.rb +185 -0
- data/lib/resque/stat.rb +19 -7
- data/lib/resque/tasks.rb +26 -25
- data/lib/resque/thread_signal.rb +24 -0
- data/lib/resque/vendor/utf8_util.rb +2 -8
- data/lib/resque/version.rb +1 -1
- data/lib/resque/web_runner.rb +374 -0
- data/lib/resque/worker.rb +487 -163
- data/lib/resque.rb +332 -52
- data/lib/tasks/redis.rake +11 -11
- metadata +169 -149
- data/lib/resque/failure/hoptoad.rb +0 -33
- data/lib/resque/failure/thoughtbot.rb +0 -33
- data/lib/resque/server/public/jquery-1.3.2.min.js +0 -19
- data/lib/resque/server/test_helper.rb +0 -19
- data/lib/resque/vendor/utf8_util/utf8_util_18.rb +0 -91
- data/lib/resque/vendor/utf8_util/utf8_util_19.rb +0 -5
- data/test/airbrake_test.rb +0 -27
- data/test/hoptoad_test.rb +0 -26
- data/test/job_hooks_test.rb +0 -464
- data/test/job_plugins_test.rb +0 -230
- data/test/plugin_test.rb +0 -116
- data/test/redis-test-cluster.conf +0 -115
- data/test/redis-test.conf +0 -115
- data/test/resque-web_test.rb +0 -59
- data/test/resque_failure_redis_test.rb +0 -19
- data/test/resque_test.rb +0 -278
- data/test/test_helper.rb +0 -178
- data/test/worker_test.rb +0 -657
data/lib/resque.rb
CHANGED
@@ -1,4 +1,6 @@
|
|
1
|
+
require 'mono_logger'
|
1
2
|
require 'redis/namespace'
|
3
|
+
require 'forwardable'
|
2
4
|
|
3
5
|
require 'resque/version'
|
4
6
|
|
@@ -9,16 +11,98 @@ require 'resque/failure/base'
|
|
9
11
|
|
10
12
|
require 'resque/helpers'
|
11
13
|
require 'resque/stat'
|
14
|
+
require 'resque/logging'
|
15
|
+
require 'resque/log_formatters/quiet_formatter'
|
16
|
+
require 'resque/log_formatters/verbose_formatter'
|
17
|
+
require 'resque/log_formatters/very_verbose_formatter'
|
12
18
|
require 'resque/job'
|
13
19
|
require 'resque/worker'
|
14
20
|
require 'resque/plugin'
|
21
|
+
require 'resque/data_store'
|
22
|
+
require 'resque/thread_signal'
|
15
23
|
|
16
24
|
require 'resque/vendor/utf8_util'
|
17
25
|
|
26
|
+
require 'resque/railtie' if defined?(Rails::Railtie)
|
27
|
+
|
18
28
|
module Resque
|
19
29
|
include Helpers
|
20
30
|
extend self
|
21
31
|
|
32
|
+
# Given a Ruby object, returns a string suitable for storage in a
|
33
|
+
# queue.
|
34
|
+
def encode(object)
|
35
|
+
if MultiJson.respond_to?(:dump) && MultiJson.respond_to?(:load)
|
36
|
+
MultiJson.dump object
|
37
|
+
else
|
38
|
+
MultiJson.encode object
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
# Given a string, returns a Ruby object.
|
43
|
+
def decode(object)
|
44
|
+
return unless object
|
45
|
+
|
46
|
+
begin
|
47
|
+
if MultiJson.respond_to?(:dump) && MultiJson.respond_to?(:load)
|
48
|
+
MultiJson.load object
|
49
|
+
else
|
50
|
+
MultiJson.decode object
|
51
|
+
end
|
52
|
+
rescue ::MultiJson::DecodeError => e
|
53
|
+
raise Helpers::DecodeException, e.message, e.backtrace
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
# Given a word with dashes, returns a camel cased version of it.
|
58
|
+
#
|
59
|
+
# classify('job-name') # => 'JobName'
|
60
|
+
def classify(dashed_word)
|
61
|
+
dashed_word.split('-').map(&:capitalize).join
|
62
|
+
end
|
63
|
+
|
64
|
+
# Tries to find a constant with the name specified in the argument string:
|
65
|
+
#
|
66
|
+
# constantize("Module") # => Module
|
67
|
+
# constantize("Test::Unit") # => Test::Unit
|
68
|
+
#
|
69
|
+
# The name is assumed to be the one of a top-level constant, no matter
|
70
|
+
# whether it starts with "::" or not. No lexical context is taken into
|
71
|
+
# account:
|
72
|
+
#
|
73
|
+
# C = 'outside'
|
74
|
+
# module M
|
75
|
+
# C = 'inside'
|
76
|
+
# C # => 'inside'
|
77
|
+
# constantize("C") # => 'outside', same as ::C
|
78
|
+
# end
|
79
|
+
#
|
80
|
+
# NameError is raised when the constant is unknown.
|
81
|
+
def constantize(camel_cased_word)
|
82
|
+
camel_cased_word = camel_cased_word.to_s
|
83
|
+
|
84
|
+
if camel_cased_word.include?('-')
|
85
|
+
camel_cased_word = classify(camel_cased_word)
|
86
|
+
end
|
87
|
+
|
88
|
+
names = camel_cased_word.split('::')
|
89
|
+
names.shift if names.empty? || names.first.empty?
|
90
|
+
|
91
|
+
constant = Object
|
92
|
+
names.each do |name|
|
93
|
+
args = Module.method(:const_get).arity != 1 ? [false] : []
|
94
|
+
|
95
|
+
if constant.const_defined?(name, *args)
|
96
|
+
constant = constant.const_get(name)
|
97
|
+
else
|
98
|
+
constant = constant.const_missing(name)
|
99
|
+
end
|
100
|
+
end
|
101
|
+
constant
|
102
|
+
end
|
103
|
+
|
104
|
+
extend ::Forwardable
|
105
|
+
|
22
106
|
# Accepts:
|
23
107
|
# 1. A 'hostname:port' String
|
24
108
|
# 2. A 'hostname:port:db' String (to select the Redis db)
|
@@ -26,43 +110,113 @@ module Resque
|
|
26
110
|
# 4. A Redis URL String 'redis://host:port'
|
27
111
|
# 5. An instance of `Redis`, `Redis::Client`, `Redis::DistRedis`,
|
28
112
|
# or `Redis::Namespace`.
|
113
|
+
# 6. An Hash of a redis connection {:host => 'localhost', :port => 6379, :db => 0}
|
29
114
|
def redis=(server)
|
30
115
|
case server
|
31
116
|
when String
|
32
|
-
if server =~ /
|
33
|
-
redis = Redis.
|
117
|
+
if server =~ /rediss?\:\/\//
|
118
|
+
redis = Redis.new(:url => server)
|
34
119
|
else
|
35
120
|
server, namespace = server.split('/', 2)
|
36
121
|
host, port, db = server.split(':')
|
37
|
-
redis = Redis.new(:host => host, :port => port,
|
38
|
-
:thread_safe => true, :db => db)
|
122
|
+
redis = Redis.new(:host => host, :port => port, :db => db)
|
39
123
|
end
|
40
124
|
namespace ||= :resque
|
41
125
|
|
42
|
-
@
|
126
|
+
@data_store = Resque::DataStore.new(Redis::Namespace.new(namespace, :redis => redis))
|
43
127
|
when Redis::Namespace
|
44
|
-
@
|
128
|
+
@data_store = Resque::DataStore.new(server)
|
129
|
+
when Resque::DataStore
|
130
|
+
@data_store = server
|
131
|
+
when Hash
|
132
|
+
@data_store = Resque::DataStore.new(Redis::Namespace.new(:resque, :redis => Redis.new(server)))
|
45
133
|
else
|
46
|
-
@
|
134
|
+
@data_store = Resque::DataStore.new(Redis::Namespace.new(:resque, :redis => server))
|
47
135
|
end
|
48
136
|
end
|
49
137
|
|
50
138
|
# Returns the current Redis connection. If none has been created, will
|
51
139
|
# create a new one.
|
52
140
|
def redis
|
53
|
-
return @
|
141
|
+
return @data_store if @data_store
|
54
142
|
self.redis = Redis.respond_to?(:connect) ? Redis.connect : "localhost:6379"
|
55
143
|
self.redis
|
56
144
|
end
|
145
|
+
alias :data_store :redis
|
57
146
|
|
58
147
|
def redis_id
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
148
|
+
data_store.identifier
|
149
|
+
end
|
150
|
+
|
151
|
+
# Set the data store for the processed and failed statistics.
|
152
|
+
#
|
153
|
+
# By default it uses the same as `Resque.redis`, but different stores can be used.
|
154
|
+
#
|
155
|
+
# A custom store needs to obey the following API to work correctly
|
156
|
+
#
|
157
|
+
# class NullDataStore
|
158
|
+
# # Returns the current value for the given stat.
|
159
|
+
# def stat(stat)
|
160
|
+
# end
|
161
|
+
#
|
162
|
+
# # Increments the stat by the given value.
|
163
|
+
# def increment_stat(stat, by)
|
164
|
+
# end
|
165
|
+
#
|
166
|
+
# # Decrements the stat by the given value.
|
167
|
+
# def decrement_stat(stat, by)
|
168
|
+
# end
|
169
|
+
#
|
170
|
+
# # Clear the values for the given stat.
|
171
|
+
# def clear_stat(stat)
|
172
|
+
# end
|
173
|
+
# end
|
174
|
+
def stat_data_store=(stat_data_store)
|
175
|
+
Resque::Stat.data_store = stat_data_store
|
176
|
+
end
|
177
|
+
|
178
|
+
# Returns the data store for the statistics module.
|
179
|
+
def stat_data_store
|
180
|
+
Resque::Stat.data_store
|
181
|
+
end
|
182
|
+
|
183
|
+
# Set or retrieve the current logger object
|
184
|
+
attr_accessor :logger
|
185
|
+
|
186
|
+
DEFAULT_HEARTBEAT_INTERVAL = 60
|
187
|
+
DEFAULT_PRUNE_INTERVAL = DEFAULT_HEARTBEAT_INTERVAL * 5
|
188
|
+
|
189
|
+
# Defines how often a Resque worker updates the heartbeat key. Must be less
|
190
|
+
# than the prune interval.
|
191
|
+
attr_writer :heartbeat_interval
|
192
|
+
def heartbeat_interval
|
193
|
+
if defined? @heartbeat_interval
|
194
|
+
@heartbeat_interval
|
64
195
|
else
|
65
|
-
|
196
|
+
DEFAULT_HEARTBEAT_INTERVAL
|
197
|
+
end
|
198
|
+
end
|
199
|
+
|
200
|
+
# Defines how often Resque checks for dead workers.
|
201
|
+
attr_writer :prune_interval
|
202
|
+
def prune_interval
|
203
|
+
if defined? @prune_interval
|
204
|
+
@prune_interval
|
205
|
+
else
|
206
|
+
DEFAULT_PRUNE_INTERVAL
|
207
|
+
end
|
208
|
+
end
|
209
|
+
|
210
|
+
# By default, jobs are pushed to the back of the queue and popped from
|
211
|
+
# the front, resulting in "first in, first out" (FIFO) execution order.
|
212
|
+
# Set to true to push jobs to the front of the queue instead, resulting
|
213
|
+
# in "last in, first out" (LIFO) execution order.
|
214
|
+
attr_writer :enqueue_front
|
215
|
+
def enqueue_front
|
216
|
+
if defined? @enqueue_front
|
217
|
+
@enqueue_front
|
218
|
+
else
|
219
|
+
@enqueue_front = false
|
66
220
|
end
|
67
221
|
end
|
68
222
|
|
@@ -71,41 +225,96 @@ module Resque
|
|
71
225
|
# changes you make will be permanent for the lifespan of the
|
72
226
|
# worker.
|
73
227
|
#
|
74
|
-
# Call with a block to
|
75
|
-
# Call with no arguments to return
|
228
|
+
# Call with a block to register a hook.
|
229
|
+
# Call with no arguments to return all registered hooks.
|
76
230
|
def before_first_fork(&block)
|
77
|
-
block ? (
|
231
|
+
block ? register_hook(:before_first_fork, block) : hooks(:before_first_fork)
|
78
232
|
end
|
79
233
|
|
80
|
-
#
|
81
|
-
|
82
|
-
|
234
|
+
# Register a before_first_fork proc.
|
235
|
+
def before_first_fork=(block)
|
236
|
+
register_hook(:before_first_fork, block)
|
237
|
+
end
|
83
238
|
|
84
239
|
# The `before_fork` hook will be run in the **parent** process
|
85
240
|
# before every job, so be careful- any changes you make will be
|
86
241
|
# permanent for the lifespan of the worker.
|
87
242
|
#
|
88
|
-
# Call with a block to
|
89
|
-
# Call with no arguments to return
|
243
|
+
# Call with a block to register a hook.
|
244
|
+
# Call with no arguments to return all registered hooks.
|
90
245
|
def before_fork(&block)
|
91
|
-
block ? (
|
246
|
+
block ? register_hook(:before_fork, block) : hooks(:before_fork)
|
92
247
|
end
|
93
248
|
|
94
|
-
#
|
95
|
-
|
249
|
+
# Register a before_fork proc.
|
250
|
+
def before_fork=(block)
|
251
|
+
register_hook(:before_fork, block)
|
252
|
+
end
|
96
253
|
|
97
254
|
# The `after_fork` hook will be run in the child process and is passed
|
98
255
|
# the current job. Any changes you make, therefore, will only live as
|
99
256
|
# long as the job currently being processed.
|
100
257
|
#
|
101
|
-
# Call with a block to
|
102
|
-
# Call with no arguments to return
|
258
|
+
# Call with a block to register a hook.
|
259
|
+
# Call with no arguments to return all registered hooks.
|
103
260
|
def after_fork(&block)
|
104
|
-
block ? (
|
261
|
+
block ? register_hook(:after_fork, block) : hooks(:after_fork)
|
262
|
+
end
|
263
|
+
|
264
|
+
# Register an after_fork proc.
|
265
|
+
def after_fork=(block)
|
266
|
+
register_hook(:after_fork, block)
|
267
|
+
end
|
268
|
+
|
269
|
+
# The `before_pause` hook will be run in the parent process before the
|
270
|
+
# worker has paused processing (via #pause_processing or SIGUSR2).
|
271
|
+
def before_pause(&block)
|
272
|
+
block ? register_hook(:before_pause, block) : hooks(:before_pause)
|
273
|
+
end
|
274
|
+
|
275
|
+
# Register a before_pause proc.
|
276
|
+
def before_pause=(block)
|
277
|
+
register_hook(:before_pause, block)
|
278
|
+
end
|
279
|
+
|
280
|
+
# The `after_pause` hook will be run in the parent process after the
|
281
|
+
# worker has paused (via SIGCONT).
|
282
|
+
def after_pause(&block)
|
283
|
+
block ? register_hook(:after_pause, block) : hooks(:after_pause)
|
284
|
+
end
|
285
|
+
|
286
|
+
# Register an after_pause proc.
|
287
|
+
def after_pause=(block)
|
288
|
+
register_hook(:after_pause, block)
|
289
|
+
end
|
290
|
+
|
291
|
+
# The `queue_empty` hook will be run in the **parent** process when
|
292
|
+
# the worker finds no more jobs in the queue and becomes idle.
|
293
|
+
#
|
294
|
+
# Call with a block to register a hook.
|
295
|
+
# Call with no arguments to return all registered hooks.
|
296
|
+
def queue_empty(&block)
|
297
|
+
block ? register_hook(:queue_empty, block) : hooks(:queue_empty)
|
298
|
+
end
|
299
|
+
|
300
|
+
# Register a queue_empty proc.
|
301
|
+
def queue_empty=(block)
|
302
|
+
register_hook(:queue_empty, block)
|
105
303
|
end
|
106
304
|
|
107
|
-
#
|
108
|
-
|
305
|
+
# The `worker_exit` hook will be run in the **parent** process
|
306
|
+
# after the worker has existed (via SIGQUIT, SIGTERM, SIGINT, etc.).
|
307
|
+
#
|
308
|
+
# Call with a block to register a hook.
|
309
|
+
# Call with no arguments to return all registered hooks.
|
310
|
+
def worker_exit(&block)
|
311
|
+
block ? register_hook(:worker_exit, block) : hooks(:worker_exit)
|
312
|
+
end
|
313
|
+
|
314
|
+
# Register a worker_exit proc.
|
315
|
+
def worker_exit=(block)
|
316
|
+
register_hook(:worker_exit, block)
|
317
|
+
end
|
109
318
|
|
110
319
|
def to_s
|
111
320
|
"Resque Client connected to #{redis_id}"
|
@@ -138,25 +347,24 @@ module Resque
|
|
138
347
|
#
|
139
348
|
# Returns nothing
|
140
349
|
def push(queue, item)
|
141
|
-
|
142
|
-
redis.rpush "queue:#{queue}", encode(item)
|
350
|
+
data_store.push_to_queue(queue,encode(item))
|
143
351
|
end
|
144
352
|
|
145
353
|
# Pops a job off a queue. Queue name should be a string.
|
146
354
|
#
|
147
355
|
# Returns a Ruby object.
|
148
356
|
def pop(queue)
|
149
|
-
decode
|
357
|
+
decode(data_store.pop_from_queue(queue))
|
150
358
|
end
|
151
359
|
|
152
360
|
# Returns an integer representing the size of a queue.
|
153
361
|
# Queue name should be a string.
|
154
362
|
def size(queue)
|
155
|
-
|
363
|
+
data_store.queue_size(queue)
|
156
364
|
end
|
157
365
|
|
158
|
-
# Returns an array of items currently queued
|
159
|
-
# a string.
|
366
|
+
# Returns an array of items currently queued, or the item itself
|
367
|
+
# if count = 1. Queue name should be a string.
|
160
368
|
#
|
161
369
|
# start and count should be integer and can be used for pagination.
|
162
370
|
# start is the item to begin, count is how many items to return.
|
@@ -164,36 +372,39 @@ module Resque
|
|
164
372
|
# To get the 3rd page of a 30 item, paginatied list one would use:
|
165
373
|
# Resque.peek('my_list', 59, 30)
|
166
374
|
def peek(queue, start = 0, count = 1)
|
167
|
-
|
375
|
+
results = data_store.peek_in_queue(queue,start,count)
|
376
|
+
if count == 1
|
377
|
+
decode(results)
|
378
|
+
else
|
379
|
+
results.map { |result| decode(result) }
|
380
|
+
end
|
168
381
|
end
|
169
382
|
|
170
383
|
# Does the dirty work of fetching a range of items from a Redis list
|
171
384
|
# and converting them into Ruby objects.
|
172
385
|
def list_range(key, start = 0, count = 1)
|
386
|
+
results = data_store.list_range(key, start, count)
|
173
387
|
if count == 1
|
174
|
-
decode
|
388
|
+
decode(results)
|
175
389
|
else
|
176
|
-
|
177
|
-
decode item
|
178
|
-
end
|
390
|
+
results.map { |result| decode(result) }
|
179
391
|
end
|
180
392
|
end
|
181
393
|
|
182
394
|
# Returns an array of all known Resque queues as strings.
|
183
395
|
def queues
|
184
|
-
|
396
|
+
data_store.queue_names
|
185
397
|
end
|
186
398
|
|
187
399
|
# Given a queue name, completely deletes the queue.
|
188
400
|
def remove_queue(queue)
|
189
|
-
|
190
|
-
redis.del("queue:#{queue}")
|
401
|
+
data_store.remove_queue(queue)
|
191
402
|
end
|
192
403
|
|
193
404
|
# Used internally to keep track of which queues we've created.
|
194
405
|
# Don't call this directly.
|
195
406
|
def watch_queue(queue)
|
196
|
-
|
407
|
+
data_store.watch_queue(queue)
|
197
408
|
end
|
198
409
|
|
199
410
|
|
@@ -280,17 +491,19 @@ module Resque
|
|
280
491
|
end
|
281
492
|
return if before_hooks.any? { |result| result == false }
|
282
493
|
|
283
|
-
Job.destroy(queue_from_class(klass), klass, *args)
|
494
|
+
destroyed = Job.destroy(queue_from_class(klass), klass, *args)
|
284
495
|
|
285
496
|
Plugin.after_dequeue_hooks(klass).each do |hook|
|
286
497
|
klass.send(hook, *args)
|
287
498
|
end
|
499
|
+
|
500
|
+
destroyed
|
288
501
|
end
|
289
502
|
|
290
503
|
# Given a class, try to extrapolate an appropriate queue based on a
|
291
504
|
# class instance variable or `queue` method.
|
292
505
|
def queue_from_class(klass)
|
293
|
-
klass.instance_variable_get(:@queue) ||
|
506
|
+
(klass.instance_variable_defined?(:@queue) && klass.instance_variable_get(:@queue)) ||
|
294
507
|
(klass.respond_to?(:queue) and klass.queue)
|
295
508
|
end
|
296
509
|
|
@@ -312,7 +525,7 @@ module Resque
|
|
312
525
|
queue ||= queue_from_class(klass)
|
313
526
|
|
314
527
|
if !queue
|
315
|
-
raise NoQueueError.new("Jobs must be placed onto a queue.")
|
528
|
+
raise NoQueueError.new("Jobs must be placed onto a queue. No queue could be inferred for class #{klass}")
|
316
529
|
end
|
317
530
|
|
318
531
|
if klass.to_s.empty?
|
@@ -349,12 +562,12 @@ module Resque
|
|
349
562
|
# Returns a hash, similar to redis-rb's #info, of interesting stats.
|
350
563
|
def info
|
351
564
|
return {
|
352
|
-
:pending =>
|
565
|
+
:pending => queue_sizes.inject(0) { |sum, (_queue_name, queue_size)| sum + queue_size },
|
353
566
|
:processed => Stat[:processed],
|
354
567
|
:queues => queues.size,
|
355
568
|
:workers => workers.size.to_i,
|
356
569
|
:working => working.size,
|
357
|
-
:failed =>
|
570
|
+
:failed => data_store.num_failed,
|
358
571
|
:servers => [redis_id],
|
359
572
|
:environment => ENV['RAILS_ENV'] || ENV['RACK_ENV'] || 'development'
|
360
573
|
}
|
@@ -363,9 +576,76 @@ module Resque
|
|
363
576
|
# Returns an array of all known Resque keys in Redis. Redis' KEYS operation
|
364
577
|
# is O(N) for the keyspace, so be careful - this can be slow for big databases.
|
365
578
|
def keys
|
366
|
-
|
367
|
-
|
579
|
+
data_store.all_resque_keys
|
580
|
+
end
|
581
|
+
|
582
|
+
# Returns a hash, mapping queue names to queue sizes
|
583
|
+
def queue_sizes
|
584
|
+
queue_names = queues
|
585
|
+
|
586
|
+
sizes = redis.pipelined do |piped|
|
587
|
+
queue_names.each do |name|
|
588
|
+
piped.llen("queue:#{name}")
|
589
|
+
end
|
368
590
|
end
|
591
|
+
|
592
|
+
Hash[queue_names.zip(sizes)]
|
593
|
+
end
|
594
|
+
|
595
|
+
# Returns a hash, mapping queue names to (up to `sample_size`) samples of jobs in that queue
|
596
|
+
def sample_queues(sample_size = 1000)
|
597
|
+
queue_names = queues
|
598
|
+
|
599
|
+
samples = redis.pipelined do |piped|
|
600
|
+
queue_names.each do |name|
|
601
|
+
key = "queue:#{name}"
|
602
|
+
piped.llen(key)
|
603
|
+
piped.lrange(key, 0, sample_size - 1)
|
604
|
+
end
|
605
|
+
end
|
606
|
+
|
607
|
+
hash = {}
|
608
|
+
|
609
|
+
queue_names.zip(samples.each_slice(2).to_a) do |queue_name, (queue_size, serialized_samples)|
|
610
|
+
samples = serialized_samples.map do |serialized_sample|
|
611
|
+
Job.decode(serialized_sample)
|
612
|
+
end
|
613
|
+
|
614
|
+
hash[queue_name] = {
|
615
|
+
:size => queue_size,
|
616
|
+
:samples => samples
|
617
|
+
}
|
618
|
+
end
|
619
|
+
|
620
|
+
hash
|
621
|
+
end
|
622
|
+
|
623
|
+
private
|
624
|
+
|
625
|
+
@hooks = Hash.new { |h, k| h[k] = [] }
|
626
|
+
|
627
|
+
# Register a new proc as a hook. If the block is nil this is the
|
628
|
+
# equivalent of removing all hooks of the given name.
|
629
|
+
#
|
630
|
+
# `name` is the hook that the block should be registered with.
|
631
|
+
def register_hook(name, block)
|
632
|
+
return clear_hooks(name) if block.nil?
|
633
|
+
|
634
|
+
block = Array(block)
|
635
|
+
@hooks[name].concat(block)
|
636
|
+
end
|
637
|
+
|
638
|
+
# Clear all hooks given a hook name.
|
639
|
+
def clear_hooks(name)
|
640
|
+
@hooks[name] = []
|
641
|
+
end
|
642
|
+
|
643
|
+
# Retrieve all hooks of a given name.
|
644
|
+
def hooks(name)
|
645
|
+
@hooks[name]
|
369
646
|
end
|
370
647
|
end
|
371
648
|
|
649
|
+
# Log to STDOUT by default
|
650
|
+
Resque.logger = MonoLogger.new(STDOUT)
|
651
|
+
Resque.logger.formatter = Resque::QuietFormatter.new
|
data/lib/tasks/redis.rake
CHANGED
@@ -17,7 +17,7 @@ class RedisRunner
|
|
17
17
|
end
|
18
18
|
|
19
19
|
def self.config
|
20
|
-
@config ||= if File.
|
20
|
+
@config ||= if File.exist?(redis_dir + 'etc/redis.conf')
|
21
21
|
redis_dir + 'etc/redis.conf'
|
22
22
|
else
|
23
23
|
redis_dir + '../etc/redis.conf'
|
@@ -30,7 +30,7 @@ class RedisRunner
|
|
30
30
|
|
31
31
|
# Just check for existance of dtach socket
|
32
32
|
def self.running?
|
33
|
-
File.
|
33
|
+
File.exist? dtach_socket
|
34
34
|
end
|
35
35
|
|
36
36
|
def self.start
|
@@ -79,7 +79,7 @@ namespace :redis do
|
|
79
79
|
end
|
80
80
|
|
81
81
|
desc <<-DOC
|
82
|
-
Install the latest
|
82
|
+
Install the latest version of Redis from Github (requires git, duh).
|
83
83
|
Use INSTALL_DIR env var like "rake redis:install INSTALL_DIR=~/tmp"
|
84
84
|
in order to get an alternate location for your install files.
|
85
85
|
DOC
|
@@ -90,10 +90,10 @@ namespace :redis do
|
|
90
90
|
|
91
91
|
if ENV['PREFIX']
|
92
92
|
bin_dir = "#{ENV['PREFIX']}/bin"
|
93
|
-
sh "mkdir -p #{bin_dir}" unless File.
|
93
|
+
sh "mkdir -p #{bin_dir}" unless File.exist?("#{bin_dir}")
|
94
94
|
|
95
95
|
conf_dir = "#{ENV['PREFIX']}/etc"
|
96
|
-
sh "mkdir -p #{conf_dir}" unless File.
|
96
|
+
sh "mkdir -p #{conf_dir}" unless File.exist?("#{conf_dir}")
|
97
97
|
end
|
98
98
|
|
99
99
|
%w(redis-benchmark redis-cli redis-server).each do |bin|
|
@@ -102,7 +102,7 @@ namespace :redis do
|
|
102
102
|
|
103
103
|
puts "Installed redis-benchmark, redis-cli and redis-server to #{bin_dir}"
|
104
104
|
|
105
|
-
unless File.
|
105
|
+
unless File.exist?("#{conf_dir}/redis.conf")
|
106
106
|
sh "cp #{INSTALL_DIR}/redis.conf #{conf_dir}/redis.conf"
|
107
107
|
puts "Installed redis.conf to #{conf_dir} \n You should look at this file!"
|
108
108
|
end
|
@@ -115,9 +115,9 @@ namespace :redis do
|
|
115
115
|
|
116
116
|
desc "Download package"
|
117
117
|
task :download do
|
118
|
-
sh "rm -rf #{INSTALL_DIR}/" if File.
|
119
|
-
sh "git clone git://github.com/antirez/redis.git #{INSTALL_DIR}" unless File.
|
120
|
-
sh "cd #{INSTALL_DIR} && git pull" if File.
|
118
|
+
sh "rm -rf #{INSTALL_DIR}/" if File.exist?("#{INSTALL_DIR}/.svn")
|
119
|
+
sh "git clone git://github.com/antirez/redis.git #{INSTALL_DIR}" unless File.exist?(INSTALL_DIR)
|
120
|
+
sh "cd #{INSTALL_DIR} && git pull" if File.exist?("#{INSTALL_DIR}/.git")
|
121
121
|
end
|
122
122
|
end
|
123
123
|
|
@@ -135,7 +135,7 @@ namespace :dtach do
|
|
135
135
|
|
136
136
|
if ENV['PREFIX']
|
137
137
|
bin_dir = "#{ENV['PREFIX']}/bin"
|
138
|
-
sh "mkdir -p #{bin_dir}" unless File.
|
138
|
+
sh "mkdir -p #{bin_dir}" unless File.exist?("#{bin_dir}")
|
139
139
|
end
|
140
140
|
|
141
141
|
sh "cp #{INSTALL_DIR}/dtach-0.8/dtach #{bin_dir}"
|
@@ -147,7 +147,7 @@ namespace :dtach do
|
|
147
147
|
|
148
148
|
desc "Download package"
|
149
149
|
task :download do
|
150
|
-
unless File.
|
150
|
+
unless File.exist?("#{INSTALL_DIR}/dtach-0.8.tar.gz")
|
151
151
|
require 'net/http'
|
152
152
|
|
153
153
|
url = 'http://downloads.sourceforge.net/project/dtach/dtach/0.8/dtach-0.8.tar.gz'
|