resque 1.27.2 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +5 -5
- data/HISTORY.md +67 -1
- data/README.markdown +436 -492
- data/bin/resque-web +1 -1
- data/lib/resque.rb +92 -18
- data/lib/resque/data_store.rb +21 -22
- data/lib/resque/errors.rb +7 -1
- data/lib/resque/failure.rb +1 -0
- data/lib/resque/failure/airbrake.rb +19 -7
- data/lib/resque/failure/multiple.rb +6 -2
- data/lib/resque/failure/redis.rb +1 -1
- data/lib/resque/failure/redis_multi_queue.rb +3 -3
- data/lib/resque/job.rb +2 -2
- data/lib/resque/logging.rb +1 -1
- data/lib/resque/railtie.rb +10 -0
- data/lib/resque/server.rb +7 -9
- data/lib/resque/server/public/jquery-3.6.0.min.js +2 -0
- data/lib/resque/server/public/main.js +3 -0
- data/lib/resque/server/public/ranger.js +7 -4
- data/lib/resque/server/public/style.css +3 -3
- data/lib/resque/server/test_helper.rb +1 -1
- data/lib/resque/server/views/failed.erb +2 -2
- data/lib/resque/server/views/failed_job.erb +2 -2
- data/lib/resque/server/views/layout.erb +3 -2
- data/lib/resque/server/views/next_more.erb +14 -14
- data/lib/resque/server/views/queues.erb +6 -6
- data/lib/resque/server/views/stats.erb +1 -1
- data/lib/resque/server/views/working.erb +6 -6
- data/lib/resque/stat.rb +12 -5
- data/lib/resque/tasks.rb +1 -9
- data/lib/resque/thread_signal.rb +13 -34
- data/lib/resque/vendor/utf8_util.rb +2 -8
- data/lib/resque/version.rb +1 -1
- data/lib/resque/worker.rb +90 -44
- metadata +12 -13
- data/lib/resque/server/public/jquery-1.12.4.min.js +0 -5
- data/lib/resque/vendor/utf8_util/utf8_util_18.rb +0 -91
- data/lib/resque/vendor/utf8_util/utf8_util_19.rb +0 -6
data/bin/resque-web
CHANGED
data/lib/resque.rb
CHANGED
|
@@ -23,6 +23,8 @@ require 'resque/thread_signal'
|
|
|
23
23
|
|
|
24
24
|
require 'resque/vendor/utf8_util'
|
|
25
25
|
|
|
26
|
+
require 'resque/railtie' if defined?(Rails)
|
|
27
|
+
|
|
26
28
|
module Resque
|
|
27
29
|
include Helpers
|
|
28
30
|
extend self
|
|
@@ -112,8 +114,8 @@ module Resque
|
|
|
112
114
|
def redis=(server)
|
|
113
115
|
case server
|
|
114
116
|
when String
|
|
115
|
-
if server =~ /
|
|
116
|
-
redis = Redis.
|
|
117
|
+
if server =~ /rediss?\:\/\//
|
|
118
|
+
redis = Redis.new(:url => server, :thread_safe => true)
|
|
117
119
|
else
|
|
118
120
|
server, namespace = server.split('/', 2)
|
|
119
121
|
host, port, db = server.split(':')
|
|
@@ -147,26 +149,76 @@ module Resque
|
|
|
147
149
|
data_store.identifier
|
|
148
150
|
end
|
|
149
151
|
|
|
152
|
+
# Set the data store for the processed and failed statistics.
|
|
153
|
+
#
|
|
154
|
+
# By default it uses the same as `Resque.redis`, but different stores can be used.
|
|
155
|
+
#
|
|
156
|
+
# A custom store needs to obey the following API to work correctly
|
|
157
|
+
#
|
|
158
|
+
# class NullDataStore
|
|
159
|
+
# # Returns the current value for the given stat.
|
|
160
|
+
# def stat(stat)
|
|
161
|
+
# end
|
|
162
|
+
#
|
|
163
|
+
# # Increments the stat by the given value.
|
|
164
|
+
# def increment_stat(stat, by)
|
|
165
|
+
# end
|
|
166
|
+
#
|
|
167
|
+
# # Decrements the stat by the given value.
|
|
168
|
+
# def decrement_stat(stat, by)
|
|
169
|
+
# end
|
|
170
|
+
#
|
|
171
|
+
# # Clear the values for the given stat.
|
|
172
|
+
# def clear_stat(stat)
|
|
173
|
+
# end
|
|
174
|
+
# end
|
|
175
|
+
def stat_data_store=(stat_data_store)
|
|
176
|
+
Resque::Stat.data_store = stat_data_store
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
# Returns the data store for the statistics module.
|
|
180
|
+
def stat_data_store
|
|
181
|
+
Resque::Stat.data_store
|
|
182
|
+
end
|
|
183
|
+
|
|
150
184
|
# Set or retrieve the current logger object
|
|
151
185
|
attr_accessor :logger
|
|
152
186
|
|
|
153
187
|
DEFAULT_HEARTBEAT_INTERVAL = 60
|
|
154
188
|
DEFAULT_PRUNE_INTERVAL = DEFAULT_HEARTBEAT_INTERVAL * 5
|
|
155
189
|
|
|
190
|
+
# Defines how often a Resque worker updates the heartbeat key. Must be less
|
|
191
|
+
# than the prune interval.
|
|
156
192
|
attr_writer :heartbeat_interval
|
|
157
193
|
def heartbeat_interval
|
|
158
|
-
@heartbeat_interval
|
|
194
|
+
if defined? @heartbeat_interval
|
|
195
|
+
@heartbeat_interval
|
|
196
|
+
else
|
|
197
|
+
DEFAULT_HEARTBEAT_INTERVAL
|
|
198
|
+
end
|
|
159
199
|
end
|
|
160
200
|
|
|
201
|
+
# Defines how often Resque checks for dead workers.
|
|
161
202
|
attr_writer :prune_interval
|
|
162
203
|
def prune_interval
|
|
163
|
-
@prune_interval
|
|
204
|
+
if defined? @prune_interval
|
|
205
|
+
@prune_interval
|
|
206
|
+
else
|
|
207
|
+
DEFAULT_PRUNE_INTERVAL
|
|
208
|
+
end
|
|
164
209
|
end
|
|
165
210
|
|
|
211
|
+
# By default, jobs are pushed to the back of the queue and popped from
|
|
212
|
+
# the front, resulting in "first in, first out" (FIFO) execution order.
|
|
213
|
+
# Set to true to push jobs to the front of the queue instead, resulting
|
|
214
|
+
# in "last in, first out" (LIFO) execution order.
|
|
166
215
|
attr_writer :enqueue_front
|
|
167
216
|
def enqueue_front
|
|
168
|
-
|
|
169
|
-
|
|
217
|
+
if defined? @enqueue_front
|
|
218
|
+
@enqueue_front
|
|
219
|
+
else
|
|
220
|
+
@enqueue_front = false
|
|
221
|
+
end
|
|
170
222
|
end
|
|
171
223
|
|
|
172
224
|
# The `before_first_fork` hook will be run in the **parent** process
|
|
@@ -237,6 +289,34 @@ module Resque
|
|
|
237
289
|
register_hook(:after_pause, block)
|
|
238
290
|
end
|
|
239
291
|
|
|
292
|
+
# The `queue_empty` hook will be run in the **parent** process when
|
|
293
|
+
# the worker finds no more jobs in the queue and becomes idle.
|
|
294
|
+
#
|
|
295
|
+
# Call with a block to register a hook.
|
|
296
|
+
# Call with no arguments to return all registered hooks.
|
|
297
|
+
def queue_empty(&block)
|
|
298
|
+
block ? register_hook(:queue_empty, block) : hooks(:queue_empty)
|
|
299
|
+
end
|
|
300
|
+
|
|
301
|
+
# Register a queue_empty proc.
|
|
302
|
+
def queue_empty=(block)
|
|
303
|
+
register_hook(:queue_empty, block)
|
|
304
|
+
end
|
|
305
|
+
|
|
306
|
+
# The `worker_exit` hook will be run in the **parent** process
|
|
307
|
+
# after the worker has existed (via SIGQUIT, SIGTERM, SIGINT, etc.).
|
|
308
|
+
#
|
|
309
|
+
# Call with a block to register a hook.
|
|
310
|
+
# Call with no arguments to return all registered hooks.
|
|
311
|
+
def worker_exit(&block)
|
|
312
|
+
block ? register_hook(:worker_exit, block) : hooks(:worker_exit)
|
|
313
|
+
end
|
|
314
|
+
|
|
315
|
+
# Register a worker_exit proc.
|
|
316
|
+
def worker_exit=(block)
|
|
317
|
+
register_hook(:worker_exit, block)
|
|
318
|
+
end
|
|
319
|
+
|
|
240
320
|
def to_s
|
|
241
321
|
"Resque Client connected to #{redis_id}"
|
|
242
322
|
end
|
|
@@ -424,7 +504,7 @@ module Resque
|
|
|
424
504
|
# Given a class, try to extrapolate an appropriate queue based on a
|
|
425
505
|
# class instance variable or `queue` method.
|
|
426
506
|
def queue_from_class(klass)
|
|
427
|
-
klass.instance_variable_get(:@queue) ||
|
|
507
|
+
(klass.instance_variable_defined?(:@queue) && klass.instance_variable_get(:@queue)) ||
|
|
428
508
|
(klass.respond_to?(:queue) and klass.queue)
|
|
429
509
|
end
|
|
430
510
|
|
|
@@ -483,7 +563,7 @@ module Resque
|
|
|
483
563
|
# Returns a hash, similar to redis-rb's #info, of interesting stats.
|
|
484
564
|
def info
|
|
485
565
|
return {
|
|
486
|
-
:pending => queue_sizes.inject(0) { |sum, (
|
|
566
|
+
:pending => queue_sizes.inject(0) { |sum, (_queue_name, queue_size)| sum + queue_size },
|
|
487
567
|
:processed => Stat[:processed],
|
|
488
568
|
:queues => queues.size,
|
|
489
569
|
:workers => workers.size.to_i,
|
|
@@ -543,6 +623,8 @@ module Resque
|
|
|
543
623
|
|
|
544
624
|
private
|
|
545
625
|
|
|
626
|
+
@hooks = Hash.new { |h, k| h[k] = [] }
|
|
627
|
+
|
|
546
628
|
# Register a new proc as a hook. If the block is nil this is the
|
|
547
629
|
# equivalent of removing all hooks of the given name.
|
|
548
630
|
#
|
|
@@ -550,26 +632,18 @@ module Resque
|
|
|
550
632
|
def register_hook(name, block)
|
|
551
633
|
return clear_hooks(name) if block.nil?
|
|
552
634
|
|
|
553
|
-
@hooks ||= {}
|
|
554
|
-
@hooks[name] ||= []
|
|
555
|
-
|
|
556
635
|
block = Array(block)
|
|
557
636
|
@hooks[name].concat(block)
|
|
558
637
|
end
|
|
559
638
|
|
|
560
639
|
# Clear all hooks given a hook name.
|
|
561
640
|
def clear_hooks(name)
|
|
562
|
-
@hooks
|
|
563
|
-
end
|
|
564
|
-
|
|
565
|
-
# Retrieve all hooks
|
|
566
|
-
def hooks
|
|
567
|
-
@hooks || {}
|
|
641
|
+
@hooks[name] = []
|
|
568
642
|
end
|
|
569
643
|
|
|
570
644
|
# Retrieve all hooks of a given name.
|
|
571
645
|
def hooks(name)
|
|
572
|
-
|
|
646
|
+
@hooks[name]
|
|
573
647
|
end
|
|
574
648
|
end
|
|
575
649
|
|
data/lib/resque/data_store.rb
CHANGED
|
@@ -12,7 +12,6 @@ module Resque
|
|
|
12
12
|
@failed_queue_access = FailedQueueAccess.new(@redis)
|
|
13
13
|
@workers = Workers.new(@redis)
|
|
14
14
|
@stats_access = StatsAccess.new(@redis)
|
|
15
|
-
@redis_time_available = redis_time_available?
|
|
16
15
|
end
|
|
17
16
|
|
|
18
17
|
def_delegators :@queue_access, :push_to_queue,
|
|
@@ -45,14 +44,20 @@ module Resque
|
|
|
45
44
|
:heartbeat!,
|
|
46
45
|
:remove_heartbeat,
|
|
47
46
|
:all_heartbeats,
|
|
47
|
+
:acquire_pruning_dead_worker_lock,
|
|
48
48
|
:set_worker_payload,
|
|
49
49
|
:worker_start_time,
|
|
50
50
|
:worker_done_working
|
|
51
51
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
52
|
+
def_delegators :@stats_access, :clear_stat,
|
|
53
|
+
:decrement_stat,
|
|
54
|
+
:increment_stat,
|
|
55
|
+
:stat
|
|
56
|
+
|
|
57
|
+
def decremet_stat(*args)
|
|
58
|
+
warn '[Resque] [Deprecation] Resque::DataStore #decremet_stat method is deprecated (please use #decrement_stat)'
|
|
59
|
+
decrement_stat(*args)
|
|
60
|
+
end
|
|
56
61
|
|
|
57
62
|
# Compatibility with any non-Resque classes that were using Resque.redis as a way to access Redis
|
|
58
63
|
def method_missing(sym,*args,&block)
|
|
@@ -68,19 +73,12 @@ module Resque
|
|
|
68
73
|
# Get a string identifying the underlying server.
|
|
69
74
|
# Probably should be private, but was public so must stay public
|
|
70
75
|
def identifier
|
|
71
|
-
|
|
72
|
-
if @redis.respond_to?(:server)
|
|
73
|
-
@redis.server
|
|
74
|
-
elsif @redis.respond_to?(:nodes) # distributed
|
|
75
|
-
@redis.nodes.map { |n| n.id }.join(', ')
|
|
76
|
-
else
|
|
77
|
-
@redis.client.id
|
|
78
|
-
end
|
|
76
|
+
@redis.inspect
|
|
79
77
|
end
|
|
80
78
|
|
|
81
79
|
# Force a reconnect to Redis.
|
|
82
80
|
def reconnect
|
|
83
|
-
@redis.
|
|
81
|
+
@redis._client.reconnect
|
|
84
82
|
end
|
|
85
83
|
|
|
86
84
|
# Returns an array of all known Resque keys in Redis. Redis' KEYS operation
|
|
@@ -92,17 +90,10 @@ module Resque
|
|
|
92
90
|
end
|
|
93
91
|
|
|
94
92
|
def server_time
|
|
95
|
-
time, _ = @
|
|
93
|
+
time, _ = @redis.time
|
|
96
94
|
Time.at(time)
|
|
97
95
|
end
|
|
98
96
|
|
|
99
|
-
def redis_time_available?
|
|
100
|
-
@redis.time
|
|
101
|
-
rescue Redis::CommandError
|
|
102
|
-
false
|
|
103
|
-
end
|
|
104
|
-
private :redis_time_available?
|
|
105
|
-
|
|
106
97
|
class QueueAccess
|
|
107
98
|
def initialize(redis)
|
|
108
99
|
@redis = redis
|
|
@@ -283,6 +274,10 @@ module Resque
|
|
|
283
274
|
@redis.hgetall(HEARTBEAT_KEY)
|
|
284
275
|
end
|
|
285
276
|
|
|
277
|
+
def acquire_pruning_dead_worker_lock(worker, expiry)
|
|
278
|
+
@redis.set(redis_key_for_worker_pruning, worker.to_s, :ex => expiry, :nx => true)
|
|
279
|
+
end
|
|
280
|
+
|
|
286
281
|
def set_worker_payload(worker, data)
|
|
287
282
|
@redis.set(redis_key_for_worker(worker), data)
|
|
288
283
|
end
|
|
@@ -307,6 +302,10 @@ module Resque
|
|
|
307
302
|
def redis_key_for_worker_start_time(worker)
|
|
308
303
|
"#{redis_key_for_worker(worker)}:started"
|
|
309
304
|
end
|
|
305
|
+
|
|
306
|
+
def redis_key_for_worker_pruning
|
|
307
|
+
"pruning_dead_workers_in_progress"
|
|
308
|
+
end
|
|
310
309
|
end
|
|
311
310
|
|
|
312
311
|
class StatsAccess
|
data/lib/resque/errors.rb
CHANGED
|
@@ -14,7 +14,13 @@ module Resque
|
|
|
14
14
|
super message
|
|
15
15
|
end
|
|
16
16
|
end
|
|
17
|
-
|
|
17
|
+
|
|
18
|
+
class PruneDeadWorkerDirtyExit < DirtyExit
|
|
19
|
+
def initialize(hostname, job)
|
|
20
|
+
job ||= "<Unknown Job>"
|
|
21
|
+
super("Worker #{hostname} did not gracefully exit while processing #{job}")
|
|
22
|
+
end
|
|
23
|
+
end
|
|
18
24
|
|
|
19
25
|
# Raised when child process is TERM'd so job can rescue this to do shutdown work.
|
|
20
26
|
class TermException < SignalException; end
|
data/lib/resque/failure.rb
CHANGED
|
@@ -9,7 +9,7 @@ module Resque
|
|
|
9
9
|
class Airbrake < Base
|
|
10
10
|
def self.configure(&block)
|
|
11
11
|
Resque.logger.warn "This actually sets global Airbrake configuration, " \
|
|
12
|
-
"which is probably not what you want.
|
|
12
|
+
"which is probably not what you want."
|
|
13
13
|
Resque::Failure.backend = self
|
|
14
14
|
::Airbrake.configure(&block)
|
|
15
15
|
end
|
|
@@ -21,12 +21,24 @@ module Resque
|
|
|
21
21
|
end
|
|
22
22
|
|
|
23
23
|
def save
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
:
|
|
28
|
-
|
|
29
|
-
|
|
24
|
+
notify(
|
|
25
|
+
exception,
|
|
26
|
+
parameters: {
|
|
27
|
+
payload_class: payload['class'].to_s,
|
|
28
|
+
payload_args: payload['args'].inspect
|
|
29
|
+
}
|
|
30
|
+
)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
private
|
|
34
|
+
|
|
35
|
+
def notify(exception, options)
|
|
36
|
+
if ::Airbrake.respond_to?(:notify_sync)
|
|
37
|
+
::Airbrake.notify_sync(exception, options)
|
|
38
|
+
else
|
|
39
|
+
# Older versions of Airbrake (< 5)
|
|
40
|
+
::Airbrake.notify(exception, options)
|
|
41
|
+
end
|
|
30
42
|
end
|
|
31
43
|
end
|
|
32
44
|
end
|
|
@@ -60,8 +60,12 @@ module Resque
|
|
|
60
60
|
classes.first.requeue_all
|
|
61
61
|
end
|
|
62
62
|
|
|
63
|
-
def self.
|
|
64
|
-
classes.
|
|
63
|
+
def self.requeue_queue(queue)
|
|
64
|
+
classes.first.requeue_queue(queue)
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def self.remove(index, queue = nil)
|
|
68
|
+
classes.each { |klass| klass.remove(index, queue) }
|
|
65
69
|
end
|
|
66
70
|
end
|
|
67
71
|
end
|
data/lib/resque/failure/redis.rb
CHANGED
|
@@ -30,7 +30,7 @@ module Resque
|
|
|
30
30
|
if queue
|
|
31
31
|
if class_name
|
|
32
32
|
n = 0
|
|
33
|
-
each(0, count(queue), queue, class_name) { n += 1 }
|
|
33
|
+
each(0, count(queue), queue, class_name) { n += 1 }
|
|
34
34
|
n
|
|
35
35
|
else
|
|
36
36
|
data_store.num_failed(queue).to_i
|
|
@@ -60,7 +60,7 @@ module Resque
|
|
|
60
60
|
end
|
|
61
61
|
items.each_with_index do |item, i|
|
|
62
62
|
if !class_name || (item['payload'] && item['payload']['class'] == class_name)
|
|
63
|
-
id = reversed ? (items.length - 1)
|
|
63
|
+
id = reversed ? (items.length - 1) + (offset - i) : offset + i
|
|
64
64
|
yield id, item
|
|
65
65
|
end
|
|
66
66
|
end
|
|
@@ -79,7 +79,7 @@ module Resque
|
|
|
79
79
|
end
|
|
80
80
|
|
|
81
81
|
def self.remove(id, queue = :failed)
|
|
82
|
-
data_store.
|
|
82
|
+
data_store.remove_from_failed_queue(id,queue)
|
|
83
83
|
end
|
|
84
84
|
|
|
85
85
|
def self.requeue_queue(queue)
|
data/lib/resque/job.rb
CHANGED
|
@@ -48,7 +48,7 @@ module Resque
|
|
|
48
48
|
def self.decode(object)
|
|
49
49
|
Resque.decode(object)
|
|
50
50
|
end
|
|
51
|
-
|
|
51
|
+
|
|
52
52
|
# Given a word with dashes, returns a camel cased version of it.
|
|
53
53
|
def classify(dashed_word)
|
|
54
54
|
Resque.classify(dashed_word)
|
|
@@ -154,7 +154,7 @@ module Resque
|
|
|
154
154
|
|
|
155
155
|
begin
|
|
156
156
|
# Execute before_perform hook. Abort the job gracefully if
|
|
157
|
-
# Resque::DontPerform is raised.
|
|
157
|
+
# Resque::Job::DontPerform is raised.
|
|
158
158
|
begin
|
|
159
159
|
before_hooks.each do |hook|
|
|
160
160
|
job.send(hook, *job_args)
|
data/lib/resque/logging.rb
CHANGED