ductwork 0.26.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.
Files changed (98) hide show
  1. checksums.yaml +4 -4
  2. data/.claude/skills/audit-clock-drift/SKILL.md +11 -0
  3. data/.claude/skills/audit-database-indexes/SKILL.md +11 -0
  4. data/.claude/skills/audit-database-support/SKILL.md +18 -0
  5. data/.claude/skills/audit-durability/SKILL.md +17 -0
  6. data/.saturnci/database.yml +1 -0
  7. data/CHANGELOG-PRO.md +60 -0
  8. data/CHANGELOG.md +144 -1
  9. data/CLAUDE.md +34 -0
  10. data/README.md +24 -14
  11. data/app/controllers/ductwork/application_controller.rb +3 -3
  12. data/app/controllers/ductwork/dashboards_controller.rb +1 -1
  13. data/app/controllers/ductwork/pipelines_controller.rb +4 -3
  14. data/app/helpers/ductwork/application_helper.rb +2 -2
  15. data/app/views/ductwork/dashboards/show.html.erb +12 -12
  16. data/app/views/ductwork/pipelines/index.html.erb +12 -12
  17. data/app/views/ductwork/pipelines/show.html.erb +13 -13
  18. data/app/views/ductwork/step_errors/index.html.erb +2 -2
  19. data/lib/ductwork/branch_claim.rb +185 -0
  20. data/lib/ductwork/cli.rb +73 -24
  21. data/lib/ductwork/configuration.rb +73 -3
  22. data/lib/ductwork/context.rb +17 -19
  23. data/lib/ductwork/database_clock.rb +84 -0
  24. data/lib/ductwork/dsl/branch_builder.rb +6 -1
  25. data/lib/ductwork/dsl/definition_builder.rb +24 -1
  26. data/lib/ductwork/{job_claim.rb → execution_claim.rb} +7 -6
  27. data/lib/ductwork/fault_injection.rb +35 -0
  28. data/lib/ductwork/migration_helper.rb +1 -1
  29. data/lib/ductwork/models/advancement.rb +48 -0
  30. data/lib/ductwork/models/attempt.rb +9 -0
  31. data/lib/ductwork/models/availability.rb +2 -0
  32. data/lib/ductwork/models/branch.rb +800 -0
  33. data/lib/ductwork/models/branch_link.rb +10 -0
  34. data/lib/ductwork/models/execution.rb +199 -2
  35. data/lib/ductwork/models/job.rb +8 -121
  36. data/lib/ductwork/models/pipeline.rb +150 -272
  37. data/lib/ductwork/models/process.rb +177 -5
  38. data/lib/ductwork/models/result.rb +2 -1
  39. data/lib/ductwork/models/run.rb +119 -1
  40. data/lib/ductwork/models/step.rb +26 -6
  41. data/lib/ductwork/models/transition.rb +15 -0
  42. data/lib/ductwork/models/tuple.rb +1 -4
  43. data/lib/ductwork/models/workflow.rb +3 -0
  44. data/lib/ductwork/optimistic_locking_execution_claim.rb +97 -0
  45. data/lib/ductwork/processes/health_check.rb +87 -0
  46. data/lib/ductwork/processes/job_worker.rb +86 -20
  47. data/lib/ductwork/processes/job_worker_runner.rb +25 -37
  48. data/lib/ductwork/processes/pipeline_advancer.rb +48 -76
  49. data/lib/ductwork/processes/pipeline_advancer_runner.rb +34 -44
  50. data/lib/ductwork/processes/process_supervisor.rb +87 -10
  51. data/lib/ductwork/processes/thread_supervisor.rb +55 -21
  52. data/lib/ductwork/processes/thread_supervisor_runner.rb +8 -5
  53. data/lib/ductwork/processes/worker_health_check.rb +55 -0
  54. data/lib/ductwork/row_locking_execution_claim.rb +77 -0
  55. data/lib/ductwork/testing/rspec.rb +28 -11
  56. data/lib/ductwork/version.rb +1 -1
  57. data/lib/ductwork.rb +5 -0
  58. data/lib/generators/ductwork/install/install_generator.rb +14 -4
  59. data/lib/generators/ductwork/install/templates/config/ductwork.yml +4 -0
  60. data/lib/generators/ductwork/install/templates/db/create_ductwork_advancements.rb +29 -0
  61. data/lib/generators/ductwork/install/templates/db/create_ductwork_attempts.rb +20 -0
  62. data/lib/generators/ductwork/install/templates/db/create_ductwork_availabilities.rb +18 -5
  63. data/lib/generators/ductwork/install/templates/db/create_ductwork_branch_links.rb +27 -0
  64. data/lib/generators/ductwork/install/templates/db/create_ductwork_branches.rb +37 -0
  65. data/lib/generators/ductwork/install/templates/db/create_ductwork_executions.rb +8 -1
  66. data/lib/generators/ductwork/install/templates/db/create_ductwork_pipelines.rb +0 -8
  67. data/lib/generators/ductwork/install/templates/db/create_ductwork_processes.rb +2 -0
  68. data/lib/generators/ductwork/install/templates/db/create_ductwork_results.rb +1 -0
  69. data/lib/generators/ductwork/install/templates/db/create_ductwork_runs.rb +45 -6
  70. data/lib/generators/ductwork/install/templates/db/create_ductwork_steps.rb +20 -5
  71. data/lib/generators/ductwork/install/templates/db/create_ductwork_transitions.rb +43 -0
  72. data/lib/generators/ductwork/install/templates/db/create_ductwork_tuples.rb +3 -3
  73. data/lib/generators/ductwork/update/templates/db/add_crash_count_to_ductwork_advancements.rb +7 -0
  74. data/lib/generators/ductwork/update/templates/db/add_crash_count_to_ductwork_executions.rb +14 -0
  75. data/lib/generators/ductwork/update/templates/db/add_indexes_to_ductwork_results.rb +7 -0
  76. data/lib/generators/ductwork/update/templates/db/add_indexes_to_ductwork_runs.rb +9 -0
  77. data/lib/generators/ductwork/update/templates/db/add_indexes_to_ductwork_transitions.rb +16 -0
  78. data/lib/generators/ductwork/update/templates/db/add_pipeline_started_index_to_ductwork_runs.rb +7 -0
  79. data/lib/generators/ductwork/update/templates/db/add_process_id_to_ductwork_executions.rb +35 -0
  80. data/lib/generators/ductwork/update/templates/db/add_role_to_ductwork_processes.rb +20 -0
  81. data/lib/generators/ductwork/update/templates/db/associate_branches_to_runs.rb +74 -0
  82. data/lib/generators/ductwork/update/templates/db/associate_steps_to_branches.rb +24 -0
  83. data/lib/generators/ductwork/update/templates/db/associate_steps_to_runs.rb +72 -0
  84. data/lib/generators/ductwork/update/templates/db/associate_tuples_to_runs.rb +73 -0
  85. data/lib/generators/ductwork/update/templates/db/backfill_branch_ids_on_steps.rb +21 -0
  86. data/lib/generators/ductwork/update/templates/db/create_ductwork_advancements.rb +28 -0
  87. data/lib/generators/ductwork/update/templates/db/create_ductwork_branch_links.rb +27 -0
  88. data/lib/generators/ductwork/update/templates/db/create_ductwork_branches.rb +37 -0
  89. data/lib/generators/ductwork/update/templates/db/create_ductwork_runs.rb +55 -0
  90. data/lib/generators/ductwork/update/templates/db/create_ductwork_transitions.rb +32 -0
  91. data/lib/generators/ductwork/update/templates/db/denormalize_pipeline_klass_on_availabilities.rb +11 -4
  92. data/lib/generators/ductwork/update/templates/db/migrate_tables_to_uuid_primary_key.rb +5 -0
  93. data/lib/generators/ductwork/update/templates/db/rename_runs_to_attempts.rb +7 -0
  94. data/lib/generators/ductwork/update/templates/db/update_process_associations.rb +29 -0
  95. data/lib/generators/ductwork/update/update_generator.rb +99 -0
  96. metadata +47 -6
  97. data/lib/ductwork/optimistic_locking_job_claim.rb +0 -88
  98. data/lib/ductwork/row_locking_job_claim.rb +0 -75
@@ -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 :process_id
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
@@ -18,5 +18,6 @@ class CreateDuctworkResults < Ductwork::Migration
18
18
  end
19
19
 
20
20
  add_index :ductwork_results, :execution_id, unique: true
21
+ add_index :ductwork_results, %i[result_type created_at]
21
22
  end
22
23
  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
- :execution,
9
- index: false,
8
+ :pipeline,
9
+ index: true,
10
10
  null: false,
11
- foreign_key: { to_table: :ductwork_executions }
11
+ foreign_key: { to_table: :ductwork_pipelines }
12
12
  )
13
- table.timestamp :started_at, null: false
14
- table.timestamp :completed_at
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, :execution_id, unique: true
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
- :pipeline,
8
+ :run,
9
9
  index: true,
10
10
  null: false,
11
- foreign_key: { to_table: :ductwork_pipelines }
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[pipeline_id status node]
25
- add_index :ductwork_steps, %i[pipeline_id node status]
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
- :pipeline,
8
+ :run,
9
9
  index: true,
10
10
  null: false,
11
- foreign_key: { to_table: :ductwork_pipelines }
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 pipeline_id], unique: true
20
+ add_index :ductwork_tuples, %i[run_id key], unique: true
21
21
  end
22
22
  end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ class AddCrashCountToDuctworkAdvancements < Ductwork::Migration
4
+ def change
5
+ add_column :ductwork_advancements, :crash_count, :integer
6
+ end
7
+ 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,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ class AddIndexesToDuctworkResults < Ductwork::Migration
4
+ def change
5
+ add_index :ductwork_results, %i[result_type created_at]
6
+ end
7
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ class AddIndexesToDuctworkRuns < Ductwork::Migration
4
+ def change
5
+ add_index :ductwork_runs, :started_at
6
+ add_index :ductwork_runs, %i[pipeline_klass started_at]
7
+ add_index :ductwork_runs, %i[status started_at]
8
+ end
9
+ 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
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ class AddPipelineStartedIndexToDuctworkRuns < Ductwork::Migration
4
+ def change
5
+ add_index :ductwork_runs, %i[pipeline_id started_at]
6
+ end
7
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ class AddProcessIdToDuctworkExecution < Ductwork::Migration
4
+ def up
5
+ options = if postgresql?
6
+ {
7
+ type: uuid_column_type,
8
+ index: true,
9
+ null: true,
10
+ foreign_key: { to_table: :ductwork_processes },
11
+ }
12
+ else
13
+ {
14
+ type: uuid_column_type,
15
+ limit: 36,
16
+ index: true,
17
+ null: true,
18
+ foreign_key: { to_table: :ductwork_processes },
19
+ }
20
+ end
21
+
22
+ add_reference :ductwork_executions, :process, **options
23
+
24
+ Ductwork::Execution.find_each do |execution|
25
+ execution.update!(process: execution.availability.process)
26
+ end
27
+ end
28
+
29
+ def down
30
+ remove_reference :ductwork_executions,
31
+ :process,
32
+ foreign_key: { to_table: :ductwork_processes },
33
+ index: true
34
+ end
35
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ class AddRoleToDuctworkProcesses < Ductwork::Migration
4
+ def up
5
+ add_column :ductwork_processes, :role, :string
6
+
7
+ # NOTE: ideally there are no process records that exist because the
8
+ # ductwork process should not be running. either way, we explicitly
9
+ # don't set it to supervisor so it won't report in the health checks
10
+ Ductwork::Process
11
+ .where(role: nil)
12
+ .update_all(role: "pipeline_advancer")
13
+
14
+ change_column_null :ductwork_executions, :crash_count, false
15
+ end
16
+
17
+ def down
18
+ remove_column :ductwork_processes, :role, :string
19
+ end
20
+ end
@@ -0,0 +1,74 @@
1
+ # frozen_string_literal: true
2
+
3
+ class AssociateBranchesToRuns < Ductwork::Migration
4
+ def up
5
+ options = if postgresql?
6
+ {
7
+ type: uuid_column_type,
8
+ index: true,
9
+ null: true,
10
+ foreign_key: { to_table: :ductwork_runs },
11
+ }
12
+ else
13
+ {
14
+ type: uuid_column_type,
15
+ limit: 36,
16
+ index: true,
17
+ null: true,
18
+ foreign_key: { to_table: :ductwork_runs },
19
+ }
20
+ end
21
+
22
+ add_reference :ductwork_branches, :run, **options
23
+
24
+ Ductwork::Pipeline.find_each do |pipeline|
25
+ run = Ductwork::Run.create!(
26
+ pipeline: pipeline,
27
+ pipeline_klass: pipeline.klass,
28
+ definition: pipeline.definition,
29
+ definition_sha1: pipeline.definition_sha1,
30
+ status: pipeline.status,
31
+ triggered_at: pipeline.triggered_at,
32
+ started_at: pipeline.started_at,
33
+ completed_at: pipeline.completed_at,
34
+ halted_at: pipeline.halted_at
35
+ )
36
+ pipeline.branches.update_all(run_id: run.id)
37
+ end
38
+
39
+ remove_index :ductwork_branches, %w[pipeline_id started_at]
40
+ remove_reference :ductwork_branches, :pipeline, index: true, foreign_key: { to_table: :ductwork_pipelines }
41
+
42
+ add_index :ductwork_branches, %w[run_id started_at]
43
+ end
44
+
45
+ def down
46
+ options = if postgresql?
47
+ {
48
+ type: uuid_column_type,
49
+ index: true,
50
+ null: true,
51
+ foreign_key: { to_table: :ductwork_pipelines },
52
+ }
53
+ else
54
+ {
55
+ type: uuid_column_type,
56
+ limit: 36,
57
+ index: true,
58
+ null: true,
59
+ foreign_key: { to_table: :ductwork_pipelines },
60
+ }
61
+ end
62
+
63
+ add_reference :ductwork_branches, :pipeline, **options
64
+
65
+ Ductwork::Run.find_each do |run|
66
+ Ductwork::Branch.where(run_id: run.id).update_all(pipeline_id: run.pipeline_id)
67
+ end
68
+
69
+ remove_index :ductwork_branches, %w[run_id started_at]
70
+ remove_reference :ductwork_branches, :run, index: true, foreign_key: { to_table: :ductwork_runs }
71
+
72
+ add_index :ductwork_branches, %w[pipeline_id started_at]
73
+ end
74
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ class AssociateStepsToBranches < Ductwork::Migration
4
+ def change
5
+ options = if postgresql?
6
+ {
7
+ type: uuid_column_type,
8
+ index: true,
9
+ null: true,
10
+ foreign_key: { to_table: :ductwork_branches },
11
+ }
12
+ else
13
+ {
14
+ type: uuid_column_type,
15
+ limit: 36,
16
+ index: true,
17
+ null: true,
18
+ foreign_key: { to_table: :ductwork_branches },
19
+ }
20
+ end
21
+
22
+ add_reference :ductwork_steps, :branch, **options
23
+ end
24
+ end
@@ -0,0 +1,72 @@
1
+ # frozen_string_literal: true
2
+
3
+ class AssociateStepsToRuns < Ductwork::Migration
4
+ def up
5
+ options = if postgresql?
6
+ {
7
+ type: uuid_column_type,
8
+ index: true,
9
+ null: true,
10
+ foreign_key: { to_table: :ductwork_runs },
11
+ }
12
+ else
13
+ {
14
+ type: uuid_column_type,
15
+ limit: 36,
16
+ index: true,
17
+ null: true,
18
+ foreign_key: { to_table: :ductwork_runs },
19
+ }
20
+ end
21
+
22
+ add_reference :ductwork_steps, :run, **options
23
+
24
+ Ductwork::Pipeline.find_each do |pipeline|
25
+ run = Ductwork::Run.find_by(pipeline_id: pipeline.id)
26
+ pipeline.steps.update_all(run_id: run.id) if run
27
+ end
28
+
29
+ remove_index :ductwork_steps, %i[pipeline_id status node]
30
+ remove_index :ductwork_steps, %i[pipeline_id node status]
31
+ remove_index :ductwork_steps, %i[pipeline_id status]
32
+ remove_reference :ductwork_steps, :pipeline, index: true, foreign_key: { to_table: :ductwork_pipelines }
33
+
34
+ add_index :ductwork_steps, %i[run_id status node]
35
+ add_index :ductwork_steps, %i[run_id node status]
36
+ add_index :ductwork_steps, %i[run_id status]
37
+ end
38
+
39
+ def down
40
+ options = if postgresql?
41
+ {
42
+ type: uuid_column_type,
43
+ index: true,
44
+ null: true,
45
+ foreign_key: { to_table: :ductwork_pipelines },
46
+ }
47
+ else
48
+ {
49
+ type: uuid_column_type,
50
+ limit: 36,
51
+ index: true,
52
+ null: true,
53
+ foreign_key: { to_table: :ductwork_pipelines },
54
+ }
55
+ end
56
+
57
+ add_reference :ductwork_steps, :pipeline, **options
58
+
59
+ Ductwork::Run.find_each do |run|
60
+ Ductwork::Step.where(run_id: run.id).update_all(pipeline_id: run.pipeline_id)
61
+ end
62
+
63
+ remove_index :ductwork_steps, %i[run_id status node]
64
+ remove_index :ductwork_steps, %i[run_id node status]
65
+ remove_index :ductwork_steps, %i[run_id status]
66
+ remove_reference :ductwork_steps, :run, index: true, foreign_key: { to_table: :ductwork_runs }
67
+
68
+ add_index :ductwork_steps, %i[pipeline_id status node]
69
+ add_index :ductwork_steps, %i[pipeline_id node status]
70
+ add_index :ductwork_steps, %i[pipeline_id status]
71
+ end
72
+ end
@@ -0,0 +1,73 @@
1
+ # frozen_string_literal: true
2
+
3
+ class AssociateTuplesToRuns < Ductwork::Migration
4
+ def up
5
+ options = if postgresql?
6
+ {
7
+ type: uuid_column_type,
8
+ index: true,
9
+ null: true,
10
+ foreign_key: { to_table: :ductwork_runs },
11
+ }
12
+ else
13
+ {
14
+ type: uuid_column_type,
15
+ limit: 36,
16
+ index: true,
17
+ null: true,
18
+ foreign_key: { to_table: :ductwork_runs },
19
+ }
20
+ end
21
+
22
+ add_reference :ductwork_tuples, :run, **options
23
+
24
+ Ductwork::Pipeline.find_each do |pipeline|
25
+ run = Ductwork::Run.find_by(pipeline_id: pipeline.id)
26
+
27
+ pipeline.tuples.update_all(run_id: run.id) if run
28
+ end
29
+
30
+ remove_index :ductwork_tuples, %i[pipeline_id status node]
31
+ remove_index :ductwork_tuples, %i[pipeline_id node status]
32
+ remove_index :ductwork_tuples, %i[pipeline_id status]
33
+ remove_reference :ductwork_tuples, :pipeline, index: true, foreign_key: { to_table: :ductwork_pipelines }
34
+
35
+ add_index :ductwork_tuples, %i[run_id status node]
36
+ add_index :ductwork_tuples, %i[run_id node status]
37
+ add_index :ductwork_tuples, %i[run_id status]
38
+ end
39
+
40
+ def down
41
+ options = if postgresql?
42
+ {
43
+ type: uuid_column_type,
44
+ index: true,
45
+ null: true,
46
+ foreign_key: { to_table: :ductwork_pipelines },
47
+ }
48
+ else
49
+ {
50
+ type: uuid_column_type,
51
+ limit: 36,
52
+ index: true,
53
+ null: true,
54
+ foreign_key: { to_table: :ductwork_pipelines },
55
+ }
56
+ end
57
+
58
+ add_reference :ductwork_tuples, :pipeline, **options
59
+
60
+ Ductwork::Run.find_each do |run|
61
+ Ductwork::Tuple.where(run_id: run.id).update_all(pipeline_id: run.pipeline_id)
62
+ end
63
+
64
+ remove_index :ductwork_tuples, %i[run_id status node]
65
+ remove_index :ductwork_tuples, %i[run_id node status]
66
+ remove_index :ductwork_tuples, %i[run_id status]
67
+ remove_reference :ductwork_tuples, :run, index: true, foreign_key: { to_table: :ductwork_runs }
68
+
69
+ add_index :ductwork_tuples, %i[pipeline_id status node]
70
+ add_index :ductwork_tuples, %i[pipeline_id node status]
71
+ add_index :ductwork_tuples, %i[pipeline_id status]
72
+ end
73
+ end