taskinator 0.3.16 → 0.4.3
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.
- checksums.yaml +4 -4
- data/.travis.yml +4 -3
- data/CHANGELOG.md +63 -0
- data/Gemfile.lock +24 -27
- data/README.md +169 -68
- data/lib/taskinator/api.rb +14 -5
- data/lib/taskinator/definition/builder.rb +16 -7
- data/lib/taskinator/persistence.rb +3 -0
- data/lib/taskinator/task.rb +3 -3
- data/lib/taskinator/version.rb +1 -1
- data/lib/taskinator.rb +0 -1
- data/spec/support/test_flows.rb +38 -0
- data/spec/taskinator/api_spec.rb +16 -0
- data/spec/taskinator/definition/builder_spec.rb +48 -0
- data/spec/taskinator/persistence_spec.rb +21 -17
- data/spec/taskinator/task_spec.rb +32 -8
- data/taskinator.gemspec +0 -1
- metadata +3 -17
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: f1a746ba56e5b6a2f57736990eef9e83eafe8701fcbebaaddea258a010f3ee14
|
|
4
|
+
data.tar.gz: 5e17bc8e6a61b93d6df0691dc463fb178aca72957ab79eccaea7b9cd6c1af642
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 8f8cd10cc4c8f5d45d5cc781b379927bae7c0ed5c280e2470450a818a6a3132af75bc11a4c16eef6312056f984e912f3fa758bd45e493988cee37fd01ad92d87
|
|
7
|
+
data.tar.gz: f8b330d3f6e6766f6ddbc33becd3bb6d16826a021dd0ba16c3b2ec8308cdfe31caad40621fcbc542d6662c8aababaa946e369d251c82b24a213f955a942192a3
|
data/.travis.yml
CHANGED
|
@@ -1,15 +1,16 @@
|
|
|
1
|
+
os: linux
|
|
2
|
+
dist: xenial
|
|
1
3
|
language: ruby
|
|
2
|
-
sudo: false # See http://docs.travis-ci.com/user/migrating-from-legacy
|
|
3
4
|
cache: bundler
|
|
4
5
|
|
|
5
6
|
services:
|
|
6
|
-
- redis
|
|
7
|
+
- redis
|
|
7
8
|
|
|
8
9
|
rvm:
|
|
9
|
-
- 2.4.10
|
|
10
10
|
- 2.5.8
|
|
11
11
|
- 2.6.6
|
|
12
12
|
- 2.7.2
|
|
13
|
+
- 3.0.0
|
|
13
14
|
|
|
14
15
|
script: 'bundle exec rake spec'
|
|
15
16
|
|
data/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,66 @@
|
|
|
1
|
+
v0.4.3 - 14 Jan 2022
|
|
2
|
+
---
|
|
3
|
+
Add `#find_process` and `#find_task` methods to `Taskinator::Api`.
|
|
4
|
+
Bug fix to API when enumerating processes.
|
|
5
|
+
Updated dependencies.
|
|
6
|
+
|
|
7
|
+
v0.4.2 - 16 Mar 2021
|
|
8
|
+
---
|
|
9
|
+
Bug fix for process/task keys not expired upon completion.
|
|
10
|
+
|
|
11
|
+
v0.4.1 - 15 Mar 2021
|
|
12
|
+
---
|
|
13
|
+
Optimisation to exclude sub-processes which don't have any tasks.
|
|
14
|
+
Preparations for upgrade to Ruby 3 and ActiveSupport 6
|
|
15
|
+
|
|
16
|
+
v0.4.0 - 4 Mar 2021
|
|
17
|
+
---
|
|
18
|
+
Bug fix `job` tasks which have no arguments to the `perform` method.
|
|
19
|
+
Added support for having `perform` method as a class method.
|
|
20
|
+
|
|
21
|
+
v0.3.16 - 17 Feb 2021
|
|
22
|
+
---
|
|
23
|
+
Bug fix to deincrement pending counts for sequential tasks.
|
|
24
|
+
Bug fix to allow concurrent tasks to be retried (via Resque) and to complete processes.
|
|
25
|
+
|
|
26
|
+
v0.3.15 - 22 Nov 2018
|
|
27
|
+
---
|
|
28
|
+
Updated dependencies.
|
|
29
|
+
|
|
30
|
+
v0.3.14 - 13 Jul 2018
|
|
31
|
+
---
|
|
32
|
+
Updated dependencies.
|
|
33
|
+
Removed gemnasium.
|
|
34
|
+
|
|
35
|
+
v0.3.13 - 23 Sep 2017
|
|
36
|
+
---
|
|
37
|
+
Updated dependencies.
|
|
38
|
+
|
|
39
|
+
v0.3.12 - 23 Sep 2017
|
|
40
|
+
---
|
|
41
|
+
Spec fixes.
|
|
42
|
+
Updated dependencies.
|
|
43
|
+
|
|
44
|
+
v0.3.11 - 1 Nov 2016
|
|
45
|
+
---
|
|
46
|
+
Removed `redis-semaphore` gem and use INCRBY to track pending concurrent tasks instead.
|
|
47
|
+
Added instrumentation using statsd.
|
|
48
|
+
Bug fixes to key expiry logic.
|
|
49
|
+
Refactored process and task state transistions.
|
|
50
|
+
|
|
51
|
+
v0.3.10 - 1 Nov 2016
|
|
52
|
+
---
|
|
53
|
+
Added support for serializing to XML.
|
|
54
|
+
Improvements to process and task states.
|
|
55
|
+
|
|
56
|
+
v0.3.9 - 12 Sep 2016
|
|
57
|
+
---
|
|
58
|
+
Added benchmark for redis-mutex.
|
|
59
|
+
|
|
60
|
+
v0.3.7 - 18 Aug 2016
|
|
61
|
+
---
|
|
62
|
+
Bug fix to `option?` method.
|
|
63
|
+
|
|
1
64
|
v0.3.6 - 11 Nov 2015
|
|
2
65
|
---
|
|
3
66
|
Added visitor for performing clean up of completed processes/tasks.
|
data/Gemfile.lock
CHANGED
|
@@ -1,21 +1,20 @@
|
|
|
1
1
|
PATH
|
|
2
2
|
remote: .
|
|
3
3
|
specs:
|
|
4
|
-
taskinator (0.3
|
|
4
|
+
taskinator (0.4.3)
|
|
5
5
|
builder (>= 3.2.2)
|
|
6
6
|
connection_pool (>= 2.2.0)
|
|
7
7
|
globalid (~> 0.3)
|
|
8
8
|
json (>= 1.8.2)
|
|
9
9
|
redis (>= 3.2.1)
|
|
10
10
|
redis-namespace (>= 1.5.2)
|
|
11
|
-
redis-semaphore (>= 0.2.4)
|
|
12
11
|
statsd-ruby (~> 1.4.0)
|
|
13
12
|
thwait (~> 0.2)
|
|
14
13
|
|
|
15
14
|
GEM
|
|
16
15
|
remote: https://rubygems.org/
|
|
17
16
|
specs:
|
|
18
|
-
activesupport (5.2.
|
|
17
|
+
activesupport (5.2.6)
|
|
19
18
|
concurrent-ruby (~> 1.0, >= 1.0.2)
|
|
20
19
|
i18n (>= 0.7, < 2)
|
|
21
20
|
minitest (~> 5.1)
|
|
@@ -23,8 +22,8 @@ GEM
|
|
|
23
22
|
builder (3.2.4)
|
|
24
23
|
byebug (11.1.3)
|
|
25
24
|
coderay (1.1.3)
|
|
26
|
-
concurrent-ruby (1.1.
|
|
27
|
-
connection_pool (2.2.
|
|
25
|
+
concurrent-ruby (1.1.9)
|
|
26
|
+
connection_pool (2.2.5)
|
|
28
27
|
coveralls (0.8.23)
|
|
29
28
|
json (>= 1.8, < 3)
|
|
30
29
|
simplecov (~> 0.16.1)
|
|
@@ -33,38 +32,36 @@ GEM
|
|
|
33
32
|
tins (~> 1.6)
|
|
34
33
|
delayed_job (4.1.9)
|
|
35
34
|
activesupport (>= 3.0, < 6.2)
|
|
36
|
-
diff-lcs (1.
|
|
37
|
-
docile (1.
|
|
35
|
+
diff-lcs (1.5.0)
|
|
36
|
+
docile (1.4.0)
|
|
38
37
|
e2mmap (0.1.0)
|
|
39
38
|
fakeredis (0.7.0)
|
|
40
39
|
redis (>= 3.2, < 5.0)
|
|
41
|
-
globalid (0.
|
|
42
|
-
activesupport (>=
|
|
43
|
-
i18n (1.8.
|
|
40
|
+
globalid (0.6.0)
|
|
41
|
+
activesupport (>= 5.0)
|
|
42
|
+
i18n (1.8.11)
|
|
44
43
|
concurrent-ruby (~> 1.0)
|
|
45
|
-
json (2.
|
|
44
|
+
json (2.6.1)
|
|
46
45
|
method_source (1.0.0)
|
|
47
|
-
minitest (5.
|
|
48
|
-
mono_logger (1.1.
|
|
46
|
+
minitest (5.15.0)
|
|
47
|
+
mono_logger (1.1.1)
|
|
49
48
|
multi_json (1.15.0)
|
|
50
49
|
mustermann (1.1.1)
|
|
51
50
|
ruby2_keywords (~> 0.0.1)
|
|
52
|
-
pry (0.
|
|
51
|
+
pry (0.14.1)
|
|
53
52
|
coderay (~> 1.1)
|
|
54
53
|
method_source (~> 1.0)
|
|
55
|
-
pry-byebug (3.
|
|
54
|
+
pry-byebug (3.8.0)
|
|
56
55
|
byebug (~> 11.0)
|
|
57
|
-
pry (~> 0.
|
|
56
|
+
pry (~> 0.10)
|
|
58
57
|
rack (2.2.3)
|
|
59
58
|
rack-protection (2.1.0)
|
|
60
59
|
rack
|
|
61
|
-
rake (13.0.
|
|
62
|
-
redis (4.
|
|
60
|
+
rake (13.0.6)
|
|
61
|
+
redis (4.5.1)
|
|
63
62
|
redis-namespace (1.8.1)
|
|
64
63
|
redis (>= 3.0.4)
|
|
65
|
-
|
|
66
|
-
redis
|
|
67
|
-
resque (2.0.0)
|
|
64
|
+
resque (2.2.0)
|
|
68
65
|
mono_logger (~> 1.0)
|
|
69
66
|
multi_json (~> 1.0)
|
|
70
67
|
redis-namespace (~> 1.6)
|
|
@@ -90,9 +87,9 @@ GEM
|
|
|
90
87
|
rspec-sidekiq (3.1.0)
|
|
91
88
|
rspec-core (~> 3.0, >= 3.0.0)
|
|
92
89
|
sidekiq (>= 2.4.0)
|
|
93
|
-
rspec-support (3.10.
|
|
94
|
-
ruby2_keywords (0.0.
|
|
95
|
-
sidekiq (6.1
|
|
90
|
+
rspec-support (3.10.3)
|
|
91
|
+
ruby2_keywords (0.0.5)
|
|
92
|
+
sidekiq (6.3.1)
|
|
96
93
|
connection_pool (>= 2.2.2)
|
|
97
94
|
rack (~> 2.0)
|
|
98
95
|
redis (>= 4.2.0)
|
|
@@ -110,12 +107,12 @@ GEM
|
|
|
110
107
|
sync (0.5.0)
|
|
111
108
|
term-ansicolor (1.7.1)
|
|
112
109
|
tins (~> 1.0)
|
|
113
|
-
thor (1.1
|
|
110
|
+
thor (1.2.1)
|
|
114
111
|
thread_safe (0.3.6)
|
|
115
112
|
thwait (0.2.0)
|
|
116
113
|
e2mmap
|
|
117
114
|
tilt (2.0.10)
|
|
118
|
-
tins (1.
|
|
115
|
+
tins (1.31.0)
|
|
119
116
|
sync
|
|
120
117
|
tzinfo (1.2.9)
|
|
121
118
|
thread_safe (~> 0.1)
|
|
@@ -142,4 +139,4 @@ DEPENDENCIES
|
|
|
142
139
|
taskinator!
|
|
143
140
|
|
|
144
141
|
BUNDLED WITH
|
|
145
|
-
2.2.
|
|
142
|
+
2.2.14
|
data/README.md
CHANGED
|
@@ -5,21 +5,25 @@
|
|
|
5
5
|
[](https://codeclimate.com/github/virtualstaticvoid/taskinator)
|
|
6
6
|
[](https://coveralls.io/r/virtualstaticvoid/taskinator)
|
|
7
7
|
|
|
8
|
-
A simple orchestration library for running complex processes or workflows in Ruby.
|
|
9
|
-
|
|
10
|
-
for
|
|
8
|
+
A simple orchestration library for running complex processes or workflows in Ruby.
|
|
9
|
+
Processes are defined using a simple DSL, where the sequences and tasks are defined.
|
|
10
|
+
Processes can then be queued for execution. Sequences can be synchronous or asynchronous,
|
|
11
|
+
and the overall process can be monitored for completion or failure.
|
|
11
12
|
|
|
12
|
-
Processes and tasks are executed by background workers and you can use any one of the
|
|
13
|
+
Processes and tasks are executed by background workers and you can use any one of the
|
|
14
|
+
following gems:
|
|
13
15
|
|
|
14
16
|
* [resque](https://github.com/resque/resque)
|
|
15
17
|
* [sidekiq](https://github.com/mperham/sidekiq)
|
|
16
18
|
* [delayed_job](https://github.com/collectiveidea/delayed_job)
|
|
17
19
|
|
|
18
|
-
The configuration and state of each process and their respective tasks is stored using
|
|
20
|
+
The configuration and state of each process and their respective tasks is stored using
|
|
21
|
+
Redis key/values.
|
|
19
22
|
|
|
20
23
|
## Requirements
|
|
21
24
|
|
|
22
|
-
The latest MRI (2.1, 2.0) version. Other versions/VMs are untested but might work fine.
|
|
25
|
+
The latest MRI (2.1, 2.0) version. Other versions/VMs are untested but might work fine.
|
|
26
|
+
MRI 1.9 is not supported.
|
|
23
27
|
|
|
24
28
|
Redis 2.4 or greater is required.
|
|
25
29
|
|
|
@@ -68,7 +72,8 @@ module MyProcess
|
|
|
68
72
|
end
|
|
69
73
|
```
|
|
70
74
|
|
|
71
|
-
The `define_process` method optionally takes the list of expected arguments which are used
|
|
75
|
+
The `define_process` method optionally takes the list of expected arguments which are used
|
|
76
|
+
to validate the arguments supplied when creating a new process.
|
|
72
77
|
These should be specified with symbols.
|
|
73
78
|
|
|
74
79
|
```ruby
|
|
@@ -87,7 +92,8 @@ process = MyProcess.create_process Date.today, :option_1 => true
|
|
|
87
92
|
|
|
88
93
|
_NOTE:_ The current implementation performs a naive check on the count of arguments.
|
|
89
94
|
|
|
90
|
-
Next, specify the tasks with their corresponding implementation methods, that make up the
|
|
95
|
+
Next, specify the tasks with their corresponding implementation methods, that make up the
|
|
96
|
+
process, using the `task` method and providing the `method` to execute for the task.
|
|
91
97
|
|
|
92
98
|
```ruby
|
|
93
99
|
module MyProcess
|
|
@@ -108,7 +114,8 @@ module MyProcess
|
|
|
108
114
|
end
|
|
109
115
|
```
|
|
110
116
|
|
|
111
|
-
More complex processes may define sequential or concurrent steps, using the `sequential`
|
|
117
|
+
More complex processes may define sequential or concurrent steps, using the `sequential`
|
|
118
|
+
and `concurrent` methods respectively.
|
|
112
119
|
|
|
113
120
|
```ruby
|
|
114
121
|
module MyProcess
|
|
@@ -141,10 +148,15 @@ module MyProcess
|
|
|
141
148
|
end
|
|
142
149
|
```
|
|
143
150
|
|
|
144
|
-
It is likely that you already have worker classes for one of the queueing libraries,
|
|
151
|
+
It is likely that you already have worker classes for one of the queueing libraries,
|
|
152
|
+
such as resque or delayed_job, and wish to reuse them for executing them in the sequence
|
|
153
|
+
defined by the process definition.
|
|
145
154
|
|
|
146
|
-
Define a `job` step, providing the class of the worker, and then taskinator will execute
|
|
147
|
-
|
|
155
|
+
Define a `job` step, providing the class of the worker, and then taskinator will execute
|
|
156
|
+
that worker as part of the process definition.
|
|
157
|
+
|
|
158
|
+
The `job` step will be queued and executed on same queue as configured by `delayed_job`, or
|
|
159
|
+
that of the worker for `resque` and `sidekiq`.
|
|
148
160
|
|
|
149
161
|
```ruby
|
|
150
162
|
# E.g. A resque worker
|
|
@@ -168,8 +180,11 @@ module MyProcess
|
|
|
168
180
|
end
|
|
169
181
|
```
|
|
170
182
|
|
|
171
|
-
You can also define data driven tasks using the `for_each` method, which takes an iterator method
|
|
172
|
-
|
|
183
|
+
You can also define data driven tasks using the `for_each` method, which takes an iterator method
|
|
184
|
+
name as an argument.
|
|
185
|
+
|
|
186
|
+
The iterator method yields the parameters necessary for the task or job. Notice that the task
|
|
187
|
+
method takes a parameter in this case, which will be the return values provided by the iterator.
|
|
173
188
|
|
|
174
189
|
```ruby
|
|
175
190
|
module MyProcess
|
|
@@ -192,8 +207,9 @@ module MyProcess
|
|
|
192
207
|
end
|
|
193
208
|
```
|
|
194
209
|
|
|
195
|
-
It is possible to branch the process logic based on the options hash passed in when creating
|
|
196
|
-
The `options?` method takes the options key as an argument and calls the supplied
|
|
210
|
+
It is possible to branch the process logic based on the options hash passed in when creating
|
|
211
|
+
a process. The `options?` method takes the options key as an argument and calls the supplied
|
|
212
|
+
block if the option is present and it's value is _truthy_.
|
|
197
213
|
|
|
198
214
|
```ruby
|
|
199
215
|
module MyProcess
|
|
@@ -227,8 +243,11 @@ process2 = MyProcess.create_process
|
|
|
227
243
|
process2.tasks.count #=> 1
|
|
228
244
|
```
|
|
229
245
|
|
|
230
|
-
In addition, it is possible to transform the arguments used by a task or job, by including
|
|
231
|
-
|
|
246
|
+
In addition, it is possible to transform the arguments used by a task or job, by including
|
|
247
|
+
a `transform` step in the definition.
|
|
248
|
+
|
|
249
|
+
Similarly for the `for_each` method, `transform` takes a method name as an argument.
|
|
250
|
+
The transformer method must yield the new arguments as required.
|
|
232
251
|
|
|
233
252
|
```ruby
|
|
234
253
|
module MyProcess
|
|
@@ -273,7 +292,8 @@ module MyProcess
|
|
|
273
292
|
end
|
|
274
293
|
```
|
|
275
294
|
|
|
276
|
-
Any combination or nesting of `task`, `sequential`, `concurrent` and `for_each` steps are
|
|
295
|
+
Any combination or nesting of `task`, `sequential`, `concurrent` and `for_each` steps are
|
|
296
|
+
possible. E.g.
|
|
277
297
|
|
|
278
298
|
```ruby
|
|
279
299
|
module MyProcess
|
|
@@ -306,13 +326,17 @@ module MyProcess
|
|
|
306
326
|
end
|
|
307
327
|
```
|
|
308
328
|
|
|
309
|
-
In this example, the `work_step_begin` is executed, followed by the `work_step_all_at_once`
|
|
310
|
-
the sub process `MySubProcess` is created and
|
|
329
|
+
In this example, the `work_step_begin` is executed, followed by the `work_step_all_at_once`
|
|
330
|
+
steps which are executed concurrently, then the sub process `MySubProcess` is created and
|
|
331
|
+
executed, followed by the `work_step_one_by_one` tasks which are executed sequentially and
|
|
311
332
|
finally the `work_step_end` is executed.
|
|
312
333
|
|
|
313
|
-
It is also possible to embed conditional logic within the process definition stages in
|
|
314
|
-
|
|
315
|
-
|
|
334
|
+
It is also possible to embed conditional logic within the process definition stages in
|
|
335
|
+
order to produce steps based on the required logic.
|
|
336
|
+
|
|
337
|
+
All builder methods are available within the scope of the `define_process` block. These
|
|
338
|
+
methods include `args` and `options` which are passed into the `create_process` method
|
|
339
|
+
of the definition.
|
|
316
340
|
|
|
317
341
|
E.g.
|
|
318
342
|
|
|
@@ -332,7 +356,8 @@ module MyProcess
|
|
|
332
356
|
end
|
|
333
357
|
|
|
334
358
|
# when creating this proces, you supply to option when calling `create_process`
|
|
335
|
-
# in this example, 'args' will be an array [1,2,3]
|
|
359
|
+
# in this example, 'args' will be an array [1,2,3]
|
|
360
|
+
# and options will be a Hash {:send_notification => true}
|
|
336
361
|
MyProcess.create_process(1, 2, 3, :send_notification => true)
|
|
337
362
|
|
|
338
363
|
```
|
|
@@ -364,8 +389,12 @@ To best understand how arguments are handled, you need to break it down into 3 p
|
|
|
364
389
|
* Creation and
|
|
365
390
|
* Execution
|
|
366
391
|
|
|
367
|
-
Firstly, a process definition is declarative in that the `define_process` and a mix of
|
|
368
|
-
|
|
392
|
+
Firstly, a process definition is declarative in that the `define_process` and a mix of
|
|
393
|
+
`sequential`, `concurrent`, `for_each`, `task` and `job` directives provide the way to
|
|
394
|
+
specify the sequencing of the steps for the process.
|
|
395
|
+
|
|
396
|
+
Taskinator will interprete this definition and execute each step in the desired sequence
|
|
397
|
+
or concurrency.
|
|
369
398
|
|
|
370
399
|
Consider the following process definition:
|
|
371
400
|
|
|
@@ -411,11 +440,17 @@ end
|
|
|
411
440
|
|
|
412
441
|
There are three tasks; namely `:work_step_1`, `:work_step_2` and `:work_step_3`.
|
|
413
442
|
|
|
414
|
-
The third task, `:work_step_3`, is built up using the `for_each` iterator, which means that
|
|
443
|
+
The third task, `:work_step_3`, is built up using the `for_each` iterator, which means that
|
|
444
|
+
the number of `:work_step_3` tasks will depend on how many times the `additional_step`
|
|
445
|
+
iterator method yields to the definition.
|
|
446
|
+
|
|
447
|
+
This brings us to the creation part. When `create_process` is called on the given module,
|
|
448
|
+
you provide arguments to it, which will get passed onto the respective `task` and
|
|
449
|
+
`for_each` iterator methods.
|
|
415
450
|
|
|
416
|
-
|
|
451
|
+
So, considering the `MySimpleProcess` module shown above, `work_step_1`, `work_step_2`
|
|
452
|
+
and `work_step_3` methods each expect arguments.
|
|
417
453
|
|
|
418
|
-
So, considering the `MySimpleProcess` module shown above, `work_step_1`, `work_step_2` and `work_step_3` methods each expect arguments.
|
|
419
454
|
These will ultimately come from the arguments passed into the `create_process` method.
|
|
420
455
|
|
|
421
456
|
E.g.
|
|
@@ -438,7 +473,8 @@ process = MySimpleProcess.create_process(options)
|
|
|
438
473
|
|
|
439
474
|
```
|
|
440
475
|
|
|
441
|
-
To best understand how the process is created, consider the following "procedural" code
|
|
476
|
+
To best understand how the process is created, consider the following "procedural" code
|
|
477
|
+
for how it could work.
|
|
442
478
|
|
|
443
479
|
```ruby
|
|
444
480
|
# A process, which maps the target and a list of steps
|
|
@@ -535,11 +571,17 @@ process.execute
|
|
|
535
571
|
|
|
536
572
|
```
|
|
537
573
|
|
|
538
|
-
In reality, each task is executed by a worker process, possibly on another host, so the
|
|
574
|
+
In reality, each task is executed by a worker process, possibly on another host, so the
|
|
575
|
+
execution process isn't as simple, but this example should help you to understand
|
|
576
|
+
conceptually how the process is executed, and how the arguments are propagated through.
|
|
539
577
|
|
|
540
578
|
### Monitoring
|
|
541
579
|
|
|
542
|
-
|
|
580
|
+
NOTE: This aspect of the library is still a work in progress.
|
|
581
|
+
|
|
582
|
+
#### Processes
|
|
583
|
+
|
|
584
|
+
To monitor the state of the processes, use the `Taskinator::Api::Processes` class.
|
|
543
585
|
|
|
544
586
|
```ruby
|
|
545
587
|
processes = Taskinator::Api::Processes.new
|
|
@@ -549,11 +591,57 @@ processes.each do |process|
|
|
|
549
591
|
end
|
|
550
592
|
```
|
|
551
593
|
|
|
594
|
+
#### Debugging
|
|
595
|
+
|
|
596
|
+
To aid debugging specific processes and tasks, where the process or task identifier is
|
|
597
|
+
known, it is possible to retrieve the specific task or process using `Taskinator::Api`.
|
|
598
|
+
|
|
599
|
+
To retrieve a specific process, given the process identifier:
|
|
600
|
+
|
|
601
|
+
```ruby
|
|
602
|
+
process_id = "SUPPLY-PROCESS-IDENTIFIER"
|
|
603
|
+
process = Taskinator::Api.find_process(process_id)
|
|
604
|
+
|
|
605
|
+
puts process.inspect
|
|
606
|
+
puts process.current_state
|
|
607
|
+
puts process.tasks
|
|
608
|
+
# etc...
|
|
609
|
+
```
|
|
610
|
+
|
|
611
|
+
The type of process may be one of the following:
|
|
612
|
+
|
|
613
|
+
* `Taskinator::Process::Sequential`
|
|
614
|
+
* `Taskinator::Process::Concurrent`
|
|
615
|
+
|
|
616
|
+
Then, to retrieve a specific task, given the task identifier:
|
|
617
|
+
|
|
618
|
+
```ruby
|
|
619
|
+
task_id = "SUPPLY-TASK-IDENTIFIER"
|
|
620
|
+
task = Taskinator::Api.find_task(task_id)
|
|
621
|
+
|
|
622
|
+
puts task.inspect
|
|
623
|
+
puts task.class
|
|
624
|
+
puts task.args # for Step and Job types
|
|
625
|
+
puts task.sub_process.tasks # for SubProcess type
|
|
626
|
+
# etc...
|
|
627
|
+
```
|
|
628
|
+
|
|
629
|
+
Depending on the type of task, different attributes will be available for inspection.
|
|
630
|
+
|
|
631
|
+
The types include:
|
|
632
|
+
|
|
633
|
+
* `Taskinator::Task::Step`
|
|
634
|
+
* `Taskinator::Task::Job`
|
|
635
|
+
* `Taskinator::Task::SubProcess`
|
|
636
|
+
|
|
552
637
|
## Configuration
|
|
553
638
|
|
|
554
639
|
### Redis
|
|
555
640
|
|
|
556
|
-
By default Taskinator assumes Redis is located at `localhost:6397`. This is fine for development,
|
|
641
|
+
By default Taskinator assumes Redis is located at `localhost:6397`. This is fine for development,
|
|
642
|
+
but for many production environments you will need to point to an external Redis server.
|
|
643
|
+
You may also what to use a namespace for the Redis keys.
|
|
644
|
+
|
|
557
645
|
_NOTE:_ The configuration hash _must_ have symbolized keys.
|
|
558
646
|
|
|
559
647
|
```ruby
|
|
@@ -568,15 +656,19 @@ end
|
|
|
568
656
|
Or, alternatively, via an `ENV` variable
|
|
569
657
|
|
|
570
658
|
Set the `REDIS_PROVIDER` environment variable to the Redis server url.
|
|
571
|
-
E.g. On Heroku, with RedisGreen: set REDIS_PROVIDER=REDISGREEN_URL and Taskinator will use the
|
|
659
|
+
E.g. On Heroku, with RedisGreen: set REDIS_PROVIDER=REDISGREEN_URL and Taskinator will use the
|
|
660
|
+
value of the `REDISGREEN_URL` environment variable when connecting to Redis.
|
|
572
661
|
|
|
573
662
|
You may also use the generic `REDIS_URL` which may be set to your own private Redis server.
|
|
574
663
|
|
|
575
|
-
The Redis configuration leverages the same setup as `sidekiq`. For advanced options, checkout the
|
|
664
|
+
The Redis configuration leverages the same setup as `sidekiq`. For advanced options, checkout the
|
|
665
|
+
[Sidekiq Advanced Options](https://github.com/mperham/sidekiq/wiki/Advanced-Options#complete-control)
|
|
666
|
+
wiki page for more information.
|
|
576
667
|
|
|
577
668
|
### Queues
|
|
578
669
|
|
|
579
|
-
By default the queue names for process and task workers is `default`, however, you can specify
|
|
670
|
+
By default the queue names for process and task workers is `default`, however, you can specify
|
|
671
|
+
the queue names as follows:
|
|
580
672
|
|
|
581
673
|
```ruby
|
|
582
674
|
Taskinator.configure do |config|
|
|
@@ -589,7 +681,8 @@ end
|
|
|
589
681
|
|
|
590
682
|
### Instrumentation
|
|
591
683
|
|
|
592
|
-
It is possible to instrument processes, tasks and jobs by providing an instrumeter such
|
|
684
|
+
It is possible to instrument processes, tasks and jobs by providing an instrumeter such
|
|
685
|
+
as `ActiveSupport::Notifications`.
|
|
593
686
|
|
|
594
687
|
```ruby
|
|
595
688
|
Taskinator.configure do |config|
|
|
@@ -607,40 +700,41 @@ end
|
|
|
607
700
|
|
|
608
701
|
The following instrumentation events are issued:
|
|
609
702
|
|
|
610
|
-
| Event
|
|
611
|
-
|
|
612
|
-
| `taskinator.process.created`
|
|
613
|
-
| `taskinator.process.saved`
|
|
614
|
-
| `taskinator.process.enqueued`
|
|
615
|
-
| `taskinator.process.processing`
|
|
616
|
-
| `taskinator.process.paused`
|
|
617
|
-
| `taskinator.process.resumed`
|
|
618
|
-
| `taskinator.process.completed`
|
|
619
|
-
| `taskinator.process.cancelled`
|
|
620
|
-
| `taskinator.process.failed`
|
|
621
|
-
| `taskinator.task.enqueued`
|
|
622
|
-
| `taskinator.task.processing`
|
|
623
|
-
| `taskinator.task.completed`
|
|
624
|
-
| `taskinator.task.cancelled`
|
|
625
|
-
| `taskinator.task.failed`
|
|
703
|
+
| Event | When |
|
|
704
|
+
|---------------------------------|----------------------------------------------------------|
|
|
705
|
+
| `taskinator.process.created` | After a root process gets created |
|
|
706
|
+
| `taskinator.process.saved` | After a root process has been persisted to Redis |
|
|
707
|
+
| `taskinator.process.enqueued` | After a process or subprocess is enqueued for processing |
|
|
708
|
+
| `taskinator.process.processing` | When a process or subprocess is processing |
|
|
709
|
+
| `taskinator.process.paused` | When a process or subprocess is paused |
|
|
710
|
+
| `taskinator.process.resumed` | When a process or subprocess is resumed |
|
|
711
|
+
| `taskinator.process.completed` | After a process or subprocess has completed processing |
|
|
712
|
+
| `taskinator.process.cancelled` | After a process or subprocess has been cancelled |
|
|
713
|
+
| `taskinator.process.failed` | After a process or subprocess has failed |
|
|
714
|
+
| `taskinator.task.enqueued` | After a task has been enqueued |
|
|
715
|
+
| `taskinator.task.processing` | When a task is processing |
|
|
716
|
+
| `taskinator.task.completed` | After a task has completed |
|
|
717
|
+
| `taskinator.task.cancelled` | After a task has been cancelled |
|
|
718
|
+
| `taskinator.task.failed` | After a task has failed |
|
|
626
719
|
|
|
627
720
|
For all events, the data included contains the following information:
|
|
628
721
|
|
|
629
|
-
| Key
|
|
630
|
-
|
|
631
|
-
| `:type`
|
|
632
|
-
| `:process_uuid`
|
|
633
|
-
| `:process_options`
|
|
634
|
-
| `:uuid`
|
|
635
|
-
| `:options`
|
|
636
|
-
| `:state`
|
|
637
|
-
| `:percentage_completed`
|
|
638
|
-
| `:percentage_failed`
|
|
639
|
-
| `:percentage_cancelled`
|
|
722
|
+
| Key | Value |
|
|
723
|
+
|---------------------------------|----------------------------------------------------------|
|
|
724
|
+
| `:type` | The type name of the component reporting the event |
|
|
725
|
+
| `:process_uuid` | The UUID of the root process |
|
|
726
|
+
| `:process_options` | Options hash of the root process |
|
|
727
|
+
| `:uuid` | The UUID of the respective task, job or sub process |
|
|
728
|
+
| `:options` | Options hash of the component |
|
|
729
|
+
| `:state` | State of the component |
|
|
730
|
+
| `:percentage_completed` | The percentage of completed tasks |
|
|
731
|
+
| `:percentage_failed` | The percentage of failed tasks |
|
|
732
|
+
| `:percentage_cancelled` | The percentage of cancelled tasks |
|
|
640
733
|
|
|
641
734
|
## Notes
|
|
642
735
|
|
|
643
|
-
The persistence logic is decoupled from the implementation, so it is possible to implement
|
|
736
|
+
The persistence logic is decoupled from the implementation, so it is possible to implement
|
|
737
|
+
another backing store if required.
|
|
644
738
|
|
|
645
739
|
## Contributing
|
|
646
740
|
|
|
@@ -651,12 +745,19 @@ The persistence logic is decoupled from the implementation, so it is possible to
|
|
|
651
745
|
5. Create new Pull Request
|
|
652
746
|
|
|
653
747
|
## License
|
|
748
|
+
|
|
654
749
|
MIT Copyright (c) 2014 Chris Stefano
|
|
655
750
|
|
|
656
751
|
Portions of code are from the Sidekiq project, Copyright (c) Contributed Systems LLC.
|
|
657
752
|
|
|
658
753
|
## Inspiration
|
|
659
754
|
|
|
660
|
-
Inspired by the [sidekiq](https://github.com/mperham/sidekiq) and
|
|
755
|
+
Inspired by the [sidekiq](https://github.com/mperham/sidekiq) and
|
|
756
|
+
[workflow](https://github.com/geekq/workflow) gems.
|
|
757
|
+
|
|
758
|
+
For other workflow solutions, checkout [Stonepath](https://github.com/bokmann/stonepath),
|
|
759
|
+
the now deprecated [ruote](https://github.com/jmettraux/ruote) gem and
|
|
760
|
+
[workflow](https://github.com/geekq/workflow).
|
|
661
761
|
|
|
662
|
-
|
|
762
|
+
Alternatively, for a robust enterprise ready solution checkout the
|
|
763
|
+
[AWS Flow Framework for Ruby](http://docs.aws.amazon.com/amazonswf/latest/awsrbflowguide/welcome.html).
|
data/lib/taskinator/api.rb
CHANGED
|
@@ -13,12 +13,13 @@ module Taskinator
|
|
|
13
13
|
def each(&block)
|
|
14
14
|
return to_enum(__method__) unless block_given?
|
|
15
15
|
|
|
16
|
+
identifiers = Taskinator.redis do |conn|
|
|
17
|
+
conn.smembers(@processes_list_key)
|
|
18
|
+
end
|
|
19
|
+
|
|
16
20
|
instance_cache = {}
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
uuids.each do |uuid|
|
|
20
|
-
yield Process.fetch(uuid, instance_cache)
|
|
21
|
-
end
|
|
21
|
+
identifiers.each do |identifier|
|
|
22
|
+
yield Process.fetch(identifier, instance_cache)
|
|
22
23
|
end
|
|
23
24
|
end
|
|
24
25
|
|
|
@@ -28,5 +29,13 @@ module Taskinator
|
|
|
28
29
|
end
|
|
29
30
|
end
|
|
30
31
|
end
|
|
32
|
+
|
|
33
|
+
def self.find_process(identifier)
|
|
34
|
+
Process.fetch(identifier)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def self.find_task(identifier)
|
|
38
|
+
Task.fetch(identifier)
|
|
39
|
+
end
|
|
31
40
|
end
|
|
32
41
|
end
|
|
@@ -24,7 +24,10 @@ module Taskinator
|
|
|
24
24
|
raise ArgumentError, 'block' unless block_given?
|
|
25
25
|
|
|
26
26
|
sub_process = Process.define_sequential_process_for(@definition, options)
|
|
27
|
-
|
|
27
|
+
task = define_sub_process_task(@process, sub_process, options)
|
|
28
|
+
Builder.new(sub_process, @definition, *@args).instance_eval(&block)
|
|
29
|
+
@process.tasks << task if sub_process.tasks.any?
|
|
30
|
+
nil
|
|
28
31
|
end
|
|
29
32
|
|
|
30
33
|
# defines a sub process of tasks which are executed concurrently
|
|
@@ -32,7 +35,10 @@ module Taskinator
|
|
|
32
35
|
raise ArgumentError, 'block' unless block_given?
|
|
33
36
|
|
|
34
37
|
sub_process = Process.define_concurrent_process_for(@definition, complete_on, options)
|
|
35
|
-
|
|
38
|
+
task = define_sub_process_task(@process, sub_process, options)
|
|
39
|
+
Builder.new(sub_process, @definition, *@args).instance_eval(&block)
|
|
40
|
+
@process.tasks << task if sub_process.tasks.any?
|
|
41
|
+
nil
|
|
36
42
|
end
|
|
37
43
|
|
|
38
44
|
# dynamically defines tasks, using the given @iterator method
|
|
@@ -51,6 +57,7 @@ module Taskinator
|
|
|
51
57
|
@executor.send(method, *method_args) do |*args|
|
|
52
58
|
Builder.new(@process, @definition, *args).instance_eval(&block)
|
|
53
59
|
end
|
|
60
|
+
nil
|
|
54
61
|
end
|
|
55
62
|
|
|
56
63
|
alias_method :transform, :for_each
|
|
@@ -61,6 +68,7 @@ module Taskinator
|
|
|
61
68
|
raise NoMethodError, method unless @executor.respond_to?(method)
|
|
62
69
|
|
|
63
70
|
define_step_task(@process, method, @args, options)
|
|
71
|
+
nil
|
|
64
72
|
end
|
|
65
73
|
|
|
66
74
|
# defines a task which executes the given @job
|
|
@@ -70,6 +78,7 @@ module Taskinator
|
|
|
70
78
|
raise ArgumentError, 'job' unless job.methods.include?(:perform) || job.instance_methods.include?(:perform)
|
|
71
79
|
|
|
72
80
|
define_job_task(@process, job, @args, options)
|
|
81
|
+
nil
|
|
73
82
|
end
|
|
74
83
|
|
|
75
84
|
# defines a sub process task, for the given @definition
|
|
@@ -82,7 +91,10 @@ module Taskinator
|
|
|
82
91
|
# TODO: decide whether the sub process to dynamically receive arguments
|
|
83
92
|
|
|
84
93
|
sub_process = definition.create_sub_process(*@args, combine_options(options))
|
|
85
|
-
|
|
94
|
+
task = define_sub_process_task(@process, sub_process, options)
|
|
95
|
+
Builder.new(sub_process, definition, *@args)
|
|
96
|
+
@process.tasks << task if sub_process.tasks.any?
|
|
97
|
+
nil
|
|
86
98
|
end
|
|
87
99
|
|
|
88
100
|
private
|
|
@@ -100,10 +112,7 @@ module Taskinator
|
|
|
100
112
|
end
|
|
101
113
|
|
|
102
114
|
def define_sub_process_task(process, sub_process, options={})
|
|
103
|
-
|
|
104
|
-
Task.define_sub_process_task(process, sub_process, combine_options(options))
|
|
105
|
-
}
|
|
106
|
-
sub_process
|
|
115
|
+
Task.define_sub_process_task(process, sub_process, combine_options(options))
|
|
107
116
|
end
|
|
108
117
|
|
|
109
118
|
def define_task(process)
|
|
@@ -604,6 +604,9 @@ module Taskinator
|
|
|
604
604
|
end
|
|
605
605
|
|
|
606
606
|
def visit_tasks(tasks)
|
|
607
|
+
@conn.expire "#{@key}:tasks", expire_in
|
|
608
|
+
@conn.expire "#{@key}.count", expire_in
|
|
609
|
+
@conn.expire "#{@key}.pending", expire_in
|
|
607
610
|
tasks.each do |task|
|
|
608
611
|
RedisCleanupVisitor.new(@conn, task, expire_in).visit
|
|
609
612
|
end
|
data/lib/taskinator/task.rb
CHANGED
|
@@ -230,12 +230,12 @@ module Taskinator
|
|
|
230
230
|
# NNB: if other job types are required, may need to implement how they get invoked here!
|
|
231
231
|
# FIXME: possible implement using ActiveJob instead, so it doesn't matter how the worker is implemented
|
|
232
232
|
|
|
233
|
-
if job.
|
|
233
|
+
if job.respond_to?(:perform)
|
|
234
234
|
# resque
|
|
235
|
-
job.perform(args)
|
|
235
|
+
job.perform(*args)
|
|
236
236
|
else
|
|
237
237
|
# delayedjob and sidekiq
|
|
238
|
-
job.new.perform(args)
|
|
238
|
+
job.new.perform(*args)
|
|
239
239
|
end
|
|
240
240
|
|
|
241
241
|
# ASSUMPTION: when the job returns, the task is considered to be complete
|
data/lib/taskinator/version.rb
CHANGED
data/lib/taskinator.rb
CHANGED
data/spec/support/test_flows.rb
CHANGED
|
@@ -132,4 +132,42 @@ module TestFlows
|
|
|
132
132
|
end
|
|
133
133
|
end
|
|
134
134
|
|
|
135
|
+
module NestedTask
|
|
136
|
+
extend Taskinator::Definition
|
|
137
|
+
include Support
|
|
138
|
+
|
|
139
|
+
define_process :task_count do
|
|
140
|
+
task :task_1
|
|
141
|
+
|
|
142
|
+
concurrent do
|
|
143
|
+
task :task_2
|
|
144
|
+
task :task_3
|
|
145
|
+
|
|
146
|
+
sequential do
|
|
147
|
+
task :task_4
|
|
148
|
+
task :task_5
|
|
149
|
+
|
|
150
|
+
concurrent do
|
|
151
|
+
task :task_6
|
|
152
|
+
task :task_7
|
|
153
|
+
|
|
154
|
+
sequential do
|
|
155
|
+
task :task_8
|
|
156
|
+
task :task_9
|
|
157
|
+
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
task :task_10
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
task :task_11
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
task :task_12
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
task :task_13
|
|
170
|
+
end
|
|
171
|
+
end
|
|
172
|
+
|
|
135
173
|
end
|
data/spec/taskinator/api_spec.rb
CHANGED
|
@@ -44,4 +44,20 @@ describe Taskinator::Api, :redis => true do
|
|
|
44
44
|
end
|
|
45
45
|
end
|
|
46
46
|
|
|
47
|
+
describe "#find_process" do
|
|
48
|
+
it {
|
|
49
|
+
# fetch method is covered by persistence spec
|
|
50
|
+
expect(Taskinator::Process).to receive(:fetch) {}
|
|
51
|
+
subject.find_process 'foo:bar:process'
|
|
52
|
+
}
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
describe "#find_task" do
|
|
56
|
+
it {
|
|
57
|
+
# fetch method is covered by persistence spec
|
|
58
|
+
expect(Taskinator::Task).to receive(:fetch) {}
|
|
59
|
+
subject.find_task 'foo:bar:process:baz:task'
|
|
60
|
+
}
|
|
61
|
+
end
|
|
62
|
+
|
|
47
63
|
end
|
|
@@ -75,6 +75,22 @@ describe Taskinator::Definition::Builder do
|
|
|
75
75
|
expect(Taskinator::Process).to receive(:define_sequential_process_for).with(definition, options).and_call_original
|
|
76
76
|
subject.sequential(options, &define_block)
|
|
77
77
|
end
|
|
78
|
+
|
|
79
|
+
it "adds sub-process task" do
|
|
80
|
+
block = Proc.new {|p|
|
|
81
|
+
p.task :task_method
|
|
82
|
+
}
|
|
83
|
+
expect(process.tasks).to be_empty
|
|
84
|
+
subject.sequential(options, &block)
|
|
85
|
+
expect(process.tasks).to_not be_empty
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
it "ignores sub-processes without tasks" do
|
|
89
|
+
allow(block).to receive(:call)
|
|
90
|
+
expect(process.tasks).to be_empty
|
|
91
|
+
subject.sequential(options, &define_block)
|
|
92
|
+
expect(process.tasks).to be_empty
|
|
93
|
+
end
|
|
78
94
|
end
|
|
79
95
|
|
|
80
96
|
describe "#concurrent" do
|
|
@@ -100,6 +116,22 @@ describe Taskinator::Definition::Builder do
|
|
|
100
116
|
expect(Taskinator::Process).to receive(:define_concurrent_process_for).with(definition, Taskinator::CompleteOn::First, options).and_call_original
|
|
101
117
|
subject.concurrent(Taskinator::CompleteOn::First, options, &define_block)
|
|
102
118
|
end
|
|
119
|
+
|
|
120
|
+
it "adds sub-process task" do
|
|
121
|
+
block = Proc.new {|p|
|
|
122
|
+
p.task :task_method
|
|
123
|
+
}
|
|
124
|
+
expect(process.tasks).to be_empty
|
|
125
|
+
subject.sequential(options, &block)
|
|
126
|
+
expect(process.tasks).to_not be_empty
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
it "ignores sub-processes without tasks" do
|
|
130
|
+
allow(block).to receive(:call)
|
|
131
|
+
expect(process.tasks).to be_empty
|
|
132
|
+
subject.sequential(options, &define_block)
|
|
133
|
+
expect(process.tasks).to be_empty
|
|
134
|
+
end
|
|
103
135
|
end
|
|
104
136
|
|
|
105
137
|
describe "#for_each" do
|
|
@@ -235,6 +267,22 @@ describe Taskinator::Definition::Builder do
|
|
|
235
267
|
expect(sub_definition).to receive(:create_sub_process).with(*args, builder_options.merge(options)).and_call_original
|
|
236
268
|
subject.sub_process(sub_definition, options)
|
|
237
269
|
end
|
|
270
|
+
|
|
271
|
+
it "adds sub-process task" do
|
|
272
|
+
block = Proc.new {|p|
|
|
273
|
+
p.task :task_method
|
|
274
|
+
}
|
|
275
|
+
expect(process.tasks).to be_empty
|
|
276
|
+
subject.sequential(options, &block)
|
|
277
|
+
expect(process.tasks).to_not be_empty
|
|
278
|
+
end
|
|
279
|
+
|
|
280
|
+
it "ignores sub-processes without tasks" do
|
|
281
|
+
allow(block).to receive(:call)
|
|
282
|
+
expect(process.tasks).to be_empty
|
|
283
|
+
subject.sequential(options, &define_block)
|
|
284
|
+
expect(process.tasks).to be_empty
|
|
285
|
+
end
|
|
238
286
|
end
|
|
239
287
|
|
|
240
288
|
end
|
|
@@ -370,24 +370,27 @@ describe Taskinator::Persistence, :redis => true do
|
|
|
370
370
|
TestFlows::Job,
|
|
371
371
|
TestFlows::SubProcess,
|
|
372
372
|
TestFlows::Sequential,
|
|
373
|
-
TestFlows::Concurrent
|
|
373
|
+
TestFlows::Concurrent,
|
|
374
|
+
TestFlows::EmptySequentialProcessTest,
|
|
375
|
+
TestFlows::EmptyConcurrentProcessTest,
|
|
376
|
+
TestFlows::NestedTask,
|
|
374
377
|
].each do |definition|
|
|
375
378
|
|
|
376
379
|
describe "#{definition.name} expire immediately" do
|
|
377
380
|
it {
|
|
378
|
-
process = definition.create_process(1)
|
|
379
|
-
|
|
380
381
|
Taskinator.redis do |conn|
|
|
381
|
-
|
|
382
|
+
# sanity check
|
|
383
|
+
expect(conn.keys).to be_empty
|
|
382
384
|
|
|
383
|
-
process.
|
|
385
|
+
process = definition.create_process(1)
|
|
384
386
|
|
|
385
|
-
|
|
387
|
+
# sanity check
|
|
388
|
+
expect(conn.hget(process.key, :uuid)).to eq(process.uuid)
|
|
386
389
|
|
|
387
|
-
|
|
388
|
-
expect(conn.hget(task.key, :uuid)).to be_nil
|
|
389
|
-
end
|
|
390
|
+
process.cleanup(0) # immediately
|
|
390
391
|
|
|
392
|
+
# ensure nothing left behind
|
|
393
|
+
expect(conn.keys).to be_empty
|
|
391
394
|
end
|
|
392
395
|
}
|
|
393
396
|
end
|
|
@@ -396,9 +399,14 @@ describe Taskinator::Persistence, :redis => true do
|
|
|
396
399
|
|
|
397
400
|
describe "expires in future" do
|
|
398
401
|
it {
|
|
399
|
-
process = TestFlows::Task.create_process(1)
|
|
400
|
-
|
|
401
402
|
Taskinator.redis do |conn|
|
|
403
|
+
|
|
404
|
+
# sanity check
|
|
405
|
+
expect(conn.keys).to be_empty
|
|
406
|
+
|
|
407
|
+
process = TestFlows::Task.create_process(1)
|
|
408
|
+
|
|
409
|
+
# sanity check
|
|
402
410
|
expect(conn.hget(process.key, :uuid)).to eq(process.uuid)
|
|
403
411
|
|
|
404
412
|
process.cleanup(2)
|
|
@@ -411,12 +419,8 @@ describe Taskinator::Persistence, :redis => true do
|
|
|
411
419
|
|
|
412
420
|
sleep 3
|
|
413
421
|
|
|
414
|
-
#
|
|
415
|
-
expect(conn.
|
|
416
|
-
recursively_enumerate_tasks(process.tasks) do |task|
|
|
417
|
-
expect(conn.hget(task.key, :uuid)).to be_nil
|
|
418
|
-
end
|
|
419
|
-
|
|
422
|
+
# ensure nothing left behind
|
|
423
|
+
expect(conn.keys).to be_empty
|
|
420
424
|
end
|
|
421
425
|
}
|
|
422
426
|
end
|
|
@@ -227,7 +227,7 @@ describe Taskinator::Task do
|
|
|
227
227
|
|
|
228
228
|
method = subject.method
|
|
229
229
|
|
|
230
|
-
executor.class_eval do
|
|
230
|
+
executor.singleton_class.class_eval do
|
|
231
231
|
define_method method do |*args|
|
|
232
232
|
# this method executes in the scope of the executor
|
|
233
233
|
# store the context in an instance variable
|
|
@@ -334,13 +334,23 @@ describe Taskinator::Task do
|
|
|
334
334
|
end
|
|
335
335
|
end
|
|
336
336
|
|
|
337
|
-
|
|
337
|
+
class TestJobClassNoArgs
|
|
338
|
+
def perform
|
|
339
|
+
end
|
|
340
|
+
end
|
|
341
|
+
|
|
342
|
+
module TestJobModuleNoArgs
|
|
343
|
+
def self.perform
|
|
344
|
+
end
|
|
345
|
+
end
|
|
346
|
+
|
|
347
|
+
subject { Taskinator::Task.define_job_task(process, TestJob, [1, {:a => 1, :b => 2}]) }
|
|
338
348
|
|
|
339
349
|
it_should_behave_like "a task", Taskinator::Task::Job
|
|
340
350
|
|
|
341
351
|
describe ".define_job_task" do
|
|
342
352
|
it "sets the queue to use" do
|
|
343
|
-
task = Taskinator::Task.define_job_task(process, TestJob, {:a => 1, :b => 2}, :queue => :foo)
|
|
353
|
+
task = Taskinator::Task.define_job_task(process, TestJob, [1, {:a => 1, :b => 2}], :queue => :foo)
|
|
344
354
|
expect(task.queue).to eq(:foo)
|
|
345
355
|
end
|
|
346
356
|
end
|
|
@@ -367,23 +377,37 @@ describe Taskinator::Task do
|
|
|
367
377
|
|
|
368
378
|
describe "#start" do
|
|
369
379
|
it {
|
|
370
|
-
task = Taskinator::Task.define_job_task(process, TestJobClass, {:a => 1, :b => 2})
|
|
380
|
+
task = Taskinator::Task.define_job_task(process, TestJobClass, [1, {:a => 1, :b => 2}])
|
|
381
|
+
expect(process).to receive(:task_completed).with(task)
|
|
382
|
+
expect_any_instance_of(TestJobClass).to receive(:perform).with(1, {:a => 1, :b => 2})
|
|
383
|
+
task.start!
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
it {
|
|
387
|
+
task = Taskinator::Task.define_job_task(process, TestJobModule, [2, {:a => 1, :b => 2}])
|
|
388
|
+
expect(process).to receive(:task_completed).with(task)
|
|
389
|
+
expect(TestJobModule).to receive(:perform).with(2, {:a => 1, :b => 2})
|
|
390
|
+
task.start!
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
it {
|
|
394
|
+
task = Taskinator::Task.define_job_task(process, TestJobClassNoArgs, nil)
|
|
371
395
|
expect(process).to receive(:task_completed).with(task)
|
|
372
|
-
expect_any_instance_of(
|
|
396
|
+
expect_any_instance_of(TestJobClassNoArgs).to receive(:perform).and_call_original
|
|
373
397
|
task.start!
|
|
374
398
|
}
|
|
375
399
|
|
|
376
400
|
it {
|
|
377
|
-
task = Taskinator::Task.define_job_task(process,
|
|
401
|
+
task = Taskinator::Task.define_job_task(process, TestJobModuleNoArgs, nil)
|
|
378
402
|
expect(process).to receive(:task_completed).with(task)
|
|
379
|
-
expect(
|
|
403
|
+
expect(TestJobModuleNoArgs).to receive(:perform).and_call_original
|
|
380
404
|
task.start!
|
|
381
405
|
}
|
|
382
406
|
|
|
383
407
|
it "is instrumented" do
|
|
384
408
|
allow(process).to receive(:task_completed).with(subject)
|
|
385
409
|
|
|
386
|
-
allow(TestJob).to receive(:perform).with({:a => 1, :b => 2})
|
|
410
|
+
allow(TestJob).to receive(:perform).with(1, {:a => 1, :b => 2})
|
|
387
411
|
|
|
388
412
|
instrumentation_block = SpecSupport::Block.new
|
|
389
413
|
|
data/taskinator.gemspec
CHANGED
|
@@ -23,7 +23,6 @@ Gem::Specification.new do |spec|
|
|
|
23
23
|
# core
|
|
24
24
|
spec.add_dependency 'redis' , '>= 3.2.1'
|
|
25
25
|
spec.add_dependency 'redis-namespace' , '>= 1.5.2'
|
|
26
|
-
spec.add_dependency 'redis-semaphore' , '>= 0.2.4'
|
|
27
26
|
spec.add_dependency 'connection_pool' , '>= 2.2.0'
|
|
28
27
|
spec.add_dependency 'json' , '>= 1.8.2'
|
|
29
28
|
spec.add_dependency 'builder' , '>= 3.2.2'
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: taskinator
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.3
|
|
4
|
+
version: 0.4.3
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Chris Stefano
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date:
|
|
11
|
+
date: 2022-01-14 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: redis
|
|
@@ -38,20 +38,6 @@ dependencies:
|
|
|
38
38
|
- - ">="
|
|
39
39
|
- !ruby/object:Gem::Version
|
|
40
40
|
version: 1.5.2
|
|
41
|
-
- !ruby/object:Gem::Dependency
|
|
42
|
-
name: redis-semaphore
|
|
43
|
-
requirement: !ruby/object:Gem::Requirement
|
|
44
|
-
requirements:
|
|
45
|
-
- - ">="
|
|
46
|
-
- !ruby/object:Gem::Version
|
|
47
|
-
version: 0.2.4
|
|
48
|
-
type: :runtime
|
|
49
|
-
prerelease: false
|
|
50
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
51
|
-
requirements:
|
|
52
|
-
- - ">="
|
|
53
|
-
- !ruby/object:Gem::Version
|
|
54
|
-
version: 0.2.4
|
|
55
41
|
- !ruby/object:Gem::Dependency
|
|
56
42
|
name: connection_pool
|
|
57
43
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -246,7 +232,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
246
232
|
- !ruby/object:Gem::Version
|
|
247
233
|
version: '0'
|
|
248
234
|
requirements: []
|
|
249
|
-
rubygems_version: 3.
|
|
235
|
+
rubygems_version: 3.2.3
|
|
250
236
|
signing_key:
|
|
251
237
|
specification_version: 4
|
|
252
238
|
summary: A simple orchestration library for running complex processes or workflows
|