ductwork 0.2.1 → 0.3.1
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/CHANGELOG.md +16 -0
- data/README.md +1 -1
- data/app/models/ductwork/job.rb +31 -16
- data/app/models/ductwork/pipeline.rb +134 -63
- data/lib/ductwork/cli.rb +6 -6
- data/lib/ductwork/configuration.rb +0 -1
- data/lib/ductwork/dsl/definition_builder.rb +32 -25
- data/lib/ductwork/engine.rb +1 -1
- data/lib/ductwork/processes/job_worker.rb +4 -8
- data/lib/ductwork/processes/job_worker_runner.rb +9 -13
- data/lib/ductwork/processes/pipeline_advancer.rb +4 -8
- data/lib/ductwork/processes/pipeline_advancer_runner.rb +10 -14
- data/lib/ductwork/processes/supervisor.rb +10 -14
- data/lib/ductwork/processes/supervisor_runner.rb +2 -3
- data/lib/ductwork/version.rb +1 -1
- data/lib/ductwork.rb +1 -1
- metadata +3 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: ab38ab3c5ce61b7fd6d48eac93d93bcaa33b3476a7b8b6e126403da7b4c2ea04
|
|
4
|
+
data.tar.gz: 7f3c09913cf33f5f2d6b42f2f3b786b09b35d343d1277262f77b0f9ff666b653
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: cd1bbacdd428ed77064e526a7a2bfe639aad474c19d002417b5d3893856cef916baaba805124e1f4042c90ce972328f3de8c9747c88a96d8b3a60d88a26b183a
|
|
7
|
+
data.tar.gz: cef8abdee38bea2530ba058fde0062699fc53f190e3e67deb8c369f7c1badae7a7e9538f11ce6fc8f1bce488e15a9ab97271bf6fbc0c59f38f8eea1187f376e8
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,21 @@
|
|
|
1
1
|
# Ductwork Changelog
|
|
2
2
|
|
|
3
|
+
## [0.3.1]
|
|
4
|
+
|
|
5
|
+
- chore: bump dependencies and update necessary files
|
|
6
|
+
- chore: update email address in gemspec
|
|
7
|
+
- chore: move `logger` out of config to top-level `Ductwork` module
|
|
8
|
+
- chore: promote `Ductwork::Pipeline#parsed_definition` to a public method
|
|
9
|
+
- fix: raise when trying to collapse a most recently divided pipeline and vice versa
|
|
10
|
+
|
|
11
|
+
## [0.3.0]
|
|
12
|
+
|
|
13
|
+
- fix: correctly create collapsing and combining steps and jobs for complex pipelines
|
|
14
|
+
- fix: add a new step and job for each active branch in a running pipeline
|
|
15
|
+
- fix: add a new node and edge for each active branch of the definition
|
|
16
|
+
- feat: add info-level logging for job events
|
|
17
|
+
- feat: add info-level logging for pipeline events
|
|
18
|
+
|
|
3
19
|
## [0.2.1]
|
|
4
20
|
|
|
5
21
|
- fix: do not splat arguments when executing a job nor triggering a pipeline
|
data/README.md
CHANGED
|
@@ -103,7 +103,7 @@ end
|
|
|
103
103
|
|
|
104
104
|
**Important:** Return values must be JSON-serializable.
|
|
105
105
|
|
|
106
|
-
See [Defining Pipelines](https://docs.getductwork.io/
|
|
106
|
+
See [Defining Pipelines](https://docs.getductwork.io/getting-started/defining-pipelines.html) for detailed documentation.
|
|
107
107
|
|
|
108
108
|
### 4. Run Ductwork
|
|
109
109
|
|
data/app/models/ductwork/job.rb
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module Ductwork
|
|
4
|
-
class Job < Ductwork::Record
|
|
4
|
+
class Job < Ductwork::Record # rubocop:todo Metrics/ClassLength
|
|
5
5
|
belongs_to :step, class_name: "Ductwork::Step"
|
|
6
6
|
has_many :executions, class_name: "Ductwork::Execution", foreign_key: "job_id", dependent: :destroy
|
|
7
7
|
|
|
@@ -39,7 +39,7 @@ module Ductwork
|
|
|
39
39
|
end
|
|
40
40
|
|
|
41
41
|
if rows_updated == 1
|
|
42
|
-
Ductwork.
|
|
42
|
+
Ductwork.logger.debug(
|
|
43
43
|
msg: "Job claimed",
|
|
44
44
|
role: :job_worker,
|
|
45
45
|
process_id: process_id,
|
|
@@ -49,7 +49,7 @@ module Ductwork
|
|
|
49
49
|
.joins(executions: :availability)
|
|
50
50
|
.find_by(ductwork_availabilities: { id:, process_id: })
|
|
51
51
|
else
|
|
52
|
-
Ductwork.
|
|
52
|
+
Ductwork.logger.debug(
|
|
53
53
|
msg: "Did not claim job, avoided race condition",
|
|
54
54
|
role: :job_worker,
|
|
55
55
|
process_id: process_id,
|
|
@@ -61,13 +61,13 @@ module Ductwork
|
|
|
61
61
|
end
|
|
62
62
|
|
|
63
63
|
def self.enqueue(step, args)
|
|
64
|
-
Ductwork::Record.transaction do
|
|
65
|
-
|
|
64
|
+
job = Ductwork::Record.transaction do
|
|
65
|
+
j = step.create_job!(
|
|
66
66
|
klass: step.klass,
|
|
67
67
|
started_at: Time.current,
|
|
68
68
|
input_args: JSON.dump({ args: })
|
|
69
69
|
)
|
|
70
|
-
execution =
|
|
70
|
+
execution = j.executions.create!(
|
|
71
71
|
started_at: Time.current,
|
|
72
72
|
retry_count: 0
|
|
73
73
|
)
|
|
@@ -75,14 +75,22 @@ module Ductwork
|
|
|
75
75
|
started_at: Time.current
|
|
76
76
|
)
|
|
77
77
|
|
|
78
|
-
|
|
78
|
+
j
|
|
79
79
|
end
|
|
80
|
+
|
|
81
|
+
Ductwork.logger.info(
|
|
82
|
+
msg: "Job enqueued",
|
|
83
|
+
job_id: job.id,
|
|
84
|
+
job_klass: job.klass
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
job
|
|
80
88
|
end
|
|
81
89
|
|
|
82
90
|
def execute(pipeline)
|
|
83
91
|
# i don't _really_ like this, but it should be fine for now...
|
|
84
92
|
execution = executions.order(:created_at).last
|
|
85
|
-
logger.debug(
|
|
93
|
+
Ductwork.logger.debug(
|
|
86
94
|
msg: "Executing job",
|
|
87
95
|
role: :job_worker,
|
|
88
96
|
pipeline: pipeline,
|
|
@@ -103,12 +111,13 @@ module Ductwork
|
|
|
103
111
|
execution_failed!(execution, run, e)
|
|
104
112
|
result = "failure"
|
|
105
113
|
ensure
|
|
106
|
-
logger.
|
|
107
|
-
msg: "
|
|
108
|
-
role: :job_worker,
|
|
114
|
+
Ductwork.logger.info(
|
|
115
|
+
msg: "Job executed",
|
|
109
116
|
pipeline: pipeline,
|
|
117
|
+
job_id: id,
|
|
110
118
|
job_klass: klass,
|
|
111
|
-
result: result
|
|
119
|
+
result: result,
|
|
120
|
+
role: :job_worker
|
|
112
121
|
)
|
|
113
122
|
end
|
|
114
123
|
end
|
|
@@ -121,10 +130,6 @@ module Ductwork
|
|
|
121
130
|
|
|
122
131
|
private
|
|
123
132
|
|
|
124
|
-
def logger
|
|
125
|
-
Ductwork.configuration.logger
|
|
126
|
-
end
|
|
127
|
-
|
|
128
133
|
def execution_succeeded!(execution, run, output_payload)
|
|
129
134
|
payload = JSON.dump({ payload: output_payload })
|
|
130
135
|
|
|
@@ -167,6 +172,16 @@ module Ductwork
|
|
|
167
172
|
end
|
|
168
173
|
end
|
|
169
174
|
|
|
175
|
+
Ductwork.logger.warn(
|
|
176
|
+
msg: "Job errored",
|
|
177
|
+
error_klass: error.class.name,
|
|
178
|
+
error_message: error.message,
|
|
179
|
+
job_id: id,
|
|
180
|
+
job_klass: klass,
|
|
181
|
+
pipeline_id: pipeline.id,
|
|
182
|
+
role: :job_worker
|
|
183
|
+
)
|
|
184
|
+
|
|
170
185
|
# NOTE: perform lifecycle hook execution outside of the transaction as
|
|
171
186
|
# to not unnecessarily hold it open
|
|
172
187
|
if halted
|
|
@@ -56,8 +56,8 @@ module Ductwork
|
|
|
56
56
|
step_klass = pipeline_definition.dig(:nodes, 0)
|
|
57
57
|
definition = JSON.dump(pipeline_definition)
|
|
58
58
|
|
|
59
|
-
Record.transaction do
|
|
60
|
-
|
|
59
|
+
pipeline = Record.transaction do
|
|
60
|
+
p = create!(
|
|
61
61
|
klass: name.to_s,
|
|
62
62
|
status: :in_progress,
|
|
63
63
|
definition: definition,
|
|
@@ -65,7 +65,7 @@ module Ductwork
|
|
|
65
65
|
triggered_at: Time.current,
|
|
66
66
|
last_advanced_at: Time.current
|
|
67
67
|
)
|
|
68
|
-
step =
|
|
68
|
+
step = p.steps.create!(
|
|
69
69
|
klass: step_klass,
|
|
70
70
|
status: :in_progress,
|
|
71
71
|
step_type: :start,
|
|
@@ -73,28 +73,44 @@ module Ductwork
|
|
|
73
73
|
)
|
|
74
74
|
Ductwork::Job.enqueue(step, args)
|
|
75
75
|
|
|
76
|
-
|
|
76
|
+
p
|
|
77
77
|
end
|
|
78
|
+
|
|
79
|
+
Ductwork.logger.info(
|
|
80
|
+
msg: "Pipeline triggered",
|
|
81
|
+
pipeline_id: pipeline.id,
|
|
82
|
+
role: :application
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
pipeline
|
|
78
86
|
end
|
|
79
87
|
end
|
|
80
88
|
|
|
81
89
|
def advance!
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
90
|
+
# NOTE: there could be A LOT of steps advancing for a single pipeline
|
|
91
|
+
# so instead of loading everything into memory and using ruby collection
|
|
92
|
+
# methods we make multiple queries. may need to revisist this once
|
|
93
|
+
# we do extensive load testing
|
|
94
|
+
advancing = steps.advancing
|
|
95
|
+
edges = if advancing.exists?
|
|
96
|
+
parsed_definition
|
|
97
|
+
.fetch(:edges, {})
|
|
98
|
+
.select { |k| k.in?(advancing.pluck(:klass)) }
|
|
99
|
+
end
|
|
86
100
|
|
|
87
101
|
Ductwork::Record.transaction do
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
if edge.nil?
|
|
91
|
-
conditionally_complete_pipeline
|
|
102
|
+
if edges.nil? || edges.values.all?(&:empty?)
|
|
103
|
+
conditionally_complete_pipeline(advancing)
|
|
92
104
|
else
|
|
93
|
-
|
|
105
|
+
advance_to_next_steps_by_type(edges, advancing)
|
|
94
106
|
end
|
|
95
107
|
end
|
|
96
108
|
end
|
|
97
109
|
|
|
110
|
+
def parsed_definition
|
|
111
|
+
@parsed_definition ||= JSON.parse(definition).with_indifferent_access
|
|
112
|
+
end
|
|
113
|
+
|
|
98
114
|
private
|
|
99
115
|
|
|
100
116
|
def create_step_and_enqueue_job(klass:, step_type:, input_arg:)
|
|
@@ -104,38 +120,64 @@ module Ductwork
|
|
|
104
120
|
Ductwork::Job.enqueue(next_step, input_arg)
|
|
105
121
|
end
|
|
106
122
|
|
|
107
|
-
def
|
|
108
|
-
|
|
109
|
-
end
|
|
123
|
+
def conditionally_complete_pipeline(advancing)
|
|
124
|
+
advancing.update!(status: :completed, completed_at: Time.current)
|
|
110
125
|
|
|
111
|
-
def conditionally_complete_pipeline
|
|
112
126
|
if steps.where(status: %w[in_progress pending]).none?
|
|
113
127
|
update!(status: :completed, completed_at: Time.current)
|
|
128
|
+
|
|
129
|
+
Ductwork.logger.info(
|
|
130
|
+
msg: "Pipeline completed",
|
|
131
|
+
pipeline_id: id,
|
|
132
|
+
role: :pipeline_advancer
|
|
133
|
+
)
|
|
114
134
|
end
|
|
115
135
|
end
|
|
116
136
|
|
|
117
|
-
def
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
step_type = edge[:type] == "chain" ? "default" : edge[:type]
|
|
121
|
-
|
|
122
|
-
if step_type.in?(%w[default divide])
|
|
123
|
-
advance_to_next_steps(step_type, step, edge)
|
|
124
|
-
elsif step_type == "combine"
|
|
125
|
-
combine_next_steps(step_type, edge)
|
|
126
|
-
elsif step_type == "expand"
|
|
127
|
-
expand_to_next_steps(step_type, step, edge)
|
|
128
|
-
elsif step_type == "collapse"
|
|
129
|
-
collapse_next_steps(step_type, step, edge)
|
|
137
|
+
def advance_to_next_steps_by_type(edges, advancing)
|
|
138
|
+
if edges.all? { |_, v| v.dig(-1, :type) == "combine" }
|
|
139
|
+
conditionally_combine_next_steps(edges, advancing)
|
|
130
140
|
else
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
141
|
+
edges.each do |step_klass, step_edges|
|
|
142
|
+
edge = step_edges[-1]
|
|
143
|
+
# NOTE: "chain" is used by ActiveRecord so we have to call
|
|
144
|
+
# this enum value "default" :sad:
|
|
145
|
+
step_type = edge[:type] == "chain" ? "default" : edge[:type]
|
|
146
|
+
|
|
147
|
+
if step_type == "collapse"
|
|
148
|
+
conditionally_collapse_next_steps(step_klass, edge, advancing)
|
|
149
|
+
else
|
|
150
|
+
advance_non_merging_steps(step_klass, edges, advancing)
|
|
151
|
+
end
|
|
152
|
+
end
|
|
153
|
+
end
|
|
154
|
+
advancing.update!(status: :completed, completed_at: Time.current)
|
|
155
|
+
log_pipeline_advanced(edges)
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
def advance_non_merging_steps(step_klass, edges, advancing)
|
|
159
|
+
advancing.where(klass: step_klass).find_each do |step|
|
|
160
|
+
edge = edges.dig(step.klass, -1)
|
|
161
|
+
# NOTE: "chain" is used by ActiveRecord so we have to call
|
|
162
|
+
# this enum value "default" :sad:
|
|
163
|
+
step_type = edge[:type] == "chain" ? "default" : edge[:type]
|
|
164
|
+
|
|
165
|
+
if step_type.in?(%w[default divide])
|
|
166
|
+
advance_to_next_steps(step_type, advancing, edge)
|
|
167
|
+
elsif step_type == "expand"
|
|
168
|
+
expand_to_next_steps(step_type, advancing, edge)
|
|
169
|
+
else
|
|
170
|
+
Ductwork.logger.error(
|
|
171
|
+
msg: "Invalid step type",
|
|
172
|
+
step_type: step_type,
|
|
173
|
+
pipeline_id: id,
|
|
174
|
+
role: :pipeline_advancer
|
|
175
|
+
)
|
|
176
|
+
end
|
|
135
177
|
end
|
|
136
178
|
end
|
|
137
179
|
|
|
138
|
-
def advance_to_next_steps(step_type,
|
|
180
|
+
def advance_to_next_steps(step_type, advancing, edge)
|
|
139
181
|
edge[:to].each do |to_klass|
|
|
140
182
|
next_step = steps.create!(
|
|
141
183
|
klass: to_klass,
|
|
@@ -143,53 +185,82 @@ module Ductwork
|
|
|
143
185
|
step_type: step_type,
|
|
144
186
|
started_at: Time.current
|
|
145
187
|
)
|
|
146
|
-
Ductwork::Job.enqueue(next_step,
|
|
188
|
+
Ductwork::Job.enqueue(next_step, advancing.take.job.return_value)
|
|
147
189
|
end
|
|
148
190
|
end
|
|
149
191
|
|
|
150
|
-
def
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
).map(&:return_value)
|
|
159
|
-
create_step_and_enqueue_job(
|
|
160
|
-
klass: edge[:to].sole,
|
|
161
|
-
step_type: step_type,
|
|
162
|
-
input_arg: input_arg
|
|
192
|
+
def conditionally_combine_next_steps(edges, advancing)
|
|
193
|
+
if steps.where(status: %w[pending in_progress], klass: edges.keys).none?
|
|
194
|
+
combine_next_steps(edges, advancing)
|
|
195
|
+
else
|
|
196
|
+
Ductwork.logger.debug(
|
|
197
|
+
msg: "Not all divided steps have completed; not combining",
|
|
198
|
+
pipeline_id: id,
|
|
199
|
+
role: :pipeline_advancer
|
|
163
200
|
)
|
|
164
201
|
end
|
|
165
202
|
end
|
|
166
203
|
|
|
167
|
-
def
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
204
|
+
def combine_next_steps(edges, advancing)
|
|
205
|
+
klass = edges.values.sample.dig(-1, :to).sole
|
|
206
|
+
step_type = "combine"
|
|
207
|
+
groups = advancing
|
|
208
|
+
.group(:klass)
|
|
209
|
+
.count
|
|
210
|
+
.keys
|
|
211
|
+
.map { |k| advancing.where(klass: k) }
|
|
212
|
+
|
|
213
|
+
groups.first.zip(*groups[1..]).each do |group|
|
|
214
|
+
input_arg = Ductwork::Job
|
|
215
|
+
.where(step_id: group.map(&:id))
|
|
216
|
+
.map(&:return_value)
|
|
217
|
+
create_step_and_enqueue_job(klass:, step_type:, input_arg:)
|
|
174
218
|
end
|
|
175
219
|
end
|
|
176
220
|
|
|
177
|
-
def
|
|
178
|
-
|
|
179
|
-
input_arg = Job.where(
|
|
180
|
-
step: steps.completed.where(klass: step.klass)
|
|
181
|
-
).map(&:return_value)
|
|
221
|
+
def expand_to_next_steps(step_type, advancing, edge)
|
|
222
|
+
Array(advancing.take.job.return_value).each do |input_arg|
|
|
182
223
|
create_step_and_enqueue_job(
|
|
183
224
|
klass: edge[:to].sole,
|
|
184
225
|
step_type: step_type,
|
|
185
226
|
input_arg: input_arg
|
|
186
227
|
)
|
|
228
|
+
end
|
|
229
|
+
end
|
|
230
|
+
|
|
231
|
+
def conditionally_collapse_next_steps(step_klass, edge, advancing)
|
|
232
|
+
if steps.where(status: %w[pending in_progress], klass: step_klass).none?
|
|
233
|
+
collapse_next_steps(edge[:to].sole, advancing)
|
|
187
234
|
else
|
|
188
|
-
Ductwork.
|
|
189
|
-
msg: "Not all expanded steps have completed",
|
|
235
|
+
Ductwork.logger.debug(
|
|
236
|
+
msg: "Not all expanded steps have completed; not collapsing",
|
|
237
|
+
pipeline_id: id,
|
|
190
238
|
role: :pipeline_advancer
|
|
191
239
|
)
|
|
192
240
|
end
|
|
193
241
|
end
|
|
242
|
+
|
|
243
|
+
def collapse_next_steps(klass, advancing)
|
|
244
|
+
step_type = "collapse"
|
|
245
|
+
input_arg = []
|
|
246
|
+
|
|
247
|
+
# NOTE: because of expanding based on return values, there
|
|
248
|
+
# could be A LOT of jobs so we want to use batch methods
|
|
249
|
+
# to avoid creating too many in-memory objects
|
|
250
|
+
Ductwork::Job.where(step_id: advancing.ids).find_each do |job|
|
|
251
|
+
input_arg << job.return_value
|
|
252
|
+
end
|
|
253
|
+
|
|
254
|
+
create_step_and_enqueue_job(klass:, step_type:, input_arg:)
|
|
255
|
+
end
|
|
256
|
+
|
|
257
|
+
def log_pipeline_advanced(edges)
|
|
258
|
+
Ductwork.logger.info(
|
|
259
|
+
msg: "Pipeline advanced",
|
|
260
|
+
pipeline_id: id,
|
|
261
|
+
transitions: edges.map { |_, v| v.dig(-1, :type) },
|
|
262
|
+
role: :pipeline_advancer
|
|
263
|
+
)
|
|
264
|
+
end
|
|
194
265
|
end
|
|
195
266
|
end
|
data/lib/ductwork/cli.rb
CHANGED
|
@@ -8,12 +8,12 @@ module Ductwork
|
|
|
8
8
|
def start!(args)
|
|
9
9
|
options = parse_options(args)
|
|
10
10
|
Ductwork.configuration = Configuration.new(**options)
|
|
11
|
-
Ductwork.
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
Ductwork.
|
|
11
|
+
Ductwork.logger = if Ductwork.configuration.logger_source == "rails"
|
|
12
|
+
Rails.logger
|
|
13
|
+
else
|
|
14
|
+
Ductwork::Configuration::DEFAULT_LOGGER
|
|
15
|
+
end
|
|
16
|
+
Ductwork.logger.level = Ductwork.configuration.logger_level
|
|
17
17
|
|
|
18
18
|
Ductwork::Processes::SupervisorRunner.start!
|
|
19
19
|
end
|
|
@@ -12,8 +12,8 @@ module Ductwork
|
|
|
12
12
|
nodes: [],
|
|
13
13
|
edges: {},
|
|
14
14
|
}
|
|
15
|
-
@
|
|
16
|
-
@
|
|
15
|
+
@divergences = []
|
|
16
|
+
@last_nodes = []
|
|
17
17
|
end
|
|
18
18
|
|
|
19
19
|
def start(klass)
|
|
@@ -29,7 +29,7 @@ module Ductwork
|
|
|
29
29
|
def chain(klass)
|
|
30
30
|
validate_classes!(klass)
|
|
31
31
|
validate_definition_started!(action: "chaining")
|
|
32
|
-
|
|
32
|
+
add_edge_to_last_nodes(klass, type: :chain)
|
|
33
33
|
add_new_nodes(klass)
|
|
34
34
|
|
|
35
35
|
self
|
|
@@ -38,10 +38,10 @@ module Ductwork
|
|
|
38
38
|
def divide(to:)
|
|
39
39
|
validate_classes!(to)
|
|
40
40
|
validate_definition_started!(action: "dividing chain")
|
|
41
|
-
|
|
41
|
+
add_edge_to_last_nodes(*to, type: :divide)
|
|
42
42
|
add_new_nodes(*to)
|
|
43
43
|
|
|
44
|
-
|
|
44
|
+
divergences.push(:divide)
|
|
45
45
|
|
|
46
46
|
if block_given?
|
|
47
47
|
branches = to.map do |klass|
|
|
@@ -58,9 +58,9 @@ module Ductwork
|
|
|
58
58
|
def combine(into:)
|
|
59
59
|
validate_classes!(into)
|
|
60
60
|
validate_definition_started!(action: "combining steps")
|
|
61
|
-
|
|
61
|
+
validate_can_combine!
|
|
62
62
|
|
|
63
|
-
|
|
63
|
+
divergences.pop
|
|
64
64
|
|
|
65
65
|
last_nodes = definition[:nodes].reverse.select do |node|
|
|
66
66
|
definition[:edges][node].empty?
|
|
@@ -79,10 +79,10 @@ module Ductwork
|
|
|
79
79
|
def expand(to:)
|
|
80
80
|
validate_classes!(to)
|
|
81
81
|
validate_definition_started!(action: "expanding chain")
|
|
82
|
-
|
|
82
|
+
add_edge_to_last_nodes(to, type: :expand)
|
|
83
83
|
add_new_nodes(to)
|
|
84
84
|
|
|
85
|
-
|
|
85
|
+
divergences.push(:expand)
|
|
86
86
|
|
|
87
87
|
self
|
|
88
88
|
end
|
|
@@ -90,11 +90,11 @@ module Ductwork
|
|
|
90
90
|
def collapse(into:)
|
|
91
91
|
validate_classes!(into)
|
|
92
92
|
validate_definition_started!(action: "collapsing steps")
|
|
93
|
-
|
|
94
|
-
|
|
93
|
+
validate_can_collapse!
|
|
94
|
+
add_edge_to_last_nodes(into, type: :collapse)
|
|
95
95
|
add_new_nodes(into)
|
|
96
96
|
|
|
97
|
-
|
|
97
|
+
divergences.pop
|
|
98
98
|
|
|
99
99
|
self
|
|
100
100
|
end
|
|
@@ -117,7 +117,7 @@ module Ductwork
|
|
|
117
117
|
|
|
118
118
|
private
|
|
119
119
|
|
|
120
|
-
attr_reader :definition, :
|
|
120
|
+
attr_reader :definition, :last_nodes, :divergences
|
|
121
121
|
|
|
122
122
|
def validate_classes!(klasses)
|
|
123
123
|
valid = Array(klasses).all? do |klass|
|
|
@@ -149,32 +149,39 @@ module Ductwork
|
|
|
149
149
|
end
|
|
150
150
|
end
|
|
151
151
|
|
|
152
|
-
def
|
|
153
|
-
if
|
|
152
|
+
def validate_can_combine!
|
|
153
|
+
if divergences.empty?
|
|
154
154
|
raise CombineError, "Must divide pipeline definition before combining steps"
|
|
155
|
+
elsif divergences[-1] != :divide
|
|
156
|
+
raise CombineError, "Ambiguous combine on most recently expanded definition"
|
|
155
157
|
end
|
|
156
158
|
end
|
|
157
159
|
|
|
158
|
-
def
|
|
159
|
-
if
|
|
160
|
+
def validate_can_collapse!
|
|
161
|
+
if divergences.empty?
|
|
160
162
|
raise CollapseError, "Must expand pipeline definition before collapsing steps"
|
|
163
|
+
elsif divergences[-1] != :expand
|
|
164
|
+
raise CollapseError, "Ambiguous collapse on most recently divided definition"
|
|
161
165
|
end
|
|
162
166
|
end
|
|
163
167
|
|
|
164
168
|
def add_new_nodes(*klasses)
|
|
165
|
-
|
|
169
|
+
nodes = klasses.map(&:name)
|
|
170
|
+
@last_nodes = Array(nodes)
|
|
171
|
+
|
|
172
|
+
definition[:nodes].push(*nodes)
|
|
166
173
|
klasses.each do |klass|
|
|
167
174
|
definition[:edges][klass.name] ||= []
|
|
168
175
|
end
|
|
169
176
|
end
|
|
170
177
|
|
|
171
|
-
def
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
+
def add_edge_to_last_nodes(*klasses, type:)
|
|
179
|
+
last_nodes.each do |last_node|
|
|
180
|
+
definition[:edges][last_node] << {
|
|
181
|
+
to: klasses.map(&:name),
|
|
182
|
+
type: type,
|
|
183
|
+
}
|
|
184
|
+
end
|
|
178
185
|
end
|
|
179
186
|
end
|
|
180
187
|
end
|
data/lib/ductwork/engine.rb
CHANGED
|
@@ -8,7 +8,7 @@ module Ductwork
|
|
|
8
8
|
|
|
9
9
|
initializer "ductwork.configure" do
|
|
10
10
|
Ductwork.configuration ||= Ductwork::Configuration.new
|
|
11
|
-
Ductwork.
|
|
11
|
+
Ductwork.logger ||= Ductwork::Configuration::DEFAULT_LOGGER
|
|
12
12
|
end
|
|
13
13
|
|
|
14
14
|
initializer "ductwork.validate_definitions", after: :load_config_initializers do
|
|
@@ -10,13 +10,13 @@ module Ductwork
|
|
|
10
10
|
|
|
11
11
|
def run
|
|
12
12
|
run_hooks_for(:start)
|
|
13
|
-
logger.debug(
|
|
13
|
+
Ductwork.logger.debug(
|
|
14
14
|
msg: "Entering main work loop",
|
|
15
15
|
role: :job_worker,
|
|
16
16
|
pipeline: pipeline
|
|
17
17
|
)
|
|
18
18
|
while running_context.running?
|
|
19
|
-
logger.debug(
|
|
19
|
+
Ductwork.logger.debug(
|
|
20
20
|
msg: "Attempting to claim job",
|
|
21
21
|
role: :job_worker,
|
|
22
22
|
pipeline: pipeline
|
|
@@ -30,7 +30,7 @@ module Ductwork
|
|
|
30
30
|
job.execute(pipeline)
|
|
31
31
|
end
|
|
32
32
|
else
|
|
33
|
-
logger.debug(
|
|
33
|
+
Ductwork.logger.debug(
|
|
34
34
|
msg: "No job to claim, looping",
|
|
35
35
|
role: :job_worker,
|
|
36
36
|
pipeline: pipeline
|
|
@@ -47,7 +47,7 @@ module Ductwork
|
|
|
47
47
|
attr_reader :pipeline, :running_context
|
|
48
48
|
|
|
49
49
|
def shutdown
|
|
50
|
-
logger.debug(
|
|
50
|
+
Ductwork.logger.debug(
|
|
51
51
|
msg: "Shutting down",
|
|
52
52
|
role: :job_worker,
|
|
53
53
|
pipeline: pipeline
|
|
@@ -62,10 +62,6 @@ module Ductwork
|
|
|
62
62
|
end
|
|
63
63
|
end
|
|
64
64
|
end
|
|
65
|
-
|
|
66
|
-
def logger
|
|
67
|
-
Ductwork.configuration.logger
|
|
68
|
-
end
|
|
69
65
|
end
|
|
70
66
|
end
|
|
71
67
|
end
|
|
@@ -25,7 +25,7 @@ module Ductwork
|
|
|
25
25
|
|
|
26
26
|
def run
|
|
27
27
|
create_process!
|
|
28
|
-
logger.debug(
|
|
28
|
+
Ductwork.logger.debug(
|
|
29
29
|
msg: "Entering main work loop",
|
|
30
30
|
role: :job_worker_runner,
|
|
31
31
|
pipeline: pipeline
|
|
@@ -55,7 +55,7 @@ module Ductwork
|
|
|
55
55
|
pipeline,
|
|
56
56
|
running_context
|
|
57
57
|
)
|
|
58
|
-
logger.debug(
|
|
58
|
+
Ductwork.logger.debug(
|
|
59
59
|
msg: "Creating new thread",
|
|
60
60
|
role: :job_worker_runner,
|
|
61
61
|
pipeline: pipeline
|
|
@@ -65,7 +65,7 @@ module Ductwork
|
|
|
65
65
|
end
|
|
66
66
|
thread.name = "ductwork.job_worker_#{i}"
|
|
67
67
|
|
|
68
|
-
logger.debug(
|
|
68
|
+
Ductwork.logger.debug(
|
|
69
69
|
msg: "Created new thread",
|
|
70
70
|
role: :job_worker_runner,
|
|
71
71
|
pipeline: pipeline
|
|
@@ -90,13 +90,13 @@ module Ductwork
|
|
|
90
90
|
end
|
|
91
91
|
|
|
92
92
|
def attempt_synchronize_threads
|
|
93
|
-
logger.debug(
|
|
93
|
+
Ductwork.logger.debug(
|
|
94
94
|
msg: "Attempting to synchronize threads",
|
|
95
95
|
role: :job_worker_runner,
|
|
96
96
|
pipeline: pipeline
|
|
97
97
|
)
|
|
98
98
|
threads.each { |thread| thread.join(0.1) }
|
|
99
|
-
logger.debug(
|
|
99
|
+
Ductwork.logger.debug(
|
|
100
100
|
msg: "Synchronizing threads timed out",
|
|
101
101
|
role: :job_worker_runner,
|
|
102
102
|
pipeline: pipeline
|
|
@@ -104,11 +104,11 @@ module Ductwork
|
|
|
104
104
|
end
|
|
105
105
|
|
|
106
106
|
def report_heartbeat!
|
|
107
|
-
logger.debug(msg: "Reporting heartbeat", role: :job_worker_runner)
|
|
107
|
+
Ductwork.logger.debug(msg: "Reporting heartbeat", role: :job_worker_runner)
|
|
108
108
|
Ductwork.wrap_with_app_executor do
|
|
109
109
|
Ductwork::Process.report_heartbeat!
|
|
110
110
|
end
|
|
111
|
-
logger.debug(msg: "Reported heartbeat", role: :job_worker_runner)
|
|
111
|
+
Ductwork.logger.debug(msg: "Reported heartbeat", role: :job_worker_runner)
|
|
112
112
|
end
|
|
113
113
|
|
|
114
114
|
def shutdown!
|
|
@@ -122,7 +122,7 @@ module Ductwork
|
|
|
122
122
|
timeout = Ductwork.configuration.job_worker_shutdown_timeout
|
|
123
123
|
deadline = Time.current + timeout
|
|
124
124
|
|
|
125
|
-
logger.debug(msg: "Attempting graceful shutdown", role: :job_worker_runner)
|
|
125
|
+
Ductwork.logger.debug(msg: "Attempting graceful shutdown", role: :job_worker_runner)
|
|
126
126
|
while Time.current < deadline && threads.any?(&:alive?)
|
|
127
127
|
threads.each do |thread|
|
|
128
128
|
break if Time.current < deadline
|
|
@@ -138,7 +138,7 @@ module Ductwork
|
|
|
138
138
|
threads.each do |thread|
|
|
139
139
|
if thread.alive?
|
|
140
140
|
thread.kill
|
|
141
|
-
logger.debug(
|
|
141
|
+
Ductwork.logger.debug(
|
|
142
142
|
msg: "Killed thread",
|
|
143
143
|
role: :job_worker_runner,
|
|
144
144
|
thread: thread.name
|
|
@@ -155,10 +155,6 @@ module Ductwork
|
|
|
155
155
|
).delete
|
|
156
156
|
end
|
|
157
157
|
end
|
|
158
|
-
|
|
159
|
-
def logger
|
|
160
|
-
Ductwork.configuration.logger
|
|
161
|
-
end
|
|
162
158
|
end
|
|
163
159
|
end
|
|
164
160
|
end
|
|
@@ -28,7 +28,7 @@ module Ductwork
|
|
|
28
28
|
.update_all(claimed_for_advancing_at: Time.current)
|
|
29
29
|
|
|
30
30
|
if rows_updated == 1
|
|
31
|
-
logger.debug(
|
|
31
|
+
Ductwork.logger.debug(
|
|
32
32
|
msg: "Pipeline claimed",
|
|
33
33
|
pipeline: klass,
|
|
34
34
|
role: :pipeline_advancer
|
|
@@ -37,13 +37,13 @@ module Ductwork
|
|
|
37
37
|
pipeline = Ductwork::Pipeline.find(id)
|
|
38
38
|
pipeline.advance!
|
|
39
39
|
|
|
40
|
-
logger.debug(
|
|
40
|
+
Ductwork.logger.debug(
|
|
41
41
|
msg: "Pipeline advanced",
|
|
42
42
|
pipeline: klass,
|
|
43
43
|
role: :pipeline_advancer
|
|
44
44
|
)
|
|
45
45
|
else
|
|
46
|
-
logger.debug(
|
|
46
|
+
Ductwork.logger.debug(
|
|
47
47
|
msg: "Did not claim pipeline, avoided race condition",
|
|
48
48
|
pipeline: klass,
|
|
49
49
|
role: :pipeline_advancer
|
|
@@ -57,7 +57,7 @@ module Ductwork
|
|
|
57
57
|
last_advanced_at: Time.current
|
|
58
58
|
)
|
|
59
59
|
else
|
|
60
|
-
logger.debug(
|
|
60
|
+
Ductwork.logger.debug(
|
|
61
61
|
msg: "No pipeline needs advancing",
|
|
62
62
|
pipeline: klass,
|
|
63
63
|
id: id,
|
|
@@ -82,10 +82,6 @@ module Ductwork
|
|
|
82
82
|
end
|
|
83
83
|
end
|
|
84
84
|
end
|
|
85
|
-
|
|
86
|
-
def logger
|
|
87
|
-
Ductwork.configuration.logger
|
|
88
|
-
end
|
|
89
85
|
end
|
|
90
86
|
end
|
|
91
87
|
end
|
|
@@ -25,7 +25,7 @@ module Ductwork
|
|
|
25
25
|
|
|
26
26
|
def run
|
|
27
27
|
create_process!
|
|
28
|
-
logger.debug(
|
|
28
|
+
Ductwork.logger.debug(
|
|
29
29
|
msg: "Entering main work loop",
|
|
30
30
|
role: :pipeline_advancer_runner
|
|
31
31
|
)
|
|
@@ -51,7 +51,7 @@ module Ductwork
|
|
|
51
51
|
klass
|
|
52
52
|
)
|
|
53
53
|
|
|
54
|
-
logger.debug(
|
|
54
|
+
Ductwork.logger.debug(
|
|
55
55
|
msg: "Creating new thread",
|
|
56
56
|
role: :pipeline_advancer_runner,
|
|
57
57
|
pipeline: klass
|
|
@@ -63,7 +63,7 @@ module Ductwork
|
|
|
63
63
|
end
|
|
64
64
|
thread.name = "ductwork.pipeline_advancer.#{klass}"
|
|
65
65
|
|
|
66
|
-
logger.debug(
|
|
66
|
+
Ductwork.logger.debug(
|
|
67
67
|
msg: "Created new thread",
|
|
68
68
|
role: :pipeline_advancer_runner,
|
|
69
69
|
thread: thread.name,
|
|
@@ -75,12 +75,12 @@ module Ductwork
|
|
|
75
75
|
end
|
|
76
76
|
|
|
77
77
|
def attempt_synchronize_threads
|
|
78
|
-
logger.debug(
|
|
78
|
+
Ductwork.logger.debug(
|
|
79
79
|
msg: "Attempting to synchronize threads",
|
|
80
80
|
role: :pipeline_advancer_runner
|
|
81
81
|
)
|
|
82
82
|
threads.each { |thread| thread.join(0.1) }
|
|
83
|
-
logger.debug(
|
|
83
|
+
Ductwork.logger.debug(
|
|
84
84
|
msg: "Synchronizing threads timed out",
|
|
85
85
|
role: :pipeline_advancer_runner
|
|
86
86
|
)
|
|
@@ -97,11 +97,11 @@ module Ductwork
|
|
|
97
97
|
end
|
|
98
98
|
|
|
99
99
|
def report_heartbeat!
|
|
100
|
-
logger.debug(msg: "Reporting heartbeat", role: :pipeline_advancer_runner)
|
|
100
|
+
Ductwork.logger.debug(msg: "Reporting heartbeat", role: :pipeline_advancer_runner)
|
|
101
101
|
Ductwork.wrap_with_app_executor do
|
|
102
102
|
Ductwork::Process.report_heartbeat!
|
|
103
103
|
end
|
|
104
|
-
logger.debug(msg: "Reported heartbeat", role: :pipeline_advancer_runner)
|
|
104
|
+
Ductwork.logger.debug(msg: "Reported heartbeat", role: :pipeline_advancer_runner)
|
|
105
105
|
end
|
|
106
106
|
|
|
107
107
|
def shutdown
|
|
@@ -113,7 +113,7 @@ module Ductwork
|
|
|
113
113
|
end
|
|
114
114
|
|
|
115
115
|
def log_shutting_down
|
|
116
|
-
logger.debug(msg: "Shutting down", role: :pipeline_advancer_runner)
|
|
116
|
+
Ductwork.logger.debug(msg: "Shutting down", role: :pipeline_advancer_runner)
|
|
117
117
|
end
|
|
118
118
|
|
|
119
119
|
def stop_running_context
|
|
@@ -124,7 +124,7 @@ module Ductwork
|
|
|
124
124
|
timeout = Ductwork.configuration.pipeline_shutdown_timeout
|
|
125
125
|
deadline = Time.current + timeout
|
|
126
126
|
|
|
127
|
-
logger.debug(
|
|
127
|
+
Ductwork.logger.debug(
|
|
128
128
|
msg: "Attempting graceful shutdown",
|
|
129
129
|
role: :pipeline_advancer_runner
|
|
130
130
|
)
|
|
@@ -143,7 +143,7 @@ module Ductwork
|
|
|
143
143
|
threads.each do |thread|
|
|
144
144
|
if thread.alive?
|
|
145
145
|
thread.kill
|
|
146
|
-
logger.debug(
|
|
146
|
+
Ductwork.logger.debug(
|
|
147
147
|
msg: "Killed thread",
|
|
148
148
|
role: :pipeline_advancer_runner,
|
|
149
149
|
thread: thread.name
|
|
@@ -160,10 +160,6 @@ module Ductwork
|
|
|
160
160
|
).delete
|
|
161
161
|
end
|
|
162
162
|
end
|
|
163
|
-
|
|
164
|
-
def logger
|
|
165
|
-
Ductwork.configuration.logger
|
|
166
|
-
end
|
|
167
163
|
end
|
|
168
164
|
end
|
|
169
165
|
end
|
|
@@ -22,14 +22,14 @@ module Ductwork
|
|
|
22
22
|
end
|
|
23
23
|
|
|
24
24
|
workers << { metadata:, pid:, block: }
|
|
25
|
-
logger.debug(
|
|
25
|
+
Ductwork.logger.debug(
|
|
26
26
|
msg: "Started child process (#{pid}) with metadata #{metadata}",
|
|
27
27
|
pid: pid
|
|
28
28
|
)
|
|
29
29
|
end
|
|
30
30
|
|
|
31
31
|
def run
|
|
32
|
-
logger.debug(msg: "Entering main work loop", role: :supervisor, pid: ::Process.pid)
|
|
32
|
+
Ductwork.logger.debug(msg: "Entering main work loop", role: :supervisor, pid: ::Process.pid)
|
|
33
33
|
|
|
34
34
|
while running
|
|
35
35
|
sleep(Ductwork.configuration.supervisor_polling_timeout)
|
|
@@ -42,7 +42,7 @@ module Ductwork
|
|
|
42
42
|
def shutdown
|
|
43
43
|
@running = false
|
|
44
44
|
|
|
45
|
-
logger.debug(msg: "Beginning shutdown", role: :supervisor)
|
|
45
|
+
Ductwork.logger.debug(msg: "Beginning shutdown", role: :supervisor)
|
|
46
46
|
terminate_gracefully
|
|
47
47
|
wait_for_workers_to_exit
|
|
48
48
|
terminate_immediately
|
|
@@ -54,7 +54,7 @@ module Ductwork
|
|
|
54
54
|
attr_reader :running
|
|
55
55
|
|
|
56
56
|
def check_workers
|
|
57
|
-
logger.debug(msg: "Checking workers are alive", role: :supervisor)
|
|
57
|
+
Ductwork.logger.debug(msg: "Checking workers are alive", role: :supervisor)
|
|
58
58
|
|
|
59
59
|
workers.each do |worker|
|
|
60
60
|
if process_dead?(worker[:pid])
|
|
@@ -63,7 +63,7 @@ module Ductwork
|
|
|
63
63
|
worker[:block].call(worker[:metadata])
|
|
64
64
|
end
|
|
65
65
|
worker[:pid] = new_pid
|
|
66
|
-
logger.debug(
|
|
66
|
+
Ductwork.logger.debug(
|
|
67
67
|
msg: "Restarted process (#{old_pid}) as (#{new_pid})",
|
|
68
68
|
role: :supervisor,
|
|
69
69
|
old_pid: old_pid,
|
|
@@ -72,12 +72,12 @@ module Ductwork
|
|
|
72
72
|
end
|
|
73
73
|
end
|
|
74
74
|
|
|
75
|
-
logger.debug(msg: "All workers are alive or revived", role: :supervisor)
|
|
75
|
+
Ductwork.logger.debug(msg: "All workers are alive or revived", role: :supervisor)
|
|
76
76
|
end
|
|
77
77
|
|
|
78
78
|
def terminate_gracefully
|
|
79
79
|
workers.each do |worker|
|
|
80
|
-
logger.debug(
|
|
80
|
+
Ductwork.logger.debug(
|
|
81
81
|
msg: "Sending TERM signal to process (#{worker[:pid]})",
|
|
82
82
|
role: :supervisor,
|
|
83
83
|
pid: worker[:pid],
|
|
@@ -95,7 +95,7 @@ module Ductwork
|
|
|
95
95
|
workers.each_with_index do |worker, index|
|
|
96
96
|
if ::Process.wait(worker[:pid], ::Process::WNOHANG)
|
|
97
97
|
workers[index] = nil
|
|
98
|
-
logger.debug(
|
|
98
|
+
Ductwork.logger.debug(
|
|
99
99
|
msg: "Child process (#{worker[:pid]}) stopped successfully",
|
|
100
100
|
role: :supervisor,
|
|
101
101
|
pid: worker[:pid]
|
|
@@ -108,7 +108,7 @@ module Ductwork
|
|
|
108
108
|
|
|
109
109
|
def terminate_immediately
|
|
110
110
|
workers.each_with_index do |worker, index|
|
|
111
|
-
logger.debug(
|
|
111
|
+
Ductwork.logger.debug(
|
|
112
112
|
msg: "Sending KILL signal to process (#{worker[:pid]})",
|
|
113
113
|
role: :supervisor,
|
|
114
114
|
pid: worker[:pid],
|
|
@@ -117,7 +117,7 @@ module Ductwork
|
|
|
117
117
|
::Process.kill(:KILL, worker[:pid])
|
|
118
118
|
::Process.wait(worker[:pid])
|
|
119
119
|
workers[index] = nil
|
|
120
|
-
logger.debug(
|
|
120
|
+
Ductwork.logger.debug(
|
|
121
121
|
msg: "Child process (#{worker[:pid]}) killed after timeout",
|
|
122
122
|
role: :supervisor,
|
|
123
123
|
pid: worker[:pid]
|
|
@@ -151,10 +151,6 @@ module Ductwork
|
|
|
151
151
|
def now
|
|
152
152
|
::Process.clock_gettime(::Process::CLOCK_MONOTONIC)
|
|
153
153
|
end
|
|
154
|
-
|
|
155
|
-
def logger
|
|
156
|
-
Ductwork.configuration.logger
|
|
157
|
-
end
|
|
158
154
|
end
|
|
159
155
|
end
|
|
160
156
|
end
|
|
@@ -6,10 +6,9 @@ module Ductwork
|
|
|
6
6
|
def self.start!
|
|
7
7
|
supervisor = Ductwork::Processes::Supervisor.new
|
|
8
8
|
pipelines_to_advance = Ductwork.configuration.pipelines
|
|
9
|
-
logger = Ductwork.configuration.logger
|
|
10
9
|
|
|
11
10
|
supervisor.add_worker(metadata: { pipelines: pipelines_to_advance }) do
|
|
12
|
-
logger.debug(
|
|
11
|
+
Ductwork.logger.debug(
|
|
13
12
|
msg: "Starting Pipeline Advancer process",
|
|
14
13
|
role: :supervisor_runner
|
|
15
14
|
)
|
|
@@ -19,7 +18,7 @@ module Ductwork
|
|
|
19
18
|
|
|
20
19
|
pipelines_to_advance.each do |pipeline|
|
|
21
20
|
supervisor.add_worker(metadata: { pipeline: }) do
|
|
22
|
-
logger.debug(
|
|
21
|
+
Ductwork.logger.debug(
|
|
23
22
|
msg: "Starting Job Worker Runner process",
|
|
24
23
|
role: :supervisor_runner,
|
|
25
24
|
pipeline: pipeline
|
data/lib/ductwork/version.rb
CHANGED
data/lib/ductwork.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: ductwork
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.3.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Tyler Ewing
|
|
@@ -86,7 +86,7 @@ dependencies:
|
|
|
86
86
|
description: Ductwork lets you build complex pipelines quickly and easily using intuitive
|
|
87
87
|
Ruby tooling and a natural DSL.
|
|
88
88
|
email:
|
|
89
|
-
-
|
|
89
|
+
- contact@getductwork.io
|
|
90
90
|
executables: []
|
|
91
91
|
extensions: []
|
|
92
92
|
extra_rdoc_files: []
|
|
@@ -140,7 +140,7 @@ licenses:
|
|
|
140
140
|
- LGPL-3.0-or-later
|
|
141
141
|
metadata:
|
|
142
142
|
homepage_uri: https://github.com/ductwork/ductwork
|
|
143
|
-
changelog_uri: https://github.com/ductwork/ductwork/blob/main/
|
|
143
|
+
changelog_uri: https://github.com/ductwork/ductwork/blob/main/CHANGELOG.md
|
|
144
144
|
rubygems_mfa_required: 'true'
|
|
145
145
|
documentation_uri: https://docs.getductwork.io/
|
|
146
146
|
rdoc_options: []
|