dynflow 0.8.28 → 0.8.29

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 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