gush 2.1.0 → 4.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/ruby.yml +7 -28
- data/.rubocop.yml +232 -0
- data/CHANGELOG.md +1 -77
- data/README.md +156 -10
- data/gush.gemspec +13 -8
- data/lib/gush/cli/overview.rb +2 -0
- data/lib/gush/cli.rb +27 -4
- data/lib/gush/client.rb +115 -18
- data/lib/gush/graph.rb +2 -2
- data/lib/gush/job.rb +14 -3
- data/lib/gush/migrate/1_create_gush_workflows_created.rb +21 -0
- data/lib/gush/migration.rb +37 -0
- data/lib/gush/version.rb +3 -0
- data/lib/gush/worker.rb +1 -1
- data/lib/gush/workflow.rb +29 -16
- data/lib/gush.rb +1 -0
- data/spec/features/integration_spec.rb +7 -6
- data/spec/gush/client_spec.rb +316 -7
- data/spec/gush/job_spec.rb +48 -2
- data/spec/gush/migrate/1_create_gush_workflows_created_spec.rb +42 -0
- data/spec/gush/migration_spec.rb +23 -0
- data/spec/gush/workflow_spec.rb +120 -12
- data/spec/spec_helper.rb +29 -7
- metadata +64 -12
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0b1ccfa69ff21ec6820d9178edb51872cdbf28b7686bcf3a4de4170e3bc7f370
|
4
|
+
data.tar.gz: c44dae0cc5480366a3cf65ccd461ca55c991945ee090dfa3a386061f66001e09
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 474efac8af5d337283f29fbb7c4e9027690519c413f4d71c08d1d4c36aa78bc805d68b199fbf4395744854d40e238b19c570f0c2daa6c0676efd58f746b38b1c
|
7
|
+
data.tar.gz: c8635120b6071458cf6f99542f846adb806a4233e17b4a77444acd191763b470e2d41560f14b17e5b59d6dfeb514c52933eac05ce5119ca590f12b07209626b9
|
data/.github/workflows/ruby.yml
CHANGED
@@ -26,35 +26,10 @@ jobs:
|
|
26
26
|
runs-on: ubuntu-latest
|
27
27
|
strategy:
|
28
28
|
matrix:
|
29
|
-
rails_version: ['
|
30
|
-
ruby-version: ['
|
31
|
-
exclude:
|
32
|
-
- ruby-version: '3.0'
|
33
|
-
rails_version: '4.2.7'
|
34
|
-
- ruby-version: '3.1'
|
35
|
-
rails_version: '4.2.7'
|
36
|
-
- ruby-version: '3.0'
|
37
|
-
rails_version: '5.0'
|
38
|
-
- ruby-version: '3.1'
|
39
|
-
rails_version: '5.0'
|
40
|
-
- ruby-version: '3.0'
|
41
|
-
rails_version: '5.1'
|
42
|
-
- ruby-version: '3.1'
|
43
|
-
rails_version: '5.1'
|
44
|
-
- ruby-version: '3.0'
|
45
|
-
rails_version: '5.2'
|
46
|
-
- ruby-version: '3.1'
|
47
|
-
rails_version: '5.2'
|
48
|
-
- ruby-version: '3.0'
|
49
|
-
rails_version: '6.0'
|
50
|
-
- ruby-version: '3.1'
|
51
|
-
rails_version: '6.0'
|
52
|
-
- ruby-version: '3.1'
|
53
|
-
rails_version: '6.1'
|
54
|
-
- ruby-version: '2.6'
|
55
|
-
rails_version: '7.0'
|
29
|
+
rails_version: ['6.1.0', '7.0', '7.1.0']
|
30
|
+
ruby-version: ['3.0', '3.1', '3.2', '3.3']
|
56
31
|
steps:
|
57
|
-
- uses: actions/checkout@
|
32
|
+
- uses: actions/checkout@v4
|
58
33
|
- name: Set up Ruby
|
59
34
|
uses: ruby/setup-ruby@v1
|
60
35
|
with:
|
@@ -64,6 +39,10 @@ jobs:
|
|
64
39
|
RAILS_VERSION: "${{ matrix.rails_version }}"
|
65
40
|
- name: Install Graphviz
|
66
41
|
run: sudo apt-get install graphviz
|
42
|
+
- name: Run code lint
|
43
|
+
run: bundle exec rubocop
|
44
|
+
env:
|
45
|
+
RAILS_VERSION: "${{ matrix.rails_version }}"
|
67
46
|
- name: Run tests
|
68
47
|
run: bundle exec rspec
|
69
48
|
env:
|
data/.rubocop.yml
ADDED
@@ -0,0 +1,232 @@
|
|
1
|
+
require:
|
2
|
+
- rubocop-rake
|
3
|
+
- rubocop-rspec
|
4
|
+
|
5
|
+
AllCops:
|
6
|
+
NewCops: disable
|
7
|
+
|
8
|
+
Gemspec/OrderedDependencies:
|
9
|
+
Enabled: false
|
10
|
+
|
11
|
+
Layout/ArgumentAlignment:
|
12
|
+
Enabled: false
|
13
|
+
|
14
|
+
Layout/CaseIndentation:
|
15
|
+
Enabled: false
|
16
|
+
|
17
|
+
Layout/EmptyLinesAroundBlockBody:
|
18
|
+
Enabled: false
|
19
|
+
|
20
|
+
Layout/ExtraSpacing:
|
21
|
+
Enabled: false
|
22
|
+
|
23
|
+
Layout/FirstHashElementIndentation:
|
24
|
+
Enabled: false
|
25
|
+
|
26
|
+
Layout/HashAlignment:
|
27
|
+
Enabled: false
|
28
|
+
|
29
|
+
Layout/SpaceAroundEqualsInParameterDefault:
|
30
|
+
Enabled: false
|
31
|
+
|
32
|
+
Layout/SpaceAroundOperators:
|
33
|
+
Enabled: false
|
34
|
+
|
35
|
+
Layout/SpaceBeforeBlockBraces:
|
36
|
+
Enabled: false
|
37
|
+
|
38
|
+
Layout/SpaceInsideBlockBraces:
|
39
|
+
Enabled: false
|
40
|
+
|
41
|
+
Layout/SpaceInsideHashLiteralBraces:
|
42
|
+
Enabled: false
|
43
|
+
|
44
|
+
Lint/ConstantDefinitionInBlock:
|
45
|
+
Exclude:
|
46
|
+
- spec/**/*
|
47
|
+
|
48
|
+
Lint/RedundantSplatExpansion:
|
49
|
+
Enabled: false
|
50
|
+
|
51
|
+
Lint/ToJSON:
|
52
|
+
Enabled: false
|
53
|
+
|
54
|
+
Lint/UnusedBlockArgument:
|
55
|
+
Enabled: false
|
56
|
+
|
57
|
+
Lint/UnusedMethodArgument:
|
58
|
+
Enabled: false
|
59
|
+
|
60
|
+
Lint/UselessAssignment:
|
61
|
+
Enabled: false
|
62
|
+
|
63
|
+
Metrics/AbcSize:
|
64
|
+
Enabled: false
|
65
|
+
|
66
|
+
Metrics/BlockLength:
|
67
|
+
Enabled: false
|
68
|
+
|
69
|
+
Metrics/ClassLength:
|
70
|
+
Enabled: false
|
71
|
+
|
72
|
+
Metrics/CyclomaticComplexity:
|
73
|
+
Enabled: false
|
74
|
+
|
75
|
+
Metrics/MethodLength:
|
76
|
+
Enabled: false
|
77
|
+
|
78
|
+
Naming/MemoizedInstanceVariableName:
|
79
|
+
Enabled: false
|
80
|
+
|
81
|
+
Naming/PredicateName:
|
82
|
+
Enabled: false
|
83
|
+
|
84
|
+
Naming/RescuedExceptionsVariableName:
|
85
|
+
Enabled: false
|
86
|
+
|
87
|
+
Style/BlockDelimiters:
|
88
|
+
Enabled: false
|
89
|
+
|
90
|
+
Style/ClassVars:
|
91
|
+
Enabled: false
|
92
|
+
|
93
|
+
Style/CombinableLoops:
|
94
|
+
Enabled: false
|
95
|
+
|
96
|
+
Style/ConditionalAssignment:
|
97
|
+
Enabled: false
|
98
|
+
|
99
|
+
Style/Documentation:
|
100
|
+
Enabled: false
|
101
|
+
|
102
|
+
Style/EmptyCaseCondition:
|
103
|
+
Enabled: false
|
104
|
+
|
105
|
+
Style/EmptyMethod:
|
106
|
+
Enabled: false
|
107
|
+
|
108
|
+
Style/FrozenStringLiteralComment:
|
109
|
+
Enabled: false
|
110
|
+
|
111
|
+
Style/GuardClause:
|
112
|
+
Enabled: false
|
113
|
+
|
114
|
+
Style/HashSyntax:
|
115
|
+
Enabled: false
|
116
|
+
|
117
|
+
Style/IfUnlessModifier:
|
118
|
+
Enabled: false
|
119
|
+
|
120
|
+
Style/InverseMethods:
|
121
|
+
Enabled: false
|
122
|
+
|
123
|
+
Style/MethodCallWithoutArgsParentheses:
|
124
|
+
Enabled: false
|
125
|
+
|
126
|
+
Style/NumericLiteralPrefix:
|
127
|
+
Enabled: false
|
128
|
+
|
129
|
+
Style/PercentLiteralDelimiters:
|
130
|
+
Enabled: false
|
131
|
+
|
132
|
+
Style/RaiseArgs:
|
133
|
+
Enabled: false
|
134
|
+
|
135
|
+
Style/SafeNavigation:
|
136
|
+
Enabled: false
|
137
|
+
|
138
|
+
Style/SpecialGlobalVars:
|
139
|
+
Enabled: false
|
140
|
+
|
141
|
+
Style/StringLiterals:
|
142
|
+
Enabled: false
|
143
|
+
|
144
|
+
Style/SymbolProc:
|
145
|
+
Enabled: false
|
146
|
+
|
147
|
+
Style/UnlessElse:
|
148
|
+
Enabled: false
|
149
|
+
|
150
|
+
Style/WordArray:
|
151
|
+
Enabled: false
|
152
|
+
|
153
|
+
Layout/LineLength:
|
154
|
+
Enabled: false
|
155
|
+
|
156
|
+
RSpec/AnyInstance:
|
157
|
+
Enabled: false
|
158
|
+
|
159
|
+
RSpec/BeEq:
|
160
|
+
Enabled: false
|
161
|
+
|
162
|
+
RSpec/ContextWording:
|
163
|
+
Enabled: false
|
164
|
+
|
165
|
+
RSpec/DescribedClass:
|
166
|
+
Enabled: false
|
167
|
+
|
168
|
+
RSpec/EmptyExampleGroup:
|
169
|
+
Enabled: false
|
170
|
+
|
171
|
+
RSpec/EmptyLineAfterExampleGroup:
|
172
|
+
Enabled: false
|
173
|
+
|
174
|
+
RSpec/EmptyLineAfterSubject:
|
175
|
+
Enabled: false
|
176
|
+
|
177
|
+
RSpec/ExampleLength:
|
178
|
+
Enabled: false
|
179
|
+
|
180
|
+
RSpec/ExampleWording:
|
181
|
+
Enabled: false
|
182
|
+
|
183
|
+
RSpec/ExpectChange:
|
184
|
+
Enabled: false
|
185
|
+
|
186
|
+
RSpec/HookArgument:
|
187
|
+
EnforcedStyle: each
|
188
|
+
|
189
|
+
RSpec/LeakyConstantDeclaration:
|
190
|
+
Enabled: false
|
191
|
+
|
192
|
+
RSpec/LetSetup:
|
193
|
+
Enabled: false
|
194
|
+
|
195
|
+
RSpec/MatchArray:
|
196
|
+
Enabled: false
|
197
|
+
|
198
|
+
RSpec/MessageSpies:
|
199
|
+
EnforcedStyle: receive
|
200
|
+
|
201
|
+
RSpec/MultipleExpectations:
|
202
|
+
Enabled: false
|
203
|
+
|
204
|
+
RSpec/MultipleMemoizedHelpers:
|
205
|
+
Enabled: false
|
206
|
+
|
207
|
+
RSpec/NamedSubject:
|
208
|
+
Enabled: false
|
209
|
+
|
210
|
+
RSpec/NestedGroups:
|
211
|
+
Enabled: false
|
212
|
+
|
213
|
+
RSpec/NotToNot:
|
214
|
+
Enabled: false
|
215
|
+
|
216
|
+
RSpec/PredicateMatcher:
|
217
|
+
Enabled: false
|
218
|
+
|
219
|
+
RSpec/ReceiveCounts:
|
220
|
+
Enabled: false
|
221
|
+
|
222
|
+
RSpec/SpecFilePathFormat:
|
223
|
+
Enabled: false
|
224
|
+
|
225
|
+
RSpec/StubbedMock:
|
226
|
+
Enabled: false
|
227
|
+
|
228
|
+
RSpec/SubjectStub:
|
229
|
+
Enabled: false
|
230
|
+
|
231
|
+
RSpec/VerifiedDoubles:
|
232
|
+
Enabled: false
|
data/CHANGELOG.md
CHANGED
@@ -1,79 +1,3 @@
|
|
1
1
|
# Changelog
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
|
6
|
-
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
|
7
|
-
|
8
|
-
## 2.1.0
|
9
|
-
|
10
|
-
### Added
|
11
|
-
|
12
|
-
- Allow RedisMutex’s locking duration and polling interval to be customizable, thanks to @thukim! [See pull request](https://github.com/chaps-io/gush/pull/74)
|
13
|
-
- Support for Rails 7.0 and Ruby 3.0-3.1, thanks to @joshRpowell and @kzkn!
|
14
|
-
|
15
|
-
## 2.0.1
|
16
|
-
|
17
|
-
### Fixed
|
18
|
-
|
19
|
-
- Fix bug when retried jobs didn't correctly reset their failed flag when ran again (Thanks to @theo-delaune-argus and @mickael-palma-argus! [See issue](https://github.com/chaps-io/gush/issues/61))
|
20
|
-
|
21
|
-
## 2.0.0
|
22
|
-
|
23
|
-
### Changed
|
24
|
-
|
25
|
-
- *[BREAKING]* Store gush jobs on redis hash instead of plain keys - this improves performance when retrieving keys (Thanks to @Saicheg! [See pull request](https://github.com/chaps-io/gush/pull/56))
|
26
|
-
|
27
|
-
|
28
|
-
### Added
|
29
|
-
|
30
|
-
- Allow setting queue for each job via `:queue` option in `run` method (Thanks to @devilankur18! [See pull request](https://github.com/chaps-io/gush/pull/58))
|
31
|
-
|
32
|
-
|
33
|
-
## 1.1.1 - 2018-06-09
|
34
|
-
|
35
|
-
### Changed
|
36
|
-
|
37
|
-
- Relax dependency on ActiveSupport to work with 4.2 up to 5.X (Thanks to @iacobus! [See pull request](https://github.com/chaps-io/gush/pull/54))
|
38
|
-
|
39
|
-
|
40
|
-
## 1.1.0 - 2018-02-05
|
41
|
-
|
42
|
-
### Added
|
43
|
-
|
44
|
-
- Added ability to specify TTL for Redis keys and manually expire whole workflows (Thanks to @dmitrypol! [See pull request](https://github.com/chaps-io/gush/pull/48))
|
45
|
-
- Loosened dependency on redis-rb library to >= 3.2 and < 5.0 (Thanks to @mofumofu3n! [See pull request](https://github.com/chaps-io/gush/pull/52))
|
46
|
-
|
47
|
-
### Fixed
|
48
|
-
|
49
|
-
- Improved performance of (de)serializing workflows by not storing job array inside workflow JSON and other smaller improvements ([See pull request](https://github.com/chaps-io/gush/pull/53))
|
50
|
-
|
51
|
-
|
52
|
-
## 1.0.0 - 2017-10-02
|
53
|
-
|
54
|
-
### Added
|
55
|
-
|
56
|
-
- **BREAKING CHANGE** Gush now uses ActiveJob instead of directly Sidekiq, this allows programmers to use multiple backends, instead of just one. Including in-process or even synchronous backends. See http://guides.rubyonrails.org/active_job_basics.html
|
57
|
-
|
58
|
-
### Fixed
|
59
|
-
|
60
|
-
- Fix graph rendering with `gush viz` command. Sometimes it rendered the last job detached from others, because it was using a class name instead of job name as ID.
|
61
|
-
- Fix performance problems with unserializing jobs. This greatly **increased performance** by avoiding redundant calls to Redis storage. Should help a lot with huge workflows spawning thousands of jobs. Previously each job loaded whole workflow instance when executed.
|
62
|
-
|
63
|
-
### Changed
|
64
|
-
|
65
|
-
- **BREAKING CHANGE** `Gushfile.rb` is now renamed to `Gushfile`
|
66
|
-
- **BREAKING CHANGE** Internal code for reporting status via Redis pub/sub has been removed, since it wasn't used for a long time.
|
67
|
-
- **BREAKING CHANGE** jobs are expected to have a `perform` method instead of `work` like in < 1.0.0 versions.
|
68
|
-
- **BREAKING CHANGE** `payloads` method available inside jobs is now an array of hashes, instead of a hash, this allows for a more flexible approach to reusing a single job in many situations. Previously payloads were grouped by predecessor's class name, so you were forced to hardcode that class name in its descendants' code.
|
69
|
-
|
70
|
-
### Removed
|
71
|
-
|
72
|
-
- `gush workers` command is now removed. This is now up to the developer to start background processes depending on chosen ActiveJob adapter.
|
73
|
-
- `environment` was removed since it was no longer needed (it was Sidekiq specific)
|
74
|
-
|
75
|
-
## 0.4.0
|
76
|
-
|
77
|
-
### Removed
|
78
|
-
|
79
|
-
- remove hard dependency on Yajl, so Gush can work with non-MRI Rubies ([#31](https://github.com/chaps-io/gush/pull/31) by [Nick Rakochy](https://github.com/chaps-io/gush/pull/31))
|
3
|
+
Changes for each release are available at https://github.com/chaps-io/gush/releases
|
data/README.md
CHANGED
@@ -1,5 +1,9 @@
|
|
1
1
|
# Gush
|
2
2
|
|
3
|
+
![Gem Version](https://img.shields.io/gem/v/gush)
|
4
|
+
![GitHub Actions Workflow Status](https://img.shields.io/github/actions/workflow/status/chaps-io/gush/ruby.yml)
|
5
|
+
|
6
|
+
|
3
7
|
Gush is a parallel workflow runner using only Redis as storage and [ActiveJob](http://guides.rubyonrails.org/v4.2/active_job_basics.html#introduction) for scheduling and executing jobs.
|
4
8
|
|
5
9
|
## Theory
|
@@ -15,7 +19,7 @@ This README is about the latest `master` code, which might differ from what is r
|
|
15
19
|
### 1. Add `gush` to Gemfile
|
16
20
|
|
17
21
|
```ruby
|
18
|
-
gem 'gush', '~>
|
22
|
+
gem 'gush', '~> 3.0'
|
19
23
|
```
|
20
24
|
|
21
25
|
### 2. Create `Gushfile`
|
@@ -74,7 +78,17 @@ end
|
|
74
78
|
|
75
79
|
and this is how the graph will look like:
|
76
80
|
|
77
|
-
|
81
|
+
```mermaid
|
82
|
+
graph TD
|
83
|
+
A{Start} --> B[FetchJob1]
|
84
|
+
A --> C[FetchJob2]
|
85
|
+
B --> D[PersistJob1]
|
86
|
+
C --> E[PersistJob2]
|
87
|
+
D --> F[NormalizeJob]
|
88
|
+
E --> F
|
89
|
+
F --> G[IndexJob]
|
90
|
+
G --> H{Finish}
|
91
|
+
```
|
78
92
|
|
79
93
|
|
80
94
|
## Defining workflows
|
@@ -145,9 +159,11 @@ Let's assume we are writing a book publishing workflow which needs to know where
|
|
145
159
|
|
146
160
|
```ruby
|
147
161
|
class PublishBookWorkflow < Gush::Workflow
|
148
|
-
def configure(url, isbn)
|
162
|
+
def configure(url, isbn, publish: false)
|
149
163
|
run FetchBook, params: { url: url }
|
150
|
-
|
164
|
+
if publish
|
165
|
+
run PublishBook, params: { book_isbn: isbn }, after: FetchBook
|
166
|
+
end
|
151
167
|
end
|
152
168
|
end
|
153
169
|
```
|
@@ -155,7 +171,7 @@ end
|
|
155
171
|
and then create your workflow with those arguments:
|
156
172
|
|
157
173
|
```ruby
|
158
|
-
PublishBookWorkflow.create("http://url.com/book.pdf", "978-0470081204")
|
174
|
+
PublishBookWorkflow.create("http://url.com/book.pdf", "978-0470081204", publish: true)
|
159
175
|
```
|
160
176
|
|
161
177
|
and that's basically it for defining workflows, see below on how to define jobs:
|
@@ -211,7 +227,7 @@ For example, in case of Sidekiq this would be:
|
|
211
227
|
bundle exec sidekiq -q gush
|
212
228
|
```
|
213
229
|
|
214
|
-
**[Click here to see backends section in official ActiveJob documentation about configuring backends](http://guides.rubyonrails.org/
|
230
|
+
**[Click here to see backends section in official ActiveJob documentation about configuring backends](http://guides.rubyonrails.org/active_job_basics.html#backends)**
|
215
231
|
|
216
232
|
**Hint**: gush uses `gush` queue name by default. Keep that in mind, because some backends (like Sidekiq) will only run jobs from explicitly stated queues.
|
217
233
|
|
@@ -240,8 +256,53 @@ flow.status
|
|
240
256
|
|
241
257
|
`reload` is needed to see the latest status, since workflows are updated asynchronously.
|
242
258
|
|
259
|
+
## Loading workflows
|
260
|
+
|
261
|
+
### Finding a workflow by id
|
262
|
+
|
263
|
+
```
|
264
|
+
flow = Workflow.find(id)
|
265
|
+
```
|
266
|
+
|
267
|
+
### Paging through workflows
|
268
|
+
|
269
|
+
To get workflows with pagination, use start and stop (inclusive) index values:
|
270
|
+
|
271
|
+
```
|
272
|
+
flows = Workflow.page(0, 99)
|
273
|
+
```
|
274
|
+
|
275
|
+
Or in reverse order:
|
276
|
+
|
277
|
+
```
|
278
|
+
flows = Workflow.page(0, 99, order: :desc)
|
279
|
+
```
|
280
|
+
|
243
281
|
## Advanced features
|
244
282
|
|
283
|
+
### Global parameters for jobs
|
284
|
+
|
285
|
+
Workflows can accept a hash of `globals` that are automatically forwarded as parameters to all jobs.
|
286
|
+
|
287
|
+
This is useful to have common functionality across workflow and job classes, such as tracking the creator id for all instances:
|
288
|
+
|
289
|
+
```ruby
|
290
|
+
class SimpleWorkflow < Gush::Workflow
|
291
|
+
def configure(url_to_fetch_from)
|
292
|
+
run DownloadJob, params: { url: url_to_fetch_from }
|
293
|
+
end
|
294
|
+
end
|
295
|
+
|
296
|
+
flow = SimpleWorkflow.create('http://foo.com', globals: { creator_id: 123 })
|
297
|
+
flow.globals
|
298
|
+
=> {:creator_id=>123}
|
299
|
+
flow.jobs.first.params
|
300
|
+
=> {:creator_id=>123, :url=>"http://foo.com"}
|
301
|
+
```
|
302
|
+
|
303
|
+
**Note:** job params with the same key as globals will take precedence over the globals.
|
304
|
+
|
305
|
+
|
245
306
|
### Pipelining
|
246
307
|
|
247
308
|
Gush offers a useful tool to pass results of a job to its dependencies, so they can act differently.
|
@@ -319,7 +380,21 @@ flow = NotifyWorkflow.create([54, 21, 24, 154, 65]) # 5 user ids as an argument
|
|
319
380
|
|
320
381
|
it will generate a workflow with 5 `NotificationJob`s and one `AdminNotificationJob` which will depend on all of them:
|
321
382
|
|
322
|
-
|
383
|
+
|
384
|
+
```mermaid
|
385
|
+
graph TD
|
386
|
+
A{Start} --> B[NotificationJob]
|
387
|
+
A --> C[NotificationJob]
|
388
|
+
A --> D[NotificationJob]
|
389
|
+
A --> E[NotificationJob]
|
390
|
+
A --> F[NotificationJob]
|
391
|
+
B --> G[AdminNotificationJob]
|
392
|
+
C --> G
|
393
|
+
D --> G
|
394
|
+
E --> G
|
395
|
+
F --> G
|
396
|
+
G --> H{Finish}
|
397
|
+
```
|
323
398
|
|
324
399
|
### Dynamic queue for jobs
|
325
400
|
|
@@ -338,6 +413,54 @@ class NotifyWorkflow < Gush::Workflow
|
|
338
413
|
end
|
339
414
|
```
|
340
415
|
|
416
|
+
### Dynamic waitable time for jobs
|
417
|
+
|
418
|
+
There might be a case you want to configure a job to be executed after a time. Based on above example, we want to configure `AdminNotificationJob` to be executed after 5 seconds.
|
419
|
+
|
420
|
+
```ruby
|
421
|
+
|
422
|
+
class NotifyWorkflow < Gush::Workflow
|
423
|
+
def configure(user_ids)
|
424
|
+
notification_jobs = user_ids.map do |user_id|
|
425
|
+
run NotificationJob, params: {user_id: user_id}, queue: 'user'
|
426
|
+
end
|
427
|
+
|
428
|
+
run AdminNotificationJob, after: notification_jobs, queue: 'admin', wait: 5.seconds
|
429
|
+
end
|
430
|
+
end
|
431
|
+
```
|
432
|
+
|
433
|
+
### Customization of ActiveJob enqueueing
|
434
|
+
|
435
|
+
There might be a case when you want to customize enqueing a job with more than just the above two options (`queue` and `wait`).
|
436
|
+
|
437
|
+
To pass additional options to `ActiveJob.set`, override `Job#worker_options`, e.g.:
|
438
|
+
|
439
|
+
```ruby
|
440
|
+
|
441
|
+
class ScheduledJob < Gush::Job
|
442
|
+
|
443
|
+
def worker_options
|
444
|
+
super.merge(wait_until: Time.at(params[:start_at]))
|
445
|
+
end
|
446
|
+
|
447
|
+
end
|
448
|
+
```
|
449
|
+
|
450
|
+
Or to entirely customize the ActiveJob integration, override `Job#enqueue_worker!`, e.g.:
|
451
|
+
|
452
|
+
```ruby
|
453
|
+
|
454
|
+
class SynchronousJob < Gush::Job
|
455
|
+
|
456
|
+
def enqueue_worker!(options = {})
|
457
|
+
Gush::Worker.perform_now(workflow_id, name)
|
458
|
+
end
|
459
|
+
|
460
|
+
end
|
461
|
+
```
|
462
|
+
|
463
|
+
|
341
464
|
## Command line interface (CLI)
|
342
465
|
|
343
466
|
### Checking status
|
@@ -348,12 +471,18 @@ end
|
|
348
471
|
bundle exec gush show <workflow_id>
|
349
472
|
```
|
350
473
|
|
351
|
-
- of
|
474
|
+
- of a page of workflows:
|
352
475
|
|
353
476
|
```
|
354
477
|
bundle exec gush list
|
355
478
|
```
|
356
479
|
|
480
|
+
- of the most recent 100 workflows
|
481
|
+
|
482
|
+
```
|
483
|
+
bundle exec gush list -99 -1
|
484
|
+
```
|
485
|
+
|
357
486
|
### Vizualizing workflows as image
|
358
487
|
|
359
488
|
This requires that you have imagemagick installed on your computer:
|
@@ -379,7 +508,9 @@ end
|
|
379
508
|
|
380
509
|
### Cleaning up afterwards
|
381
510
|
|
382
|
-
Running `NotifyWorkflow.create` inserts multiple keys into Redis every time it is
|
511
|
+
Running `NotifyWorkflow.create` inserts multiple keys into Redis every time it is run. This data might be useful for analysis but at a certain point it can be purged. By default gush and Redis will keep keys forever. To configure expiration you need to do two things.
|
512
|
+
|
513
|
+
1. Create an initializer that specifies `config.ttl` in seconds. Best NOT to set TTL to be too short (like minutes) but about a week in length.
|
383
514
|
|
384
515
|
```ruby
|
385
516
|
# config/initializers/gush.rb
|
@@ -390,7 +521,9 @@ Gush.configure do |config|
|
|
390
521
|
end
|
391
522
|
```
|
392
523
|
|
393
|
-
|
524
|
+
2. Call `Client#expire_workflows` periodically, which will clear all expired stored workflow and job data and indexes. This method can be called at any rate, but ideally should be called at least once for every 1000 workflows created.
|
525
|
+
|
526
|
+
If you need more control over individual workflow expiration, you can call `flow.expire!(ttl)` with a TTL different from the Gush configuration, or with -1 to never expire the workflow.
|
394
527
|
|
395
528
|
### Avoid overlapping workflows
|
396
529
|
|
@@ -408,6 +541,19 @@ def find_by_class klass
|
|
408
541
|
end
|
409
542
|
```
|
410
543
|
|
544
|
+
## Gush 3.0 Migration
|
545
|
+
|
546
|
+
Gush 3.0 adds indexing for fast workflow pagination and changes the mechanism for expiring workflow data from Redis.
|
547
|
+
|
548
|
+
### Migration
|
549
|
+
|
550
|
+
Run `bundle exec gush migrate` after upgrading. This will update internal data structures.
|
551
|
+
|
552
|
+
### Expiration API
|
553
|
+
|
554
|
+
Periodically run `Gush::Client.new.expire_workflows` to expire data. Workflows will be automatically enrolled in this expiration, so there is no longer a need to call `workflow.expire!`.
|
555
|
+
|
556
|
+
|
411
557
|
## Contributors
|
412
558
|
|
413
559
|
- [Mateusz Lenik](https://github.com/mlen)
|
data/gush.gemspec
CHANGED
@@ -1,12 +1,13 @@
|
|
1
|
-
|
2
|
-
lib = File.expand_path('../lib', __FILE__)
|
1
|
+
lib = File.expand_path('lib', __dir__)
|
3
2
|
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
3
|
|
4
|
+
require_relative 'lib/gush/version'
|
5
|
+
|
5
6
|
Gem::Specification.new do |spec|
|
6
7
|
spec.name = "gush"
|
7
|
-
spec.version =
|
8
|
-
spec.authors = ["Piotrek Okoński"]
|
9
|
-
spec.email = ["piotrek@okonski.org"]
|
8
|
+
spec.version = Gush::VERSION
|
9
|
+
spec.authors = ["Piotrek Okoński", "Michał Krzyżanowski"]
|
10
|
+
spec.email = ["piotrek@okonski.org", "michal.krzyzanowski+github@gmail.com"]
|
10
11
|
spec.summary = "Fast and distributed workflow runner based on ActiveJob and Redis"
|
11
12
|
spec.description = "Gush is a parallel workflow runner using Redis as storage and ActiveJob for executing jobs."
|
12
13
|
spec.homepage = "https://github.com/chaps-io/gush"
|
@@ -16,11 +17,12 @@ Gem::Specification.new do |spec|
|
|
16
17
|
spec.executables = "gush"
|
17
18
|
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
18
19
|
spec.require_paths = ["lib"]
|
20
|
+
spec.required_ruby_version = '>= 3.0.0'
|
19
21
|
|
20
|
-
spec.add_dependency "activejob", ">=
|
22
|
+
spec.add_dependency "activejob", ">= 6.1.0", "< 7.2"
|
21
23
|
spec.add_dependency "concurrent-ruby", "~> 1.0"
|
22
24
|
spec.add_dependency "multi_json", "~> 1.11"
|
23
|
-
spec.add_dependency "redis", ">= 3.2", "<
|
25
|
+
spec.add_dependency "redis", ">= 3.2", "< 6"
|
24
26
|
spec.add_dependency "redis-mutex", "~> 4.0.1"
|
25
27
|
spec.add_dependency "hiredis", "~> 0.6"
|
26
28
|
spec.add_dependency "graphviz", "~> 1.2"
|
@@ -29,7 +31,10 @@ Gem::Specification.new do |spec|
|
|
29
31
|
spec.add_dependency "thor", ">= 0.19", "< 1.3"
|
30
32
|
spec.add_dependency "launchy", "~> 2.4"
|
31
33
|
spec.add_development_dependency "bundler"
|
32
|
-
spec.add_development_dependency "rake", "~>
|
34
|
+
spec.add_development_dependency "rake", "~> 12"
|
35
|
+
spec.add_development_dependency "rubocop", '~> 1.65.0'
|
36
|
+
spec.add_development_dependency "rubocop-rake", '~> 0.6.0'
|
37
|
+
spec.add_development_dependency "rubocop-rspec", '~> 3.0.3'
|
33
38
|
spec.add_development_dependency "rspec", '~> 3.0'
|
34
39
|
spec.add_development_dependency "pry", '~> 0.10'
|
35
40
|
end
|
data/lib/gush/cli/overview.rb
CHANGED
@@ -34,6 +34,7 @@ module Gush
|
|
34
34
|
end
|
35
35
|
|
36
36
|
private
|
37
|
+
|
37
38
|
def rows
|
38
39
|
[].tap do |rows|
|
39
40
|
columns.each_pair do |name, value|
|
@@ -91,6 +92,7 @@ module Gush
|
|
91
92
|
|
92
93
|
def jobs_by_type(type)
|
93
94
|
return sorted_jobs if type == :all
|
95
|
+
|
94
96
|
jobs.select{|j| j.public_send("#{type}?") }
|
95
97
|
end
|
96
98
|
|