dynflow 0.8.28 → 0.8.29

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
  SHA1:
3
- metadata.gz: f99f6ec873faccef730dd4fb6ce79c51312bc21b
4
- data.tar.gz: 74aa5ea6b417558dbfcda2c8b1d2f440a92691ea
3
+ metadata.gz: 52093e656e80afba8c3cfef6548c6abb978013af
4
+ data.tar.gz: 16d58712d8e3db65deb9f4c5f74a6f44d1fa4e0a
5
5
  SHA512:
6
- metadata.gz: 1b9a59414928f60a2cda255b246f43499914674bf3d11dbf8055298ca3313b70cc5bd0723faec8564783390699e0e949134dc246ce0cd4ef2e00fb68542a85ad
7
- data.tar.gz: 05f6150a9224ee2e1ce2eb0308f6b3ee35e75ffdfad4498259c2d4337622d7df8c30139974650adb16ea535de2027df7e23dba52b435ddaace0de6f15409fce1
6
+ metadata.gz: aeb9f630d85d36e9800f85faaaffcae780aa6b00e5783b5a001aa94dbf4eac56ebc9574eeee8115de0aea5c86e3451735e4e57d4e31bf96aaf3bc7f7f8b05a9b
7
+ data.tar.gz: ec28ae5dc1222952d5d5676937889e7c2f247f279475c24a6c24656f806d1fff60c78351ef343727af5635defe5dc3936298823e9cb88540ac28b772544bf182
data/dynflow.gemspec CHANGED
@@ -29,8 +29,8 @@ Gem::Specification.new do |s|
29
29
  s.add_development_dependency "rack-test"
30
30
  s.add_development_dependency "minitest"
31
31
  s.add_development_dependency "minitest-reporters"
32
- s.add_development_dependency "activerecord", '< 5.0.0'
33
- s.add_development_dependency 'activejob', '< 5.0.0'
32
+ s.add_development_dependency "activerecord"
33
+ s.add_development_dependency 'activejob'
34
34
  s.add_development_dependency "sqlite3"
35
35
  s.add_development_dependency "sinatra"
36
36
  s.add_development_dependency 'mocha'
data/lib/dynflow.rb CHANGED
@@ -23,6 +23,7 @@ module Dynflow
23
23
 
24
24
  require 'dynflow/utils'
25
25
  require 'dynflow/round_robin'
26
+ require 'dynflow/dead_letter_silencer'
26
27
  require 'dynflow/actor'
27
28
  require 'dynflow/actors'
28
29
  require 'dynflow/errors'
@@ -1,21 +1,25 @@
1
1
  module Dynflow
2
2
  module ActiveJob
3
3
  module QueueAdapters
4
+ module QueueMethods
5
+ def enqueue(job)
6
+ ::Rails.application.dynflow.world.trigger(JobWrapper, job.serialize)
7
+ end
8
+
9
+ def enqueue_at(job, timestamp)
10
+ ::Rails.application.dynflow.world.delay(JobWrapper, { :start_at => Time.at(timestamp) }, job.serialize)
11
+ end
12
+ end
13
+
4
14
  # To use Dynflow, set the queue_adapter config to +:dynflow+.
5
15
  #
6
16
  # Rails.application.config.active_job.queue_adapter = :dynflow
7
17
  class DynflowAdapter
8
- extend ActiveSupport::Concern
9
-
10
- class << self
11
- def enqueue(job)
12
- ::Rails.application.dynflow.world.trigger(JobWrapper, job.serialize)
13
- end
18
+ # For ActiveJob >= 5
19
+ include QueueMethods
14
20
 
15
- def enqueue_at(job, timestamp)
16
- ::Rails.application.dynflow.world.delay(JobWrapper, { :start_at => Time.at(timestamp) }, job.serialize)
17
- end
18
- end
21
+ # For ActiveJob <= 4
22
+ extend QueueMethods
19
23
  end
20
24
 
21
25
  class JobWrapper < Dynflow::Action
@@ -97,6 +97,13 @@ module Dynflow
97
97
  true
98
98
  end
99
99
 
100
+ config_attr :silent_dead_letter_matchers, Array do
101
+ # By default suppress dead letters sent by Clock
102
+ [
103
+ DeadLetterSilencer::Matcher.new(::Dynflow::Clock)
104
+ ]
105
+ end
106
+
100
107
  config_attr :delayed_executor, DelayedExecutors::Abstract, NilClass do |world|
101
108
  options = { :poll_interval => 15,
102
109
  :time_source => -> { Time.now.utc } }
@@ -0,0 +1,46 @@
1
+ module Dynflow
2
+ class DeadLetterSilencer < Concurrent::Actor::DefaultDeadLetterHandler
3
+ def initialize(matchers)
4
+ @matchers = Type! matchers, Array
5
+ end
6
+
7
+ def should_drop?(dead_letter)
8
+ @matchers.any? { |matcher| matcher.match? dead_letter }
9
+ end
10
+
11
+ def on_message(dead_letter)
12
+ super unless should_drop?(dead_letter)
13
+ end
14
+
15
+ private
16
+
17
+ class Matcher
18
+ Any = Algebrick.atom
19
+
20
+ def initialize(from, message = Any, to = Any)
21
+ @from = from
22
+ @message = message
23
+ @to = to
24
+ end
25
+
26
+ def match?(dead_letter)
27
+ evaluate(dead_letter.sender.actor_class, @from) &&
28
+ evaluate(dead_letter.message, @message) &&
29
+ evaluate(dead_letter.address.actor_class, @to)
30
+ end
31
+
32
+ private
33
+
34
+ def evaluate(thing, condition)
35
+ case condition
36
+ when Any
37
+ true
38
+ when Proc
39
+ condition.call(thing)
40
+ else
41
+ condition == thing
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
@@ -53,6 +53,10 @@ module Dynflow
53
53
  super
54
54
  end
55
55
 
56
+ def dead_letter_routing
57
+ @world.dead_letter_handler
58
+ end
59
+
56
60
  private
57
61
 
58
62
  def on_message(message)
@@ -1,3 +1,3 @@
1
1
  module Dynflow
2
- VERSION = '0.8.28'
2
+ VERSION = '0.8.29'
3
3
  end
data/lib/dynflow/world.rb CHANGED
@@ -8,7 +8,7 @@ module Dynflow
8
8
  :transaction_adapter, :logger_adapter, :coordinator,
9
9
  :persistence, :action_classes, :subscription_index,
10
10
  :middleware, :auto_rescue, :clock, :meta, :delayed_executor, :auto_validity_check, :validity_check_timeout, :throttle_limiter,
11
- :terminated, :execution_plan_cleaner
11
+ :terminated, :dead_letter_handler, :execution_plan_cleaner
12
12
 
13
13
  def initialize(config)
14
14
  @id = SecureRandom.uuid
@@ -29,6 +29,7 @@ module Dynflow
29
29
  @middleware = Middleware::World.new
30
30
  @middleware.use Middleware::Common::Transaction if @transaction_adapter
31
31
  @client_dispatcher = spawn_and_wait(Dispatcher::ClientDispatcher, "client-dispatcher", self)
32
+ @dead_letter_handler = spawn_and_wait(DeadLetterSilencer, 'default_dead_letter_handler', config_for_world.silent_dead_letter_matchers)
32
33
  @meta = config_for_world.meta
33
34
  @auto_validity_check = config_for_world.auto_validity_check
34
35
  @validity_check_timeout = config_for_world.validity_check_timeout
data/test/action_test.rb CHANGED
@@ -623,9 +623,9 @@ module Dynflow
623
623
  specify 'polls for sub plans state' do
624
624
  world.stub :clock, clock do
625
625
  total = 2
626
- triggered_plan = world.trigger(PollingParentAction, count: total)
627
- plan = world.persistence.load_execution_plan(triggered_plan.id)
626
+ plan = world.plan(PollingParentAction, count: total)
628
627
  plan.state.must_equal :planned
628
+ world.execute(plan.id)
629
629
  clock.pending_pings.count.must_equal 0
630
630
  wait_for do
631
631
  plan.sub_plans.count == total &&
@@ -634,7 +634,7 @@ module Dynflow
634
634
  clock.pending_pings.count.must_equal 1
635
635
  clock.progress
636
636
  wait_for do
637
- plan = world.persistence.load_execution_plan(triggered_plan.id)
637
+ plan = world.persistence.load_execution_plan(plan.id)
638
638
  plan.state == :stopped
639
639
  end
640
640
  clock.pending_pings.count.must_equal 0
@@ -644,12 +644,12 @@ module Dynflow
644
644
  specify 'starts polling for sub plans at the beginning' do
645
645
  world.stub :clock, clock do
646
646
  total = 2
647
- triggered_plan = world.trigger(PollingBulkParentAction, count: total)
648
- plan = world.persistence.load_execution_plan(triggered_plan.id)
647
+ plan = world.plan(PollingBulkParentAction, count: total)
649
648
  assert_nil plan.entry_action.output[:planning_finished]
650
649
  clock.pending_pings.count.must_equal 0
650
+ world.execute(plan.id)
651
651
  wait_for do
652
- plan = world.persistence.load_execution_plan(triggered_plan.id)
652
+ plan = world.persistence.load_execution_plan(plan.id)
653
653
  plan.entry_action.output[:planning_finished] == 1
654
654
  end
655
655
  # Poll was set during #initiate
@@ -664,7 +664,7 @@ module Dynflow
664
664
  # Poll again
665
665
  clock.progress
666
666
  wait_for do
667
- plan = world.persistence.load_execution_plan(triggered_plan.id)
667
+ plan = world.persistence.load_execution_plan(plan.id)
668
668
  plan.state == :stopped
669
669
  end
670
670
  plan.entry_action.output[:poll].must_equal 1
@@ -12,7 +12,7 @@ module Dynflow
12
12
  describe 'running jobs' do
13
13
  before(:all) do
14
14
  world = WorldFactory.create_world
15
- ::ActiveJob::QueueAdapters.include(::Dynflow::ActiveJob::QueueAdapters)
15
+ ::ActiveJob::QueueAdapters.send(:include, ::Dynflow::ActiveJob::QueueAdapters)
16
16
  ::ActiveJob::Base.queue_adapter = :dynflow
17
17
  dynflow_mock = Minitest::Mock.new
18
18
  dynflow_mock.expect(:world, world)
@@ -20,7 +20,14 @@ module Dynflow
20
20
  rails_app_mock .expect(:dynflow, dynflow_mock)
21
21
  rails_mock = Minitest::Mock.new
22
22
  rails_mock.expect(:application, rails_app_mock)
23
- ::Rails = rails_mock
23
+ @original_rails = ::Rails
24
+ Object.send(:remove_const, 'Rails')
25
+ Object.const_set('Rails', rails_mock)
26
+ end
27
+
28
+ after(:all) do
29
+ Object.send(:remove_const, 'Rails')
30
+ Object.const_set('Rails', @original_rails)
24
31
  end
25
32
 
26
33
  it 'is able to run the job right away' do
@@ -0,0 +1,46 @@
1
+ require_relative 'test_helper'
2
+ require 'ostruct'
3
+
4
+ module Dynflow
5
+ module DeadLetterSilencerTest
6
+ describe ::Dynflow::DeadLetterSilencer do
7
+ include Dynflow::Testing::Factories
8
+ include TestHelpers
9
+
10
+ let(:world) { WorldFactory.create_world }
11
+
12
+ it 'is started for each world' do
13
+ world.dead_letter_handler.actor_class
14
+ .must_equal ::Dynflow::DeadLetterSilencer
15
+ end
16
+
17
+ describe ::Dynflow::DeadLetterSilencer::Matcher do
18
+ let(:any) { DeadLetterSilencer::Matcher::Any }
19
+ let(:sender) { ::Dynflow::Clock }
20
+ let(:msg) { :ping }
21
+ let(:receiver) { ::Dynflow::DeadLetterSilencer }
22
+ let(:letter) do
23
+ OpenStruct.new(:sender => OpenStruct.new(:actor_class => sender),
24
+ :message => msg,
25
+ :address => OpenStruct.new(:actor_class => receiver))
26
+ end
27
+
28
+ it 'matches any' do
29
+ DeadLetterSilencer::Matcher.new(any, any, any).match?(letter).must_equal true
30
+ end
31
+
32
+ it 'matches comparing for equality' do
33
+ DeadLetterSilencer::Matcher.new(sender, msg, receiver)
34
+ .match?(letter).must_equal true
35
+ DeadLetterSilencer::Matcher.new(any, :bad, any).match?(letter).must_equal false
36
+ end
37
+
38
+ it 'matches by calling the proc' do
39
+ condition = proc { |actor_class| actor_class.is_a? Class }
40
+ DeadLetterSilencer::Matcher.new(condition, any, condition)
41
+ .match?(letter).must_equal true
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
@@ -231,7 +231,12 @@ module Dynflow
231
231
  stored = adapter.to_hash.fetch(:execution_plans).find { |ep| ep[:uuid].strip == original[:id] }
232
232
  stored.each { |k, v| stored[k] = v.to_s if v.is_a? Time }
233
233
  adapter.class::META_DATA.fetch(:execution_plan).each do |name|
234
- stored.fetch(name.to_sym).must_equal original.fetch(name.to_sym)
234
+ value = original.fetch(name.to_sym)
235
+ if value.nil?
236
+ stored.fetch(name.to_sym).must_be_nil
237
+ else
238
+ stored.fetch(name.to_sym).must_equal value
239
+ end
235
240
  end
236
241
  end
237
242
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: dynflow
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.8.28
4
+ version: 0.8.29
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ivan Necas
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2017-08-24 00:00:00.000000000 Z
12
+ date: 2017-09-11 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: multi_json
@@ -155,30 +155,30 @@ dependencies:
155
155
  name: activerecord
156
156
  requirement: !ruby/object:Gem::Requirement
157
157
  requirements:
158
- - - "<"
158
+ - - ">="
159
159
  - !ruby/object:Gem::Version
160
- version: 5.0.0
160
+ version: '0'
161
161
  type: :development
162
162
  prerelease: false
163
163
  version_requirements: !ruby/object:Gem::Requirement
164
164
  requirements:
165
- - - "<"
165
+ - - ">="
166
166
  - !ruby/object:Gem::Version
167
- version: 5.0.0
167
+ version: '0'
168
168
  - !ruby/object:Gem::Dependency
169
169
  name: activejob
170
170
  requirement: !ruby/object:Gem::Requirement
171
171
  requirements:
172
- - - "<"
172
+ - - ">="
173
173
  - !ruby/object:Gem::Version
174
- version: 5.0.0
174
+ version: '0'
175
175
  type: :development
176
176
  prerelease: false
177
177
  version_requirements: !ruby/object:Gem::Requirement
178
178
  requirements:
179
- - - "<"
179
+ - - ">="
180
180
  - !ruby/object:Gem::Version
181
- version: 5.0.0
181
+ version: '0'
182
182
  - !ruby/object:Gem::Dependency
183
183
  name: sqlite3
184
184
  requirement: !ruby/object:Gem::Requirement
@@ -406,6 +406,7 @@ files:
406
406
  - lib/dynflow/coordinator_adapters.rb
407
407
  - lib/dynflow/coordinator_adapters/abstract.rb
408
408
  - lib/dynflow/coordinator_adapters/sequel.rb
409
+ - lib/dynflow/dead_letter_silencer.rb
409
410
  - lib/dynflow/delayed_executors.rb
410
411
  - lib/dynflow/delayed_executors/abstract.rb
411
412
  - lib/dynflow/delayed_executors/abstract_core.rb
@@ -517,12 +518,13 @@ files:
517
518
  - lib/dynflow/world.rb
518
519
  - test/abnormal_states_recovery_test.rb
519
520
  - test/action_test.rb
520
- - test/activejob_adapter.rb
521
+ - test/activejob_adapter_test.rb
521
522
  - test/batch_sub_tasks_test.rb
522
523
  - test/clock_test.rb
523
524
  - test/concurrency_control_test.rb
524
525
  - test/coordinator_test.rb
525
526
  - test/daemon_test.rb
527
+ - test/dead_letter_silencer_test.rb
526
528
  - test/dispatcher_test.rb
527
529
  - test/execution_plan_cleaner_test.rb
528
530
  - test/execution_plan_test.rb
@@ -596,12 +598,13 @@ summary: DYNamic workFLOW engine
596
598
  test_files:
597
599
  - test/abnormal_states_recovery_test.rb
598
600
  - test/action_test.rb
599
- - test/activejob_adapter.rb
601
+ - test/activejob_adapter_test.rb
600
602
  - test/batch_sub_tasks_test.rb
601
603
  - test/clock_test.rb
602
604
  - test/concurrency_control_test.rb
603
605
  - test/coordinator_test.rb
604
606
  - test/daemon_test.rb
607
+ - test/dead_letter_silencer_test.rb
605
608
  - test/dispatcher_test.rb
606
609
  - test/execution_plan_cleaner_test.rb
607
610
  - test/execution_plan_test.rb