dynflow 1.1.6 → 1.2.0.pre1
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/Gemfile +1 -1
- data/dynflow.gemspec +2 -2
- data/examples/clock_benchmark.rb +35 -0
- data/examples/memory_limit_watcher.rb +1 -1
- data/lib/dynflow/action.rb +2 -2
- data/lib/dynflow/action/suspended.rb +1 -1
- data/lib/dynflow/action/with_sub_plans.rb +1 -1
- data/lib/dynflow/actor.rb +2 -2
- data/lib/dynflow/actors/execution_plan_cleaner.rb +1 -1
- data/lib/dynflow/clock.rb +11 -8
- data/lib/dynflow/delayed_executors/abstract.rb +1 -1
- data/lib/dynflow/delayed_plan.rb +1 -1
- data/lib/dynflow/director.rb +4 -4
- data/lib/dynflow/director/execution_plan_manager.rb +2 -2
- data/lib/dynflow/director/running_steps_manager.rb +4 -4
- data/lib/dynflow/dispatcher/client_dispatcher.rb +13 -12
- data/lib/dynflow/dispatcher/executor_dispatcher.rb +5 -5
- data/lib/dynflow/execution_plan.rb +1 -1
- data/lib/dynflow/execution_plan/steps/plan_step.rb +4 -2
- data/lib/dynflow/executors/abstract.rb +6 -6
- data/lib/dynflow/executors/parallel.rb +6 -6
- data/lib/dynflow/executors/parallel/core.rb +1 -1
- data/lib/dynflow/rails/daemon.rb +1 -1
- data/lib/dynflow/testing/dummy_executor.rb +2 -2
- data/lib/dynflow/testing/dummy_world.rb +1 -1
- data/lib/dynflow/testing/in_thread_executor.rb +5 -5
- data/lib/dynflow/testing/in_thread_world.rb +6 -6
- data/lib/dynflow/throttle_limiter.rb +5 -5
- data/lib/dynflow/utils.rb +3 -140
- data/lib/dynflow/utils/indifferent_hash.rb +143 -0
- data/lib/dynflow/utils/priority_queue.rb +64 -0
- data/lib/dynflow/version.rb +1 -1
- data/lib/dynflow/world.rb +22 -22
- data/lib/dynflow/world/invalidation.rb +1 -1
- data/test/batch_sub_tasks_test.rb +4 -4
- data/test/concurrency_control_test.rb +6 -6
- data/test/daemon_test.rb +2 -2
- data/test/dispatcher_test.rb +6 -6
- data/test/execution_plan_test.rb +11 -0
- data/test/executor_test.rb +1 -1
- data/test/support/dummy_example.rb +1 -1
- data/test/test_helper.rb +17 -17
- data/test/utils_test.rb +56 -0
- data/test/world_test.rb +2 -2
- metadata +14 -9
    
        checksums.yaml
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            ---
         | 
| 2 2 | 
             
            SHA256:
         | 
| 3 | 
            -
              metadata.gz:  | 
| 4 | 
            -
              data.tar.gz:  | 
| 3 | 
            +
              metadata.gz: 6fcc2f12b17318ec09919aae5873ee7aa9e16ab3b6f96508b94c0cf8ae3d0a91
         | 
| 4 | 
            +
              data.tar.gz: 9a71c09beb7ed1b0cf75ca858dfacc41ae4e33f5c2b82af142f1aac809b2b8f9
         | 
| 5 5 | 
             
            SHA512:
         | 
| 6 | 
            -
              metadata.gz:  | 
| 7 | 
            -
              data.tar.gz:  | 
| 6 | 
            +
              metadata.gz: e48b378aacfd2a9cdd2c57a3759abe33ba22439615af459a0c779ddf448cfab5b7cf76de655d2450c2190e444fc2586578e86af465488bb0c622e7c94fd96cdb
         | 
| 7 | 
            +
              data.tar.gz: 0d99ae6fb8f1d68d503610e49f69e1ce344129d0588e2ebe9a57bf3d540790d8f68229f0fa50b7aa1305d539f902c21dbf57ff96871ddaffadf37e56cbcf0924
         | 
    
        data/Gemfile
    CHANGED
    
    
    
        data/dynflow.gemspec
    CHANGED
    
    | @@ -21,8 +21,8 @@ Gem::Specification.new do |s| | |
| 21 21 | 
             
              s.add_dependency "multi_json"
         | 
| 22 22 | 
             
              s.add_dependency "apipie-params"
         | 
| 23 23 | 
             
              s.add_dependency "algebrick", '~> 0.7.0'
         | 
| 24 | 
            -
              s.add_dependency "concurrent-ruby", '~> 1. | 
| 25 | 
            -
              s.add_dependency "concurrent-ruby-edge", '~> 0. | 
| 24 | 
            +
              s.add_dependency "concurrent-ruby", '~> 1.1.3'
         | 
| 25 | 
            +
              s.add_dependency "concurrent-ruby-edge", '~> 0.4.1'
         | 
| 26 26 | 
             
              s.add_dependency "sequel", '>= 4.0.0'
         | 
| 27 27 |  | 
| 28 28 | 
             
              s.add_development_dependency "rake"
         | 
| @@ -0,0 +1,35 @@ | |
| 1 | 
            +
            require 'dynflow'
         | 
| 2 | 
            +
            require 'benchmark'
         | 
| 3 | 
            +
             | 
| 4 | 
            +
            class Receiver
         | 
| 5 | 
            +
              def initialize(limit, future)
         | 
| 6 | 
            +
                @limit = limit
         | 
| 7 | 
            +
                @future = future
         | 
| 8 | 
            +
                @counter = 0
         | 
| 9 | 
            +
              end
         | 
| 10 | 
            +
             | 
| 11 | 
            +
              def null
         | 
| 12 | 
            +
                @counter += 1
         | 
| 13 | 
            +
                @future.fulfill(true) if @counter >= @limit
         | 
| 14 | 
            +
              end
         | 
| 15 | 
            +
            end
         | 
| 16 | 
            +
             | 
| 17 | 
            +
            def test_case(count)
         | 
| 18 | 
            +
              future   = Concurrent::Promises.resolvable_future
         | 
| 19 | 
            +
              clock    = Dynflow::Clock.spawn(:name => 'clock')
         | 
| 20 | 
            +
              receiver = Receiver.new(count, future)
         | 
| 21 | 
            +
             | 
| 22 | 
            +
              count.times do
         | 
| 23 | 
            +
                clock.ping(receiver, 0, nil, :null)
         | 
| 24 | 
            +
              end
         | 
| 25 | 
            +
              future.wait
         | 
| 26 | 
            +
            end
         | 
| 27 | 
            +
             | 
| 28 | 
            +
            Benchmark.bm do |bm|
         | 
| 29 | 
            +
              bm.report('   100') { test_case 100 }
         | 
| 30 | 
            +
              bm.report('  1000') { test_case 1_000 }
         | 
| 31 | 
            +
              bm.report('  5000') { test_case 5_000 }
         | 
| 32 | 
            +
              bm.report(' 10000') { test_case 10_000 }
         | 
| 33 | 
            +
              bm.report(' 50000') { test_case 50_000 }
         | 
| 34 | 
            +
              bm.report('100000') { test_case 100_000 }
         | 
| 35 | 
            +
            end
         | 
    
        data/lib/dynflow/action.rb
    CHANGED
    
    | @@ -161,11 +161,11 @@ module Dynflow | |
| 161 161 | 
             
                end
         | 
| 162 162 |  | 
| 163 163 | 
             
                def caller_action
         | 
| 164 | 
            -
                   | 
| 164 | 
            +
                  phase! Present
         | 
| 165 165 | 
             
                  return nil if @caller_action_id
         | 
| 166 166 | 
             
                  return @caller_action if @caller_action
         | 
| 167 167 |  | 
| 168 | 
            -
                  caller_execution_plan = if @caller_execution_plan_id | 
| 168 | 
            +
                  caller_execution_plan = if @caller_execution_plan_id.nil?
         | 
| 169 169 | 
             
                                            execution_plan
         | 
| 170 170 | 
             
                                          else
         | 
| 171 171 | 
             
                                            world.persistence.load_execution_plan(@caller_execution_plan_id)
         | 
| @@ -171,7 +171,7 @@ module Dynflow | |
| 171 171 | 
             
                def notify_on_finish(plans)
         | 
| 172 172 | 
             
                  suspend do |suspended_action|
         | 
| 173 173 | 
             
                    plans.each do |plan|
         | 
| 174 | 
            -
                      plan.finished. | 
| 174 | 
            +
                      plan.finished.on_resolution! do |success, value|
         | 
| 175 175 | 
             
                        suspended_action << SubPlanFinished[plan.id, success && (value.result == :success)]
         | 
| 176 176 | 
             
                      end
         | 
| 177 177 | 
             
                    end
         | 
    
        data/lib/dynflow/actor.rb
    CHANGED
    
    | @@ -20,7 +20,7 @@ module Dynflow | |
| 20 20 | 
             
                    message, terminated_future = envelope
         | 
| 21 21 | 
             
                    if :start_termination == message
         | 
| 22 22 | 
             
                      context.start_termination(terminated_future)
         | 
| 23 | 
            -
                      envelope.future. | 
| 23 | 
            +
                      envelope.future.fulfill true if !envelope.future.nil?
         | 
| 24 24 | 
             
                      Concurrent::Actor::Behaviour::MESSAGE_PROCESSED
         | 
| 25 25 | 
             
                    else
         | 
| 26 26 | 
             
                      pass envelope
         | 
| @@ -35,7 +35,7 @@ module Dynflow | |
| 35 35 | 
             
                end
         | 
| 36 36 |  | 
| 37 37 | 
             
                def finish_termination
         | 
| 38 | 
            -
                  @terminated. | 
| 38 | 
            +
                  @terminated.fulfill(true)
         | 
| 39 39 | 
             
                  reference.tell(:terminate!)
         | 
| 40 40 | 
             
                end
         | 
| 41 41 |  | 
| @@ -13,7 +13,7 @@ module Dynflow | |
| 13 13 | 
             
                  end
         | 
| 14 14 |  | 
| 15 15 | 
             
                  def spawn
         | 
| 16 | 
            -
                    Concurrent. | 
| 16 | 
            +
                    Concurrent::Promises.resolvable_future.tap do |initialized|
         | 
| 17 17 | 
             
                      @core = core_class.spawn(:name => 'execution-plan-cleaner',
         | 
| 18 18 | 
             
                                               :args => [@world, @options],
         | 
| 19 19 | 
             
                                               :initialized => initialized)
         | 
    
        data/lib/dynflow/clock.rb
    CHANGED
    
    | @@ -1,6 +1,4 @@ | |
| 1 1 | 
             
            module Dynflow
         | 
| 2 | 
            -
              require 'set'
         | 
| 3 | 
            -
             | 
| 4 2 | 
             
              class Clock < Actor
         | 
| 5 3 |  | 
| 6 4 | 
             
                include Algebrick::Types
         | 
| @@ -48,8 +46,9 @@ module Dynflow | |
| 48 46 | 
             
                           Pill = type { fields Float }
         | 
| 49 47 | 
             
                end
         | 
| 50 48 |  | 
| 51 | 
            -
                def initialize
         | 
| 52 | 
            -
                  @ | 
| 49 | 
            +
                def initialize(logger = nil)
         | 
| 50 | 
            +
                  @logger        = logger
         | 
| 51 | 
            +
                  @timers        = Utils::PriorityQueue.new { |a, b| b <=> a }
         | 
| 53 52 | 
             
                  @sleeping_pill = None
         | 
| 54 53 | 
             
                  @sleep_barrier = Mutex.new
         | 
| 55 54 | 
             
                  @sleeper       = Thread.new { sleeping }
         | 
| @@ -72,7 +71,7 @@ module Dynflow | |
| 72 71 | 
             
                end
         | 
| 73 72 |  | 
| 74 73 | 
             
                def add_timer(timer)
         | 
| 75 | 
            -
                  @timers. | 
| 74 | 
            +
                  @timers.push timer
         | 
| 76 75 | 
             
                  if @timers.size == 1
         | 
| 77 76 | 
             
                    sleep_to timer
         | 
| 78 77 | 
             
                  else
         | 
| @@ -84,13 +83,17 @@ module Dynflow | |
| 84 83 |  | 
| 85 84 | 
             
                def run_ready_timers
         | 
| 86 85 | 
             
                  while first_timer && first_timer.when <= Time.now
         | 
| 87 | 
            -
                     | 
| 88 | 
            -
             | 
| 86 | 
            +
                    begin
         | 
| 87 | 
            +
                      first_timer.apply
         | 
| 88 | 
            +
                    rescue => e
         | 
| 89 | 
            +
                      @logger && @logger.error("Failed to apply clock event #{first_timer}, exception: #{e}")
         | 
| 90 | 
            +
                    end
         | 
| 91 | 
            +
                    @timers.pop
         | 
| 89 92 | 
             
                  end
         | 
| 90 93 | 
             
                end
         | 
| 91 94 |  | 
| 92 95 | 
             
                def first_timer
         | 
| 93 | 
            -
                  @timers. | 
| 96 | 
            +
                  @timers.top
         | 
| 94 97 | 
             
                end
         | 
| 95 98 |  | 
| 96 99 | 
             
                def wakeup
         | 
    
        data/lib/dynflow/delayed_plan.rb
    CHANGED
    
    | @@ -47,7 +47,7 @@ module Dynflow | |
| 47 47 | 
             
                  return true
         | 
| 48 48 | 
             
                end
         | 
| 49 49 |  | 
| 50 | 
            -
                def execute(future = Concurrent. | 
| 50 | 
            +
                def execute(future = Concurrent::Promises.resolvable_future)
         | 
| 51 51 | 
             
                  @world.execute(@execution_plan_uuid, future)
         | 
| 52 52 | 
             
                  ::Dynflow::World::Triggered[@execution_plan_uuid, future]
         | 
| 53 53 | 
             
                end
         | 
    
        data/lib/dynflow/director.rb
    CHANGED
    
    | @@ -13,7 +13,7 @@ module Dynflow | |
| 13 13 | 
             
                  fields! execution_plan_id: String,
         | 
| 14 14 | 
             
                          step_id:           Integer,
         | 
| 15 15 | 
             
                          event:             Object,
         | 
| 16 | 
            -
                          result:            Concurrent:: | 
| 16 | 
            +
                          result:            Concurrent::Promises::ResolvableFuture
         | 
| 17 17 | 
             
                end
         | 
| 18 18 |  | 
| 19 19 | 
             
                UnprocessableEvent = Class.new(Dynflow::Error)
         | 
| @@ -103,7 +103,7 @@ module Dynflow | |
| 103 103 | 
             
                    raise Dynflow::Error, "no manager for #{event.inspect}"
         | 
| 104 104 | 
             
                  end
         | 
| 105 105 | 
             
                rescue Dynflow::Error => e
         | 
| 106 | 
            -
                  event.result. | 
| 106 | 
            +
                  event.result.reject e.message
         | 
| 107 107 | 
             
                  raise e
         | 
| 108 108 | 
             
                end
         | 
| 109 109 |  | 
| @@ -203,13 +203,13 @@ module Dynflow | |
| 203 203 | 
             
                  @execution_plan_managers[execution_plan_id] =
         | 
| 204 204 | 
             
                      ExecutionPlanManager.new(@world, execution_plan, finished)
         | 
| 205 205 | 
             
                rescue Dynflow::Error => e
         | 
| 206 | 
            -
                  finished. | 
| 206 | 
            +
                  finished.reject e
         | 
| 207 207 | 
             
                  nil
         | 
| 208 208 | 
             
                end
         | 
| 209 209 |  | 
| 210 210 | 
             
                def set_future(manager)
         | 
| 211 211 | 
             
                  @rescued_steps.delete(manager.execution_plan.id)
         | 
| 212 | 
            -
                  manager.future. | 
| 212 | 
            +
                  manager.future.fulfill manager.execution_plan
         | 
| 213 213 | 
             
                end
         | 
| 214 214 | 
             
              end
         | 
| 215 215 | 
             
            end
         | 
| @@ -9,7 +9,7 @@ module Dynflow | |
| 9 9 | 
             
                  def initialize(world, execution_plan, future)
         | 
| 10 10 | 
             
                    @world                 = Type! world, World
         | 
| 11 11 | 
             
                    @execution_plan        = Type! execution_plan, ExecutionPlan
         | 
| 12 | 
            -
                    @future                = Type! future, Concurrent:: | 
| 12 | 
            +
                    @future                = Type! future, Concurrent::Promises::ResolvableFuture
         | 
| 13 13 | 
             
                    @running_steps_manager = RunningStepsManager.new(world)
         | 
| 14 14 |  | 
| 15 15 | 
             
                    unless [:planned, :paused].include? execution_plan.state
         | 
| @@ -20,7 +20,7 @@ module Dynflow | |
| 20 20 | 
             
                  end
         | 
| 21 21 |  | 
| 22 22 | 
             
                  def start
         | 
| 23 | 
            -
                    raise "The future was already set" if @future. | 
| 23 | 
            +
                    raise "The future was already set" if @future.resolved?
         | 
| 24 24 | 
             
                    start_run or start_finalize or finish
         | 
| 25 25 | 
             
                  end
         | 
| 26 26 |  | 
| @@ -15,7 +15,7 @@ module Dynflow | |
| 15 15 | 
             
                    pending_work = @events.clear.values.flatten(1)
         | 
| 16 16 | 
             
                    pending_work.each do |w|
         | 
| 17 17 | 
             
                      if EventWorkItem === w
         | 
| 18 | 
            -
                        w.event.result. | 
| 18 | 
            +
                        w.event.result.reject UnprocessableEvent.new("dropping due to termination")
         | 
| 19 19 | 
             
                      end
         | 
| 20 20 | 
             
                    end
         | 
| 21 21 | 
             
                  end
         | 
| @@ -32,7 +32,7 @@ module Dynflow | |
| 32 32 | 
             
                  def done(step)
         | 
| 33 33 | 
             
                    Type! step, ExecutionPlan::Steps::RunStep
         | 
| 34 34 | 
             
                    @events.shift(step.id).tap do |work|
         | 
| 35 | 
            -
                      work.event.result. | 
| 35 | 
            +
                      work.event.result.fulfill true if EventWorkItem === work
         | 
| 36 36 | 
             
                    end
         | 
| 37 37 |  | 
| 38 38 | 
             
                    if step.state == :suspended
         | 
| @@ -41,7 +41,7 @@ module Dynflow | |
| 41 41 | 
             
                      while (event = @events.shift(step.id))
         | 
| 42 42 | 
             
                        message = "step #{step.execution_plan_id}:#{step.id} dropping event #{event.event}"
         | 
| 43 43 | 
             
                        @world.logger.warn message
         | 
| 44 | 
            -
                        event.event.result. | 
| 44 | 
            +
                        event.event.result.reject UnprocessableEvent.new(message).
         | 
| 45 45 | 
             
                            tap { |e| e.set_backtrace(caller) }
         | 
| 46 46 | 
             
                      end
         | 
| 47 47 | 
             
                      raise 'assert' unless @events.empty?(step.id)
         | 
| @@ -64,7 +64,7 @@ module Dynflow | |
| 64 64 |  | 
| 65 65 | 
             
                    step = @running_steps[event.step_id]
         | 
| 66 66 | 
             
                    unless step
         | 
| 67 | 
            -
                      event.result. | 
| 67 | 
            +
                      event.result.reject UnprocessableEvent.new('step is not suspended, it cannot process events')
         | 
| 68 68 | 
             
                      return next_work_items
         | 
| 69 69 | 
             
                    end
         | 
| 70 70 |  | 
| @@ -4,24 +4,24 @@ module Dynflow | |
| 4 4 |  | 
| 5 5 | 
             
                  TrackedRequest = Algebrick.type do
         | 
| 6 6 | 
             
                    fields! id: Integer, request: Request,
         | 
| 7 | 
            -
                            accepted: Concurrent:: | 
| 7 | 
            +
                            accepted: Concurrent::Promises::ResolvableFuture, finished: Concurrent::Promises::ResolvableFuture
         | 
| 8 8 | 
             
                  end
         | 
| 9 9 |  | 
| 10 10 | 
             
                  module TrackedRequest
         | 
| 11 11 | 
             
                    def accept!
         | 
| 12 | 
            -
                      accepted. | 
| 12 | 
            +
                      accepted.fulfill true unless accepted.resolved?
         | 
| 13 13 | 
             
                      self
         | 
| 14 14 | 
             
                    end
         | 
| 15 15 |  | 
| 16 16 | 
             
                    def fail!(error)
         | 
| 17 | 
            -
                      accepted. | 
| 18 | 
            -
                      finished. | 
| 17 | 
            +
                      accepted.reject error unless accepted.resolved?
         | 
| 18 | 
            +
                      finished.reject error
         | 
| 19 19 | 
             
                      self
         | 
| 20 20 | 
             
                    end
         | 
| 21 21 |  | 
| 22 22 | 
             
                    def success!(resolve_to)
         | 
| 23 | 
            -
                      accepted. | 
| 24 | 
            -
                      finished. | 
| 23 | 
            +
                      accepted.fulfill true unless accepted.resolved?
         | 
| 24 | 
            +
                      finished.fulfill(resolve_to)
         | 
| 25 25 | 
             
                      self
         | 
| 26 26 | 
             
                    end
         | 
| 27 27 | 
             
                  end
         | 
| @@ -198,7 +198,7 @@ module Dynflow | |
| 198 198 |  | 
| 199 199 | 
             
                  def track_request(finished, request, timeout)
         | 
| 200 200 | 
             
                    id = @last_id += 1
         | 
| 201 | 
            -
                    tracked_request = TrackedRequest[id, request, Concurrent. | 
| 201 | 
            +
                    tracked_request = TrackedRequest[id, request, Concurrent::Promises.resolvable_future, finished]
         | 
| 202 202 | 
             
                    @tracked_requests[id] = tracked_request
         | 
| 203 203 | 
             
                    @world.clock.ping(self, timeout, [:timeout, id]) if timeout
         | 
| 204 204 | 
             
                    yield tracked_request
         | 
| @@ -208,13 +208,14 @@ module Dynflow | |
| 208 208 | 
             
                  end
         | 
| 209 209 |  | 
| 210 210 | 
             
                  def reset_tracked_request(tracked_request)
         | 
| 211 | 
            -
                    if tracked_request.finished. | 
| 211 | 
            +
                    if tracked_request.finished.resolved?
         | 
| 212 212 | 
             
                      raise Dynflow::Error.new('Can not reset resolved tracked request')
         | 
| 213 213 | 
             
                    end
         | 
| 214 | 
            -
                    unless tracked_request.accepted. | 
| 214 | 
            +
                    unless tracked_request.accepted.resolved?
         | 
| 215 215 | 
             
                      tracked_request.accept! # otherwise nobody would set the accept future
         | 
| 216 216 | 
             
                    end
         | 
| 217 | 
            -
                     | 
| 217 | 
            +
                    future = Concurrent::Promises.resolvable_future
         | 
| 218 | 
            +
                    @tracked_requests[tracked_request.id] = TrackedRequest[tracked_request.id, tracked_request.request, future, tracked_request.finished]
         | 
| 218 219 | 
             
                  end
         | 
| 219 220 |  | 
| 220 221 | 
             
                  def resolve_tracked_request(id, error = nil)
         | 
| @@ -246,10 +247,10 @@ module Dynflow | |
| 246 247 | 
             
                    return yield unless request.use_cache
         | 
| 247 248 |  | 
| 248 249 | 
             
                    if @ping_cache.fresh_record?(request.receiver_id)
         | 
| 249 | 
            -
                      future. | 
| 250 | 
            +
                      future.fulfill(true)
         | 
| 250 251 | 
             
                    else
         | 
| 251 252 | 
             
                      if @ping_cache.executor?(request.receiver_id)
         | 
| 252 | 
            -
                        future. | 
| 253 | 
            +
                        future.reject
         | 
| 253 254 | 
             
                      else
         | 
| 254 255 | 
             
                        yield
         | 
| 255 256 | 
             
                      end
         | 
| @@ -29,7 +29,7 @@ module Dynflow | |
| 29 29 | 
             
                    @world.executor.execute(execution.execution_plan_id, future)
         | 
| 30 30 | 
             
                    respond(envelope, Accepted)
         | 
| 31 31 | 
             
                  rescue Dynflow::Error => e
         | 
| 32 | 
            -
                    future. | 
| 32 | 
            +
                    future.reject(e) if future && !future.resolved?
         | 
| 33 33 | 
             
                    respond(envelope, Failed[e.message])
         | 
| 34 34 | 
             
                  end
         | 
| 35 35 |  | 
| @@ -52,7 +52,7 @@ module Dynflow | |
| 52 52 | 
             
                    end
         | 
| 53 53 | 
             
                    @world.executor.event(event_request.execution_plan_id, event_request.step_id, event_request.event, future)
         | 
| 54 54 | 
             
                  rescue Dynflow::Error => e
         | 
| 55 | 
            -
                    future. | 
| 55 | 
            +
                    future.reject(e) if future && !future.resolved?
         | 
| 56 56 | 
             
                  end
         | 
| 57 57 |  | 
| 58 58 | 
             
                  def start_termination(*args)
         | 
| @@ -60,7 +60,7 @@ module Dynflow | |
| 60 60 | 
             
                    if @current_futures.empty?
         | 
| 61 61 | 
             
                      reference.tell(:finish_termination)
         | 
| 62 62 | 
             
                    else
         | 
| 63 | 
            -
                      Concurrent. | 
| 63 | 
            +
                      Concurrent::Promises.zip_futures(*@current_futures).then { reference.tell(:finish_termination) }
         | 
| 64 64 | 
             
                    end
         | 
| 65 65 | 
             
                  end
         | 
| 66 66 |  | 
| @@ -78,13 +78,13 @@ module Dynflow | |
| 78 78 |  | 
| 79 79 | 
             
                  def on_finish
         | 
| 80 80 | 
             
                    raise "Dispatcher terminating: no new work can be started" if terminating?
         | 
| 81 | 
            -
                    future = Concurrent. | 
| 81 | 
            +
                    future = Concurrent::Promises.resolvable_future
         | 
| 82 82 | 
             
                    callbacks_future = (yield future).rescue { |reason| @world.logger.error("Unexpected fail on future #{reason}") }
         | 
| 83 83 | 
             
                    # we track currently running futures to make sure to not
         | 
| 84 84 | 
             
                    # terminate until the execution is finished (including
         | 
| 85 85 | 
             
                    # cleaning of locks etc)
         | 
| 86 86 | 
             
                    @current_futures << callbacks_future
         | 
| 87 | 
            -
                    callbacks_future. | 
| 87 | 
            +
                    callbacks_future.on_resolution! { reference.tell([:finish_execution, callbacks_future]) }
         | 
| 88 88 | 
             
                    return future
         | 
| 89 89 | 
             
                  end
         | 
| 90 90 |  | 
| @@ -305,7 +305,7 @@ module Dynflow | |
| 305 305 | 
             
                # array with the future value of the cancel result)
         | 
| 306 306 | 
             
                def cancel(force = false)
         | 
| 307 307 | 
             
                  if state == :scheduled
         | 
| 308 | 
            -
                    [Concurrent. | 
| 308 | 
            +
                    [Concurrent::Promises.resolvable_future.tap { |f| f.fulfill delay_record.cancel }]
         | 
| 309 309 | 
             
                  else
         | 
| 310 310 | 
             
                    event = force ? ::Dynflow::Action::Cancellable::Abort : ::Dynflow::Action::Cancellable::Cancel
         | 
| 311 311 | 
             
                    steps_to_cancel.map do |step|
         | 
| @@ -98,8 +98,10 @@ module Dynflow | |
| 98 98 | 
             
                                   finalize_step_id:  nil,
         | 
| 99 99 | 
             
                                   phase:             phase }
         | 
| 100 100 | 
             
                    if caller_action
         | 
| 101 | 
            -
                       | 
| 102 | 
            -
             | 
| 101 | 
            +
                      if caller_action.execution_plan_id != execution_plan_id
         | 
| 102 | 
            +
                        attributes.update(caller_execution_plan_id: caller_action.execution_plan_id)
         | 
| 103 | 
            +
                      end
         | 
| 104 | 
            +
                      attributes.update(caller_action_id: caller_action.id)
         | 
| 103 105 | 
             
                    end
         | 
| 104 106 | 
             
                    @action = action_class.new(attributes, world)
         | 
| 105 107 | 
             
                    persistence.save_action(execution_plan_id, @action)
         | 
| @@ -10,20 +10,20 @@ module Dynflow | |
| 10 10 | 
             
                  end
         | 
| 11 11 |  | 
| 12 12 | 
             
                  # @param execution_plan_id [String] id of execution plan
         | 
| 13 | 
            -
                  # @param finished [Concurrent:: | 
| 13 | 
            +
                  # @param finished [Concurrent::Promises::ResolvableFuture]
         | 
| 14 14 | 
             
                  # @param wait_for_acceptance [TrueClass|FalseClass] should the executor confirm receiving
         | 
| 15 15 | 
             
                  # the event, disable if calling executor from within executor
         | 
| 16 | 
            -
                  # @return [Concurrent:: | 
| 16 | 
            +
                  # @return [Concurrent::Promises::ResolvableFuture]
         | 
| 17 17 | 
             
                  # @raise when execution_plan_id is not accepted
         | 
| 18 | 
            -
                  def execute(execution_plan_id, finished = Concurrent. | 
| 18 | 
            +
                  def execute(execution_plan_id, finished = Concurrent::Promises.resolvable_future, wait_for_acceptance = true)
         | 
| 19 19 | 
             
                    raise NotImplementedError
         | 
| 20 20 | 
             
                  end
         | 
| 21 21 |  | 
| 22 | 
            -
                  def event(execution_plan_id, step_id, event, future = Concurrent. | 
| 22 | 
            +
                  def event(execution_plan_id, step_id, event, future = Concurrent::Promises.resolvable_future)
         | 
| 23 23 | 
             
                    raise NotImplementedError
         | 
| 24 24 | 
             
                  end
         | 
| 25 25 |  | 
| 26 | 
            -
                  def terminate(future = Concurrent. | 
| 26 | 
            +
                  def terminate(future = Concurrent::Promises.resolvable_future)
         | 
| 27 27 | 
             
                    raise NotImplementedError
         | 
| 28 28 | 
             
                  end
         | 
| 29 29 |  | 
| @@ -31,7 +31,7 @@ module Dynflow | |
| 31 31 | 
             
                    raise NotImplementedError
         | 
| 32 32 | 
             
                  end
         | 
| 33 33 |  | 
| 34 | 
            -
                  # @return [Concurrent:: | 
| 34 | 
            +
                  # @return [Concurrent::Promises::ResolvableFuture]
         | 
| 35 35 | 
             
                  def initialized
         | 
| 36 36 | 
             
                    raise NotImplementedError
         | 
| 37 37 | 
             
                  end
         |