dwf 0.1.12 → 0.1.13

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 4d6ec140cc691fd53ceb5d495add6e3a76ce6a7b6daf2bfbde982e6e40a19987
4
- data.tar.gz: 66911fc7ec1a54e38aaaca160ee455b2463f1888ecbbf981c64a17e9d790c1a2
3
+ metadata.gz: 131525481d493d4765a33fa9b5d34524f74f221b05399b1a39b2cc47d439aa65
4
+ data.tar.gz: 4483fe6f6e98e45579d65362c2ba3f0298303522d103647c0a3d04085580af16
5
5
  SHA512:
6
- metadata.gz: 240a6603023b3edc79b69d1793e8805e86ec298f2927df7bd3a1342ad241fb3a0b619a6aa50266348003c20f751fdd58dc91a68dead319916ca351a09bee5f0f
7
- data.tar.gz: 4af1937daa29fab5f2fd818f22a64bea90d35074a0ca70109e4a36cbaa754fae199799167385f78579a854152563c1e8074c6ff91ad931e544b20c67bf7de054
6
+ metadata.gz: 9a5e357a59d77faa86eaa54552f19b2ad06b82af8a72bc80a2c66fd8ba56584391c08878c0e0b16cf32df6e6dc564d01bf60b0c16b2bcafe7649b6522643db48
7
+ data.tar.gz: 056e815c2beed08ca7e8fccc0c22afb291c849f9195b1bd5d7469d22096b62a37a8a457a21eeaa949f436e034586217e3aacf4e7d6cf44e52f325c88126c8b6c
@@ -13,12 +13,16 @@ jobs:
13
13
 
14
14
  runs-on: ubuntu-latest
15
15
 
16
+ strategy:
17
+ matrix:
18
+ ruby-version: ["2.5", "2.6", "2.7", "3.0"]
19
+
16
20
  steps:
17
21
  - uses: actions/checkout@v2
18
22
  - name: Set up Ruby
19
23
  uses: ruby/setup-ruby@477b21f02be01bcb8030d50f37cfec92bfa615b6
20
24
  with:
21
- ruby-version: 3.0.0
25
+ ruby-version: ${{ matrix.ruby-version }}
22
26
  - name: Install dependencies
23
27
  run: bundle install
24
28
  - name: Run tests
data/CHANGELOG.md CHANGED
@@ -1,5 +1,42 @@
1
1
  # Changelog
2
2
  All notable changes to this project will be documented in this file.
3
+
4
+ ## 0.1.12
5
+ ### Added
6
+ #### Dynamic workflows
7
+ There might be a case when you have to contruct the workflow dynamically depend on the input
8
+ As an example, let's write a workflow which puts from 1 to 100 into the terminal parallely . Additionally after finish all job, it will puts the finshed word into the terminal
9
+ ```ruby
10
+ class FirstMainItem < Dwf::Item
11
+ def perform
12
+ puts "#{self.class.name}: running #{params}"
13
+ end
14
+ end
15
+
16
+ SecondMainItem = Class.new(FirstMainItem)
17
+
18
+ class TestWf < Dwf::Workflow
19
+ def configure
20
+ items = (1..100).to_a.map do |number|
21
+ run FirstMainItem, params: number
22
+ end
23
+ run SecondMainItem, after: items, params: "finished"
24
+ end
25
+ end
26
+
27
+ ```
28
+ We can achieve that because run method returns the id of the created job, which we can use for chaining dependencies.
29
+ Now, when we create the workflow like this:
30
+ ```ruby
31
+ wf = TestWf.create
32
+ # wf.callback_type = Dwf::Workflow::SK_BATCH
33
+ wf.start!
34
+ ```
35
+
36
+ ## 0.1.12
37
+ ### Added
38
+ #### Subworkflow for all callback types
39
+ same with `0.1.11`
3
40
  ## 0.1.11
4
41
  ### Added
5
42
  #### Subworkflow - Only support sidekiq pro
data/README.md CHANGED
@@ -199,6 +199,36 @@ Main flow: ThirtItem running
199
199
  Main flow: ThirtItem finish
200
200
  ```
201
201
 
202
+ ## Dynamic workflows
203
+ There might be a case when you have to contruct the workflow dynamically depend on the input
204
+ As an example, let's write a workflow which puts from 1 to 100 into the terminal parallelly . Additionally after finish all job, it will puts the finshed word into the terminal
205
+ ```ruby
206
+ class FirstMainItem < Dwf::Item
207
+ def perform
208
+ puts "#{self.class.name}: running #{params}"
209
+ end
210
+ end
211
+
212
+ SecondMainItem = Class.new(FirstMainItem)
213
+
214
+ class TestWf < Dwf::Workflow
215
+ def configure
216
+ items = (1..100).to_a.map do |number|
217
+ run FirstMainItem, params: number
218
+ end
219
+ run SecondMainItem, after: items, params: "finished"
220
+ end
221
+ end
222
+
223
+ ```
224
+ We can achieve that because run method returns the id of the created job, which we can use for chaining dependencies.
225
+ Now, when we create the workflow like this:
226
+ ```ruby
227
+ wf = TestWf.create
228
+ # wf.callback_type = Dwf::Workflow::SK_BATCH
229
+ wf.start!
230
+ ```
231
+
202
232
  # Todo
203
233
  - [x] Make it work
204
234
  - [x] Support pass params
data/dwf.gemspec CHANGED
@@ -28,6 +28,7 @@ Gem::Specification.new do |spec|
28
28
  spec.add_development_dependency 'byebug', '~> 11.1.3'
29
29
  spec.add_development_dependency 'mock_redis', '~> 0.27.2'
30
30
  spec.add_dependency 'redis', '~> 4.2.0'
31
+ spec.add_dependency 'redis-mutex', '~> 4.0.2'
31
32
  spec.add_development_dependency 'rspec', '~> 3.2'
32
33
  spec.add_dependency 'sidekiq', '~> 6.2.0'
33
34
  spec.add_development_dependency 'simplecov'
data/lib/dwf/callback.rb CHANGED
@@ -45,6 +45,7 @@ module Dwf
45
45
  )
46
46
  batch.jobs do
47
47
  jobs.each do |job|
48
+ job.reload
48
49
  job.persist_and_perform_async! if job.ready_to_start?
49
50
  end
50
51
  end
@@ -67,10 +68,8 @@ module Dwf
67
68
  end.compact
68
69
  end
69
70
 
70
- def with_lock(workflow_id, job_name)
71
- client.check_or_lock(workflow_id, job_name)
72
- yield
73
- client.release_lock(workflow_id, job_name)
71
+ def with_lock(workflow_id, job_name, &block)
72
+ client.check_or_lock(workflow_id, job_name, &block)
74
73
  end
75
74
 
76
75
  def start_with_batch(node)
data/lib/dwf/client.rb CHANGED
@@ -1,4 +1,5 @@
1
1
  require_relative 'errors'
2
+ require 'redis-mutex'
2
3
 
3
4
  module Dwf
4
5
  class Client
@@ -63,14 +64,9 @@ module Dwf
63
64
  redis.hset("dwf.jobs.#{job.workflow_id}.#{job.klass}", job.id, job.as_json)
64
65
  end
65
66
 
66
- def check_or_lock(workflow_id, job_name)
67
+ def check_or_lock(workflow_id, job_name, &block)
67
68
  key = "wf_enqueue_outgoing_jobs_#{workflow_id}-#{job_name}"
68
-
69
- if key_exists?(key)
70
- sleep 2
71
- else
72
- set(key, 'running')
73
- end
69
+ RedisMutex.with_lock(key, sleep: 0.3, block: 2, &block)
74
70
  end
75
71
 
76
72
  def release_lock(workflow_id, job_name)
@@ -175,7 +171,9 @@ module Dwf
175
171
  end
176
172
 
177
173
  def redis
178
- @redis ||= Redis.new(config.redis_opts)
174
+ @redis ||= Redis.new(config.redis_opts).tap do |instance|
175
+ RedisClassy.redis = instance
176
+ end
179
177
  end
180
178
  end
181
179
  end
data/lib/dwf/item.rb CHANGED
@@ -49,8 +49,8 @@ module Dwf
49
49
  assign_attributes(item.to_hash)
50
50
  end
51
51
 
52
- def perform_async
53
- Dwf::Worker.set(queue: queue || client.config.namespace)
52
+ def perform_async(options = {})
53
+ Dwf::Worker.set(options.merge(queue: queue || client.config.namespace))
54
54
  .perform_async(workflow_id, name)
55
55
  end
56
56
 
@@ -124,11 +124,13 @@ module Dwf
124
124
  return workflow.enqueue_outgoing_jobs if leaf?
125
125
 
126
126
  outgoing.each do |job_name|
127
- client.check_or_lock(workflow_id, job_name)
128
- out = client.find_node(job_name, workflow_id)
129
- out.persist_and_perform_async! if out.ready_to_start?
130
- client.release_lock(workflow_id, job_name)
127
+ client.check_or_lock(workflow_id, job_name) do
128
+ out = client.find_node(job_name, workflow_id)
129
+ out.persist_and_perform_async! if out.ready_to_start?
130
+ end
131
131
  end
132
+ rescue RedisMutex::LockError
133
+ perform_async(wait: 2)
132
134
  end
133
135
 
134
136
  def to_hash
data/lib/dwf/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Dwf
4
- VERSION = '0.1.12'
4
+ VERSION = '0.1.13'
5
5
  end
data/lib/dwf/workflow.rb CHANGED
@@ -178,10 +178,10 @@ module Dwf
178
178
  return unless sub_workflow?
179
179
 
180
180
  outgoing.each do |job_name|
181
- client.check_or_lock(parent_id, job_name)
182
- node = client.find_node(job_name, parent_id)
183
- node.persist_and_perform_async! if node.ready_to_start?
184
- client.release_lock(parent_id, job_name)
181
+ client.check_or_lock(parent_id, job_name) do
182
+ node = client.find_node(job_name, parent_id)
183
+ node.persist_and_perform_async! if node.ready_to_start?
184
+ end
185
185
  end
186
186
  end
187
187
 
@@ -170,24 +170,18 @@ describe Dwf::Client, client: true do
170
170
 
171
171
  before do
172
172
  allow(client).to receive(:set)
173
- redis.set("wf_enqueue_outgoing_jobs_#{workflow_id}-#{job_name}", 'running')
174
- client.check_or_lock(workflow_id, job_name)
175
- end
176
-
177
- it { expect(client).not_to have_received(:set) }
178
- end
179
-
180
- context 'job is not running' do
181
- let(:job_name) { 'ahihi' }
182
-
183
- before do
184
- allow(redis).to receive(:set)
185
- client.check_or_lock(workflow_id, job_name)
173
+ allow(RedisMutex).to receive(:with_lock)
174
+ client.check_or_lock(workflow_id, job_name) {}
186
175
  end
187
176
 
188
177
  it do
189
- expect(redis).to have_received(:set)
190
- .with("wf_enqueue_outgoing_jobs_#{workflow_id}-#{job_name}", 'running')
178
+ expect(RedisMutex)
179
+ .to have_received(:with_lock)
180
+ .with(
181
+ "wf_enqueue_outgoing_jobs_#{workflow_id}-#{job_name}",
182
+ sleep: 0.3,
183
+ block: 2
184
+ )
191
185
  end
192
186
  end
193
187
  end
@@ -175,14 +175,10 @@ describe Dwf::Item, item: true do
175
175
  item.enqueue_outgoing_jobs
176
176
  end
177
177
 
178
- context 'outgoing jobs ready to start' do
179
- let(:started_at) { nil }
180
- it { expect(a_item).to have_received(:persist_and_perform_async!) }
181
- end
182
-
183
- context 'outgoing jobs havent ready to start' do
184
- let(:started_at) { Time.now.to_i }
185
- it { expect(a_item).not_to have_received(:persist_and_perform_async!) }
178
+ it do
179
+ expect(client_double).to have_received(:check_or_lock) do |&block|
180
+ expect(block).to be_kind_of Proc
181
+ end
186
182
  end
187
183
  end
188
184
 
@@ -360,23 +360,17 @@ describe Dwf::Workflow, workflow: true do
360
360
  let(:item) do
361
361
  Dwf::Item.new(
362
362
  workflow_id: SecureRandom.uuid,
363
- id: SecureRandom.uuid,
364
- started_at: started_at
363
+ id: SecureRandom.uuid
365
364
  )
366
365
  end
367
366
  before do
368
- allow(item).to receive(:persist_and_perform_async!)
369
367
  workflow.enqueue_outgoing_jobs
370
368
  end
371
369
 
372
- context 'outgoing jobs ready to start' do
373
- let(:started_at) { nil }
374
- it { expect(item).to have_received(:persist_and_perform_async!) }
375
- end
376
-
377
- context 'outgoing jobs havent ready to start' do
378
- let(:started_at) { Time.now.to_i }
379
- it { expect(item).not_to have_received(:persist_and_perform_async!) }
370
+ it do
371
+ expect(client).to have_received(:check_or_lock) do |&block|
372
+ expect(block).to be_kind_of Proc
373
+ end
380
374
  end
381
375
  end
382
376
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: dwf
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.12
4
+ version: 0.1.13
5
5
  platform: ruby
6
6
  authors:
7
7
  - dthtien
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-10-10 00:00:00.000000000 Z
11
+ date: 2021-10-16 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: byebug
@@ -52,6 +52,20 @@ dependencies:
52
52
  - - "~>"
53
53
  - !ruby/object:Gem::Version
54
54
  version: 4.2.0
55
+ - !ruby/object:Gem::Dependency
56
+ name: redis-mutex
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: 4.0.2
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: 4.0.2
55
69
  - !ruby/object:Gem::Dependency
56
70
  name: rspec
57
71
  requirement: !ruby/object:Gem::Requirement