resque 1.26.0 → 1.27.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of resque might be problematic. Click here for more details.

checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: f654ccc902abcf33c2340b1e7a718224404880aa
4
- data.tar.gz: bb83a2fabd7698745fcde434567c85f4852b48a4
3
+ metadata.gz: b5f0cb00378f55254a8f68c68fed023ea569e1a8
4
+ data.tar.gz: bf7219b392370ef84f16ab57ff3646c64544a17d
5
5
  SHA512:
6
- metadata.gz: b4c51510f6ce42a7c4b2b31bda3c5758d91e1503fbdc7fac70ae976ef02aa3d519258e38da5a74def54db4487eb673df309b16b3b09b9bc9870beb844f474dd5
7
- data.tar.gz: bda8063a0120e1a9e12c483b0a8c0aac1c472104f129f36d1867d2b75663ab2d712c6c965d1735edc1b3f436cc4272f9d1f9037cc3af1e5608c9ada00592949e
6
+ metadata.gz: fd8e4634086ea6c5f5147799d35596924abb49a71fa1c99223017c4ade6b24b5afba32f2e70985b18af6493530fd554ecd76947a7f7ff04f32e5aeb43c707477
7
+ data.tar.gz: 484db5ed7b0b3be036c9523a2720a16edaa43748a65d34a2add2932784fc37440e0922596529b229d471177c03599df65ab7d57b980b99850c39ab98dfe26e4f
data/HISTORY.md CHANGED
@@ -1,6 +1,14 @@
1
- ## 1.27.0 (TBD)
1
+ ## 1.28.0 (TBD)
2
2
 
3
- None yet!
3
+
4
+ ## 1.27.0 (2017-02-08)
5
+
6
+ * Update jQuery from 1.3.2 to 1.12.4
7
+
8
+ * Fix issue where calling Worker.find, Worker.all, or Worker.working from withing
9
+ a running job would rewrite the PID file with the PID of the forked worker.
10
+ This causes a change to the Worker#new API that may affect plugin
11
+ implementations. See Worker#new and Worker#prepare for details. (@jeremywadsack)
4
12
 
5
13
  ## 1.26.0 (2016-03-10)
6
14
 
@@ -524,8 +524,8 @@ or set the Redis connection string if you need to do something like select a dif
524
524
  Using Passenger? Resque ships with a `config.ru` you can use. See
525
525
  Phusion's guide:
526
526
 
527
- Apache: <http://www.modrails.com/documentation/Users%20guide%20Apache.html#_deploying_a_rack_based_ruby_application>
528
- Nginx: <http://www.modrails.com/documentation/Users%20guide%20Nginx.html#deploying_a_rack_app>
527
+ Apache: <https://www.phusionpassenger.com/library/deploy/apache/deploy/ruby/>
528
+ Nginx: <https://www.phusionpassenger.com/library/deploy/nginx/deploy/ruby/>
529
529
 
530
530
  ### Rack::URLMap
531
531
 
@@ -687,7 +687,7 @@ Don't forget you can define a `resque:setup` hook in
687
687
  `lib/tasks/whatever.rake` that loads the `environment` task every time.
688
688
 
689
689
 
690
- ### In a Rails 3 app, as a gem
690
+ ### In a Rails 3.x or 4.x app, as a gem
691
691
 
692
692
  First include it in your Gemfile.
693
693
 
@@ -18,6 +18,8 @@ require 'resque/log_formatters/very_verbose_formatter'
18
18
  require 'resque/job'
19
19
  require 'resque/worker'
20
20
  require 'resque/plugin'
21
+ require 'resque/data_store'
22
+ require 'resque/thread_signal'
21
23
 
22
24
  require 'resque/vendor/utf8_util'
23
25
 
@@ -120,33 +122,29 @@ module Resque
120
122
  end
121
123
  namespace ||= :resque
122
124
 
123
- @redis = Redis::Namespace.new(namespace, :redis => redis)
125
+ @data_store = Resque::DataStore.new(Redis::Namespace.new(namespace, :redis => redis))
124
126
  when Redis::Namespace
125
- @redis = server
127
+ @data_store = Resque::DataStore.new(server)
128
+ when Resque::DataStore
129
+ @data_store = server
126
130
  when Hash
127
- @redis = Redis::Namespace.new(:resque, :redis => Redis.new(server))
131
+ @data_store = Resque::DataStore.new(Redis::Namespace.new(:resque, :redis => Redis.new(server)))
128
132
  else
129
- @redis = Redis::Namespace.new(:resque, :redis => server)
133
+ @data_store = Resque::DataStore.new(Redis::Namespace.new(:resque, :redis => server))
130
134
  end
131
135
  end
132
136
 
133
137
  # Returns the current Redis connection. If none has been created, will
134
138
  # create a new one.
135
139
  def redis
136
- return @redis if @redis
140
+ return @data_store if @data_store
137
141
  self.redis = Redis.respond_to?(:connect) ? Redis.connect : "localhost:6379"
138
142
  self.redis
139
143
  end
144
+ alias :data_store :redis
140
145
 
141
146
  def redis_id
142
- # support 1.x versions of redis-rb
143
- if redis.respond_to?(:server)
144
- redis.server
145
- elsif redis.respond_to?(:nodes) # distributed
146
- redis.nodes.map { |n| n.id }.join(', ')
147
- else
148
- redis.client.id
149
- end
147
+ data_store.identifier
150
148
  end
151
149
 
152
150
  # Set or retrieve the current logger object
@@ -165,6 +163,12 @@ module Resque
165
163
  @prune_interval || DEFAULT_PRUNE_INTERVAL
166
164
  end
167
165
 
166
+ attr_writer :enqueue_front
167
+ def enqueue_front
168
+ return @enqueue_front unless @enqueue_front.nil?
169
+ @enqueue_front = false
170
+ end
171
+
168
172
  # The `before_first_fork` hook will be run in the **parent** process
169
173
  # only once, before forking to run the first job. Be careful- any
170
174
  # changes you make will be permanent for the lifespan of the
@@ -264,24 +268,20 @@ module Resque
264
268
  #
265
269
  # Returns nothing
266
270
  def push(queue, item)
267
- encoded = encode(item)
268
- redis.pipelined do
269
- watch_queue(queue)
270
- redis.rpush "queue:#{queue}", encoded
271
- end
271
+ data_store.push_to_queue(queue,encode(item))
272
272
  end
273
273
 
274
274
  # Pops a job off a queue. Queue name should be a string.
275
275
  #
276
276
  # Returns a Ruby object.
277
277
  def pop(queue)
278
- decode redis.lpop("queue:#{queue}")
278
+ decode(data_store.pop_from_queue(queue))
279
279
  end
280
280
 
281
281
  # Returns an integer representing the size of a queue.
282
282
  # Queue name should be a string.
283
283
  def size(queue)
284
- redis.llen("queue:#{queue}").to_i
284
+ data_store.queue_size(queue)
285
285
  end
286
286
 
287
287
  # Returns an array of items currently queued. Queue name should be
@@ -293,38 +293,39 @@ module Resque
293
293
  # To get the 3rd page of a 30 item, paginatied list one would use:
294
294
  # Resque.peek('my_list', 59, 30)
295
295
  def peek(queue, start = 0, count = 1)
296
- list_range("queue:#{queue}", start, count)
296
+ results = data_store.peek_in_queue(queue,start,count)
297
+ if count == 1
298
+ decode(results)
299
+ else
300
+ results.map { |result| decode(result) }
301
+ end
297
302
  end
298
303
 
299
304
  # Does the dirty work of fetching a range of items from a Redis list
300
305
  # and converting them into Ruby objects.
301
306
  def list_range(key, start = 0, count = 1)
307
+ results = data_store.list_range(key, start, count)
302
308
  if count == 1
303
- decode redis.lindex(key, start)
309
+ decode(results)
304
310
  else
305
- Array(redis.lrange(key, start, start+count-1)).map do |item|
306
- decode item
307
- end
311
+ results.map { |result| decode(result) }
308
312
  end
309
313
  end
310
314
 
311
315
  # Returns an array of all known Resque queues as strings.
312
316
  def queues
313
- Array(redis.smembers(:queues))
317
+ data_store.queue_names
314
318
  end
315
319
 
316
320
  # Given a queue name, completely deletes the queue.
317
321
  def remove_queue(queue)
318
- redis.pipelined do
319
- redis.srem(:queues, queue.to_s)
320
- redis.del("queue:#{queue}")
321
- end
322
+ data_store.remove_queue(queue)
322
323
  end
323
324
 
324
325
  # Used internally to keep track of which queues we've created.
325
326
  # Don't call this directly.
326
327
  def watch_queue(queue)
327
- redis.sadd(:queues, queue.to_s)
328
+ data_store.watch_queue(queue)
328
329
  end
329
330
 
330
331
 
@@ -487,7 +488,7 @@ module Resque
487
488
  :queues => queues.size,
488
489
  :workers => workers.size.to_i,
489
490
  :working => working.size,
490
- :failed => Resque.redis.llen(:failed).to_i,
491
+ :failed => data_store.num_failed,
491
492
  :servers => [redis_id],
492
493
  :environment => ENV['RAILS_ENV'] || ENV['RACK_ENV'] || 'development'
493
494
  }
@@ -496,9 +497,7 @@ module Resque
496
497
  # Returns an array of all known Resque keys in Redis. Redis' KEYS operation
497
498
  # is O(N) for the keyspace, so be careful - this can be slow for big databases.
498
499
  def keys
499
- redis.keys("*").map do |key|
500
- key.sub("#{redis.namespace}:", '')
501
- end
500
+ data_store.all_resque_keys
502
501
  end
503
502
 
504
503
  # Returns a hash, mapping queue names to queue sizes
@@ -0,0 +1,326 @@
1
+ module Resque
2
+ # An interface between Resque's persistence and the actual
3
+ # implementation.
4
+ class DataStore
5
+ extend Forwardable
6
+
7
+ HEARTBEAT_KEY = "workers:heartbeat"
8
+
9
+ def initialize(redis)
10
+ @redis = redis
11
+ @queue_access = QueueAccess.new(@redis)
12
+ @failed_queue_access = FailedQueueAccess.new(@redis)
13
+ @workers = Workers.new(@redis)
14
+ @stats_access = StatsAccess.new(@redis)
15
+ end
16
+
17
+ def_delegators :@queue_access, :push_to_queue,
18
+ :pop_from_queue,
19
+ :queue_size,
20
+ :peek_in_queue,
21
+ :queue_names,
22
+ :remove_queue,
23
+ :everything_in_queue,
24
+ :remove_from_queue,
25
+ :watch_queue,
26
+ :list_range
27
+
28
+ def_delegators :@failed_queue_access, :add_failed_queue,
29
+ :remove_failed_queue,
30
+ :num_failed,
31
+ :failed_queue_names,
32
+ :push_to_failed_queue,
33
+ :clear_failed_queue,
34
+ :update_item_in_failed_queue,
35
+ :remove_from_failed_queue
36
+ def_delegators :@workers, :worker_ids,
37
+ :workers_map,
38
+ :get_worker_payload,
39
+ :worker_exists?,
40
+ :register_worker,
41
+ :worker_started,
42
+ :unregister_worker,
43
+ :heartbeat,
44
+ :heartbeat!,
45
+ :remove_heartbeat,
46
+ :all_heartbeats,
47
+ :set_worker_payload,
48
+ :worker_start_time,
49
+ :worker_done_working
50
+
51
+ def_delegators :@stats_access, :clear_stat,
52
+ :decremet_stat,
53
+ :increment_stat,
54
+ :stat
55
+
56
+ # Compatibility with any non-Resque classes that were using Resque.redis as a way to access Redis
57
+ def method_missing(sym,*args,&block)
58
+ # TODO: deprecation warning?
59
+ @redis.send(sym,*args,&block)
60
+ end
61
+
62
+ # make use respond like redis
63
+ def respond_to?(method,include_all=false)
64
+ @redis.respond_to?(method,include_all)
65
+ end
66
+
67
+ # Get a string identifying the underlying server.
68
+ # Probably should be private, but was public so must stay public
69
+ def identifier
70
+ # support 1.x versions of redis-rb
71
+ if @redis.respond_to?(:server)
72
+ @redis.server
73
+ elsif @redis.respond_to?(:nodes) # distributed
74
+ @redis.nodes.map { |n| n.id }.join(', ')
75
+ else
76
+ @redis.client.id
77
+ end
78
+ end
79
+
80
+ # Force a reconnect to Redis.
81
+ def reconnect
82
+ @redis.client.reconnect
83
+ end
84
+
85
+ # Returns an array of all known Resque keys in Redis. Redis' KEYS operation
86
+ # is O(N) for the keyspace, so be careful - this can be slow for big databases.
87
+ def all_resque_keys
88
+ @redis.keys("*").map do |key|
89
+ key.sub("#{@redis.namespace}:", '')
90
+ end
91
+ end
92
+
93
+ def server_time
94
+ time, _ = @redis.time
95
+ Time.at(time)
96
+ end
97
+
98
+ class QueueAccess
99
+ def initialize(redis)
100
+ @redis = redis
101
+ end
102
+ def push_to_queue(queue,encoded_item)
103
+ @redis.pipelined do
104
+ watch_queue(queue)
105
+ @redis.rpush redis_key_for_queue(queue), encoded_item
106
+ end
107
+ end
108
+
109
+ # Pop whatever is on queue
110
+ def pop_from_queue(queue)
111
+ @redis.lpop(redis_key_for_queue(queue))
112
+ end
113
+
114
+ # Get the number of items in the queue
115
+ def queue_size(queue)
116
+ @redis.llen(redis_key_for_queue(queue)).to_i
117
+ end
118
+
119
+ # Examine items in the queue.
120
+ #
121
+ # NOTE: if count is 1, you will get back an object, otherwise you will
122
+ # get an Array. I'm not making this up.
123
+ def peek_in_queue(queue, start = 0, count = 1)
124
+ list_range(redis_key_for_queue(queue), start, count)
125
+ end
126
+
127
+ def queue_names
128
+ Array(@redis.smembers(:queues))
129
+ end
130
+
131
+ def remove_queue(queue)
132
+ @redis.pipelined do
133
+ @redis.srem(:queues, queue.to_s)
134
+ @redis.del(redis_key_for_queue(queue))
135
+ end
136
+ end
137
+
138
+ def everything_in_queue(queue)
139
+ @redis.lrange(redis_key_for_queue(queue), 0, -1)
140
+ end
141
+
142
+ # Remove data from the queue, if it's there, returning the number of removed elements
143
+ def remove_from_queue(queue,data)
144
+ @redis.lrem(redis_key_for_queue(queue), 0, data)
145
+ end
146
+
147
+ # Private: do not call
148
+ def watch_queue(queue)
149
+ @redis.sadd(:queues, queue.to_s)
150
+ end
151
+
152
+ # Private: do not call
153
+ def list_range(key, start = 0, count = 1)
154
+ if count == 1
155
+ @redis.lindex(key, start)
156
+ else
157
+ Array(@redis.lrange(key, start, start+count-1))
158
+ end
159
+ end
160
+
161
+ private
162
+
163
+ def redis_key_for_queue(queue)
164
+ "queue:#{queue}"
165
+ end
166
+
167
+ end
168
+
169
+ class FailedQueueAccess
170
+ def initialize(redis)
171
+ @redis = redis
172
+ end
173
+
174
+ def add_failed_queue(failed_queue_name)
175
+ @redis.sadd(:failed_queues, failed_queue_name)
176
+ end
177
+
178
+ def remove_failed_queue(failed_queue_name=:failed)
179
+ @redis.del(failed_queue_name)
180
+ end
181
+
182
+ def num_failed(failed_queue_name=:failed)
183
+ @redis.llen(failed_queue_name).to_i
184
+ end
185
+
186
+ def failed_queue_names(find_queue_names_in_key=nil)
187
+ if find_queue_names_in_key.nil?
188
+ [:failed]
189
+ else
190
+ Array(@redis.smembers(find_queue_names_in_key))
191
+ end
192
+ end
193
+
194
+ def push_to_failed_queue(data,failed_queue_name=:failed)
195
+ @redis.rpush(failed_queue_name,data)
196
+ end
197
+
198
+ def clear_failed_queue(failed_queue_name=:failed)
199
+ @redis.del(failed_queue_name)
200
+ end
201
+
202
+ def update_item_in_failed_queue(index_in_failed_queue,new_item_data,failed_queue_name=:failed)
203
+ @redis.lset(failed_queue_name, index_in_failed_queue, new_item_data)
204
+ end
205
+
206
+ def remove_from_failed_queue(index_in_failed_queue,failed_queue_name=nil)
207
+ failed_queue_name ||= :failed
208
+ hopefully_unique_value_we_can_use_to_delete_job = ""
209
+ @redis.lset(failed_queue_name, index_in_failed_queue, hopefully_unique_value_we_can_use_to_delete_job)
210
+ @redis.lrem(failed_queue_name, 1, hopefully_unique_value_we_can_use_to_delete_job)
211
+ end
212
+ end
213
+
214
+ class Workers
215
+ def initialize(redis)
216
+ @redis = redis
217
+ end
218
+
219
+ def worker_ids
220
+ Array(@redis.smembers(:workers))
221
+ end
222
+
223
+ # Given a list of worker ids, returns a map of those ids to the worker's value
224
+ # in redis, even if that value maps to nil
225
+ def workers_map(worker_ids)
226
+ redis_keys = worker_ids.map { |id| "worker:#{id}" }
227
+ @redis.mapped_mget(*redis_keys)
228
+ end
229
+
230
+ # return the worker's payload i.e. job
231
+ def get_worker_payload(worker_id)
232
+ @redis.get("worker:#{worker_id}")
233
+ end
234
+
235
+ def worker_exists?(worker_id)
236
+ @redis.sismember(:workers, worker_id)
237
+ end
238
+
239
+ def register_worker(worker)
240
+ @redis.pipelined do
241
+ @redis.sadd(:workers, worker)
242
+ worker_started(worker)
243
+ end
244
+ end
245
+
246
+ def worker_started(worker)
247
+ @redis.set(redis_key_for_worker_start_time(worker), Time.now.to_s)
248
+ end
249
+
250
+ def unregister_worker(worker, &block)
251
+ @redis.pipelined do
252
+ @redis.srem(:workers, worker)
253
+ @redis.del(redis_key_for_worker(worker))
254
+ @redis.del(redis_key_for_worker_start_time(worker))
255
+ @redis.hdel(HEARTBEAT_KEY, worker.to_s)
256
+
257
+ block.call
258
+ end
259
+ end
260
+
261
+ def remove_heartbeat(worker)
262
+ @redis.hdel(HEARTBEAT_KEY, worker.to_s)
263
+ end
264
+
265
+ def heartbeat(worker)
266
+ heartbeat = @redis.hget(HEARTBEAT_KEY, worker.to_s)
267
+ heartbeat && Time.parse(heartbeat)
268
+ end
269
+
270
+ def heartbeat!(worker, time)
271
+ @redis.hset(HEARTBEAT_KEY, worker.to_s, time.iso8601)
272
+ end
273
+
274
+ def all_heartbeats
275
+ @redis.hgetall(HEARTBEAT_KEY)
276
+ end
277
+
278
+ def set_worker_payload(worker, data)
279
+ @redis.set(redis_key_for_worker(worker), data)
280
+ end
281
+
282
+ def worker_start_time(worker)
283
+ @redis.get(redis_key_for_worker_start_time(worker))
284
+ end
285
+
286
+ def worker_done_working(worker, &block)
287
+ @redis.pipelined do
288
+ @redis.del(redis_key_for_worker(worker))
289
+ block.call
290
+ end
291
+ end
292
+
293
+ private
294
+
295
+ def redis_key_for_worker(worker)
296
+ "worker:#{worker}"
297
+ end
298
+
299
+ def redis_key_for_worker_start_time(worker)
300
+ "#{redis_key_for_worker(worker)}:started"
301
+ end
302
+
303
+ end
304
+
305
+ class StatsAccess
306
+ def initialize(redis)
307
+ @redis = redis
308
+ end
309
+ def stat(stat)
310
+ @redis.get("stat:#{stat}").to_i
311
+ end
312
+
313
+ def increment_stat(stat, by = 1)
314
+ @redis.incrby("stat:#{stat}", by)
315
+ end
316
+
317
+ def decremet_stat(stat, by = 1)
318
+ @redis.decrby("stat:#{stat}", by)
319
+ end
320
+
321
+ def clear_stat(stat)
322
+ @redis.del("stat:#{stat}")
323
+ end
324
+ end
325
+ end
326
+ end