sidekiq-superworker 0.1.9 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +40 -23
- data/Rakefile +2 -7
- data/lib/sidekiq-superworker.rb +3 -1
- data/lib/sidekiq/superworker.rb +5 -32
- data/lib/sidekiq/superworker/dsl_hash.rb +3 -1
- data/lib/sidekiq/superworker/integrations/sidekiq_monitor.rb +27 -0
- data/lib/sidekiq/superworker/processor.rb +2 -9
- data/lib/sidekiq/superworker/server/middleware.rb +7 -9
- data/lib/sidekiq/superworker/subjob.rb +164 -0
- data/lib/sidekiq/superworker/subjob_processor.rb +1 -2
- data/lib/sidekiq/superworker/superjob_processor.rb +3 -1
- data/lib/sidekiq/superworker/version.rb +1 -1
- data/lib/sidekiq/superworker/worker.rb +7 -3
- data/lib/sidekiq/superworker/worker_class.rb +3 -13
- metadata +16 -18
- data/app/models/sidekiq/superworker/subjob.rb +0 -37
- data/app/views/sidekiq/superworker/subjobs.slim +0 -16
- data/lib/generators/sidekiq/superworker/install/install_generator.rb +0 -27
- data/lib/generators/sidekiq/superworker/install/templates/create_sidekiq_superworker_subjobs.rb +0 -26
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 53beee36ab3ca54954950636adfc11d3e5d6c8e5
|
4
|
+
data.tar.gz: 168d1b43b60f7085b1cb5f3185208d88c94effbd
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f9606cbc54a87383c448ab89ba308df8ca4713caa92920a8d9cb4365c7ede28447ba811e5eced133310b8e32b613ff5f3a56afc51e10cc284141ae2f08cdf218
|
7
|
+
data.tar.gz: f2b6beafee29aec0ed97ecf22f334697ea7c235d2886cbd3c95f668e2a8e009303b617a204082b64777fd171ee668700ac32b3cb44fc8cc16518d1466c5d6ffb
|
data/README.md
CHANGED
@@ -1,20 +1,20 @@
|
|
1
1
|
Sidekiq Superworker
|
2
2
|
===================
|
3
|
-
|
3
|
+
Define dependency graphs of Sidekiq jobs
|
4
4
|
|
5
5
|
Overview
|
6
6
|
--------
|
7
7
|
|
8
|
-
Sidekiq Superworker lets you create superworkers, which are simple or complex
|
8
|
+
Sidekiq Superworker lets you create superworkers, which are simple or complex graphs of Sidekiq workers.
|
9
9
|
|
10
|
-
For example, you can define complex
|
10
|
+
For example, you can define complex graphs of workers and use both serial and parallel worker configurations:
|
11
11
|
|
12
12
|
[![](https://raw.github.com/socialpandas/sidekiq-superworker/master/doc/diagram-complex.png)](https://raw.github.com/socialpandas/sidekiq-superworker/master/doc/diagram-complex.png)
|
13
13
|
|
14
14
|
*(Worker10 will run after Worker5, Worker7, Worker8, and Worker9 have all completed.)*
|
15
15
|
|
16
16
|
```ruby
|
17
|
-
Superworker.
|
17
|
+
Superworker.define(:MySuperworker, :user_id, :comment_id) do
|
18
18
|
Worker1 :user_id
|
19
19
|
Worker2 :user_id do
|
20
20
|
parallel do
|
@@ -41,12 +41,12 @@ And you can run it like any other worker:
|
|
41
41
|
MySuperworker.perform_async(23, 852)
|
42
42
|
```
|
43
43
|
|
44
|
-
You can also define simple serial
|
44
|
+
You can also define simple serial sequences of workers:
|
45
45
|
|
46
46
|
[![](https://raw.github.com/socialpandas/sidekiq-superworker/master/doc/diagram-simple.png)](https://raw.github.com/socialpandas/sidekiq-superworker/master/doc/diagram-simple.png)
|
47
47
|
|
48
48
|
```ruby
|
49
|
-
Superworker.
|
49
|
+
Superworker.define(:MySuperworker, :user_id, :comment_id) do
|
50
50
|
Worker1 :user_id, :comment_id
|
51
51
|
Worker2 :comment_id
|
52
52
|
Worker3 :user_id
|
@@ -60,11 +60,6 @@ Include it in your Gemfile:
|
|
60
60
|
|
61
61
|
gem 'sidekiq-superworker'
|
62
62
|
|
63
|
-
Install and run the migration:
|
64
|
-
|
65
|
-
rails g sidekiq:superworker:install
|
66
|
-
rake db:migrate
|
67
|
-
|
68
63
|
Usage
|
69
64
|
-----
|
70
65
|
|
@@ -73,12 +68,12 @@ First, define a superworker in a file that's included during the initialization
|
|
73
68
|
```ruby
|
74
69
|
# config/initializers/superworkers.rb
|
75
70
|
|
76
|
-
Superworker.
|
71
|
+
Superworker.define(:MySuperworker, :user_id, :comment_id) do
|
77
72
|
Worker1 :user_id, :comment_id
|
78
73
|
Worker2 :comment_id
|
79
74
|
end
|
80
75
|
|
81
|
-
Superworker.
|
76
|
+
Superworker.define(:MyOtherSuperworker, :comment_id) do
|
82
77
|
Worker2 :comment_id
|
83
78
|
Worker3 :comment_id
|
84
79
|
end
|
@@ -95,7 +90,7 @@ MySuperworker.perform_async(23, 852)
|
|
95
90
|
You can define any number of arguments for the superworker and pass them to different subworkers as you see fit:
|
96
91
|
|
97
92
|
```ruby
|
98
|
-
Superworker.
|
93
|
+
Superworker.define(:MySuperworker, :user_id, :comment_id) do
|
99
94
|
Worker1 :user_id, :comment_id
|
100
95
|
Worker2 :comment_id
|
101
96
|
Worker3 :user_id
|
@@ -105,7 +100,7 @@ end
|
|
105
100
|
If you want to set any static arguments for the subworkers, you can do that by using any values that are not symbols (e.g. strings, integers, etc):
|
106
101
|
|
107
102
|
```ruby
|
108
|
-
Superworker.
|
103
|
+
Superworker.define(:MySuperworker, :user_id, :comment_id) do
|
109
104
|
Worker1 100, :user_id, :comment_id
|
110
105
|
Worker2 'all'
|
111
106
|
end
|
@@ -114,7 +109,7 @@ end
|
|
114
109
|
If a subworker doesn't take any arguments, you'll need to include parentheses after it:
|
115
110
|
|
116
111
|
```ruby
|
117
|
-
Superworker.
|
112
|
+
Superworker.define(:MySuperworker, :user_id, :comment_id) do
|
118
113
|
Worker1 :user_id, :comment_id
|
119
114
|
Worker2()
|
120
115
|
end
|
@@ -125,20 +120,20 @@ end
|
|
125
120
|
To refer to a namespaced worker (e.g. `MyModule::Worker1`), replace the two colons with two underscores:
|
126
121
|
|
127
122
|
```ruby
|
128
|
-
Superworker.
|
123
|
+
Superworker.define(:MySuperworker, :user_id, :comment_id) do
|
129
124
|
MyModule__Worker1 :user_id, :comment_id
|
130
125
|
end
|
131
126
|
```
|
132
127
|
|
133
128
|
### Options
|
134
129
|
|
135
|
-
####
|
130
|
+
#### Delete subjobs after their superjob completes
|
136
131
|
|
137
|
-
When a superjob is queued, records for all of its subjobs are created. By default
|
132
|
+
When a superjob is queued, records for all of its subjobs are created. By default these records are deleted when the superjob finished. This can be changed by setting the following option to false:
|
138
133
|
|
139
134
|
```ruby
|
140
135
|
# config/initializers/superworker.rb
|
141
|
-
Sidekiq::Superworker.options[:
|
136
|
+
Sidekiq::Superworker.options[:delete_subjobs_after_superjob_completes] = false
|
142
137
|
```
|
143
138
|
|
144
139
|
### Logging
|
@@ -154,14 +149,14 @@ Sidekiq::Superworker::Logging.logger = logger
|
|
154
149
|
|
155
150
|
### Monitoring
|
156
151
|
|
157
|
-
Using [sidekiq_monitor](https://github.com/socialpandas/sidekiq_monitor) with Sidekiq Superworker is
|
152
|
+
Using [sidekiq_monitor](https://github.com/socialpandas/sidekiq_monitor) with Sidekiq Superworker is encouraged, as it lets you easily monitor when a superjob is running, when it has finished, whether it has encountered errors, and the status of all of its subjobs.
|
158
153
|
|
159
154
|
### Batch Jobs
|
160
155
|
|
161
156
|
By using a `batch` block, you can create batches of subjobs that are all associated with the superjob. The following will run Worker1 and Worker2 in serial for every user ID in the array passed to perform_async.
|
162
157
|
|
163
158
|
```ruby
|
164
|
-
Superworker.
|
159
|
+
Superworker.define(:MyBatchSuperworker, :user_ids) do
|
165
160
|
batch user_ids: :user_id do
|
166
161
|
Worker1 :user_id
|
167
162
|
Worker2 :user_id
|
@@ -174,7 +169,7 @@ MyBatchSuperworker.perform_async([30, 31, 32, 33, 34, 35])
|
|
174
169
|
You can also use multiple arguments:
|
175
170
|
|
176
171
|
```ruby
|
177
|
-
Superworker.
|
172
|
+
Superworker.define(:MyBatchSuperworker, :user_ids, :comment_ids) do
|
178
173
|
batch user_ids: :user_id, comment_ids: :comment_id do
|
179
174
|
Worker1 :user_id, :comment_id
|
180
175
|
Worker2 :user_id
|
@@ -215,6 +210,28 @@ If a subjob encounters an exception, the subjobs that depend on it won't run, bu
|
|
215
210
|
|
216
211
|
If sidekiq_monitor is being used, the exception will be bubbled up to the superjob, which lets you easily see when your superjobs have failed.
|
217
212
|
|
213
|
+
Upgrading to 1.x
|
214
|
+
----------------
|
215
|
+
|
216
|
+
If you were previously using Sidekiq Superworker 0.x and are upgrading to 1.x, there are some changes to be aware of:
|
217
|
+
|
218
|
+
### Redis replaced ActiveRecord
|
219
|
+
|
220
|
+
ActiveRecord was used as the datastore in 0.x due to application-specific requirements, but Redis is a far better choice for many reasons, especially given that Sidekiq uses Redis. When upgrading to 1.x, you'll need to let all of your superjobs complete, then upgrade to 1.x, then resume running superjobs. You can drop the 'sidekiq_superworker_subjobs' table, if you like.
|
221
|
+
|
222
|
+
### Superworker.define replaced Superworker.create
|
223
|
+
|
224
|
+
The name of the `Superworker.create` method caused confusion, as some users would call it multiple times. Since it defines a class, it's been renamed to `Superworker.define`. You'll need to replace it accordingly.
|
225
|
+
|
226
|
+
Testing
|
227
|
+
-------
|
228
|
+
|
229
|
+
Sidekiq Superworker is tested against multiple sets of gem dependencies (currently: no gems, Rails 3, and Rails 4), so please run the tests with [Appraisal](https://github.com/thoughtbot/appraisal) before submitting a PR. Thanks!
|
230
|
+
|
231
|
+
```bash
|
232
|
+
appraisal rspec
|
233
|
+
```
|
234
|
+
|
218
235
|
License
|
219
236
|
-------
|
220
237
|
|
data/Rakefile
CHANGED
@@ -2,11 +2,6 @@
|
|
2
2
|
require "bundler/gem_tasks"
|
3
3
|
require "rake/testtask"
|
4
4
|
|
5
|
-
|
5
|
+
require 'rspec/core/rake_task'
|
6
6
|
|
7
|
-
|
8
|
-
t.libs << "lib"
|
9
|
-
t.libs << "test"
|
10
|
-
t.test_files = FileList["test/**/*_test.rb"]
|
11
|
-
t.verbose = true
|
12
|
-
end
|
7
|
+
RSpec::Core::RakeTask.new('spec')
|
data/lib/sidekiq-superworker.rb
CHANGED
data/lib/sidekiq/superworker.rb
CHANGED
@@ -1,16 +1,15 @@
|
|
1
1
|
require 'sidekiq'
|
2
|
-
require 'activerecord-import'
|
3
2
|
|
4
3
|
directory = File.dirname(File.absolute_path(__FILE__))
|
5
4
|
require "#{directory}/client_ext.rb"
|
6
|
-
Dir.glob("#{directory}/superworker
|
7
|
-
Dir.glob("#{directory}
|
8
|
-
Dir.glob("#{directory}/../../app/models/sidekiq/superworker/*.rb") { |file| require file }
|
5
|
+
Dir.glob("#{directory}/superworker/*.rb") { |file| require file }
|
6
|
+
Dir.glob("#{directory}/superworker/server/*.rb") { |file| require file }
|
9
7
|
|
10
8
|
module Sidekiq
|
11
9
|
module Superworker
|
12
10
|
DEFAULTS = {
|
13
|
-
|
11
|
+
delete_subjobs_after_superjob_completes: true,
|
12
|
+
subjob_redis_prefix: 'subjob'
|
14
13
|
}
|
15
14
|
|
16
15
|
def self.options
|
@@ -29,9 +28,6 @@ module Sidekiq
|
|
29
28
|
logger.debug(message)
|
30
29
|
end
|
31
30
|
|
32
|
-
def self.table_name_prefix
|
33
|
-
'sidekiq_superworker_'
|
34
|
-
end
|
35
31
|
end
|
36
32
|
end
|
37
33
|
|
@@ -44,28 +40,5 @@ end
|
|
44
40
|
Superworker = Sidekiq::Superworker::Worker unless Object.const_defined?('Superworker')
|
45
41
|
|
46
42
|
if defined?(Sidekiq::Monitor)
|
47
|
-
|
48
|
-
# Make Cleaner ignore superjobs, as they don't exist in Redis and thus won't be synced with Sidekiq::Monitor::Job
|
49
|
-
Sidekiq::Monitor::Cleaner.add_ignored_queue(Sidekiq::Superworker::SuperjobProcessor.queue_name) if defined?(Sidekiq::Monitor)
|
50
|
-
|
51
|
-
# Add a custom view that shows the subjobs for a superjob
|
52
|
-
custom_views_directory = "#{directory}/../../app/views/sidekiq/superworker/subjobs"
|
53
|
-
Sidekiq::Monitor::CustomViews.add 'Subjobs', custom_views_directory do |job|
|
54
|
-
job.queue == Sidekiq::Superworker::SuperjobProcessor.queue_name.to_s
|
55
|
-
end
|
56
|
-
|
57
|
-
# Add a "superjob:{id}" search filter
|
58
|
-
Sidekiq::Monitor::JobsDatatable.add_search_filter({
|
59
|
-
pattern: /^superjob:([\d]+)$/,
|
60
|
-
filter: lambda do |search_term, records|
|
61
|
-
superjob_id = search_term[/^superjob:([\d]+)$/, 1]
|
62
|
-
superjob = Sidekiq::Monitor::Job.find_by_id(superjob_id)
|
63
|
-
# Return empty set
|
64
|
-
return records.where(id: nil) unless superjob
|
65
|
-
superjob_jid = superjob.jid
|
66
|
-
subjob_jids = Sidekiq::Superworker::Subjob.where(superjob_id: superjob_jid).map(&:jid).compact
|
67
|
-
records.where(jid: subjob_jids + [superjob_jid])
|
68
|
-
end
|
69
|
-
})
|
70
|
-
|
43
|
+
require "#{directory}/superworker/integrations/sidekiq_monitor"
|
71
44
|
end
|
@@ -91,7 +91,8 @@ module Sidekiq
|
|
91
91
|
subworker_class: 'batch_child',
|
92
92
|
arg_keys: iteration_keys,
|
93
93
|
arg_values: iteration_args.values,
|
94
|
-
parent_id: batch_id
|
94
|
+
parent_id: batch_id,
|
95
|
+
children_ids: []
|
95
96
|
}
|
96
97
|
@records[batch_child_id] = batch_child
|
97
98
|
|
@@ -109,6 +110,7 @@ module Sidekiq
|
|
109
110
|
@records[last_subjob_id][:next_id] = subjob_id if last_subjob_id
|
110
111
|
last_subjob_id = subjob_id
|
111
112
|
nested_hash_to_records(children, parent_id: subjob_id, scoped_args: iteration_args)
|
113
|
+
batch_child[:children_ids] << subjob_id
|
112
114
|
end
|
113
115
|
|
114
116
|
children_ids << batch_child_id
|
@@ -0,0 +1,27 @@
|
|
1
|
+
directory = File.dirname(File.absolute_path(__FILE__))
|
2
|
+
|
3
|
+
# Make Cleaner ignore superjobs, as they don't exist in Redis and thus won't be synced with Sidekiq::Monitor::Job
|
4
|
+
Sidekiq::Monitor::Cleaner.add_ignored_queue(Sidekiq::Superworker::SuperjobProcessor.queue_name) if defined?(Sidekiq::Monitor)
|
5
|
+
|
6
|
+
# Add a custom view that shows the subjobs for a superjob
|
7
|
+
custom_views_directory = "#{directory}/../../../../app/views/sidekiq/superworker/subjobs"
|
8
|
+
Sidekiq::Monitor::CustomViews.add 'Subjobs', custom_views_directory do |job|
|
9
|
+
job.queue == Sidekiq::Superworker::SuperjobProcessor.queue_name.to_s
|
10
|
+
end
|
11
|
+
|
12
|
+
# Add a "superjob:{id}" search filter
|
13
|
+
Sidekiq::Monitor::JobsDatatable.add_search_filter({
|
14
|
+
pattern: /^superjob:([\d]+)$/,
|
15
|
+
filter: lambda do |search_term, records|
|
16
|
+
superjob_id = search_term[/^superjob:([\d]+)$/, 1]
|
17
|
+
superjob = Sidekiq::Monitor::Job.find_by_id(superjob_id)
|
18
|
+
# Return empty set
|
19
|
+
return records.where(id: nil) unless superjob
|
20
|
+
superjob_jid = superjob.jid
|
21
|
+
subjob_jids = Sidekiq::Superworker::Subjob.find_by_superjob_jid(superjob_jid).map(&:jid).compact
|
22
|
+
records.where(jid: subjob_jids + [superjob_jid])
|
23
|
+
end
|
24
|
+
})
|
25
|
+
|
26
|
+
# If jobs are deleted after a completion
|
27
|
+
Sidekiq::Superworker.options[:delete_subjobs_after_superjob_completes] = false
|
@@ -11,7 +11,7 @@ module Sidekiq
|
|
11
11
|
raise "Job has nil jid: #{item}" if item['jid'].nil?
|
12
12
|
|
13
13
|
Superworker.debug "JID ##{item['jid']}: Error thrown"
|
14
|
-
subjob =
|
14
|
+
subjob = Subjob.find_by_jid(item['jid'])
|
15
15
|
SubjobProcessor.error(subjob, worker, item, exception) if subjob
|
16
16
|
end
|
17
17
|
|
@@ -21,16 +21,9 @@ module Sidekiq
|
|
21
21
|
raise "Job has nil jid: #{item}" if item['jid'].nil?
|
22
22
|
|
23
23
|
Superworker.debug "JID ##{item['jid']}: Passing job from Sidekiq to Superworker"
|
24
|
-
subjob =
|
24
|
+
subjob = Subjob.find_by_jid(item['jid'])
|
25
25
|
SubjobProcessor.complete(subjob) if subjob
|
26
26
|
end
|
27
|
-
|
28
|
-
# Note: The job may've been created outside of sidekiq-superworker, so a nil return value
|
29
|
-
# for this method isn't necessarily problematic
|
30
|
-
def find_subjob_by_jid(jid)
|
31
|
-
Superworker.debug "JID ##{jid}: Finding Subjob"
|
32
|
-
Subjob.find_by_jid(jid)
|
33
|
-
end
|
34
27
|
end
|
35
28
|
end
|
36
29
|
end
|
@@ -7,16 +7,14 @@ module Sidekiq
|
|
7
7
|
end
|
8
8
|
|
9
9
|
def call(worker, item, queue)
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
raise exception
|
16
|
-
end
|
17
|
-
@processor.complete(item)
|
18
|
-
return_value
|
10
|
+
begin
|
11
|
+
return_value = yield
|
12
|
+
rescue Exception => exception
|
13
|
+
@processor.error(worker, item, queue, exception)
|
14
|
+
raise exception
|
19
15
|
end
|
16
|
+
@processor.complete(item)
|
17
|
+
return_value
|
20
18
|
end
|
21
19
|
end
|
22
20
|
end
|
@@ -0,0 +1,164 @@
|
|
1
|
+
module Sidekiq
|
2
|
+
module Superworker
|
3
|
+
class Subjob
|
4
|
+
include ActiveModel::Validations
|
5
|
+
include ActiveModel::Naming
|
6
|
+
|
7
|
+
ATTRIBUTES = [:subjob_id, :superjob_id, :parent_id, :children_ids, :next_id, :children_ids,
|
8
|
+
:subworker_class, :superworker_class, :arg_keys, :arg_values, :status, :descendants_are_complete,
|
9
|
+
:meta]
|
10
|
+
|
11
|
+
attr_accessor *ATTRIBUTES
|
12
|
+
|
13
|
+
validates_presence_of :subjob_id, :subworker_class, :superworker_class, :status
|
14
|
+
|
15
|
+
class << self
|
16
|
+
def create(attributes={})
|
17
|
+
if attributes.is_a?(Array)
|
18
|
+
attributes.collect { |attribute| create(attribute) }
|
19
|
+
else
|
20
|
+
object = new(attributes)
|
21
|
+
object.save
|
22
|
+
object
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def find_by_jid(jid)
|
27
|
+
hash = Sidekiq.redis do |conn|
|
28
|
+
conn.hgetall("#{redis_prefix}:#{jid}")
|
29
|
+
end
|
30
|
+
return nil if hash.blank?
|
31
|
+
hash.collect do |key, value|
|
32
|
+
hash[key] = ActiveSupport::JSON.decode(value)
|
33
|
+
end
|
34
|
+
new(hash)
|
35
|
+
end
|
36
|
+
|
37
|
+
def find_by_key(key)
|
38
|
+
return nil if key.blank?
|
39
|
+
jid = key.split(':', 2).last
|
40
|
+
find_by_jid(jid)
|
41
|
+
end
|
42
|
+
|
43
|
+
def find_by_superjob_jid(jid)
|
44
|
+
keys = Sidekiq.redis do |conn|
|
45
|
+
conn.keys("#{redis_prefix}:#{jid}:*")
|
46
|
+
end
|
47
|
+
keys.collect { |key| find_by_key(key) }
|
48
|
+
end
|
49
|
+
|
50
|
+
def all
|
51
|
+
keys.collect { |key| find_by_key(key) }
|
52
|
+
end
|
53
|
+
|
54
|
+
def count
|
55
|
+
keys.length
|
56
|
+
end
|
57
|
+
|
58
|
+
def keys
|
59
|
+
Sidekiq.redis do |conn|
|
60
|
+
conn.keys("#{redis_prefix}:*")
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def delete_subjobs_for(superjob_id)
|
65
|
+
Sidekiq.redis do |conn|
|
66
|
+
key = self.jid(superjob_id, '*')
|
67
|
+
keys = conn.keys("#{redis_prefix}:#{key}")
|
68
|
+
conn.del(keys) if keys.any?
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def transaction(&block)
|
73
|
+
result = nil
|
74
|
+
Sidekiq.redis do |conn|
|
75
|
+
conn.multi do
|
76
|
+
result = yield
|
77
|
+
end
|
78
|
+
end
|
79
|
+
result
|
80
|
+
end
|
81
|
+
|
82
|
+
def jid(superjob_id, subjob_id)
|
83
|
+
"#{superjob_id}:#{subjob_id}"
|
84
|
+
end
|
85
|
+
|
86
|
+
def redis_prefix
|
87
|
+
Superworker.options[:subjob_redis_prefix]
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
def initialize(params={})
|
92
|
+
if params.present?
|
93
|
+
params.each do |attribute, value|
|
94
|
+
self.public_send("#{attribute}=", value)
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
def save
|
100
|
+
return false unless self.valid?
|
101
|
+
Sidekiq.redis do |conn|
|
102
|
+
conn.mapped_hmset(key, to_param)
|
103
|
+
end
|
104
|
+
true
|
105
|
+
end
|
106
|
+
|
107
|
+
def update_attributes(pairs = {})
|
108
|
+
pairs.each_pair { |attribute, value| send("#{attribute}=", value) }
|
109
|
+
self.save
|
110
|
+
end
|
111
|
+
|
112
|
+
def update_attribute(attribute, value)
|
113
|
+
send("#{attribute.to_s}=", value)
|
114
|
+
return false unless self.valid?
|
115
|
+
Sidekiq.redis do |conn|
|
116
|
+
conn.hset(key, attribute.to_s, value.to_json)
|
117
|
+
end
|
118
|
+
true
|
119
|
+
end
|
120
|
+
|
121
|
+
def jid
|
122
|
+
self.class.jid(superjob_id, subjob_id)
|
123
|
+
end
|
124
|
+
|
125
|
+
def key
|
126
|
+
"#{self.class.redis_prefix}:#{jid}"
|
127
|
+
end
|
128
|
+
|
129
|
+
def descendants_are_complete
|
130
|
+
@descendants_are_complete || false
|
131
|
+
end
|
132
|
+
|
133
|
+
def parent
|
134
|
+
self.class.find_by_jid(self.class.jid(superjob_id, parent_id))
|
135
|
+
end
|
136
|
+
|
137
|
+
def children
|
138
|
+
return [] if children_ids.blank?
|
139
|
+
children = children_ids.collect { |id| self.class.find_by_jid(self.class.jid(superjob_id,id)) }
|
140
|
+
children.reject(&:nil?)
|
141
|
+
end
|
142
|
+
|
143
|
+
def next
|
144
|
+
self.class.find_by_jid(self.class.jid(superjob_id,next_id))
|
145
|
+
end
|
146
|
+
|
147
|
+
def ==(other)
|
148
|
+
self.jid == other.jid
|
149
|
+
end
|
150
|
+
|
151
|
+
def to_info
|
152
|
+
"Subjob ##{jid} (#{superworker_class} > #{subworker_class})"
|
153
|
+
end
|
154
|
+
|
155
|
+
def to_param
|
156
|
+
param = {}
|
157
|
+
ATTRIBUTES.each do |attribute|
|
158
|
+
param["#{attribute.to_s}".to_sym] = send(attribute).to_json
|
159
|
+
end
|
160
|
+
param
|
161
|
+
end
|
162
|
+
end
|
163
|
+
end
|
164
|
+
end
|
@@ -38,9 +38,8 @@ module Sidekiq
|
|
38
38
|
# the job fires off. If the job started first, it could finish before the ActiveRecord update
|
39
39
|
# transaction completes, causing a race condition when finding the ActiveRecord record in
|
40
40
|
# Processor#complete.
|
41
|
-
jid =
|
41
|
+
jid = subjob.jid
|
42
42
|
subjob.update_attributes(
|
43
|
-
jid: jid,
|
44
43
|
status: 'queued'
|
45
44
|
)
|
46
45
|
enqueue_in_sidekiq(subjob, klass, jid)
|
@@ -26,13 +26,15 @@ module Sidekiq
|
|
26
26
|
end
|
27
27
|
|
28
28
|
# Enqueue the first root-level subjob
|
29
|
-
first_subjob = subjobs.
|
29
|
+
first_subjob = subjobs.find { |subjob| subjob.parent_id.nil? }
|
30
30
|
SubjobProcessor.enqueue(first_subjob)
|
31
31
|
end
|
32
32
|
|
33
33
|
def self.complete(superjob_id)
|
34
34
|
Superworker.debug "Superworker ##{superjob_id}: Complete"
|
35
35
|
|
36
|
+
Subjob.delete_subjobs_for(superjob_id) if Superworker.options[:delete_subjobs_after_superjob_completes]
|
37
|
+
|
36
38
|
# Set the superjob Sidekiq::Monitor::Job as being complete
|
37
39
|
if defined?(Sidekiq::Monitor)
|
38
40
|
job = Sidekiq::Monitor::Job.where(queue: queue_name, jid: superjob_id).first
|
@@ -1,15 +1,19 @@
|
|
1
1
|
module Sidekiq
|
2
2
|
module Superworker
|
3
3
|
class Worker
|
4
|
-
def self.
|
4
|
+
def self.define(*args, &block)
|
5
5
|
class_name = args.shift.to_sym
|
6
6
|
nested_hash = DSLParser.new.parse(block)
|
7
|
-
|
7
|
+
define_class(class_name, args, nested_hash)
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.create(*args, &block)
|
11
|
+
raise NoMethodError.new("Superworker.create has been replaced by Superworker.define.\nPlease see 'Upgrading to 1.x' at https://github.com/socialpandas/sidekiq-superworker.\nSuperworker.create called at #{caller[0]}")
|
8
12
|
end
|
9
13
|
|
10
14
|
protected
|
11
15
|
|
12
|
-
def self.
|
16
|
+
def self.define_class(class_name, arg_keys, nested_hash)
|
13
17
|
klass = Class.new(Sidekiq::Superworker::WorkerClass) do
|
14
18
|
@class_name = class_name
|
15
19
|
@arg_keys = arg_keys
|
@@ -12,6 +12,7 @@ module Sidekiq
|
|
12
12
|
options = initialize_superjob(arg_values)
|
13
13
|
subjobs = create_subjobs(arg_values, options)
|
14
14
|
SuperjobProcessor.create(@superjob_id, @class_name, arg_values, subjobs, options)
|
15
|
+
@superjob_id
|
15
16
|
end
|
16
17
|
|
17
18
|
protected
|
@@ -48,19 +49,8 @@ module Sidekiq
|
|
48
49
|
end
|
49
50
|
record
|
50
51
|
end
|
51
|
-
|
52
|
-
|
53
|
-
end
|
54
|
-
|
55
|
-
def insert_subjobs(records)
|
56
|
-
if Sidekiq::Superworker.options[:insert_method] == :multiple
|
57
|
-
subjobs = records.collect { |record| Sidekiq::Superworker::Subjob.new(record) }
|
58
|
-
Sidekiq::Superworker::Subjob.import(subjobs)
|
59
|
-
Sidekiq::Superworker::Subjob.where(superjob_id: @superjob_id).order(:subjob_id)
|
60
|
-
else
|
61
|
-
Sidekiq::Superworker::Subjob.transaction do
|
62
|
-
Sidekiq::Superworker::Subjob.create(records)
|
63
|
-
end
|
52
|
+
Sidekiq::Superworker::Subjob.transaction do
|
53
|
+
Sidekiq::Superworker::Subjob.create(records)
|
64
54
|
end
|
65
55
|
end
|
66
56
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sidekiq-superworker
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 1.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Tom Benner
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-
|
11
|
+
date: 2014-10-20 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: sidekiq
|
@@ -25,35 +25,35 @@ dependencies:
|
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: 2.1.0
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
|
-
name:
|
28
|
+
name: activesupport
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
31
|
- - '>='
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version: '
|
33
|
+
version: '3.2'
|
34
34
|
type: :runtime
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
38
|
- - '>='
|
39
39
|
- !ruby/object:Gem::Version
|
40
|
-
version: '
|
40
|
+
version: '3.2'
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
|
-
name:
|
42
|
+
name: activemodel
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
44
44
|
requirements:
|
45
|
-
- -
|
45
|
+
- - '>='
|
46
46
|
- !ruby/object:Gem::Version
|
47
47
|
version: '3.2'
|
48
|
-
type: :
|
48
|
+
type: :runtime
|
49
49
|
prerelease: false
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
51
51
|
requirements:
|
52
|
-
- -
|
52
|
+
- - '>='
|
53
53
|
- !ruby/object:Gem::Version
|
54
54
|
version: '3.2'
|
55
55
|
- !ruby/object:Gem::Dependency
|
56
|
-
name:
|
56
|
+
name: appraisal
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
58
58
|
requirements:
|
59
59
|
- - '>='
|
@@ -67,7 +67,7 @@ dependencies:
|
|
67
67
|
- !ruby/object:Gem::Version
|
68
68
|
version: '0'
|
69
69
|
- !ruby/object:Gem::Dependency
|
70
|
-
name: rspec
|
70
|
+
name: rspec
|
71
71
|
requirement: !ruby/object:Gem::Requirement
|
72
72
|
requirements:
|
73
73
|
- - ~>
|
@@ -81,7 +81,7 @@ dependencies:
|
|
81
81
|
- !ruby/object:Gem::Version
|
82
82
|
version: '2.12'
|
83
83
|
- !ruby/object:Gem::Dependency
|
84
|
-
name:
|
84
|
+
name: rake
|
85
85
|
requirement: !ruby/object:Gem::Requirement
|
86
86
|
requirements:
|
87
87
|
- - '>='
|
@@ -94,7 +94,7 @@ dependencies:
|
|
94
94
|
- - '>='
|
95
95
|
- !ruby/object:Gem::Version
|
96
96
|
version: '0'
|
97
|
-
description:
|
97
|
+
description: Define dependency graphs of Sidekiq jobs
|
98
98
|
email:
|
99
99
|
- tombenner@gmail.com
|
100
100
|
executables: []
|
@@ -104,19 +104,17 @@ files:
|
|
104
104
|
- MIT-LICENSE
|
105
105
|
- README.md
|
106
106
|
- Rakefile
|
107
|
-
- app/models/sidekiq/superworker/subjob.rb
|
108
|
-
- app/views/sidekiq/superworker/subjobs.slim
|
109
|
-
- lib/generators/sidekiq/superworker/install/install_generator.rb
|
110
|
-
- lib/generators/sidekiq/superworker/install/templates/create_sidekiq_superworker_subjobs.rb
|
111
107
|
- lib/sidekiq-superworker.rb
|
112
108
|
- lib/sidekiq/client_ext.rb
|
113
109
|
- lib/sidekiq/superworker.rb
|
114
110
|
- lib/sidekiq/superworker/dsl_evaluator.rb
|
115
111
|
- lib/sidekiq/superworker/dsl_hash.rb
|
116
112
|
- lib/sidekiq/superworker/dsl_parser.rb
|
113
|
+
- lib/sidekiq/superworker/integrations/sidekiq_monitor.rb
|
117
114
|
- lib/sidekiq/superworker/logging.rb
|
118
115
|
- lib/sidekiq/superworker/processor.rb
|
119
116
|
- lib/sidekiq/superworker/server/middleware.rb
|
117
|
+
- lib/sidekiq/superworker/subjob.rb
|
120
118
|
- lib/sidekiq/superworker/subjob_processor.rb
|
121
119
|
- lib/sidekiq/superworker/superjob_processor.rb
|
122
120
|
- lib/sidekiq/superworker/version.rb
|
@@ -145,6 +143,6 @@ rubyforge_project:
|
|
145
143
|
rubygems_version: 2.2.2
|
146
144
|
signing_key:
|
147
145
|
specification_version: 4
|
148
|
-
summary:
|
146
|
+
summary: Define dependency graphs of Sidekiq jobs
|
149
147
|
test_files: []
|
150
148
|
has_rdoc:
|
@@ -1,37 +0,0 @@
|
|
1
|
-
module Sidekiq
|
2
|
-
module Superworker
|
3
|
-
class Subjob < ActiveRecord::Base
|
4
|
-
attr_accessible :jid, :subjob_id, :superjob_id, :parent_id, :children_ids, :next_id,
|
5
|
-
:subworker_class, :superworker_class, :arg_keys, :arg_values, :status, :descendants_are_complete,
|
6
|
-
:meta if ActiveRecord::VERSION::MAJOR < 4 || ActiveRecord.constants.include?(:MassAssignmentSecurity)
|
7
|
-
|
8
|
-
serialize :arg_keys
|
9
|
-
serialize :arg_values
|
10
|
-
serialize :children_ids
|
11
|
-
serialize :meta
|
12
|
-
|
13
|
-
validates_presence_of :subjob_id, :subworker_class, :superworker_class, :status
|
14
|
-
|
15
|
-
def relatives
|
16
|
-
self.class.where(superjob_id: superjob_id)
|
17
|
-
end
|
18
|
-
|
19
|
-
def parent
|
20
|
-
return nil if parent_id.nil?
|
21
|
-
relatives.where(subjob_id: parent_id).first
|
22
|
-
end
|
23
|
-
|
24
|
-
def children
|
25
|
-
relatives.where(parent_id: subjob_id).order(:subjob_id)
|
26
|
-
end
|
27
|
-
|
28
|
-
def next
|
29
|
-
relatives.where(subjob_id: next_id).first
|
30
|
-
end
|
31
|
-
|
32
|
-
def to_info
|
33
|
-
"Subjob ##{id} (#{superworker_class} > #{subworker_class})"
|
34
|
-
end
|
35
|
-
end
|
36
|
-
end
|
37
|
-
end
|
@@ -1,16 +0,0 @@
|
|
1
|
-
- subjobs = Sidekiq::Superworker::Subjob.where(superjob_id: job.jid).where('subworker_class != ?', 'batch').order('subjob_id')
|
2
|
-
|
3
|
-
table.table.table-condensed.table-striped.table-hover
|
4
|
-
tr
|
5
|
-
th ID
|
6
|
-
th Class
|
7
|
-
th Args
|
8
|
-
th Status
|
9
|
-
th JID
|
10
|
-
- subjobs.each do |subjob|
|
11
|
-
tr
|
12
|
-
td = subjob.id
|
13
|
-
td = subjob.subworker_class
|
14
|
-
td = subjob.arg_values
|
15
|
-
td = subjob.status
|
16
|
-
td = subjob.jid
|
@@ -1,27 +0,0 @@
|
|
1
|
-
require 'rails/generators'
|
2
|
-
require 'rails/generators/base'
|
3
|
-
|
4
|
-
module Sidekiq
|
5
|
-
module Superworker
|
6
|
-
module Generators
|
7
|
-
class InstallGenerator < ::Rails::Generators::Base
|
8
|
-
include ::Rails::Generators::Migration
|
9
|
-
source_root File.expand_path('../templates', __FILE__)
|
10
|
-
desc "Install the migrations"
|
11
|
-
|
12
|
-
def self.next_migration_number(path)
|
13
|
-
unless @prev_migration_nr
|
14
|
-
@prev_migration_nr = Time.now.utc.strftime("%Y%m%d%H%M%S").to_i
|
15
|
-
else
|
16
|
-
@prev_migration_nr += 1
|
17
|
-
end
|
18
|
-
@prev_migration_nr.to_s
|
19
|
-
end
|
20
|
-
|
21
|
-
def install_migrations
|
22
|
-
migration_template "create_sidekiq_superworker_subjobs.rb", "db/migrate/create_sidekiq_superworker_subjobs.rb"
|
23
|
-
end
|
24
|
-
end
|
25
|
-
end
|
26
|
-
end
|
27
|
-
end
|
data/lib/generators/sidekiq/superworker/install/templates/create_sidekiq_superworker_subjobs.rb
DELETED
@@ -1,26 +0,0 @@
|
|
1
|
-
class CreateSidekiqSuperworkerSubjobs < ActiveRecord::Migration
|
2
|
-
def change
|
3
|
-
create_table :sidekiq_superworker_subjobs do |t|
|
4
|
-
t.string :jid
|
5
|
-
t.string :superjob_id, null: false
|
6
|
-
t.integer :subjob_id, null: false
|
7
|
-
t.integer :parent_id
|
8
|
-
t.text :children_ids
|
9
|
-
t.integer :next_id
|
10
|
-
t.string :superworker_class, null: false
|
11
|
-
t.string :subworker_class, null: false
|
12
|
-
t.text :arg_keys
|
13
|
-
t.text :arg_values
|
14
|
-
t.string :status, null: false
|
15
|
-
t.boolean :descendants_are_complete, default: false
|
16
|
-
t.text :meta
|
17
|
-
|
18
|
-
t.timestamps
|
19
|
-
end
|
20
|
-
|
21
|
-
add_index :sidekiq_superworker_subjobs, :jid
|
22
|
-
add_index :sidekiq_superworker_subjobs, :subjob_id
|
23
|
-
add_index :sidekiq_superworker_subjobs, [:superjob_id, :subjob_id]
|
24
|
-
add_index :sidekiq_superworker_subjobs, [:superjob_id, :parent_id]
|
25
|
-
end
|
26
|
-
end
|