mongo-resque 1.17.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (46) hide show
  1. data/HISTORY.md +278 -0
  2. data/LICENSE +20 -0
  3. data/README.markdown +76 -0
  4. data/bin/resque +67 -0
  5. data/bin/resque-web +23 -0
  6. data/docs/HOOKS.md +132 -0
  7. data/docs/PLUGINS.md +93 -0
  8. data/lib/resque/errors.rb +13 -0
  9. data/lib/resque/failure/base.rb +64 -0
  10. data/lib/resque/failure/hoptoad.rb +133 -0
  11. data/lib/resque/failure/mongo.rb +45 -0
  12. data/lib/resque/failure/multiple.rb +54 -0
  13. data/lib/resque/failure/redis.rb +46 -0
  14. data/lib/resque/failure.rb +70 -0
  15. data/lib/resque/helpers.rb +75 -0
  16. data/lib/resque/job.rb +215 -0
  17. data/lib/resque/plugin.rb +51 -0
  18. data/lib/resque/server/public/favicon.ico +0 -0
  19. data/lib/resque/server/public/idle.png +0 -0
  20. data/lib/resque/server/public/jquery-1.3.2.min.js +19 -0
  21. data/lib/resque/server/public/jquery.relatize_date.js +95 -0
  22. data/lib/resque/server/public/poll.png +0 -0
  23. data/lib/resque/server/public/ranger.js +73 -0
  24. data/lib/resque/server/public/reset.css +48 -0
  25. data/lib/resque/server/public/style.css +92 -0
  26. data/lib/resque/server/public/working.png +0 -0
  27. data/lib/resque/server/test_helper.rb +19 -0
  28. data/lib/resque/server/views/error.erb +1 -0
  29. data/lib/resque/server/views/failed.erb +65 -0
  30. data/lib/resque/server/views/key_sets.erb +19 -0
  31. data/lib/resque/server/views/key_string.erb +11 -0
  32. data/lib/resque/server/views/layout.erb +42 -0
  33. data/lib/resque/server/views/next_more.erb +10 -0
  34. data/lib/resque/server/views/overview.erb +4 -0
  35. data/lib/resque/server/views/queues.erb +69 -0
  36. data/lib/resque/server/views/stats.erb +73 -0
  37. data/lib/resque/server/views/workers.erb +109 -0
  38. data/lib/resque/server/views/working.erb +72 -0
  39. data/lib/resque/server.rb +268 -0
  40. data/lib/resque/stat.rb +54 -0
  41. data/lib/resque/tasks.rb +42 -0
  42. data/lib/resque/version.rb +3 -0
  43. data/lib/resque/worker.rb +497 -0
  44. data/lib/resque.rb +417 -0
  45. data/lib/tasks/resque.rake +2 -0
  46. metadata +148 -0
data/lib/resque.rb ADDED
@@ -0,0 +1,417 @@
1
+
2
+ begin
3
+ require 'yajl'
4
+ rescue LoadError
5
+ require 'json'
6
+ end
7
+
8
+ require 'mongo'
9
+
10
+ require 'resque/version'
11
+
12
+ require 'resque/errors'
13
+
14
+ require 'resque/failure'
15
+ require 'resque/failure/base'
16
+
17
+ require 'resque/helpers'
18
+ require 'resque/stat'
19
+ require 'resque/job'
20
+ require 'resque/worker'
21
+ require 'resque/plugin'
22
+
23
+ module Resque
24
+ include Helpers
25
+ extend self
26
+ @delay_allowed = []
27
+
28
+ # Set the queue database. Expects a Mongo::DB object.
29
+ def mongo=(database)
30
+ if database.is_a?(Mongo::DB)
31
+ @mongo = database
32
+ initialize_mongo
33
+ else
34
+ raise ArgumentError, "Resque.mongo= expects a Mongo::DB database, not a #{database.class}."
35
+ end
36
+ end
37
+
38
+ # Returns the current Mongo::DB. If none has been created, it will
39
+ # create a new one called 'resque'.
40
+ def mongo
41
+ return @mongo if @mongo
42
+ self.mongo = Mongo::Connection.new.db("resque")
43
+ @mongo
44
+ end
45
+
46
+ def initialize_mongo
47
+ mongo_workers.create_index :worker
48
+ mongo_stats.create_index :stat
49
+ delay_allowed = mongo_stats.find_one({ :stat => 'Delayable Queues'}, { :fields => ['value']})
50
+ @delay_allowed = delay_allowed['value'].map{ |queue| queue.to_sym} if delay_allowed
51
+ end
52
+
53
+ def mongo_workers
54
+ mongo['resque.workers']
55
+ end
56
+
57
+ def mongo_stats
58
+ mongo['resque.metrics']
59
+ end
60
+
61
+ def mongo_failures
62
+ mongo['resque.failures']
63
+ end
64
+
65
+ # The `before_first_fork` hook will be run in the **parent** process
66
+ # only once, before forking to run the first job. Be careful- any
67
+ # changes you make will be permanent for the lifespan of the
68
+ # worker.
69
+ #
70
+ # Call with a block to set the hook.
71
+ # Call with no arguments to return the hook.
72
+ def before_first_fork(&block)
73
+ block ? (@before_first_fork = block) : @before_first_fork
74
+ end
75
+
76
+ # Set a proc that will be called in the parent process before the
77
+ # worker forks for the first time.
78
+ def before_first_fork=(before_first_fork)
79
+ @before_first_fork = before_first_fork
80
+ end
81
+
82
+ # The `before_fork` hook will be run in the **parent** process
83
+ # before every job, so be careful- any changes you make will be
84
+ # permanent for the lifespan of the worker.
85
+ #
86
+ # Call with a block to set the hook.
87
+ # Call with no arguments to return the hook.
88
+ def before_fork(&block)
89
+ block ? (@before_fork = block) : @before_fork
90
+ end
91
+
92
+ # Set the before_fork proc.
93
+ def before_fork=(before_fork)
94
+ @before_fork = before_fork
95
+ end
96
+
97
+ # The `after_fork` hook will be run in the child process and is passed
98
+ # the current job. Any changes you make, therefore, will only live as
99
+ # long as the job currently being processed.
100
+ #
101
+ # Call with a block to set the hook.
102
+ # Call with no arguments to return the hook.
103
+ def after_fork(&block)
104
+ block ? (@after_fork = block) : @after_fork
105
+ end
106
+
107
+ # Set the after_fork proc.
108
+ def after_fork=(after_fork)
109
+ @after_fork = after_fork
110
+ end
111
+
112
+ def to_s
113
+ connection_info = mongo.connection.primary_pool
114
+ "Resque Client connected to #{connection_info.host}:#{connection_info.port}/#{mongo.name}"
115
+ end
116
+
117
+ def allows_delayed_jobs(klass)
118
+ klass.instance_variable_get(:@delayed_jobs) ||
119
+ (klass.respond_to?(:delayed_jobs) and klass.delayed_jobs)
120
+ end
121
+
122
+ def queue_allows_delayed(queue)
123
+ queue = namespace_queue(queue)
124
+ @delay_allowed.include?(queue.to_sym) || @delay_allowed.include?(queue.to_s)
125
+ end
126
+
127
+ def enable_delay(queue)
128
+ queue = namespace_queue(queue)
129
+ unless queue_allows_delayed queue
130
+ @delay_allowed << queue
131
+ mongo_stats.update({:stat => 'Delayable Queues'}, { '$addToSet' => { 'value' => queue}}, { :upsert => true})
132
+ end
133
+ end
134
+
135
+ # If 'inline' is true Resque will call #perform method inline
136
+ # without queuing it into Redis and without any Resque callbacks.
137
+ # The 'inline' is false Resque jobs will be put in queue regularly.
138
+ def inline?
139
+ @inline
140
+ end
141
+ alias_method :inline, :inline?
142
+
143
+ def inline=(inline)
144
+ @inline = inline
145
+ end
146
+
147
+ #
148
+ # queue manipulation
149
+ #
150
+
151
+ # Pushes a job onto a queue. Queue name should be a string and the
152
+ # item should be any JSON-able Ruby object.
153
+ #
154
+ # Resque works generally expect the `item` to be a hash with the following
155
+ # keys:
156
+ #
157
+ # class - The String name of the job to run.
158
+ # args - An Array of arguments to pass the job. Usually passed
159
+ # via `class.to_class.perform(*args)`.
160
+ #
161
+ # Example
162
+ #
163
+ # Resque.push('archive', :class => 'Archive', :args => [ 35, 'tar' ])
164
+ #
165
+ # Returns nothing
166
+ def push(queue, item)
167
+ queue = namespace_queue(queue)
168
+ item[:resque_enqueue_timestamp] = Time.now
169
+ mongo[queue] << item
170
+ end
171
+
172
+ # Pops a job off a queue. Queue name should be a string.
173
+ #
174
+ # Returns a Ruby object.
175
+ def pop(queue)
176
+ queue = namespace_queue(queue)
177
+ query = {}
178
+ if queue_allows_delayed queue
179
+ query['delay_until'] = { '$lt' => Time.now }
180
+ end
181
+ #sorting will result in significant performance penalties for large queues, you have been warned.
182
+ item = mongo[queue].find_and_modify(:query => query, :remove => true, :sort => [[:_id, :asc]] )
183
+ rescue Mongo::OperationFailure => e
184
+ return nil if e.message =~ /No matching object/
185
+ raise e
186
+ end
187
+
188
+ # Returns an integer representing the size of a queue.
189
+ # Queue name should be a string.
190
+ def size(queue)
191
+ queue = namespace_queue(queue)
192
+ mongo[queue].count
193
+ end
194
+
195
+ def delayed_size(queue)
196
+ queue = namespace_queue(queue)
197
+ if queue_allows_delayed queue
198
+ mongo[queue].find({'delay_until' => { '$gt' => Time.now }}).count
199
+ else
200
+ mongo[queue].count
201
+ end
202
+ end
203
+
204
+ def ready_size(queue)
205
+ queue = namespace_queue(queue)
206
+ if queue_allows_delayed queue
207
+ mongo[queue].find({'delay_until' => { '$lt' => Time.now }}).count
208
+ else
209
+ mongo[queue].count
210
+ end
211
+ end
212
+
213
+
214
+ # Returns an array of items currently queued. Queue name should be
215
+ # a string.
216
+ #
217
+ # start and count should be integer and can be used for pagination.
218
+ # start is the item to begin, count is how many items to return.
219
+ #
220
+ # To get the 3rd page of a 30 item, paginatied list one would use:
221
+ # Resque.peek('my_list', 59, 30)
222
+ def peek(queue, start = 0, count = 1, mode = :ready)
223
+ list_range(queue, start, count, mode)
224
+ end
225
+
226
+ # Does the dirty work of fetching a range of items from a Redis list
227
+ # and converting them into Ruby objects.
228
+ def list_range(key, start = 0, count = 1, mode = :ready)
229
+ query = { }
230
+ sort = []
231
+ if queue_allows_delayed(key)
232
+ if mode == :ready
233
+ query['delay_until'] = { '$not' => { '$gt' => Time.new}}
234
+ elsif mode == :delayed
235
+ query['delay_until'] = { '$gt' => Time.new}
236
+ elsif mode == :delayed_sorted
237
+ query['delay_until'] = { '$gt' => Time.new}
238
+ sort << ['delay_until', 1]
239
+ elsif mode == :all_sorted
240
+ query = {}
241
+ sort << ['delay_until', 1]
242
+ end
243
+ end
244
+ queue = namespace_queue(key)
245
+ items = mongo[queue].find(query, { :limit => count, :skip => start, :sort => sort}).to_a.map{ |i| i}
246
+ count > 1 ? items : items.first
247
+ end
248
+
249
+ # Returns an array of all known Resque queues as strings.
250
+ def queues
251
+ mongo.collection_names.
252
+ select { |name| name =~ /resque\.queues\./ }.
253
+ collect { |name| name.split(".")[2..-1].join('.') }
254
+ end
255
+
256
+ # Returns the mongo collection for a given queue
257
+ def collection_for_queue(queue)
258
+ queue = namespace_queue(queue)
259
+ mongo[queue]
260
+ end
261
+
262
+ # Given a queue name, completely deletes the queue.
263
+ def remove_queue(queue)
264
+ queue = namespace_queue(queue)
265
+ mongo[queue].drop
266
+ end
267
+
268
+ #
269
+ # job shortcuts
270
+ #
271
+
272
+ # This method can be used to conveniently add a job to a queue.
273
+ # It assumes the class you're passing it is a real Ruby class (not
274
+ # a string or reference) which either:
275
+ #
276
+ # a) has a @queue ivar set
277
+ # b) responds to `queue`
278
+ #
279
+ # If either of those conditions are met, it will use the value obtained
280
+ # from performing one of the above operations to determine the queue.
281
+ #
282
+ # If no queue can be inferred this method will raise a `Resque::NoQueueError`
283
+ #
284
+ # This method is considered part of the `stable` API.
285
+ def enqueue(klass, *args)
286
+ Job.create(queue_from_class(klass), klass, *args)
287
+
288
+ Plugin.after_enqueue_hooks(klass).each do |hook|
289
+ klass.send(hook, *args)
290
+ end
291
+ end
292
+
293
+ # This method can be used to conveniently remove a job from a queue.
294
+ # It assumes the class you're passing it is a real Ruby class (not
295
+ # a string or reference) which either:
296
+ #
297
+ # a) has a @queue ivar set
298
+ # b) responds to `queue`
299
+ #
300
+ # If either of those conditions are met, it will use the value obtained
301
+ # from performing one of the above operations to determine the queue.
302
+ #
303
+ # If no queue can be inferred this method will raise a `Resque::NoQueueError`
304
+ #
305
+ # If no args are given, this method will dequeue *all* jobs matching
306
+ # the provided class. See `Resque::Job.destroy` for more
307
+ # information.
308
+ #
309
+ # Returns the number of jobs destroyed.
310
+ #
311
+ # Example:
312
+ #
313
+ # # Removes all jobs of class `UpdateNetworkGraph`
314
+ # Resque.dequeue(GitHub::Jobs::UpdateNetworkGraph)
315
+ #
316
+ # # Removes all jobs of class `UpdateNetworkGraph` with matching args.
317
+ # Resque.dequeue(GitHub::Jobs::UpdateNetworkGraph, 'repo:135325')
318
+ #
319
+ # This method is considered part of the `stable` API.
320
+ def dequeue(klass, *args)
321
+ Job.destroy(queue_from_class(klass), klass, *args)
322
+ end
323
+
324
+ # Given a class, try to extrapolate an appropriate queue based on a
325
+ # class instance variable or `queue` method.
326
+ def queue_from_class(klass)
327
+ klass.instance_variable_get(:@queue) ||
328
+ (klass.respond_to?(:queue) and klass.queue)
329
+ end
330
+
331
+ # This method will return a `Resque::Job` object or a non-true value
332
+ # depending on whether a job can be obtained. You should pass it the
333
+ # precise name of a queue: case matters.
334
+ #
335
+ # This method is considered part of the `stable` API.
336
+ def reserve(queue)
337
+ Job.reserve(queue)
338
+ end
339
+
340
+ # Validates if the given klass could be a valid Resque job
341
+ #
342
+ # If no queue can be inferred this method will raise a `Resque::NoQueueError`
343
+ #
344
+ # If given klass is nil this method will raise a `Resque::NoClassError`
345
+ def validate(klass, queue = nil)
346
+ queue ||= queue_from_class(klass)
347
+
348
+ if !queue
349
+ raise NoQueueError.new("Jobs must be placed onto a queue.")
350
+ end
351
+
352
+ if klass.to_s.empty?
353
+ raise NoClassError.new("Jobs must be given a class.")
354
+ end
355
+ end
356
+
357
+
358
+ #
359
+ # worker shortcuts
360
+ #
361
+
362
+ # A shortcut to Worker.all
363
+ def workers
364
+ Worker.all
365
+ end
366
+
367
+ # A shortcut to Worker.working
368
+ def working
369
+ Worker.working
370
+ end
371
+
372
+ # A shortcut to unregister_worker
373
+ # useful for command line tool
374
+ def remove_worker(worker_id)
375
+ worker = Resque::Worker.find(worker_id)
376
+ worker.unregister_worker
377
+ end
378
+
379
+ #
380
+ # stats
381
+ #
382
+
383
+ # Returns a hash, similar to redis-rb's #info, of interesting stats.
384
+ def info
385
+ return {
386
+ :pending => queues.inject(0) { |m,k| m + size(k) },
387
+ :processed => Stat[:processed],
388
+ :queues => queues.size,
389
+ :workers => workers.size.to_i,
390
+ :working => working.count,
391
+ :failed => Stat[:failed],
392
+ :servers => to_s,
393
+ :environment => ENV['RAILS_ENV'] || ENV['RACK_ENV'] || 'development'
394
+ }
395
+ end
396
+
397
+ # Returns an array of all known Resque keys in Redis. Redis' KEYS operation
398
+ # is O(N) for the keyspace, so be careful - this can be slow for big databases.
399
+ def keys
400
+ names = mongo.collection_names
401
+ end
402
+
403
+ def drop
404
+ mongo.collections.each{ |collection| collection.drop unless collection.name =~ /^system./ }
405
+ @mongo = nil
406
+ end
407
+
408
+ private
409
+ def namespace_queue(queue)
410
+ queue = queue.to_s
411
+ if queue.start_with?('resque.queues.')
412
+ queue
413
+ else
414
+ "resque.queues.#{queue}"
415
+ end
416
+ end
417
+ end
@@ -0,0 +1,2 @@
1
+ $LOAD_PATH.unshift File.expand_path("../..", __FILE__)
2
+ require 'resque/tasks'
metadata ADDED
@@ -0,0 +1,148 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: mongo-resque
3
+ version: !ruby/object:Gem::Version
4
+ prerelease:
5
+ version: 1.17.1
6
+ platform: ruby
7
+ authors:
8
+ - David Backeus
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+
13
+ date: 2011-07-19 00:00:00 Z
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: vegas
17
+ prerelease: false
18
+ requirement: &id001 !ruby/object:Gem::Requirement
19
+ none: false
20
+ requirements:
21
+ - - ~>
22
+ - !ruby/object:Gem::Version
23
+ version: 0.1.2
24
+ type: :runtime
25
+ version_requirements: *id001
26
+ - !ruby/object:Gem::Dependency
27
+ name: sinatra
28
+ prerelease: false
29
+ requirement: &id002 !ruby/object:Gem::Requirement
30
+ none: false
31
+ requirements:
32
+ - - ">="
33
+ - !ruby/object:Gem::Version
34
+ version: 0.9.2
35
+ type: :runtime
36
+ version_requirements: *id002
37
+ - !ruby/object:Gem::Dependency
38
+ name: json
39
+ prerelease: false
40
+ requirement: &id003 !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ">="
44
+ - !ruby/object:Gem::Version
45
+ version: 1.4.6
46
+ - - <
47
+ - !ruby/object:Gem::Version
48
+ version: 1.6.0
49
+ type: :runtime
50
+ version_requirements: *id003
51
+ - !ruby/object:Gem::Dependency
52
+ name: mongo
53
+ prerelease: false
54
+ requirement: &id004 !ruby/object:Gem::Requirement
55
+ none: false
56
+ requirements:
57
+ - - ~>
58
+ - !ruby/object:Gem::Version
59
+ version: 1.3.0
60
+ type: :runtime
61
+ version_requirements: *id004
62
+ description: " Resque is a Redis-backed Ruby library for creating background jobs,\n placing those jobs on multiple queues, and processing them later.\n\n Mongo-Resque is the same thing, but for mongo. It would not exist\n without the work of defunkt and ctrochalakis on github.\n"
63
+ email:
64
+ - david@streamio.se
65
+ executables:
66
+ - resque
67
+ - resque-web
68
+ extensions: []
69
+
70
+ extra_rdoc_files:
71
+ - LICENSE
72
+ - README.markdown
73
+ files:
74
+ - lib/resque/errors.rb
75
+ - lib/resque/failure/base.rb
76
+ - lib/resque/failure/hoptoad.rb
77
+ - lib/resque/failure/mongo.rb
78
+ - lib/resque/failure/multiple.rb
79
+ - lib/resque/failure/redis.rb
80
+ - lib/resque/failure.rb
81
+ - lib/resque/helpers.rb
82
+ - lib/resque/job.rb
83
+ - lib/resque/plugin.rb
84
+ - lib/resque/server/public/favicon.ico
85
+ - lib/resque/server/public/idle.png
86
+ - lib/resque/server/public/jquery-1.3.2.min.js
87
+ - lib/resque/server/public/jquery.relatize_date.js
88
+ - lib/resque/server/public/poll.png
89
+ - lib/resque/server/public/ranger.js
90
+ - lib/resque/server/public/reset.css
91
+ - lib/resque/server/public/style.css
92
+ - lib/resque/server/public/working.png
93
+ - lib/resque/server/test_helper.rb
94
+ - lib/resque/server/views/error.erb
95
+ - lib/resque/server/views/failed.erb
96
+ - lib/resque/server/views/key_sets.erb
97
+ - lib/resque/server/views/key_string.erb
98
+ - lib/resque/server/views/layout.erb
99
+ - lib/resque/server/views/next_more.erb
100
+ - lib/resque/server/views/overview.erb
101
+ - lib/resque/server/views/queues.erb
102
+ - lib/resque/server/views/stats.erb
103
+ - lib/resque/server/views/workers.erb
104
+ - lib/resque/server/views/working.erb
105
+ - lib/resque/server.rb
106
+ - lib/resque/stat.rb
107
+ - lib/resque/tasks.rb
108
+ - lib/resque/version.rb
109
+ - lib/resque/worker.rb
110
+ - lib/resque.rb
111
+ - lib/tasks/resque.rake
112
+ - bin/resque
113
+ - bin/resque-web
114
+ - docs/HOOKS.md
115
+ - docs/PLUGINS.md
116
+ - README.markdown
117
+ - LICENSE
118
+ - HISTORY.md
119
+ homepage: http://github.com/dbackeus/mongo-resque
120
+ licenses: []
121
+
122
+ post_install_message:
123
+ rdoc_options: []
124
+
125
+ require_paths:
126
+ - lib
127
+ required_ruby_version: !ruby/object:Gem::Requirement
128
+ none: false
129
+ requirements:
130
+ - - ">="
131
+ - !ruby/object:Gem::Version
132
+ version: "0"
133
+ required_rubygems_version: !ruby/object:Gem::Requirement
134
+ none: false
135
+ requirements:
136
+ - - ">="
137
+ - !ruby/object:Gem::Version
138
+ version: "0"
139
+ requirements: []
140
+
141
+ rubyforge_project:
142
+ rubygems_version: 1.7.2
143
+ signing_key:
144
+ specification_version: 3
145
+ summary: Mongo-Resque is a mongo-backed queueing system
146
+ test_files: []
147
+
148
+ has_rdoc: