ductwork 0.25.0 → 1.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.
- checksums.yaml +4 -4
- data/.claude/skills/audit-clock-drift/SKILL.md +11 -0
- data/.claude/skills/audit-database-indexes/SKILL.md +11 -0
- data/.claude/skills/audit-database-support/SKILL.md +18 -0
- data/.claude/skills/audit-durability/SKILL.md +17 -0
- data/.saturnci/database.yml +1 -0
- data/CHANGELOG-PRO.md +72 -0
- data/CHANGELOG.md +148 -1
- data/CLAUDE.md +34 -0
- data/README.md +24 -14
- data/app/controllers/ductwork/application_controller.rb +3 -3
- data/app/controllers/ductwork/dashboards_controller.rb +1 -1
- data/app/controllers/ductwork/pipelines_controller.rb +4 -3
- data/app/helpers/ductwork/application_helper.rb +2 -2
- data/app/views/ductwork/dashboards/show.html.erb +12 -12
- data/app/views/ductwork/pipelines/index.html.erb +12 -12
- data/app/views/ductwork/pipelines/show.html.erb +13 -13
- data/app/views/ductwork/step_errors/index.html.erb +2 -2
- data/lib/ductwork/branch_claim.rb +185 -0
- data/lib/ductwork/cli.rb +73 -24
- data/lib/ductwork/configuration.rb +73 -3
- data/lib/ductwork/context.rb +17 -19
- data/lib/ductwork/database_clock.rb +84 -0
- data/lib/ductwork/dsl/branch_builder.rb +46 -1
- data/lib/ductwork/dsl/definition_builder.rb +101 -2
- data/lib/ductwork/{job_claim.rb → execution_claim.rb} +7 -6
- data/lib/ductwork/fault_injection.rb +35 -0
- data/lib/ductwork/migration_helper.rb +1 -1
- data/lib/ductwork/models/advancement.rb +48 -0
- data/lib/ductwork/models/attempt.rb +9 -0
- data/lib/ductwork/models/availability.rb +2 -0
- data/lib/ductwork/models/branch.rb +800 -0
- data/lib/ductwork/models/branch_link.rb +10 -0
- data/lib/ductwork/models/execution.rb +199 -2
- data/lib/ductwork/models/job.rb +8 -121
- data/lib/ductwork/models/pipeline.rb +152 -255
- data/lib/ductwork/models/process.rb +177 -5
- data/lib/ductwork/models/result.rb +2 -1
- data/lib/ductwork/models/run.rb +119 -1
- data/lib/ductwork/models/step.rb +26 -6
- data/lib/ductwork/models/transition.rb +15 -0
- data/lib/ductwork/models/tuple.rb +1 -4
- data/lib/ductwork/models/workflow.rb +3 -0
- data/lib/ductwork/optimistic_locking_execution_claim.rb +97 -0
- data/lib/ductwork/processes/health_check.rb +87 -0
- data/lib/ductwork/processes/job_worker.rb +86 -20
- data/lib/ductwork/processes/job_worker_runner.rb +25 -37
- data/lib/ductwork/processes/pipeline_advancer.rb +48 -76
- data/lib/ductwork/processes/pipeline_advancer_runner.rb +34 -44
- data/lib/ductwork/processes/process_supervisor.rb +87 -10
- data/lib/ductwork/processes/thread_supervisor.rb +55 -21
- data/lib/ductwork/processes/thread_supervisor_runner.rb +8 -5
- data/lib/ductwork/processes/worker_health_check.rb +55 -0
- data/lib/ductwork/row_locking_execution_claim.rb +77 -0
- data/lib/ductwork/testing/rspec.rb +28 -11
- data/lib/ductwork/version.rb +1 -1
- data/lib/ductwork.rb +5 -0
- data/lib/generators/ductwork/install/install_generator.rb +14 -4
- data/lib/generators/ductwork/install/templates/config/ductwork.yml +4 -0
- data/lib/generators/ductwork/install/templates/db/create_ductwork_advancements.rb +29 -0
- data/lib/generators/ductwork/install/templates/db/create_ductwork_attempts.rb +20 -0
- data/lib/generators/ductwork/install/templates/db/create_ductwork_availabilities.rb +18 -5
- data/lib/generators/ductwork/install/templates/db/create_ductwork_branch_links.rb +27 -0
- data/lib/generators/ductwork/install/templates/db/create_ductwork_branches.rb +37 -0
- data/lib/generators/ductwork/install/templates/db/create_ductwork_executions.rb +8 -1
- data/lib/generators/ductwork/install/templates/db/create_ductwork_pipelines.rb +0 -8
- data/lib/generators/ductwork/install/templates/db/create_ductwork_processes.rb +2 -0
- data/lib/generators/ductwork/install/templates/db/create_ductwork_results.rb +1 -0
- data/lib/generators/ductwork/install/templates/db/create_ductwork_runs.rb +45 -6
- data/lib/generators/ductwork/install/templates/db/create_ductwork_steps.rb +20 -5
- data/lib/generators/ductwork/install/templates/db/create_ductwork_transitions.rb +43 -0
- data/lib/generators/ductwork/install/templates/db/create_ductwork_tuples.rb +3 -3
- data/lib/generators/ductwork/update/templates/db/add_crash_count_to_ductwork_advancements.rb +7 -0
- data/lib/generators/ductwork/update/templates/db/add_crash_count_to_ductwork_executions.rb +14 -0
- data/lib/generators/ductwork/update/templates/db/add_indexes_to_ductwork_results.rb +7 -0
- data/lib/generators/ductwork/update/templates/db/add_indexes_to_ductwork_runs.rb +9 -0
- data/lib/generators/ductwork/update/templates/db/add_indexes_to_ductwork_transitions.rb +16 -0
- data/lib/generators/ductwork/update/templates/db/add_pipeline_started_index_to_ductwork_runs.rb +7 -0
- data/lib/generators/ductwork/update/templates/db/add_process_id_to_ductwork_executions.rb +35 -0
- data/lib/generators/ductwork/update/templates/db/add_role_to_ductwork_processes.rb +20 -0
- data/lib/generators/ductwork/update/templates/db/associate_branches_to_runs.rb +74 -0
- data/lib/generators/ductwork/update/templates/db/associate_steps_to_branches.rb +24 -0
- data/lib/generators/ductwork/update/templates/db/associate_steps_to_runs.rb +72 -0
- data/lib/generators/ductwork/update/templates/db/associate_tuples_to_runs.rb +73 -0
- data/lib/generators/ductwork/update/templates/db/backfill_branch_ids_on_steps.rb +21 -0
- data/lib/generators/ductwork/update/templates/db/create_ductwork_advancements.rb +28 -0
- data/lib/generators/ductwork/update/templates/db/create_ductwork_branch_links.rb +27 -0
- data/lib/generators/ductwork/update/templates/db/create_ductwork_branches.rb +37 -0
- data/lib/generators/ductwork/update/templates/db/create_ductwork_runs.rb +55 -0
- data/lib/generators/ductwork/update/templates/db/create_ductwork_transitions.rb +32 -0
- data/lib/generators/ductwork/update/templates/db/denormalize_pipeline_klass_on_availabilities.rb +11 -4
- data/lib/generators/ductwork/update/templates/db/migrate_tables_to_uuid_primary_key.rb +5 -0
- data/lib/generators/ductwork/update/templates/db/rename_runs_to_attempts.rb +7 -0
- data/lib/generators/ductwork/update/templates/db/update_process_associations.rb +29 -0
- data/lib/generators/ductwork/update/update_generator.rb +99 -0
- metadata +47 -6
- data/lib/ductwork/optimistic_locking_job_claim.rb +0 -88
- data/lib/ductwork/row_locking_job_claim.rb +0 -75
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Ductwork
|
|
4
|
+
class RowLockingExecutionClaim
|
|
5
|
+
def initialize(klass, owner_process_id)
|
|
6
|
+
@availability = nil
|
|
7
|
+
@execution = nil
|
|
8
|
+
@klass = klass
|
|
9
|
+
@process_id = owner_process_id
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def latest
|
|
13
|
+
Ductwork::Record.transaction do
|
|
14
|
+
claim_availability
|
|
15
|
+
|
|
16
|
+
if availability.present?
|
|
17
|
+
Ductwork.logger.debug(
|
|
18
|
+
msg: "Job claimed",
|
|
19
|
+
role: :job_worker,
|
|
20
|
+
process_id: process_id,
|
|
21
|
+
availability_id: availability.id
|
|
22
|
+
)
|
|
23
|
+
update_state
|
|
24
|
+
else
|
|
25
|
+
Ductwork.logger.debug(
|
|
26
|
+
msg: "No available job to claim",
|
|
27
|
+
role: :job_worker,
|
|
28
|
+
process_id: process_id,
|
|
29
|
+
pipeline: klass
|
|
30
|
+
)
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
execution
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
private
|
|
38
|
+
|
|
39
|
+
attr_reader :availability, :execution, :klass, :process_id
|
|
40
|
+
|
|
41
|
+
def claim_availability
|
|
42
|
+
sql = Ductwork::DatabaseClock.now_sql("ductwork_availabilities.started_at")
|
|
43
|
+
@availability = Ductwork::Availability
|
|
44
|
+
.where(sql)
|
|
45
|
+
.where(completed_at: nil, pipeline_klass: klass)
|
|
46
|
+
.order(:started_at)
|
|
47
|
+
.lock("FOR UPDATE SKIP LOCKED")
|
|
48
|
+
.limit(1)
|
|
49
|
+
.first
|
|
50
|
+
|
|
51
|
+
return unless availability
|
|
52
|
+
|
|
53
|
+
completed_at = Time.current
|
|
54
|
+
@execution = availability.execution
|
|
55
|
+
|
|
56
|
+
availability.update_columns(completed_at:, process_id:)
|
|
57
|
+
execution.update_columns(process_id:)
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def update_state
|
|
61
|
+
step = execution.job.step
|
|
62
|
+
|
|
63
|
+
Ductwork::Step
|
|
64
|
+
.where(id: step.id)
|
|
65
|
+
.where.not(status: "in_progress")
|
|
66
|
+
.update_all(status: "in_progress", updated_at: Time.current)
|
|
67
|
+
Ductwork::Run
|
|
68
|
+
.where(id: step.run_id)
|
|
69
|
+
.where.not(status: "in_progress")
|
|
70
|
+
.update_all(status: "in_progress", updated_at: Time.current)
|
|
71
|
+
Ductwork::Pipeline
|
|
72
|
+
.where(id: Ductwork::Run.where(id: step.run_id).select(:pipeline_id))
|
|
73
|
+
.where.not(status: "in_progress")
|
|
74
|
+
.update_all(status: "in_progress", updated_at: Time.current)
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
|
@@ -66,7 +66,7 @@ RSpec::Matchers.define(:have_set_context) do |expected|
|
|
|
66
66
|
supports_block_expectations
|
|
67
67
|
|
|
68
68
|
match do |block|
|
|
69
|
-
ctx = Ductwork::Context.new(
|
|
69
|
+
ctx = Ductwork::Context.new(run_id)
|
|
70
70
|
before_values = expected.map { |k, _| ctx.get(k.to_s) }
|
|
71
71
|
|
|
72
72
|
block.call
|
|
@@ -76,7 +76,21 @@ RSpec::Matchers.define(:have_set_context) do |expected|
|
|
|
76
76
|
before_values.all?(&:nil?) && after_context == expected
|
|
77
77
|
end
|
|
78
78
|
|
|
79
|
-
chain :for_pipeline
|
|
79
|
+
chain :for_pipeline do |pipeline|
|
|
80
|
+
@run_id = pipeline.current_run.id
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
chain :for_run do |given_run_id|
|
|
84
|
+
@run_id = given_run_id
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def run_id
|
|
88
|
+
if @run_id.blank?
|
|
89
|
+
raise ArgumentError, "Must chain with .for_pipeline or .for_run"
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
@run_id
|
|
93
|
+
end
|
|
80
94
|
|
|
81
95
|
failure_message do
|
|
82
96
|
"Context does not match expected result"
|
|
@@ -88,25 +102,28 @@ module Ductwork
|
|
|
88
102
|
module RSpec
|
|
89
103
|
def pipeline_for(klass, **attrs)
|
|
90
104
|
definition = klass.pipeline_definition.to_json
|
|
91
|
-
definition_sha1 =
|
|
105
|
+
definition_sha1 = Digest::SHA1.hexdigest(definition)
|
|
106
|
+
pipeline_klass = klass.name.to_s
|
|
107
|
+
now = Time.current
|
|
92
108
|
status = attrs[:status] || "in_progress"
|
|
93
|
-
triggered_at = attrs[:triggered_at] ||
|
|
94
|
-
started_at = attrs[:started_at] ||
|
|
95
|
-
last_advanced_at = attrs[:last_advanced_at] || Time.current
|
|
109
|
+
triggered_at = attrs[:triggered_at] || now
|
|
110
|
+
started_at = attrs[:started_at] || now
|
|
96
111
|
|
|
97
|
-
Ductwork::Pipeline.create!(
|
|
98
|
-
|
|
112
|
+
pipeline = Ductwork::Pipeline.create!(klass:, status:)
|
|
113
|
+
pipeline.runs.create!(
|
|
114
|
+
pipeline_klass:,
|
|
99
115
|
definition:,
|
|
100
116
|
definition_sha1:,
|
|
101
117
|
status:,
|
|
102
118
|
triggered_at:,
|
|
103
|
-
started_at
|
|
104
|
-
last_advanced_at:
|
|
119
|
+
started_at:
|
|
105
120
|
)
|
|
121
|
+
|
|
122
|
+
pipeline
|
|
106
123
|
end
|
|
107
124
|
|
|
108
125
|
def set_pipeline_context(pipeline, **key_values)
|
|
109
|
-
ctx = Ductwork::Context.new(pipeline.id)
|
|
126
|
+
ctx = Ductwork::Context.new(pipeline.current_run.id)
|
|
110
127
|
key_values.each do |key, value|
|
|
111
128
|
ctx.set(key.to_s, value)
|
|
112
129
|
end
|
data/lib/ductwork/version.rb
CHANGED
data/lib/ductwork.rb
CHANGED
|
@@ -65,6 +65,7 @@ module Ductwork
|
|
|
65
65
|
def validate!
|
|
66
66
|
step_directory = Rails.root.join("app/steps")
|
|
67
67
|
pipeline_directory = Rails.root.join("app/pipelines")
|
|
68
|
+
workflow_directory = Rails.root.join("app/workflows")
|
|
68
69
|
loader = if defined?(Rails.autoloaders)
|
|
69
70
|
Rails.autoloaders.main
|
|
70
71
|
else
|
|
@@ -79,6 +80,10 @@ module Ductwork
|
|
|
79
80
|
loader.eager_load_dir(pipeline_directory)
|
|
80
81
|
end
|
|
81
82
|
|
|
83
|
+
if workflow_directory.exist?
|
|
84
|
+
loader.eager_load_dir(workflow_directory)
|
|
85
|
+
end
|
|
86
|
+
|
|
82
87
|
true
|
|
83
88
|
end
|
|
84
89
|
|
|
@@ -17,20 +17,30 @@ module Ductwork
|
|
|
17
17
|
|
|
18
18
|
migration_template "db/create_ductwork_pipelines.rb",
|
|
19
19
|
"db/migrate/create_ductwork_pipelines.rb"
|
|
20
|
+
migration_template "db/create_ductwork_runs.rb",
|
|
21
|
+
"db/migrate/create_ductwork_runs.rb"
|
|
22
|
+
migration_template "db/create_ductwork_branches.rb",
|
|
23
|
+
"db/migrate/create_ductwork_branches.rb"
|
|
24
|
+
migration_template "db/create_ductwork_branch_links.rb",
|
|
25
|
+
"db/migrate/create_ductwork_branch_links.rb"
|
|
20
26
|
migration_template "db/create_ductwork_steps.rb",
|
|
21
27
|
"db/migrate/create_ductwork_steps.rb"
|
|
22
28
|
migration_template "db/create_ductwork_jobs.rb",
|
|
23
29
|
"db/migrate/create_ductwork_jobs.rb"
|
|
30
|
+
migration_template "db/create_ductwork_processes.rb",
|
|
31
|
+
"db/migrate/create_ductwork_processes.rb"
|
|
24
32
|
migration_template "db/create_ductwork_executions.rb",
|
|
25
33
|
"db/migrate/create_ductwork_executions.rb"
|
|
26
34
|
migration_template "db/create_ductwork_availabilities.rb",
|
|
27
35
|
"db/migrate/create_ductwork_availabilities.rb"
|
|
28
|
-
migration_template "db/
|
|
29
|
-
"db/migrate/
|
|
36
|
+
migration_template "db/create_ductwork_attempts.rb",
|
|
37
|
+
"db/migrate/create_ductwork_attempts.rb"
|
|
30
38
|
migration_template "db/create_ductwork_results.rb",
|
|
31
39
|
"db/migrate/create_ductwork_results.rb"
|
|
32
|
-
migration_template "db/
|
|
33
|
-
"db/migrate/
|
|
40
|
+
migration_template "db/create_ductwork_transitions.rb",
|
|
41
|
+
"db/migrate/create_ductwork_transitions.rb"
|
|
42
|
+
migration_template "db/create_ductwork_advancements.rb",
|
|
43
|
+
"db/migrate/create_ductwork_advancements.rb"
|
|
34
44
|
migration_template "db/create_ductwork_tuples.rb",
|
|
35
45
|
"db/migrate/create_ductwork_tuples.rb"
|
|
36
46
|
|
|
@@ -9,12 +9,16 @@ default: &default
|
|
|
9
9
|
polling_timeout: 1
|
|
10
10
|
shutdown_timeout: 20
|
|
11
11
|
pipeline_advancer:
|
|
12
|
+
count: 3
|
|
13
|
+
max_crash: 10
|
|
14
|
+
max_retry: 3
|
|
12
15
|
polling_timeout: 1
|
|
13
16
|
shutdown_timeout: 20
|
|
14
17
|
steps:
|
|
15
18
|
max_depth: -1
|
|
16
19
|
supervisor:
|
|
17
20
|
polling_timeout: 1
|
|
21
|
+
reaper_timeout: 300
|
|
18
22
|
shutdown_timeout: 30
|
|
19
23
|
|
|
20
24
|
development:
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class CreateDuctworkAdvancements < Ductwork::Migration
|
|
4
|
+
def change
|
|
5
|
+
create_ductwork_table :ductwork_advancements do |table|
|
|
6
|
+
belongs_to(
|
|
7
|
+
table,
|
|
8
|
+
:process,
|
|
9
|
+
index: true,
|
|
10
|
+
null: true,
|
|
11
|
+
foreign_key: { to_table: :ductwork_processes }
|
|
12
|
+
)
|
|
13
|
+
belongs_to(
|
|
14
|
+
table,
|
|
15
|
+
:transition,
|
|
16
|
+
index: true,
|
|
17
|
+
null: false,
|
|
18
|
+
foreign_key: { to_table: :ductwork_transitions }
|
|
19
|
+
)
|
|
20
|
+
table.datetime :started_at, null: false
|
|
21
|
+
table.datetime :completed_at
|
|
22
|
+
table.integer :crash_count
|
|
23
|
+
table.string :error_klass
|
|
24
|
+
table.string :error_message
|
|
25
|
+
table.text :error_backtrace
|
|
26
|
+
table.timestamps null: false
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class CreateDuctworkAttempts < Ductwork::Migration
|
|
4
|
+
def change
|
|
5
|
+
create_ductwork_table :ductwork_attempts do |table|
|
|
6
|
+
belongs_to(
|
|
7
|
+
table,
|
|
8
|
+
:execution,
|
|
9
|
+
index: false,
|
|
10
|
+
null: false,
|
|
11
|
+
foreign_key: { to_table: :ductwork_executions }
|
|
12
|
+
)
|
|
13
|
+
table.timestamp :started_at, null: false
|
|
14
|
+
table.timestamp :completed_at
|
|
15
|
+
table.timestamps null: false
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
add_index :ductwork_attempts, :execution_id, unique: true
|
|
19
|
+
end
|
|
20
|
+
end
|
|
@@ -10,18 +10,31 @@ class CreateDuctworkAvailabilities < Ductwork::Migration
|
|
|
10
10
|
null: false,
|
|
11
11
|
foreign_key: { to_table: :ductwork_executions }
|
|
12
12
|
)
|
|
13
|
+
belongs_to(
|
|
14
|
+
table,
|
|
15
|
+
:process,
|
|
16
|
+
index: true,
|
|
17
|
+
null: true,
|
|
18
|
+
foreign_key: { to_table: :ductwork_processes }
|
|
19
|
+
)
|
|
13
20
|
table.timestamp :started_at, null: false
|
|
14
21
|
table.timestamp :completed_at
|
|
15
|
-
table.integer :process_id
|
|
16
22
|
table.string :pipeline_klass, null: false
|
|
17
23
|
table.timestamps null: false
|
|
18
24
|
end
|
|
19
25
|
|
|
20
26
|
add_index :ductwork_availabilities, :execution_id, unique: true
|
|
21
27
|
add_index :ductwork_availabilities, %i[id process_id]
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
28
|
+
|
|
29
|
+
if mysql?
|
|
30
|
+
add_index :ductwork_availabilities,
|
|
31
|
+
%i[pipeline_klass completed_at started_at],
|
|
32
|
+
name: "index_ductwork_availabilities_on_claim_latest"
|
|
33
|
+
else
|
|
34
|
+
add_index :ductwork_availabilities,
|
|
35
|
+
%i[pipeline_klass started_at],
|
|
36
|
+
name: "index_ductwork_availabilities_on_claim_latest",
|
|
37
|
+
where: "completed_at IS NULL"
|
|
38
|
+
end
|
|
26
39
|
end
|
|
27
40
|
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class CreateDuctworkBranchLinks < Ductwork::Migration
|
|
4
|
+
def change
|
|
5
|
+
create_ductwork_table :ductwork_branch_links do |table|
|
|
6
|
+
belongs_to(
|
|
7
|
+
table,
|
|
8
|
+
:parent_branch,
|
|
9
|
+
index: true,
|
|
10
|
+
null: false,
|
|
11
|
+
foreign_key: { to_table: :ductwork_branches }
|
|
12
|
+
)
|
|
13
|
+
belongs_to(
|
|
14
|
+
table,
|
|
15
|
+
:child_branch,
|
|
16
|
+
index: true,
|
|
17
|
+
null: false,
|
|
18
|
+
foreign_key: { to_table: :ductwork_branches }
|
|
19
|
+
)
|
|
20
|
+
table.timestamps null: false
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
add_index :ductwork_branch_links,
|
|
24
|
+
%w[parent_branch_id child_branch_id],
|
|
25
|
+
unique: true
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class CreateDuctworkBranches < Ductwork::Migration
|
|
4
|
+
def change
|
|
5
|
+
create_ductwork_table :ductwork_branches do |table|
|
|
6
|
+
belongs_to(
|
|
7
|
+
table,
|
|
8
|
+
:run,
|
|
9
|
+
index: true,
|
|
10
|
+
null: false,
|
|
11
|
+
foreign_key: { to_table: :ductwork_runs }
|
|
12
|
+
)
|
|
13
|
+
table.string :pipeline_klass, null: false
|
|
14
|
+
table.string :status, null: false
|
|
15
|
+
table.string :halt_reason
|
|
16
|
+
table.string :claim_token
|
|
17
|
+
table.datetime :claimed_for_advancing_at
|
|
18
|
+
table.datetime :last_advanced_at, null: false
|
|
19
|
+
table.datetime :started_at, null: false
|
|
20
|
+
table.datetime :completed_at
|
|
21
|
+
table.timestamps null: false
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
if mysql?
|
|
25
|
+
add_index :ductwork_branches,
|
|
26
|
+
%w[pipeline_klass status claimed_for_advancing_at last_advanced_at],
|
|
27
|
+
name: "index_ductwork_branches_on_claim_latest"
|
|
28
|
+
else
|
|
29
|
+
add_index :ductwork_branches,
|
|
30
|
+
%w[pipeline_klass status last_advanced_at],
|
|
31
|
+
where: "claimed_for_advancing_at IS NULL",
|
|
32
|
+
name: "index_ductwork_branches_on_claim_latest"
|
|
33
|
+
end
|
|
34
|
+
add_index :ductwork_branches, %w[run_id started_at]
|
|
35
|
+
add_index :ductwork_branches, %w[run_id status]
|
|
36
|
+
end
|
|
37
|
+
end
|
|
@@ -10,10 +10,17 @@ class CreateDuctworkExecutions < Ductwork::Migration
|
|
|
10
10
|
null: false,
|
|
11
11
|
foreign_key: { to_table: :ductwork_jobs }
|
|
12
12
|
)
|
|
13
|
+
belongs_to(
|
|
14
|
+
table,
|
|
15
|
+
:process,
|
|
16
|
+
index: true,
|
|
17
|
+
null: true,
|
|
18
|
+
foreign_key: { to_table: :ductwork_processes }
|
|
19
|
+
)
|
|
13
20
|
table.timestamp :started_at, null: false
|
|
14
21
|
table.timestamp :completed_at
|
|
15
22
|
table.integer :retry_count, null: false
|
|
16
|
-
table.integer :
|
|
23
|
+
table.integer :crash_count, null: false
|
|
17
24
|
table.timestamps null: false
|
|
18
25
|
end
|
|
19
26
|
|
|
@@ -4,14 +4,6 @@ class CreateDuctworkPipelines < Ductwork::Migration
|
|
|
4
4
|
def change
|
|
5
5
|
create_ductwork_table :ductwork_pipelines do |table|
|
|
6
6
|
table.string :klass, null: false
|
|
7
|
-
table.text :definition, null: false
|
|
8
|
-
table.string :definition_sha1, null: false
|
|
9
|
-
table.timestamp :triggered_at, null: false
|
|
10
|
-
table.timestamp :started_at, null: false
|
|
11
|
-
table.timestamp :completed_at
|
|
12
|
-
table.timestamp :halted_at
|
|
13
|
-
table.timestamp :claimed_for_advancing_at
|
|
14
|
-
table.timestamp :last_advanced_at, null: false
|
|
15
7
|
table.string :status, null: false
|
|
16
8
|
table.timestamps null: false
|
|
17
9
|
end
|
|
@@ -5,10 +5,12 @@ class CreateDuctworkProcesses < Ductwork::Migration
|
|
|
5
5
|
create_ductwork_table :ductwork_processes do |table|
|
|
6
6
|
table.integer :pid, null: false
|
|
7
7
|
table.string :machine_identifier, null: false
|
|
8
|
+
table.string :role, null: false
|
|
8
9
|
table.timestamp :last_heartbeat_at, null: false
|
|
9
10
|
table.timestamps null: false
|
|
10
11
|
end
|
|
11
12
|
|
|
13
|
+
add_index :ductwork_processes, :last_heartbeat_at
|
|
12
14
|
add_index :ductwork_processes, %i[pid machine_identifier], unique: true
|
|
13
15
|
end
|
|
14
16
|
end
|
|
@@ -5,16 +5,55 @@ class CreateDuctworkRuns < Ductwork::Migration
|
|
|
5
5
|
create_ductwork_table :ductwork_runs do |table|
|
|
6
6
|
belongs_to(
|
|
7
7
|
table,
|
|
8
|
-
:
|
|
9
|
-
index:
|
|
8
|
+
:pipeline,
|
|
9
|
+
index: true,
|
|
10
10
|
null: false,
|
|
11
|
-
foreign_key: { to_table: :
|
|
11
|
+
foreign_key: { to_table: :ductwork_pipelines }
|
|
12
12
|
)
|
|
13
|
-
table.
|
|
14
|
-
table.
|
|
13
|
+
table.string :pipeline_klass, null: false
|
|
14
|
+
table.text :definition, null: false
|
|
15
|
+
table.string :definition_sha1, null: false
|
|
16
|
+
table.string :status, null: false
|
|
17
|
+
table.datetime :triggered_at, null: false
|
|
18
|
+
table.datetime :started_at, null: false
|
|
19
|
+
table.datetime :completed_at
|
|
20
|
+
table.datetime :halted_at
|
|
21
|
+
table.datetime :on_halt_dispatched_at
|
|
15
22
|
table.timestamps null: false
|
|
16
23
|
end
|
|
17
24
|
|
|
18
|
-
add_index :ductwork_runs,
|
|
25
|
+
add_index :ductwork_runs, %i[pipeline_id status]
|
|
26
|
+
add_index :ductwork_runs, %i[pipeline_id started_at]
|
|
27
|
+
add_index :ductwork_runs, :started_at
|
|
28
|
+
add_index :ductwork_runs, %i[pipeline_klass started_at]
|
|
29
|
+
add_index :ductwork_runs, %i[status started_at]
|
|
30
|
+
|
|
31
|
+
if mysql?
|
|
32
|
+
reversible do |direction|
|
|
33
|
+
direction.up do
|
|
34
|
+
execute <<~SQL
|
|
35
|
+
ALTER TABLE ductwork_runs
|
|
36
|
+
ADD COLUMN active_pipeline_id VARCHAR(36)
|
|
37
|
+
GENERATED ALWAYS AS (
|
|
38
|
+
IF(status IN ('in_progress', 'paused'), pipeline_id, NULL)
|
|
39
|
+
)
|
|
40
|
+
SQL
|
|
41
|
+
add_index :ductwork_runs, :active_pipeline_id,
|
|
42
|
+
unique: true,
|
|
43
|
+
name: :idx_unique_active_run
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
direction.down do
|
|
47
|
+
remove_index :ductwork_runs, name: :idx_unique_active_run
|
|
48
|
+
remove_column :ductwork_runs, :active_pipeline_id
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
else
|
|
52
|
+
add_index :ductwork_runs,
|
|
53
|
+
:pipeline_id,
|
|
54
|
+
unique: true,
|
|
55
|
+
where: "status IN ('in_progress', 'paused')",
|
|
56
|
+
name: :idx_unique_active_run
|
|
57
|
+
end
|
|
19
58
|
end
|
|
20
59
|
end
|
|
@@ -5,10 +5,24 @@ class CreateDuctworkSteps < Ductwork::Migration
|
|
|
5
5
|
create_ductwork_table :ductwork_steps do |table|
|
|
6
6
|
belongs_to(
|
|
7
7
|
table,
|
|
8
|
-
:
|
|
8
|
+
:run,
|
|
9
9
|
index: true,
|
|
10
10
|
null: false,
|
|
11
|
-
foreign_key: { to_table: :
|
|
11
|
+
foreign_key: { to_table: :ductwork_runs }
|
|
12
|
+
)
|
|
13
|
+
belongs_to(
|
|
14
|
+
table,
|
|
15
|
+
:branch,
|
|
16
|
+
index: true,
|
|
17
|
+
null: false,
|
|
18
|
+
foreign_key: { to_table: :ductwork_branches }
|
|
19
|
+
)
|
|
20
|
+
belongs_to(
|
|
21
|
+
table,
|
|
22
|
+
:source_step,
|
|
23
|
+
index: true,
|
|
24
|
+
null: true,
|
|
25
|
+
foreign_key: { to_table: :ductwork_steps }
|
|
12
26
|
)
|
|
13
27
|
table.string :node, null: false
|
|
14
28
|
table.string :klass, null: false
|
|
@@ -21,9 +35,10 @@ class CreateDuctworkSteps < Ductwork::Migration
|
|
|
21
35
|
table.timestamps null: false
|
|
22
36
|
end
|
|
23
37
|
|
|
24
|
-
add_index :ductwork_steps, %i[
|
|
25
|
-
add_index :ductwork_steps, %i[
|
|
38
|
+
add_index :ductwork_steps, %i[branch_id started_at]
|
|
39
|
+
add_index :ductwork_steps, %i[run_id node status]
|
|
40
|
+
add_index :ductwork_steps, %i[run_id status node]
|
|
41
|
+
add_index :ductwork_steps, %i[status branch_id]
|
|
26
42
|
add_index :ductwork_steps, %i[status node]
|
|
27
|
-
add_index :ductwork_steps, %i[pipeline_id status]
|
|
28
43
|
end
|
|
29
44
|
end
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class CreateDuctworkTransitions < Ductwork::Migration
|
|
4
|
+
def change
|
|
5
|
+
create_ductwork_table :ductwork_transitions do |table|
|
|
6
|
+
belongs_to(
|
|
7
|
+
table,
|
|
8
|
+
:branch,
|
|
9
|
+
index: true,
|
|
10
|
+
null: false,
|
|
11
|
+
foreign_key: { to_table: :ductwork_branches }
|
|
12
|
+
)
|
|
13
|
+
belongs_to(
|
|
14
|
+
table,
|
|
15
|
+
:in_step,
|
|
16
|
+
index: true,
|
|
17
|
+
null: false,
|
|
18
|
+
foreign_key: { to_table: :ductwork_steps }
|
|
19
|
+
)
|
|
20
|
+
belongs_to(
|
|
21
|
+
table,
|
|
22
|
+
:out_step,
|
|
23
|
+
index: true,
|
|
24
|
+
null: true,
|
|
25
|
+
foreign_key: { to_table: :ductwork_steps }
|
|
26
|
+
)
|
|
27
|
+
table.datetime :started_at, null: false
|
|
28
|
+
table.datetime :completed_at
|
|
29
|
+
table.timestamps null: false
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
if mysql?
|
|
33
|
+
add_index :ductwork_transitions,
|
|
34
|
+
%i[branch_id completed_at started_at],
|
|
35
|
+
name: "index_ductwork_transitions_on_latest_open"
|
|
36
|
+
else
|
|
37
|
+
add_index :ductwork_transitions,
|
|
38
|
+
%i[branch_id started_at],
|
|
39
|
+
where: "completed_at IS NULL",
|
|
40
|
+
name: "index_ductwork_transitions_on_latest_open"
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
@@ -5,10 +5,10 @@ class CreateDuctworkTuples < Ductwork::Migration
|
|
|
5
5
|
create_ductwork_table :ductwork_tuples do |table|
|
|
6
6
|
belongs_to(
|
|
7
7
|
table,
|
|
8
|
-
:
|
|
8
|
+
:run,
|
|
9
9
|
index: true,
|
|
10
10
|
null: false,
|
|
11
|
-
foreign_key: { to_table: :
|
|
11
|
+
foreign_key: { to_table: :ductwork_runs }
|
|
12
12
|
)
|
|
13
13
|
table.string :key, null: false
|
|
14
14
|
table.string :serialized_value
|
|
@@ -17,6 +17,6 @@ class CreateDuctworkTuples < Ductwork::Migration
|
|
|
17
17
|
table.timestamps null: false
|
|
18
18
|
end
|
|
19
19
|
|
|
20
|
-
add_index :ductwork_tuples, %i[key
|
|
20
|
+
add_index :ductwork_tuples, %i[run_id key], unique: true
|
|
21
21
|
end
|
|
22
22
|
end
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class AddCrashCountToDuctworkExecutions < Ductwork::Migration
|
|
4
|
+
def change
|
|
5
|
+
add_column :ductwork_executions, :crash_count, :integer
|
|
6
|
+
|
|
7
|
+
# NOTE: change this how you see fit for your scale
|
|
8
|
+
Ductwork::Execution
|
|
9
|
+
.where(crash_count: nil)
|
|
10
|
+
.update_all(crash_count: 0)
|
|
11
|
+
|
|
12
|
+
change_column_null :ductwork_executions, :crash_count, false
|
|
13
|
+
end
|
|
14
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class AddIndexesToDuctworkTransitions < Ductwork::Migration
|
|
4
|
+
def change
|
|
5
|
+
if mysql?
|
|
6
|
+
add_index :ductwork_transitions,
|
|
7
|
+
%i[branch_id completed_at started_at],
|
|
8
|
+
name: "index_ductwork_transitions_on_latest_open"
|
|
9
|
+
else
|
|
10
|
+
add_index :ductwork_transitions,
|
|
11
|
+
%i[branch_id started_at],
|
|
12
|
+
where: "completed_at IS NULL",
|
|
13
|
+
name: "index_ductwork_transitions_on_latest_open"
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|