dynflow 1.9.3 → 2.0.0

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.
Files changed (47) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/bats.yml +50 -0
  3. data/.github/workflows/release.yml +1 -1
  4. data/.github/workflows/ruby.yml +27 -46
  5. data/.gitignore +2 -0
  6. data/.rubocop.yml +3 -0
  7. data/Dockerfile +1 -1
  8. data/Gemfile +7 -8
  9. data/README.md +4 -4
  10. data/doc/pages/source/documentation/index.md +4 -4
  11. data/dynflow.gemspec +2 -3
  12. data/examples/example_helper.rb +1 -1
  13. data/examples/execution_plan_chaining.rb +56 -0
  14. data/examples/remote_executor.rb +5 -8
  15. data/lib/dynflow/action/format.rb +4 -33
  16. data/lib/dynflow/debug/telemetry/persistence.rb +1 -1
  17. data/lib/dynflow/delayed_executors/abstract_core.rb +1 -1
  18. data/lib/dynflow/delayed_plan.rb +6 -0
  19. data/lib/dynflow/director.rb +9 -1
  20. data/lib/dynflow/executors/sidekiq/core.rb +1 -1
  21. data/lib/dynflow/executors/sidekiq/redis_locking.rb +10 -3
  22. data/lib/dynflow/extensions/msgpack.rb +4 -0
  23. data/lib/dynflow/persistence.rb +14 -2
  24. data/lib/dynflow/persistence_adapters/abstract.rb +9 -1
  25. data/lib/dynflow/persistence_adapters/sequel.rb +91 -48
  26. data/lib/dynflow/persistence_adapters/sequel_migrations/025_create_execution_plan_dependencies.rb +22 -0
  27. data/lib/dynflow/rails/daemon.rb +16 -7
  28. data/lib/dynflow/testing.rb +1 -1
  29. data/lib/dynflow/version.rb +1 -1
  30. data/lib/dynflow/world.rb +34 -13
  31. data/lib/dynflow.rb +0 -1
  32. data/test/action_test.rb +3 -3
  33. data/test/bats/helpers/common.bash +67 -0
  34. data/test/bats/helpers/containers.bash +146 -0
  35. data/test/bats/setup_suite.bash +46 -0
  36. data/test/bats/sidekiq-orchestrator.bats +178 -0
  37. data/test/bats/teardown_suite.bash +16 -0
  38. data/test/concurrency_control_test.rb +0 -1
  39. data/test/daemon_test.rb +21 -2
  40. data/test/extensions_test.rb +3 -3
  41. data/test/future_execution_test.rb +150 -3
  42. data/test/persistence_test.rb +70 -3
  43. data/test/support/dummy_example.rb +4 -0
  44. data/test/test_helper.rb +19 -4
  45. data/web/views/show.erb +24 -0
  46. metadata +15 -17
  47. data/.github/install_dependencies.sh +0 -35
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5a3ed6c515677b9b75a3dc93a46780780a1606569d47afcad07206295042d881
4
- data.tar.gz: 5740e1c8fae71a447132d52773e268ec7e02886f3e15ad82a6c636e4faf74dcd
3
+ metadata.gz: 398d1dc51c8a63beda52acc918da0c570ea91ec8c19090fa2cb7ae704cc4b1a2
4
+ data.tar.gz: 5f1527042cdb29afb348031ced662fb5ac157c4c00ff14fcac73293cba31026c
5
5
  SHA512:
6
- metadata.gz: b6727fe2aaefe4f025bc443881e5c31dda3d3158f43879e70b9e09e261ea0940e714c9e7cda5fc817f2b3ba5ebb2da74ac0978590a1472d6b532e6542bf9b1ef
7
- data.tar.gz: 7c5743be066fc27bc67956e87676ccbb1c6102a1eb03f3ee6ae0cb059d6779d0714d9446895a3a2c49d3c459c217fea262797748266e1718fdc9bd0ba63d37d4
6
+ metadata.gz: ec917c5c31642bf23ebaf335804e0a151b7ab0a56f8b5d3a5cac23ab63416149844562248bddb93cff293b9d565e02998a958491cdc4f05c94d65859a4d473ef
7
+ data.tar.gz: 80202d14de3d386a5b8d1c54a48542135ab511421f71e08ef533c50c43fb28900d3318798668fa34fabdf80feec6cba4b3c11635d6ea2b766308d8bc2fe8a2f4
@@ -0,0 +1,50 @@
1
+ name: Bats Integration Tests
2
+
3
+ on: [pull_request]
4
+
5
+ jobs:
6
+ bats:
7
+ runs-on: ubuntu-latest
8
+
9
+ env:
10
+ DB: postgresql
11
+
12
+ steps:
13
+ - uses: actions/checkout@v5
14
+
15
+ - name: Install bats
16
+ run: |
17
+ git clone --depth 1 --branch v1.11.0 https://github.com/bats-core/bats-core.git /tmp/bats-core
18
+ sudo /tmp/bats-core/install.sh /usr/local
19
+ bats --version
20
+
21
+ - name: Install podman
22
+ run: |
23
+ sudo apt-get update
24
+ sudo apt-get -y install podman
25
+ podman --version
26
+
27
+ - id: ruby_version
28
+ uses: voxpupuli/ruby-version@v1
29
+ - id: min_ruby
30
+ run: echo "version=$(echo '${{ steps.ruby_version.outputs.versions }}' | jq -r '.[-1]')" >> $GITHUB_OUTPUT
31
+ - name: Setup Ruby
32
+ uses: ruby/setup-ruby@v1
33
+ with:
34
+ # Use the minimum supported Ruby version for rubocop (last in descending list)
35
+ ruby-version: ${{ steps.min_ruby.outputs.version }}
36
+ bundler-cache: true
37
+
38
+ - name: Pull container images
39
+ run: |
40
+ podman pull docker.io/library/postgres:15
41
+ podman pull docker.io/library/redis:7-alpine
42
+
43
+ - name: Run bats tests
44
+ run: bats -x --verbose-run --print-output-on-failure test/bats/
45
+
46
+ - name: Cleanup containers (if tests fail)
47
+ if: always()
48
+ run: |
49
+ podman stop dynflow-test-postgres dynflow-test-redis 2>/dev/null || true
50
+ podman rm -f dynflow-test-postgres dynflow-test-redis 2>/dev/null || true
@@ -19,7 +19,7 @@ jobs:
19
19
  tagRegex: "v(.*)" # Optional. Returns specified group text as tag name. Full tag string is returned if regex is not defined.
20
20
  tagRegexGroup: 1 # Optional. Default is 1.
21
21
  - name: Checkout the repository
22
- uses: actions/checkout@v2
22
+ uses: actions/checkout@v5
23
23
  - name: Generate build files
24
24
  run: |
25
25
  mkdir -p dist
@@ -15,16 +15,20 @@ env:
15
15
  jobs:
16
16
  rubocop:
17
17
  runs-on: ubuntu-latest
18
+ outputs:
19
+ ruby_version: ${{ steps.ruby_version.outputs.versions }}
18
20
  steps:
19
- - uses: actions/checkout@v2
21
+ - uses: actions/checkout@v5
22
+ - id: ruby_version
23
+ uses: voxpupuli/ruby-version@v1
24
+ - id: min_ruby
25
+ run: echo "version=$(echo '${{ steps.ruby_version.outputs.versions }}' | jq -r '.[-1]')" >> $GITHUB_OUTPUT
20
26
  - name: Setup Ruby
21
27
  uses: ruby/setup-ruby@v1
22
28
  with:
23
- ruby-version: 2.7
24
- - name: Setup
25
- run: |
26
- gem install bundler --version=2.4.22
27
- bundle install --jobs=3 --retry=3
29
+ # Use the minimum supported Ruby version for rubocop (last in descending list)
30
+ ruby-version: ${{ steps.min_ruby.outputs.version }}
31
+ bundler-cache: true
28
32
  - name: Run rubocop
29
33
  run: bundle exec rubocop
30
34
 
@@ -34,50 +38,30 @@ jobs:
34
38
  strategy:
35
39
  fail-fast: false
36
40
  matrix:
37
- ruby_version:
38
- - 2.7.0
39
- - 3.0.0
40
- - 3.2.0
41
- - 3.4
41
+ ruby_version: ${{ fromJSON(needs.rubocop.outputs.ruby_version) }}
42
42
  concurrent_ruby_ext:
43
43
  - 'true'
44
44
  - 'false'
45
45
  db:
46
46
  - postgresql
47
- - mysql
48
47
  - sqlite3
49
- include:
50
- - db: postgresql
51
- conn_string: postgres://postgres@localhost/travis_ci_test
52
- - db: mysql
53
- conn_string: mysql2://root@127.0.0.1/travis_ci_test
54
- - db: sqlite3
55
- conn_string: sqlite:/
56
48
  exclude:
57
- - db: mysql
58
- ruby_version: 3.0.0
59
- - db: mysql
60
- ruby_version: 3.2.0
61
- - db: mysql
62
- ruby_version: 3.4
63
- - db: mysql
64
- concurrent_ruby_ext: 'true'
65
49
  - db: sqlite3
66
- ruby_version: 3.0.0
50
+ ruby_version: '3.0'
67
51
  - db: sqlite3
68
- ruby_version: 3.2.0
52
+ ruby_version: '3.2'
69
53
  - db: sqlite3
70
- ruby_version: 3.4
54
+ ruby_version: '3.4'
71
55
  - db: sqlite3
72
56
  concurrent_ruby_ext: 'true'
73
57
  - db: postgresql
74
- ruby_version: 3.0.0
58
+ ruby_version: '3.0'
75
59
  concurrent_ruby_ext: 'true'
76
60
  - db: postgresql
77
- ruby_version: 3.2.0
61
+ ruby_version: '3.2'
78
62
  concurrent_ruby_ext: 'true'
79
63
  - db: postgresql
80
- ruby_version: 3.4
64
+ ruby_version: '3.4'
81
65
  concurrent_ruby_ext: 'true'
82
66
 
83
67
  services:
@@ -86,31 +70,28 @@ jobs:
86
70
  ports: ['5432:5432']
87
71
  options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5
88
72
  env:
89
- POSTGRES_DB: travis_ci_test
90
- mariadb:
91
- image: mariadb:10
92
- ports: ['3306:3306']
93
- env:
94
- MYSQL_ALLOW_EMPTY_PASSWORD: 'yes'
95
- MYSQL_DATABASE: travis_ci_test
73
+ POSTGRES_DB: ci_test
96
74
  redis:
97
75
  image: redis:latest
98
76
  ports: ['6379:6379']
99
77
 
100
78
  env:
101
79
  DB: ${{ matrix.db }}
102
- DB_CONN_STRING: ${{ matrix.conn_string }}
103
80
  CONCURRENT_RUBY_EXT: "${{ matrix.concurrent_ruby_ext }}"
104
81
 
105
82
  steps:
106
- - uses: actions/checkout@v2
83
+ - uses: actions/checkout@v5
107
84
  - name: Set up Ruby
108
- # To automatically get bug fixes and new Ruby versions for ruby/setup-ruby,
109
- # change this to (see https://github.com/ruby/setup-ruby#versioning):
110
85
  uses: ruby/setup-ruby@v1
111
86
  with:
112
87
  ruby-version: ${{ matrix.ruby_version }}
113
- - name: Install dependencies
114
- run: .github/install_dependencies.sh
88
+ bundler-cache: true
115
89
  - name: Run tests
116
90
  run: bundle exec rake test
91
+ - name: Upload logs
92
+ uses: actions/upload-artifact@v4
93
+ if: ${{ failure() && contains(matrix.task, 'test') }}
94
+ with:
95
+ name: logs-${{ env.ARTIFACT_SUFFIX }}
96
+ path: test.log
97
+ retention-days: 5
data/.gitignore CHANGED
@@ -7,3 +7,5 @@ Gemfile.lock
7
7
  .idea
8
8
  .ruby-version
9
9
  examples/remote_executor_db.sqlite
10
+ /Gemfile.local.rb
11
+ /test.log
data/.rubocop.yml CHANGED
@@ -33,3 +33,6 @@ Metrics/ModuleLength:
33
33
 
34
34
  Style/LambdaCall:
35
35
  Enabled: false
36
+
37
+ Gemspec/RequiredRubyVersion:
38
+ Enabled: false
data/Dockerfile CHANGED
@@ -6,4 +6,4 @@ ADD Gemfile /data/
6
6
  ADD dynflow.gemspec /data/
7
7
  ADD lib/dynflow/version.rb /data/lib/dynflow/version.rb
8
8
  WORKDIR /data
9
- RUN bundle install --without mysql
9
+ RUN bundle install
data/Gemfile CHANGED
@@ -4,11 +4,11 @@ source 'https://rubygems.org'
4
4
 
5
5
  gemspec
6
6
 
7
- group :concurrent_ruby_ext do
7
+ group :concurrent_ruby_ext, optional: ENV.key?('CI') && ENV['CONCURRENT_RUBY_EXT'] != 'true' do
8
8
  gem 'concurrent-ruby-ext', '~> 1.1.3'
9
9
  end
10
10
 
11
- group :pry do
11
+ group :pry, optional: ENV.key?('CI') do
12
12
  gem 'pry'
13
13
  gem 'pry-byebug'
14
14
  end
@@ -18,14 +18,10 @@ group :sidekiq do
18
18
  gem 'sidekiq'
19
19
  end
20
20
 
21
- group :postgresql do
21
+ group :postgresql, optional: ENV.key?('CI') && ENV['DB'] != 'postgresql' do
22
22
  gem "pg"
23
23
  end
24
24
 
25
- group :mysql do
26
- gem "mysql2"
27
- end
28
-
29
25
  group :lint do
30
26
  gem 'theforeman-rubocop', '~> 0.0.4'
31
27
  end
@@ -37,9 +33,12 @@ end
37
33
  group :rails do
38
34
  gem 'daemons'
39
35
  gem 'logging'
40
- gem 'rails', '>= 4.2.9', '< 7'
36
+ gem 'rails', '>= 7', '< 8'
41
37
  end
42
38
 
43
39
  group :telemetry do
44
40
  gem 'statsd-instrument'
45
41
  end
42
+
43
+ local_gemfile = File.join(File.dirname(__FILE__), 'Gemfile.local.rb')
44
+ self.instance_eval(Bundler.read_file(local_gemfile)) if File.exist?(local_gemfile)
data/README.md CHANGED
@@ -126,10 +126,10 @@ The Anatomy of Action Class
126
126
  # every action needs to inherit from Dynflow::Action
127
127
  class Action < Dynflow::Action
128
128
 
129
- # OPTIONAL: the input format for the execution phase of this action
130
- # (https://github.com/iNecas/apipie-params for more details.
131
- # Validations can be performed against this description (turned off
132
- # for now)
129
+ # OPTIONAL: the input format for the execution phase of this action.
130
+ # This is purely documentation - the block is never evaluated and
131
+ # serves only as a reference for developers implementing actions.
132
+ # Input validation is not performed.
133
133
  input_format do
134
134
  param :id, Integer
135
135
  param :name, String
@@ -203,12 +203,12 @@ class AnAction < Dynflow::Action
203
203
  end
204
204
  ```
205
205
 
206
- This might me quite handy especially in combination with
206
+ This might be quite handy especially in combination with
207
207
  [subscriptions](#subscriptions) functionality.
208
208
 
209
- The format follows [apipie-params](https://github.com/iNecas/apipie-params) for more details.
210
- Validations of input/output could be performed against this description but it's not turned on
211
- by default. (It needs to be revisited and updated to be fully functional.)
209
+ These format definitions are purely for documentation purposes - the blocks are never
210
+ evaluated and serve only as a reference for developers implementing actions.
211
+ Input/output validation is not performed.
212
212
 
213
213
  {% endinfo_block %}
214
214
 
data/dynflow.gemspec CHANGED
@@ -14,14 +14,13 @@ Gem::Specification.new do |s|
14
14
  s.description = "Ruby workflow/orchestration engine"
15
15
  s.license = "MIT"
16
16
 
17
- s.files = `git ls-files`.split("\n")
17
+ s.files = `git ls-files`.split("\n").reject { |file| file == '.packit.yaml' }
18
18
  s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
19
19
  s.require_paths = ["lib"]
20
20
 
21
- s.required_ruby_version = '>= 2.7.0'
21
+ s.required_ruby_version = '>= 3.0.0'
22
22
 
23
23
  s.add_dependency "algebrick", '~> 0.7.0'
24
- s.add_dependency "apipie-params"
25
24
  s.add_dependency "concurrent-ruby", '~> 1.1.3'
26
25
  s.add_dependency "concurrent-ruby-edge", '~> 0.6.0'
27
26
  s.add_dependency "csv", "~> 3.1"
@@ -48,7 +48,7 @@ class ExampleHelper
48
48
  end
49
49
 
50
50
  def logger_adapter
51
- Dynflow::LoggerAdapters::Simple.new $stderr, Logger::FATAL
51
+ Dynflow::LoggerAdapters::Simple.new $stdout, Logger::DEBUG
52
52
  end
53
53
 
54
54
  def run_web_console(world = ExampleHelper.world)
@@ -0,0 +1,56 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require_relative 'example_helper'
5
+
6
+ class DelayedAction < Dynflow::Action
7
+ def plan(should_fail = false)
8
+ plan_self :should_fail => should_fail
9
+ end
10
+
11
+ def run
12
+ sleep 5
13
+ raise "Controlled failure" if input[:should_fail]
14
+ end
15
+
16
+ def rescue_strategy
17
+ Dynflow::Action::Rescue::Fail
18
+ end
19
+ end
20
+
21
+ if $PROGRAM_NAME == __FILE__
22
+ world = ExampleHelper.create_world do |config|
23
+ config.auto_rescue = true
24
+ end
25
+ world.action_logger.level = 1
26
+ world.logger.level = 0
27
+
28
+ plan1 = world.trigger(DelayedAction)
29
+ plan2 = world.chain(plan1.execution_plan_id, DelayedAction)
30
+ plan3 = world.chain(plan2.execution_plan_id, DelayedAction)
31
+ plan4 = world.chain(plan2.execution_plan_id, DelayedAction)
32
+
33
+ plan5 = world.trigger(DelayedAction, true)
34
+ plan6 = world.chain(plan5.execution_plan_id, DelayedAction)
35
+
36
+ puts <<-MSG.gsub(/^.*\|/, '')
37
+ |
38
+ | Execution Plan Chaining example
39
+ | ========================
40
+ |
41
+ | This example shows the execution plan chaining functionality of Dynflow, which allows execution plans to wait until another execution plan finishes.
42
+ |
43
+ | Execution plans:
44
+ | #{plan1.id} runs immediately and should run successfully.
45
+ | #{plan2.id} is delayed and should run once #{plan1.id} finishes.
46
+ | #{plan3.id} and #{plan4.id} are delayed and should run once #{plan2.id} finishes.
47
+ |
48
+ | #{plan5.id} runs immediately and is expected to fail.
49
+ | #{plan6.id} should not run at all as its prerequisite failed.
50
+ |
51
+ | Visit #{ExampleHelper::DYNFLOW_URL} to see their status.
52
+ |
53
+ MSG
54
+
55
+ ExampleHelper.run_web_console(world)
56
+ end
@@ -117,17 +117,14 @@ class RemoteExecutorExample
117
117
  Proc.new { |world| Dynflow::Connectors::Database.new(world) }
118
118
  end
119
119
 
120
- def run_client
120
+ def run_client(count)
121
121
  world = ExampleHelper.create_world do |config|
122
122
  config.persistence_adapter = persistence_adapter
123
123
  config.executor = false
124
124
  config.connector = connector
125
125
  end
126
126
 
127
- world.trigger(OrchestrateEvented::CreateInfrastructure)
128
- world.trigger(OrchestrateEvented::CreateInfrastructure, true)
129
-
130
- loop do
127
+ (count || 1000).times do
131
128
  start_time = Time.now
132
129
  world.trigger(SampleAction).finished.wait
133
130
  finished_in = Time.now - start_time
@@ -150,13 +147,13 @@ if $0 == __FILE__
150
147
  when 'server'
151
148
  puts <<~MSG
152
149
  The server is starting…. You can send the work to it by running:
153
-
150
+
154
151
  #{$0} client
155
-
152
+
156
153
  MSG
157
154
  RemoteExecutorExample.run_server
158
155
  when 'client'
159
- RemoteExecutorExample.run_client
156
+ RemoteExecutorExample.run_client(ARGV[1]&.to_i)
160
157
  else
161
158
  puts "Unknown command #{comment}"
162
159
  exit 1
@@ -1,44 +1,15 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Dynflow
4
- # Input/output format validation logic calling
5
- # input_format/output_format with block acts as a setter for
6
- # specifying the format. Without a block it acts as a getter
7
4
  module Action::Format
8
- # we don't evaluate tbe block immediatelly, but postpone it till all the
9
- # action classes are loaded, because we can use them to reference output format
10
5
  def input_format(&block)
11
- case
12
- when block && !@input_format_block
13
- @input_format_block = block
14
- when !block && @input_format_block
15
- return @input_format ||= Apipie::Params::Description.define(&@input_format_block)
16
- when block && @input_format_block
17
- raise "The input_format has already been defined in #{self.class}"
18
- when !block && !@input_format_block
19
- if superclass.respond_to? :input_format
20
- superclass.input_format
21
- else
22
- raise "The input_format has not been defined yet in #{self.class}"
23
- end
24
- end
6
+ # Format definitions are not validated
7
+ # This method is kept for backward compatibility but does nothing
25
8
  end
26
9
 
27
10
  def output_format(&block)
28
- case
29
- when block && !@output_format_block
30
- @output_format_block = block
31
- when !block && @output_format_block
32
- return @output_format ||= Apipie::Params::Description.define(&@output_format_block)
33
- when block && @output_format_block
34
- raise "The output_format has already been defined in #{self.class}"
35
- when !block && !@output_format_block
36
- if superclass.respond_to? :output_format
37
- superclass.output_format
38
- else
39
- raise "The output_format has not been defined yet in #{self.class}"
40
- end
41
- end
11
+ # Format definitions are not validated
12
+ # This method is kept for backward compatibility but does nothing
42
13
  end
43
14
  end
44
15
  end
@@ -19,7 +19,7 @@ module Dynflow
19
19
  :load_execution_plan,
20
20
  :save_execution_plan,
21
21
  :find_old_execution_plans,
22
- :find_past_delayed_plans,
22
+ :find_ready_delayed_plans,
23
23
  :delete_delayed_plans,
24
24
  :save_delayed_plan,
25
25
  :set_delayed_plan_frozen,
@@ -32,7 +32,7 @@ module Dynflow
32
32
 
33
33
  def delayed_execution_plans(time)
34
34
  with_error_handling([]) do
35
- world.persistence.find_past_delayed_plans(time)
35
+ world.persistence.find_ready_delayed_plans(time)
36
36
  end
37
37
  end
38
38
 
@@ -31,6 +31,12 @@ module Dynflow
31
31
  error("Execution plan could not be started before set time (#{@start_before})", 'timeout')
32
32
  end
33
33
 
34
+ def failed_dependencies(uuids)
35
+ bullets = uuids.map { |u| "- #{u}" }.join("\n")
36
+ msg = "Execution plan could not be started because some of its prerequisite execution plans failed:\n#{bullets}"
37
+ error(msg, 'failed-dependency')
38
+ end
39
+
34
40
  def error(message, history_entry = nil)
35
41
  execution_plan.root_plan_step.state = :error
36
42
  execution_plan.root_plan_step.error = ::Dynflow::ExecutionPlan::Steps::Error.new(message)
@@ -114,7 +114,15 @@ module Dynflow
114
114
  plan = world.persistence.load_delayed_plan(execution_plan_id)
115
115
  return if plan.nil? || plan.execution_plan.state != :scheduled
116
116
 
117
- if !plan.start_before.nil? && plan.start_before < Time.now.utc()
117
+ if plan.start_before.nil?
118
+ blocker_ids = world.persistence.find_execution_plan_dependencies(execution_plan_id)
119
+ statuses = world.persistence.find_execution_plan_statuses({ filters: { uuid: blocker_ids } })
120
+ failed = statuses.select { |_uuid, status| status[:state] == 'stopped' && status[:result] == 'error' }
121
+ if failed.any?
122
+ plan.failed_dependencies(failed.keys)
123
+ return
124
+ end
125
+ elsif plan.start_before < Time.now.utc()
118
126
  plan.timeout
119
127
  return
120
128
  end
@@ -13,7 +13,7 @@ Sidekiq.configure_server do |config|
13
13
  config[:semi_reliable_fetch] = true
14
14
  Sidekiq::ReliableFetch.setup_reliable_fetch!(config)
15
15
  end
16
- ::Sidekiq.strict_args!(false)
16
+ ::Sidekiq.strict_args!(false) if ::Sidekiq.respond_to?(:strict_args!)
17
17
 
18
18
  module Dynflow
19
19
  module Executors
@@ -41,9 +41,7 @@ module Dynflow
41
41
  def wait_for_orchestrator_lock
42
42
  mode = nil
43
43
  loop do
44
- active = ::Sidekiq.redis do |conn|
45
- conn.set(REDIS_LOCK_KEY, @world.id, :ex => REDIS_LOCK_TTL, :nx => true)
46
- end
44
+ active = try_acquire_orchestrator_lock
47
45
  break if active
48
46
  if mode.nil?
49
47
  mode = :passive
@@ -54,6 +52,15 @@ module Dynflow
54
52
  @logger.info('Acquired orchestrator lock, entering active mode.')
55
53
  end
56
54
 
55
+ def try_acquire_orchestrator_lock
56
+ ::Sidekiq.redis do |conn|
57
+ conn.set(REDIS_LOCK_KEY, @world.id, :ex => REDIS_LOCK_TTL, :nx => true)
58
+ end
59
+ rescue ::Redis::BaseError => e
60
+ @logger.error("Could not acquire orchestrator lock: #{e}")
61
+ nil
62
+ end
63
+
57
64
  def reacquire_orchestrator_lock
58
65
  case ::Sidekiq.redis { |conn| conn.eval REACQUIRE_SCRIPT, [REDIS_LOCK_KEY], [@world.id] }
59
66
  when ACQUIRE_MISSING
@@ -16,6 +16,10 @@ module Dynflow
16
16
  ::MessagePack::DefaultFactory.register_type(0x00, Time, packer: MessagePack::Time::Packer, unpacker: MessagePack::Time::Unpacker)
17
17
 
18
18
  begin
19
+ # time_with_zone added a deprecation warning in 7.1.0 which we need to account for
20
+ # it was removed again in 7.2.0
21
+ require 'active_support/deprecation'
22
+ require 'active_support/deprecator'
19
23
  require 'active_support/time_with_zone'
20
24
  unpacker = ->(payload) do
21
25
  tv = MessagePack::Timestamp.from_msgpack_ext(payload)
@@ -101,8 +101,16 @@ module Dynflow
101
101
  end
102
102
  end
103
103
 
104
- def find_past_delayed_plans(time)
105
- adapter.find_past_delayed_plans(time).map do |plan|
104
+ def find_execution_plan_dependencies(execution_plan_id)
105
+ adapter.find_execution_plan_dependencies(execution_plan_id)
106
+ end
107
+
108
+ def find_blocked_execution_plans(execution_plan_id)
109
+ adapter.find_blocked_execution_plans(execution_plan_id)
110
+ end
111
+
112
+ def find_ready_delayed_plans(time)
113
+ adapter.find_ready_delayed_plans(time).map do |plan|
106
114
  DelayedPlan.new_from_hash(@world, plan)
107
115
  end
108
116
  end
@@ -163,5 +171,9 @@ module Dynflow
163
171
  def prune_undeliverable_envelopes
164
172
  adapter.prune_undeliverable_envelopes
165
173
  end
174
+
175
+ def chain_execution_plan(first, second)
176
+ adapter.chain_execution_plan(first, second)
177
+ end
166
178
  end
167
179
  end
@@ -72,7 +72,15 @@ module Dynflow
72
72
  raise NotImplementedError
73
73
  end
74
74
 
75
- def find_past_delayed_plans(options = {})
75
+ def find_execution_plan_dependencies(execution_plan_id)
76
+ raise NotImplementedError
77
+ end
78
+
79
+ def find_blocked_execution_plans(execution_plan_id)
80
+ raise NotImplementedError
81
+ end
82
+
83
+ def find_ready_delayed_plans(options = {})
76
84
  raise NotImplementedError
77
85
  end
78
86