resque-batched-job 1.5.0 → 1.7.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,7 +1,7 @@
1
1
  module Resque
2
2
  module Plugins
3
3
  module BatchedJob
4
- VERSION = '1.5.0'
4
+ VERSION = '1.7.0'
5
5
  end
6
6
  end
7
7
  end
@@ -46,6 +46,7 @@ module Resque
46
46
  # @param id (see Resque::Plugins::BatchedJob#after_enqueue_batch)
47
47
  def after_perform_batch(id, *args)
48
48
  if remove_batched_job(id, *args) == 0
49
+
49
50
  after_batch_hooks = Resque::Plugin.after_batch_hooks(self)
50
51
  after_batch_hooks.each do |hook|
51
52
  send(hook, id, *args)
@@ -96,6 +97,31 @@ module Resque
96
97
  after_perform_batch(id, *args)
97
98
  end
98
99
 
100
+ # Build a collection of Resque::Job objects that represent each job in a
101
+ # batch of the same class.
102
+ #
103
+ # @param id (see Resque::Plugins::BatchedJob#remove_batched_job)
104
+ # @returns [Array] Collection of Resque::Job objects
105
+ def batched_jobs(id)
106
+ bid = batch(id)
107
+ regexp = /\A\{\"class\":\"#{self.name}\",\"args\":\[/
108
+ redis.lrange(bid, 0, redis.llen(bid)-1).grep(regexp).map do |string|
109
+ payload = decode(string)
110
+ payload['args'].unshift(id)
111
+ Resque::Job.new(@queue, payload)
112
+ end
113
+ end
114
+
115
+ # Collect and recreate all jobs for the given batch.
116
+ #
117
+ # @param id (see Resque::Plugins::BatchedJob#remove_batched_job)
118
+ # @returns [Integer] Number of jobs recreated.
119
+ def recreate_batched_jobs(id)
120
+ batched_jobs(id).each do |job|
121
+ job.recreate
122
+ end.size
123
+ end
124
+
99
125
  private
100
126
 
101
127
  # Lock a batch key before executing Redis commands. This will ensure
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: resque-batched-job
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.5.0
4
+ version: 1.7.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,11 +9,11 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-03-21 00:00:00.000000000 Z
12
+ date: 2012-08-16 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: resque
16
- requirement: &70272544091880 !ruby/object:Gem::Requirement
16
+ requirement: &70188523453360 !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ! '>='
@@ -21,10 +21,10 @@ dependencies:
21
21
  version: 1.10.0
22
22
  type: :runtime
23
23
  prerelease: false
24
- version_requirements: *70272544091880
24
+ version_requirements: *70188523453360
25
25
  - !ruby/object:Gem::Dependency
26
26
  name: turn
27
- requirement: &70272544091120 !ruby/object:Gem::Requirement
27
+ requirement: &70188523451760 !ruby/object:Gem::Requirement
28
28
  none: false
29
29
  requirements:
30
30
  - - ! '>='
@@ -32,7 +32,18 @@ dependencies:
32
32
  version: '0'
33
33
  type: :development
34
34
  prerelease: false
35
- version_requirements: *70272544091120
35
+ version_requirements: *70188523451760
36
+ - !ruby/object:Gem::Dependency
37
+ name: minitest
38
+ requirement: &70188523449820 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ! '>='
42
+ - !ruby/object:Gem::Version
43
+ version: '0'
44
+ type: :development
45
+ prerelease: false
46
+ version_requirements: *70188523449820
36
47
  description: ! " Resque plugin for batching jobs. When a batch/group of jobs are
37
48
  complete, \nadditional work can be performed usings batch hooks.\n"
38
49
  email: dan@dj-agiledev.com
@@ -41,16 +52,10 @@ extensions: []
41
52
  extra_rdoc_files: []
42
53
  files:
43
54
  - LICENSE
44
- - Rakefile
45
- - README.md
55
+ - lib/resque-batched-job.rb
46
56
  - lib/resque/batched_job.rb
47
- - lib/resque/plugins/batched_job/support.rb
48
- - lib/resque/plugins/batched_job/tasks.rb
49
- - lib/resque/plugins/batched_job/version.rb
50
57
  - lib/resque/plugins/batched_job.rb
51
- - lib/resque-batched-job.rb
52
- - test/test_batched_job.rb
53
- - test/test_helper.rb
58
+ - lib/resque/plugins/batched_job/version.rb
54
59
  homepage: https://github.com/drfeelngood/resque-batched-job
55
60
  licenses: []
56
61
  post_install_message:
@@ -71,11 +76,9 @@ required_rubygems_version: !ruby/object:Gem::Requirement
71
76
  version: '0'
72
77
  requirements: []
73
78
  rubyforge_project:
74
- rubygems_version: 1.8.10
79
+ rubygems_version: 1.8.16
75
80
  signing_key:
76
81
  specification_version: 3
77
82
  summary: Resque plugin
78
- test_files:
79
- - test/test_batched_job.rb
80
- - test/test_helper.rb
83
+ test_files: []
81
84
  has_rdoc:
data/README.md DELETED
@@ -1,33 +0,0 @@
1
- # Resque Batched Job [![Build Status](https://secure.travis-ci.org/drfeelngood/resque-batched-job.png)](http://travis-ci.org/drfeelngood/resque-batched-job)
2
-
3
- A [Resque](http://github.com/defunkt/resque) plugin. Requires Resque >= 1.10.0
4
-
5
- This plugin adds the ability to batch jobs and run additional hooks after the
6
- last job in a batch is performed. Using the '*after_enqueue*' hook, the job
7
- is encoded and stored in a Redis List identified by the batch id provided. By default,
8
- the batch keys look like '*batch:#{id}*'. After each job is performed, it's removed
9
- from the batch list. If the last job performed happens to be the last in the list,
10
- additional hooks are executed. These hooks are prefixed with '*after_batch*'.
11
-
12
- ## Installation
13
-
14
- $ gem install resque-batched-job
15
-
16
- ## Example
17
-
18
- ```ruby
19
- require 'resque/batched_job'
20
-
21
- module Job
22
- extend Resque::Plugins::BatchedJob
23
-
24
- def self.perform(bid, *args)
25
- prime(bid, args)
26
- end
27
-
28
- def self.after_batch_heavy_lifting(bid, *args)
29
- heavy_lifting(bid)
30
- end
31
-
32
- end
33
- ```
data/Rakefile DELETED
@@ -1,52 +0,0 @@
1
- require 'rubygems'
2
- require 'rake'
3
- require 'rake/testtask'
4
-
5
- require File.dirname(__FILE__) + '/lib/resque/plugins/batched_job/version'
6
-
7
- task :default => :test
8
-
9
- Rake::TestTask.new do |task|
10
- task.pattern = 'test/test_*.rb'
11
- task.verbose = true
12
- end
13
-
14
- begin
15
- require 'yard'
16
- YARD::Rake::YardocTask.new do |t|
17
- t.files = ['lib/**/*.rb', '-', 'README.md']
18
- end
19
- rescue LoadError
20
- end
21
-
22
- desc "Publish RubyGem and source."
23
- task :publish => [:build, :tag] do
24
- sh "git push origin v#{Resque::Plugins::BatchedJob::VERSION}"
25
- sh "git push origin master"
26
- sh "gem push resque-batched-job-#{Resque::Plugins::BatchedJob::VERSION}.gem"
27
- end
28
-
29
- desc "Tag project with current version."
30
- task :tag do
31
- sh "git tag v#{Resque::Plugins::BatchedJob::VERSION}"
32
- end
33
-
34
- desc "Build resque-batched-job RubyGem."
35
- task :build do
36
- sh "gem build resque-batched-job.gemspec"
37
- end
38
-
39
- desc "Install current resque-batched-job RubyGem."
40
- task :install => :build do
41
- sh "gem install --local resque-batched-job-#{Resque::Plugins::BatchedJob::VERSION}.gem"
42
- end
43
-
44
- desc "View changelog"
45
- task :changelog do
46
- tags = `git tag`.split("\n").reverse
47
-
48
- tags.each_slice(2) do |tags|
49
- puts "========== #{tags[1]}..#{tags[0]} =========="
50
- `git log --pretty=format:'%h : %s' --graph #{tags[1]}..#{tags[0]}`
51
- end
52
- end
@@ -1,72 +0,0 @@
1
- module Resque
2
- module Plugins
3
- module BatchedJobSupport
4
-
5
- # Checks the size of the batched job list and returns true if the list is
6
- # empty or if the key does not exist.
7
- #
8
- # @param batch_id (see Resque::Plugins::BatchedJob#batch)
9
- def batch_complete?(batch_id)
10
- mutex(batch_id) do |bid|
11
- redis.llen(bid) == 0
12
- end
13
- end
14
-
15
- # Check to see if the Redis key exists.
16
- #
17
- # @param batch_id (see Resque::Plugins::BatchedJob#batch)
18
- def batch_exist?(batch_id)
19
- mutex(batch_id) do |bid|
20
- redis.exists(bid)
21
- end
22
- end
23
-
24
- private
25
-
26
- # Lock a batch key before executing Redis commands. This will ensure
27
- # no race conditions occur when modifying batch information. Here is
28
- # an example of how this works. See http://redis.io/commands/setnx for
29
- # more information. (fixes #4) (closes #5)
30
- #
31
- # * Job2 sends SETNX batch:123:lock in order to aquire a lock.
32
- # * Job1 still has the key locked, so Job2 continues into the loop.
33
- # * Job2 sends GET to aquire the lock timestamp.
34
- # * If the timestamp does not exist (Job1 released the lock), Job2
35
- # attemps to start from the beginning again.
36
- # * If the timestamp exists and has not expired, Job2 sleeps for a
37
- # moment and then retries from the start.
38
- # * If the timestamp exists and has expired, Job2 sends GETSET to aquire
39
- # a lock. This returns the previous value of the lock.
40
- # * If the previous timestamp has not expired, another process was faster
41
- # and aquired the lock. This means Job2 has to start from the beginnig.
42
- # * If the previous timestamp is still expired the lock has been set and
43
- # processing can continue safely
44
- #
45
- # @param id (see Resque::Plugins::BatchedJob#batch)
46
- # @yield [bid] Yields the current batch id.
47
- # @yieldparam [String] The current batch id.
48
- def mutex(id, &block)
49
- is_expired = lambda do |locked_at|
50
- locked_at.to_f < Time.now.to_f
51
- end
52
- bid = batch(id)
53
- _key_ = "#{bid}:lock"
54
-
55
- until redis.setnx(_key_, Time.now.to_f + 0.5)
56
- next unless timestamp = redis.get(_key_)
57
-
58
- unless is_expired.call(timestamp)
59
- sleep(0.1)
60
- next
61
- end
62
-
63
- break unless timestamp = redis.getset(_key_, Time.now.to_f + 0.5)
64
- break if is_expired.call(timestamp)
65
- end
66
- yield(bid)
67
- ensure
68
- redis.del(_key_)
69
- end
70
- end
71
- end
72
- end
@@ -1,6 +0,0 @@
1
- namespace :resque do
2
-
3
-
4
- end
5
-
6
-
@@ -1,185 +0,0 @@
1
- require File.dirname(__FILE__) + '/test_helper'
2
-
3
- class BatchedJobTest < Test::Unit::TestCase
4
-
5
- def setup
6
- $batch_complete = false
7
- @batch_id = :foo
8
- @batch = "batch:#{@batch_id}"
9
- end
10
-
11
- def teardown
12
- redis.del(@batch)
13
- redis.del("queue:test")
14
- redis.del("#{@batch}:lock")
15
- end
16
-
17
- def test_list
18
- assert_nothing_raised do
19
- Resque::Plugin.lint(Resque::Plugins::BatchedJob)
20
- end
21
- end
22
-
23
- def test_encoding
24
- Resque.enqueue(Job, @batch_id, 123)
25
- Resque.enqueue(JobWithoutArgs, @batch_id)
26
-
27
- assert_equal("{\"class\":\"Job\",\"args\":[123]}", redis.lindex(@batch, 0))
28
- assert_equal("{\"class\":\"JobWithoutArgs\",\"args\":[]}", redis.lindex(@batch, 1))
29
- end
30
-
31
- def test_batch_key
32
- assert_nothing_raised do
33
- Resque.enqueue(Job, @batch_id, 'foobar')
34
- end
35
- assert_equal(@batch, Job.batch(@batch_id))
36
- end
37
-
38
- # Ensure the length of the Redis list matches the number of jobs we enqueue.
39
- def test_batch_size
40
- assert_nothing_raised do
41
- 5.times { Resque.enqueue(Job, @batch_id, "arg#{rand(100)}") }
42
- end
43
- assert_equal(5, redis.llen(@batch))
44
- end
45
-
46
- # Make sure the after_batch hook is fired
47
- def test_batch_hooks
48
- assert_equal(false, Job.batch_exist?(@batch_id))
49
-
50
- assert_nothing_raised do
51
- 5.times { Resque.enqueue(Job, @batch_id, "arg#{rand(100)}") }
52
- end
53
-
54
- assert_equal(false, $batch_complete)
55
- assert_equal(false, Job.batch_complete?(@batch_id))
56
- assert(Job.batch_exist?(@batch_id))
57
-
58
- assert_nothing_raised do
59
- 4.times { Resque.reserve(:test).perform }
60
- end
61
-
62
- assert_equal(false, $batch_complete)
63
- assert_equal(false, Job.batch_complete?(@batch_id))
64
- assert(Job.batch_exist?(@batch_id))
65
-
66
- assert_nothing_raised do
67
- Resque.reserve(:test).perform
68
- end
69
-
70
- assert($batch_complete)
71
- assert(Job.batch_complete?(@batch_id))
72
- assert_equal(false, Job.batch_exist?(@batch_id))
73
- end
74
-
75
- # Test that jobs with identical args behave properly.
76
- def test_duplicate_args
77
- assert_equal(false, Job.batch_exist?(@batch_id))
78
-
79
- assert_nothing_raised do
80
- 5.times { Resque.enqueue(JobWithoutArgs, @batch_id) }
81
- end
82
-
83
- assert_equal(false, $batch_complete)
84
- assert_equal(false, Job.batch_complete?(@batch_id))
85
- assert(Job.batch_exist?(@batch_id))
86
-
87
- assert_nothing_raised do
88
- 2.times { Resque.reserve(:test).perform }
89
- end
90
-
91
- assert_equal(false, $batch_complete)
92
- assert_equal(false, Job.batch_complete?(@batch_id))
93
- assert(Job.batch_exist?(@batch_id))
94
-
95
- assert_nothing_raised do
96
- 3.times { Resque.reserve(:test).perform }
97
- end
98
-
99
- assert($batch_complete)
100
- assert(Job.batch_complete?(@batch_id))
101
- assert_equal(false, Job.batch_exist?(@batch_id))
102
- end
103
-
104
- # Make sure the block is executed and the lock is removed.
105
- def test_mutex
106
- Job.send(:mutex, @batch_id) do
107
- assert(true)
108
- end
109
- assert_equal(false, redis.exists("#{@batch}:lock"))
110
- end
111
-
112
- # Make sure no race conditions occur.
113
- def test_locking
114
- threads = []
115
- x, y = 10, 5
116
-
117
- x.times do
118
- threads << Thread.new do
119
- y.times do
120
- Job.send(:mutex, @batch_id) do
121
- redis.incr(@batch)
122
- end
123
- end
124
- end
125
- end
126
- threads.each { |t| t.join }
127
-
128
- assert_equal(x * y, Integer(redis.get(@batch)))
129
- end
130
-
131
- def test_remove_batched_job
132
- Resque.enqueue(JobWithoutArgs, @batch_id)
133
- Resque.enqueue(JobWithoutArgs, @batch_id)
134
- assert_equal(1, JobWithoutArgs.remove_batched_job(@batch_id))
135
- assert_equal(0, JobWithoutArgs.remove_batched_job(@batch_id))
136
-
137
- Resque.enqueue(JobWithoutArgs, @batch_id)
138
-
139
- assert_nothing_raised do
140
- JobWithoutArgs.remove_batched_job(@batch_id)
141
- end
142
- assert(Job.batch_complete?(@batch_id))
143
- assert_equal(false, Job.batch_exist?(@batch_id))
144
- assert_equal(false, $batch_complete)
145
-
146
- Resque.enqueue(JobWithoutArgs, @batch_id)
147
-
148
- assert_nothing_raised do
149
- JobWithoutArgs.remove_batched_job!(@batch_id)
150
- end
151
- assert($batch_complete)
152
- assert(Job.batch_complete?(@batch_id))
153
- assert_equal(false, Job.batch_exist?(@batch_id))
154
- end
155
-
156
- def test_enqueue_batched_job
157
- Resque.enqueue_batched_job(JobWithoutArgs, @batch_id)
158
- assert(Job.batch_exist?(@batch_id))
159
- end
160
-
161
- def test_dequeue
162
- assert_nothing_raised do
163
- 2.times do
164
- Resque.enqueue(JobWithoutArgs, @batch_id)
165
- Resque.enqueue(Job, @batch_id, "foo")
166
- end
167
- end
168
-
169
- Resque.dequeue(JobWithoutArgs, @batch_id)
170
- assert_equal(3, redis.llen(@batch))
171
- Resque.dequeue(Job, @batch_id, "foo")
172
- assert_equal(2, redis.llen(@batch))
173
- Resque.dequeue(JobWithoutArgs, @batch_id)
174
- Resque.dequeue(Job, @batch_id, "foo")
175
- assert(Job.batch_complete?(@batch_id))
176
- assert_equal(false, Job.batch_exist?(@batch_id))
177
- end
178
-
179
- private
180
-
181
- def redis
182
- Resque.redis
183
- end
184
-
185
- end
data/test/test_helper.rb DELETED
@@ -1,34 +0,0 @@
1
- require 'rubygems'
2
- require 'test/unit'
3
- require 'thread'
4
- require 'turn/autorun'
5
- require 'resque'
6
-
7
- $:.unshift(File.expand_path(File.dirname(__FILE__)) + '/../lib')
8
- require 'resque/batched_job'
9
-
10
- class Job
11
- extend Resque::Plugins::BatchedJob
12
- @queue = :test
13
-
14
- def self.perform(batch_id, arg)
15
- end
16
-
17
- def self.after_batch_hook(batch_id, arg)
18
- $batch_complete = true
19
- end
20
-
21
- end
22
-
23
- class JobWithoutArgs
24
- extend Resque::Plugins::BatchedJob
25
- @queue = :test
26
-
27
- def self.perform(id)
28
- end
29
-
30
- def self.after_batch_hook(id)
31
- $batch_complete = true
32
- end
33
-
34
- end