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.
Files changed (38) hide show
  1. checksums.yaml +5 -5
  2. data/HISTORY.md +67 -1
  3. data/README.markdown +436 -492
  4. data/bin/resque-web +1 -1
  5. data/lib/resque.rb +92 -18
  6. data/lib/resque/data_store.rb +21 -22
  7. data/lib/resque/errors.rb +7 -1
  8. data/lib/resque/failure.rb +1 -0
  9. data/lib/resque/failure/airbrake.rb +19 -7
  10. data/lib/resque/failure/multiple.rb +6 -2
  11. data/lib/resque/failure/redis.rb +1 -1
  12. data/lib/resque/failure/redis_multi_queue.rb +3 -3
  13. data/lib/resque/job.rb +2 -2
  14. data/lib/resque/logging.rb +1 -1
  15. data/lib/resque/railtie.rb +10 -0
  16. data/lib/resque/server.rb +7 -9
  17. data/lib/resque/server/public/jquery-3.6.0.min.js +2 -0
  18. data/lib/resque/server/public/main.js +3 -0
  19. data/lib/resque/server/public/ranger.js +7 -4
  20. data/lib/resque/server/public/style.css +3 -3
  21. data/lib/resque/server/test_helper.rb +1 -1
  22. data/lib/resque/server/views/failed.erb +2 -2
  23. data/lib/resque/server/views/failed_job.erb +2 -2
  24. data/lib/resque/server/views/layout.erb +3 -2
  25. data/lib/resque/server/views/next_more.erb +14 -14
  26. data/lib/resque/server/views/queues.erb +6 -6
  27. data/lib/resque/server/views/stats.erb +1 -1
  28. data/lib/resque/server/views/working.erb +6 -6
  29. data/lib/resque/stat.rb +12 -5
  30. data/lib/resque/tasks.rb +1 -9
  31. data/lib/resque/thread_signal.rb +13 -34
  32. data/lib/resque/vendor/utf8_util.rb +2 -8
  33. data/lib/resque/version.rb +1 -1
  34. data/lib/resque/worker.rb +90 -44
  35. metadata +12 -13
  36. data/lib/resque/server/public/jquery-1.12.4.min.js +0 -5
  37. data/lib/resque/vendor/utf8_util/utf8_util_18.rb +0 -91
  38. data/lib/resque/vendor/utf8_util/utf8_util_19.rb +0 -6
data/bin/resque-web CHANGED
@@ -6,7 +6,7 @@ begin
6
6
  rescue LoadError
7
7
  require 'rubygems'
8
8
  require 'vegas'
9
- end
9
+ end
10
10
  require 'resque/server'
11
11
 
12
12
 
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 =~ /redis\:\/\//
116
- redis = Redis.connect(:url => server, :thread_safe => true)
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 || DEFAULT_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 || DEFAULT_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
- return @enqueue_front unless @enqueue_front.nil?
169
- @enqueue_front = false
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, (queue_name, queue_size)| sum + queue_size },
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 && @hooks[name] = []
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
- (@hooks && @hooks[name]) || []
646
+ @hooks[name]
573
647
  end
574
648
  end
575
649
 
@@ -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
- def_delegators :@stats_access, :clear_stat,
53
- :decremet_stat,
54
- :increment_stat,
55
- :stat
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
- # support 1.x versions of redis-rb
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.client.reconnect
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, _ = @redis_time_available ? @redis.time : Time.now
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
- class PruneDeadWorkerDirtyExit < DirtyExit; end
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
@@ -27,6 +27,7 @@ module Resque
27
27
  def self.backend=(backend)
28
28
  @backend = backend
29
29
  end
30
+ self.backend = nil
30
31
 
31
32
  # Returns the current backend class. If none has been set, falls
32
33
  # back to `Resque::Failure::Redis`
@@ -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. This will be gone in 2.0."
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
- ::Airbrake.notify(exception,
25
- :parameters => {
26
- :payload_class => payload['class'].to_s,
27
- :payload_args => payload['args'].inspect
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.remove(index, queue)
64
- classes.each { |klass| klass.remove(index) }
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
@@ -31,7 +31,7 @@ module Resque
31
31
 
32
32
  if class_name
33
33
  n = 0
34
- each(0, count(queue), queue, class_name) { n += 1 }
34
+ each(0, count(queue), queue, class_name) { n += 1 }
35
35
  n
36
36
  else
37
37
  data_store.num_failed
@@ -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) - (offset + i) : offset + i
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.remove_from_queue(id,queue)
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)
@@ -7,7 +7,7 @@ module Resque
7
7
  def self.log(severity, message)
8
8
  Resque.logger.__send__(severity, message) if Resque.logger
9
9
  end
10
-
10
+
11
11
  # Log level aliases
12
12
  def debug(message); Logging.log :debug, message; end
13
13
  def info(message); Logging.log :info, message; end
@@ -0,0 +1,10 @@
1
+ module Resque
2
+ class Railtie < Rails::Railtie
3
+ rake_tasks do
4
+ require 'resque/tasks'
5
+
6
+ # redefine ths task to load the rails env
7
+ task "resque:setup" => :environment
8
+ end
9
+ end
10
+ end