ductwork 1.2.0 → 1.4.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.
- checksums.yaml +4 -4
- data/.claude/skills/audit-common/accepted-tradeoffs.md +0 -5
- data/CHANGELOG.md +11 -0
- data/lib/ductwork/cli.rb +47 -8
- data/lib/ductwork/context.rb +1 -1
- data/lib/ductwork/database_clock.rb +5 -7
- data/lib/ductwork/execution_claim.rb +1 -1
- data/lib/ductwork/migration_helper.rb +96 -12
- data/lib/ductwork/models/record.rb +4 -0
- data/lib/ductwork/models/run.rb +1 -1
- data/lib/ductwork/processes/process_supervisor.rb +19 -13
- data/lib/ductwork/version.rb +1 -1
- data/lib/ductwork.rb +20 -19
- data/lib/generators/ductwork/update/update_generator.rb +107 -107
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 01b3ce0336857fcaf0dcaf6beb6b02f002186bd5a94df0c05761cfcd49e8a7e9
|
|
4
|
+
data.tar.gz: cbef9120711ddf5a42819f68dd44871965681317695c6244e732e8e0228b4218
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 315615bb86d68fdcb29bf8dd96d987c79be645a551a85e45825f1a623de9099a8a8ec6e9ef64465eaab259406aa82151a5f76959d10cde2f43bfd7381d5b34bd
|
|
7
|
+
data.tar.gz: 11b3af06290fa9c3261ef1cdc5ee98e45e2e103988c3b2b11fe07dc9d873c441b82ddfe3a091f1e5da6b487d0af1343e1d3b021049e01705f1c7f2bb94788050
|
|
@@ -96,11 +96,6 @@ its place through divergent behavior rather than labeling. Cause metadata
|
|
|
96
96
|
lives on `Branch#halt_reason` instead. Pipeline and Run states stay
|
|
97
97
|
`in_progress`, `completed`, `halted`.
|
|
98
98
|
|
|
99
|
-
## 7. `Ductwork.validate!` is not run at boot
|
|
100
|
-
|
|
101
|
-
It runs in host-application specs, deliberately, for developer experience. Do
|
|
102
|
-
not recommend moving it to boot-time or engine initialization.
|
|
103
|
-
|
|
104
99
|
---
|
|
105
100
|
|
|
106
101
|
# Known Open — report only with new information
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,16 @@
|
|
|
1
1
|
# Ductwork Changelog
|
|
2
2
|
|
|
3
|
+
## [1.4.0]
|
|
4
|
+
|
|
5
|
+
- feat: validate steps and pipelines/workflows during boot of main process
|
|
6
|
+
|
|
7
|
+
## [1.3.0]
|
|
8
|
+
|
|
9
|
+
- feat: add more `MigrationHelper` methods for swapping and updating the availability claim index
|
|
10
|
+
- fix: resolve the database adapter from the configuration (`Ductwork::Record.adapter`) instead of `Ductwork::Record.connection.adapter_name`, and take the database clock's reading through `connection_pool.with_connection` - `ActiveRecord::Base#connection` is soft deprecated and takes a *sticky* lease, so `ConnectionPool#with_connection` skips its own checkin (it releases only `unless lease.sticky`) and every advancer or worker thread that touched the claim hot path pinned a pool slot for its lifetime; the Rails executor checked those back in per unit of work so long-running processes did not leak, but a host app setting `config.active_record.permanent_connection_checkout = :disallowed` would have raised, and `:deprecated` emitted a warning per claim
|
|
11
|
+
- fix: `ExecutionClaim` now selects row locking on Oracle as it always intended - the predicate tests `/oracle_enhanced/i` but was matched against `connection.adapter_name.downcase`, which renders as `oracleenhanced` with no underscore, so Oracle silently fell through to optimistic locking; it is matched against the configured adapter (`oracle_enhanced`) now. Oracle is not covered by CI
|
|
12
|
+
- fix: make `Ductwork::Processes::ProcessSupervisor#shutdown` idempotent and serialize it on a mutex - `run` calls `shutdown` itself once its loop sees the running context go down, so a caller that stops the supervisor from another thread raced the supervisor's own call and both ran `reap_own_process_record!` at the same time
|
|
13
|
+
|
|
3
14
|
## [1.2.0]
|
|
4
15
|
|
|
5
16
|
- feat: add `add_availability_claim_index` and `remove_availability_claim_index` to `Ductwork::MigrationHelper`, which own the adapter-specific shape and the name of the availability claim index
|
data/lib/ductwork/cli.rb
CHANGED
|
@@ -5,6 +5,7 @@ require "optparse"
|
|
|
5
5
|
module Ductwork
|
|
6
6
|
class CLI
|
|
7
7
|
DEFAULT_COMMAND = "start"
|
|
8
|
+
BACKTRACE_FRAME_LIMIT = 5
|
|
8
9
|
|
|
9
10
|
def self.start!(args)
|
|
10
11
|
new(args).start!
|
|
@@ -31,8 +32,9 @@ module Ductwork
|
|
|
31
32
|
|
|
32
33
|
def start!
|
|
33
34
|
parse!
|
|
34
|
-
|
|
35
|
-
|
|
35
|
+
configure
|
|
36
|
+
validate!
|
|
37
|
+
execute
|
|
36
38
|
end
|
|
37
39
|
|
|
38
40
|
private
|
|
@@ -44,9 +46,9 @@ module Ductwork
|
|
|
44
46
|
top_level_parser.order(raw_args)
|
|
45
47
|
@command = raw_args.shift || DEFAULT_COMMAND
|
|
46
48
|
|
|
47
|
-
if
|
|
49
|
+
if start_command?
|
|
48
50
|
start_command_parser.parse!(raw_args)
|
|
49
|
-
elsif
|
|
51
|
+
elsif health_check_command?
|
|
50
52
|
health_command_parser.parse!(raw_args)
|
|
51
53
|
else
|
|
52
54
|
warn "Unknown command: #{command}"
|
|
@@ -55,7 +57,15 @@ module Ductwork
|
|
|
55
57
|
end
|
|
56
58
|
end
|
|
57
59
|
|
|
58
|
-
def
|
|
60
|
+
def start_command?
|
|
61
|
+
command == "start"
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def health_check_command?
|
|
65
|
+
command == "health"
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def configure
|
|
59
69
|
configuration_options[:role] = ENV.fetch("DUCTWORK_ROLE", nil)
|
|
60
70
|
Ductwork.configuration = Configuration.new(**configuration_options)
|
|
61
71
|
Ductwork.logger = if Ductwork.configuration.logger_source == "rails"
|
|
@@ -82,10 +92,39 @@ module Ductwork
|
|
|
82
92
|
BANNER
|
|
83
93
|
end
|
|
84
94
|
|
|
85
|
-
def
|
|
86
|
-
if
|
|
95
|
+
def validate!
|
|
96
|
+
if start_command?
|
|
97
|
+
Ductwork.validate!
|
|
98
|
+
end
|
|
99
|
+
rescue StandardError => e
|
|
100
|
+
report_invalid_definitions(e)
|
|
101
|
+
exit 1
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def report_invalid_definitions(error)
|
|
105
|
+
warn "ductwork failed to validate your step and pipeline definitions"
|
|
106
|
+
warn ""
|
|
107
|
+
warn " #{error.class}: #{error.message}"
|
|
108
|
+
|
|
109
|
+
definition_frames(error).each do |frame|
|
|
110
|
+
warn " #{frame}"
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
def definition_frames(error)
|
|
115
|
+
frames = error.backtrace.to_a
|
|
116
|
+
|
|
117
|
+
return frames if Ductwork.logger.debug?
|
|
118
|
+
|
|
119
|
+
application_frames = frames.grep(/\A#{Regexp.escape(Rails.root.to_s)}/)
|
|
120
|
+
|
|
121
|
+
(application_frames.presence || frames).first(BACKTRACE_FRAME_LIMIT)
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
def execute
|
|
125
|
+
if start_command?
|
|
87
126
|
launch_processes
|
|
88
|
-
elsif
|
|
127
|
+
elsif health_check_command?
|
|
89
128
|
check_health
|
|
90
129
|
end
|
|
91
130
|
end
|
data/lib/ductwork/context.rb
CHANGED
|
@@ -26,7 +26,7 @@ module Ductwork
|
|
|
26
26
|
first_set_at: Time.current,
|
|
27
27
|
last_set_at: Time.current,
|
|
28
28
|
}
|
|
29
|
-
opts = if Ductwork::Tuple.
|
|
29
|
+
opts = if Ductwork::Tuple.adapter.match?(/mysql|trilogy/i)
|
|
30
30
|
{}
|
|
31
31
|
else
|
|
32
32
|
{ unique_by: %i[run_id key] }
|
|
@@ -1,10 +1,6 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module Ductwork
|
|
4
|
-
# NOTE: these are SQL fragments that resolve against the database server's
|
|
5
|
-
# clock instead of the calling Ruby process's clock. use them in `WHERE`
|
|
6
|
-
# clauses that compare between a stored timestamp and "now" so that NTP
|
|
7
|
-
# drift between hosts cannot make healthy work look stale (or vice versa)
|
|
8
4
|
class DatabaseClock
|
|
9
5
|
def self.ago_sql(column, interval)
|
|
10
6
|
new(column).ago_sql(interval)
|
|
@@ -15,7 +11,7 @@ module Ductwork
|
|
|
15
11
|
end
|
|
16
12
|
|
|
17
13
|
def self.now
|
|
18
|
-
adapter = Ductwork::Record.
|
|
14
|
+
adapter = Ductwork::Record.adapter
|
|
19
15
|
sql =
|
|
20
16
|
case adapter
|
|
21
17
|
when /postgresql|cockroach/i
|
|
@@ -29,7 +25,9 @@ module Ductwork
|
|
|
29
25
|
else
|
|
30
26
|
raise NotImplementedError, "Database clock does not support adapter #{adapter}"
|
|
31
27
|
end
|
|
32
|
-
raw = Ductwork::Record.
|
|
28
|
+
raw = Ductwork::Record.connection_pool.with_connection do |conn|
|
|
29
|
+
conn.select_value(sql)
|
|
30
|
+
end
|
|
33
31
|
|
|
34
32
|
case raw
|
|
35
33
|
when ::Time, ::DateTime
|
|
@@ -41,7 +39,7 @@ module Ductwork
|
|
|
41
39
|
end
|
|
42
40
|
|
|
43
41
|
def initialize(column)
|
|
44
|
-
@adapter = Ductwork::Record.
|
|
42
|
+
@adapter = Ductwork::Record.adapter
|
|
45
43
|
@column = column
|
|
46
44
|
end
|
|
47
45
|
|
|
@@ -2,29 +2,113 @@
|
|
|
2
2
|
|
|
3
3
|
module Ductwork
|
|
4
4
|
module MigrationHelper
|
|
5
|
+
class OpenTransactionError < StandardError; end
|
|
6
|
+
|
|
5
7
|
AVAILABILITY_CLAIM_INDEX_NAME = "index_ductwork_availabilities_on_claim_latest"
|
|
8
|
+
AVAILABILITY_CLAIM_INDEX_NAME_V2 = "index_ductwork_availabilities_on_claim_latest_v2"
|
|
9
|
+
AVAILABILITY_CLAIM_INDEX_NAMES = [
|
|
10
|
+
AVAILABILITY_CLAIM_INDEX_NAME,
|
|
11
|
+
AVAILABILITY_CLAIM_INDEX_NAME_V2,
|
|
12
|
+
].freeze
|
|
6
13
|
|
|
7
|
-
def add_availability_claim_index(
|
|
14
|
+
def add_availability_claim_index(
|
|
15
|
+
extra_columns: [],
|
|
16
|
+
where_extra: nil,
|
|
17
|
+
name: AVAILABILITY_CLAIM_INDEX_NAME,
|
|
18
|
+
online: false
|
|
19
|
+
)
|
|
8
20
|
if mysql?
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
name: AVAILABILITY_CLAIM_INDEX_NAME
|
|
21
|
+
columns = [:pipeline_klass, :completed_at, *extra_columns, :started_at]
|
|
22
|
+
options = { name: }
|
|
12
23
|
else
|
|
13
|
-
|
|
24
|
+
columns = %i[pipeline_klass started_at]
|
|
25
|
+
options = {
|
|
26
|
+
name: name,
|
|
27
|
+
where: ["completed_at IS NULL", where_extra].compact.join(" AND "),
|
|
28
|
+
}
|
|
29
|
+
end
|
|
14
30
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
31
|
+
if online
|
|
32
|
+
add_index_online :ductwork_availabilities, columns, **options
|
|
33
|
+
else
|
|
34
|
+
add_index :ductwork_availabilities, columns, **options
|
|
19
35
|
end
|
|
20
36
|
end
|
|
21
37
|
|
|
22
|
-
def remove_availability_claim_index
|
|
23
|
-
remove_index :ductwork_availabilities,
|
|
24
|
-
|
|
38
|
+
def remove_availability_claim_index(name: AVAILABILITY_CLAIM_INDEX_NAME)
|
|
39
|
+
remove_index :ductwork_availabilities, name: name, if_exists: true
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def swap_availability_claim_index(to:, extra_columns: [], where_extra: nil)
|
|
43
|
+
unless AVAILABILITY_CLAIM_INDEX_NAMES.include?(to)
|
|
44
|
+
raise ArgumentError, "unrecognized availability claim index name: #{to}"
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
add_availability_claim_index(
|
|
48
|
+
extra_columns: extra_columns,
|
|
49
|
+
where_extra: where_extra,
|
|
50
|
+
name: to,
|
|
51
|
+
online: true
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
(AVAILABILITY_CLAIM_INDEX_NAMES - [to]).each do |stale|
|
|
55
|
+
remove_index_online :ductwork_availabilities, name: stale
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def add_index_online(table_name, column_names, **options)
|
|
60
|
+
ensure_ddl_transaction_disabled!
|
|
61
|
+
|
|
62
|
+
name = options[:name] || connection.index_name(table_name, column_names)
|
|
63
|
+
|
|
64
|
+
drop_invalid_index(table_name, name)
|
|
65
|
+
|
|
66
|
+
return if connection.index_name_exists?(table_name, name)
|
|
67
|
+
|
|
68
|
+
full_options = options.merge(name:)
|
|
69
|
+
full_options[:algorithm] = :concurrently if postgresql?
|
|
70
|
+
|
|
71
|
+
add_index table_name, column_names, **full_options
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def remove_index_online(table_name, name:)
|
|
75
|
+
ensure_ddl_transaction_disabled!
|
|
76
|
+
|
|
77
|
+
return unless connection.index_name_exists?(table_name, name)
|
|
78
|
+
|
|
79
|
+
options = { name: name, if_exists: true }
|
|
80
|
+
options[:algorithm] = :concurrently if postgresql?
|
|
81
|
+
|
|
82
|
+
remove_index table_name, **options
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def drop_invalid_index(table_name, index_name)
|
|
86
|
+
return unless postgresql?
|
|
87
|
+
|
|
88
|
+
invalid = connection.select_value(<<~SQL.squish)
|
|
89
|
+
SELECT 1 FROM pg_index
|
|
90
|
+
WHERE indexrelid = to_regclass(#{connection.quote(index_name)})
|
|
91
|
+
AND NOT indisvalid
|
|
92
|
+
SQL
|
|
93
|
+
|
|
94
|
+
return if invalid.blank?
|
|
95
|
+
|
|
96
|
+
remove_index table_name,
|
|
97
|
+
name: index_name,
|
|
98
|
+
algorithm: :concurrently,
|
|
25
99
|
if_exists: true
|
|
26
100
|
end
|
|
27
101
|
|
|
102
|
+
def ensure_ddl_transaction_disabled!
|
|
103
|
+
return unless postgresql?
|
|
104
|
+
return unless connection.transaction_open?
|
|
105
|
+
|
|
106
|
+
raise OpenTransactionError, <<~MESSAGE.squish
|
|
107
|
+
online index changes cannot run inside a transaction on PostgreSQL;
|
|
108
|
+
declare `disable_ddl_transaction!` in the migration
|
|
109
|
+
MESSAGE
|
|
110
|
+
end
|
|
111
|
+
|
|
28
112
|
def create_ductwork_table(table_name, &block)
|
|
29
113
|
if postgresql?
|
|
30
114
|
create_table table_name, id: :uuid, &block
|
data/lib/ductwork/models/run.rb
CHANGED
|
@@ -118,7 +118,7 @@ module Ductwork
|
|
|
118
118
|
# relies on the advancer retrying a deadlock victim; SQLite has no row-level
|
|
119
119
|
# locks, so `lock!` is a no-op there and the deadlock cannot occur.
|
|
120
120
|
def lock_for_terminal_resolution!
|
|
121
|
-
if Ductwork::Record.
|
|
121
|
+
if Ductwork::Record.adapter.match?(/postgresql|cockroach/)
|
|
122
122
|
lock!("FOR NO KEY UPDATE")
|
|
123
123
|
else
|
|
124
124
|
lock!
|
|
@@ -3,8 +3,6 @@
|
|
|
3
3
|
module Ductwork
|
|
4
4
|
module Processes
|
|
5
5
|
class ProcessSupervisor # rubocop:todo Metrics/ClassLength
|
|
6
|
-
# Upper bound on how long sig_kill_process will block the supervisor
|
|
7
|
-
# loop waiting for a KILL'd child to be reaped (see sig_kill_process).
|
|
8
6
|
KILL_REAP_TIMEOUT = 5 # seconds
|
|
9
7
|
KILL_REAP_INTERVAL = 0.1
|
|
10
8
|
|
|
@@ -13,6 +11,8 @@ module Ductwork
|
|
|
13
11
|
def initialize
|
|
14
12
|
@running_context = Ductwork::RunningContext.new
|
|
15
13
|
@workers = []
|
|
14
|
+
@shutdown_mutex = Mutex.new
|
|
15
|
+
@shutdown_complete = false
|
|
16
16
|
|
|
17
17
|
run_hooks_for(:start)
|
|
18
18
|
|
|
@@ -53,22 +53,28 @@ module Ductwork
|
|
|
53
53
|
end
|
|
54
54
|
|
|
55
55
|
def shutdown
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
56
|
+
shutdown_mutex.synchronize do
|
|
57
|
+
next if shutdown_complete
|
|
58
|
+
|
|
59
|
+
running_context.shutdown!
|
|
60
|
+
Ductwork.logger.debug(
|
|
61
|
+
msg: "Beginning shutdown",
|
|
62
|
+
role: :process_supervisor
|
|
63
|
+
)
|
|
61
64
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
65
|
+
terminate_gracefully
|
|
66
|
+
wait_for_workers_to_exit
|
|
67
|
+
terminate_immediately
|
|
68
|
+
reap_own_process_record!
|
|
69
|
+
run_hooks_for(:stop)
|
|
70
|
+
|
|
71
|
+
@shutdown_complete = true
|
|
72
|
+
end
|
|
67
73
|
end
|
|
68
74
|
|
|
69
75
|
private
|
|
70
76
|
|
|
71
|
-
attr_reader :running_context
|
|
77
|
+
attr_reader :running_context, :shutdown_mutex, :shutdown_complete
|
|
72
78
|
|
|
73
79
|
def adopt_or_create_process!
|
|
74
80
|
Ductwork.wrap_with_app_executor do
|
data/lib/ductwork/version.rb
CHANGED
data/lib/ductwork.rb
CHANGED
|
@@ -10,6 +10,14 @@ require "securerandom"
|
|
|
10
10
|
require "zeitwerk"
|
|
11
11
|
|
|
12
12
|
module Ductwork
|
|
13
|
+
DEFINITION_DIRECTORIES = %w[
|
|
14
|
+
app/steps
|
|
15
|
+
app/pipelines
|
|
16
|
+
app/workflows
|
|
17
|
+
].freeze
|
|
18
|
+
|
|
19
|
+
class LoadPathError < StandardError; end
|
|
20
|
+
|
|
13
21
|
class << self
|
|
14
22
|
attr_accessor :app_executor, :loader, :logger
|
|
15
23
|
attr_writer :configuration, :defined_pipelines, :hooks
|
|
@@ -67,25 +75,18 @@ module Ductwork
|
|
|
67
75
|
end
|
|
68
76
|
|
|
69
77
|
def validate!
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
if pipeline_directory.exist?
|
|
84
|
-
loader.eager_load_dir(pipeline_directory)
|
|
85
|
-
end
|
|
86
|
-
|
|
87
|
-
if workflow_directory.exist?
|
|
88
|
-
loader.eager_load_dir(workflow_directory)
|
|
78
|
+
loader = Rails.autoloaders.main
|
|
79
|
+
|
|
80
|
+
DEFINITION_DIRECTORIES.each do |relative|
|
|
81
|
+
directory = Rails.root.join(relative)
|
|
82
|
+
|
|
83
|
+
if directory.exist?
|
|
84
|
+
begin
|
|
85
|
+
loader.eager_load_dir(directory)
|
|
86
|
+
rescue Zeitwerk::Error => e
|
|
87
|
+
raise LoadPathError, "#{directory} exists but is not autoloadable: #{e.message}"
|
|
88
|
+
end
|
|
89
|
+
end
|
|
89
90
|
end
|
|
90
91
|
|
|
91
92
|
true
|
|
@@ -10,113 +10,113 @@ module Ductwork
|
|
|
10
10
|
source_root File.expand_path("templates", __dir__)
|
|
11
11
|
|
|
12
12
|
def create_files
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
13
|
+
Ductwork::Record.connection_pool.with_connection do |connection|
|
|
14
|
+
if Ductwork::Availability.column_names.exclude?("pipeline_klass")
|
|
15
|
+
migration_template "db/denormalize_pipeline_klass_on_availabilities.rb",
|
|
16
|
+
"db/migrate/denormalize_pipeline_klass_on_availabilities.rb"
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
if Ductwork::Pipeline.column_for_attribute("id").type != :uuid
|
|
20
|
+
migration_template "db/migrate_tables_to_uuid_primary_key.rb",
|
|
21
|
+
"db/migrate/migrate_tables_to_uuid_primary_key.rb"
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
if !connection.table_exists?(:ductwork_branches)
|
|
25
|
+
migration_template "db/create_ductwork_branches.rb",
|
|
26
|
+
"db/migrate/create_ductwork_branches.rb"
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
if !connection.table_exists?(:ductwork_branch_links)
|
|
30
|
+
migration_template "db/create_ductwork_branch_links.rb",
|
|
31
|
+
"db/migrate/create_ductwork_branch_links.rb"
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
if Ductwork::Step.column_names.exclude?("branch_id")
|
|
35
|
+
migration_template "db/associate_steps_to_branches.rb",
|
|
36
|
+
"db/migrate/associate_steps_to_branches.rb"
|
|
37
|
+
migration_template "db/backfill_branch_ids_on_steps.rb",
|
|
38
|
+
"db/migrate/backfill_branch_ids_on_steps.rb"
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
if !connection.table_exists?(:ductwork_transitions)
|
|
42
|
+
migration_template "db/create_ductwork_transitions.rb",
|
|
43
|
+
"db/migrate/create_ductwork_transitions.rb"
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
if !connection.table_exists?(:ductwork_advancements)
|
|
47
|
+
migration_template "db/create_ductwork_advancements.rb",
|
|
48
|
+
"db/migrate/create_ductwork_advancements.rb"
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
if Ductwork::Execution.column_names.include?("process_id")
|
|
52
|
+
migration_template "db/update_process_associations.rb",
|
|
53
|
+
"db/migrate/update_process_associations.rb"
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
if connection.table_exists?(:ductwork_runs)
|
|
57
|
+
migration_template "db/rename_runs_to_attempts.rb",
|
|
58
|
+
"db/migrate/rename_runs_to_attempts.rb"
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
if !connection.table_exists?(:ductwork_runs)
|
|
62
|
+
migration_template "db/create_ductwork_runs.rb",
|
|
63
|
+
"db/migrate/create_ductwork_runs.rb"
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
if Ductwork::Branch.column_names.include?("pipeline_id")
|
|
67
|
+
migration_template "db/associate_branches_to_runs.rb",
|
|
68
|
+
"db/migrate/associate_branches_to_runs.rb"
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
if Ductwork::Step.column_names.include?("pipeline_id")
|
|
72
|
+
migration_template "db/associate_steps_to_runs.rb",
|
|
73
|
+
"db/migrate/associate_steps_to_runs.rb"
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
if Ductwork::Tuple.column_names.include?("pipeline_id")
|
|
77
|
+
migration_template "db/associate_tuples_to_runs.rb",
|
|
78
|
+
"db/migrate/associate_tuples_to_runs.rb"
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
if Ductwork::Execution.column_names.exclude?("crash_count")
|
|
82
|
+
migration_template "db/add_crash_count_to_ductwork_executions.rb",
|
|
83
|
+
"db/migrate/add_crash_count_to_ductwork_executions.rb"
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
if Ductwork::Advancement.column_names.exclude?("crash_count")
|
|
87
|
+
migration_template "db/add_crash_count_to_ductwork_advancements.rb",
|
|
88
|
+
"db/migrate/add_crash_count_to_ductwork_advancements.rb"
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
if Ductwork::Execution.column_names.exclude?("process_id")
|
|
92
|
+
migration_template "db/add_process_id_to_ductwork_executions.rb",
|
|
93
|
+
"db/migrate/add_process_id_to_ductwork_executions.rb"
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
if Ductwork::Process.column_names.exclude?("role")
|
|
97
|
+
migration_template "db/add_role_to_ductwork_processes.rb",
|
|
98
|
+
"db/migrate/add_role_to_ductwork_processes.rb"
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
if !connection.index_exists?(:ductwork_results, %i[result_type created_at])
|
|
102
|
+
migration_template "db/add_indexes_to_ductwork_results.rb",
|
|
103
|
+
"db/migrate/add_indexes_to_ductwork_results.rb"
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
if !connection.index_exists?(:ductwork_runs, :started_at)
|
|
107
|
+
migration_template "db/add_indexes_to_ductwork_runs.rb",
|
|
108
|
+
"db/migrate/add_indexes_to_ductwork_runs.rb"
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
if !connection.index_exists?(:ductwork_runs, %i[pipeline_id started_at])
|
|
112
|
+
migration_template "db/add_pipeline_started_index_to_ductwork_runs.rb",
|
|
113
|
+
"db/migrate/add_pipeline_started_index_to_ductwork_runs.rb"
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
if !connection.index_exists?(:ductwork_transitions, name: "index_ductwork_transitions_on_latest_open")
|
|
117
|
+
migration_template "db/add_indexes_to_ductwork_transitions.rb",
|
|
118
|
+
"db/migrate/add_indexes_to_ductwork_transitions.rb"
|
|
119
|
+
end
|
|
120
120
|
end
|
|
121
121
|
end
|
|
122
122
|
end
|