job-workflow 0.2.0 → 0.3.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/CHANGELOG.md +20 -0
- data/README.md +1 -1
- data/guides/PRODUCTION_DEPLOYMENT.md +1 -1
- data/guides/README.md +1 -1
- data/lib/job_workflow/output.rb +4 -12
- data/lib/job_workflow/queue_adapters/abstract.rb +8 -0
- data/lib/job_workflow/queue_adapters/null_adapter.rb +5 -0
- data/lib/job_workflow/queue_adapters/solid_queue_adapter.rb +59 -11
- data/lib/job_workflow/runner.rb +8 -2
- data/lib/job_workflow/version.rb +1 -1
- data/lib/job_workflow/workflow_status.rb +1 -1
- data/rbs_collection.lock.yaml +11 -11
- data/sig/generated/job_workflow/output.rbs +2 -5
- data/sig/generated/job_workflow/queue_adapters/abstract.rbs +6 -0
- data/sig/generated/job_workflow/queue_adapters/null_adapter.rbs +3 -0
- data/sig/generated/job_workflow/queue_adapters/solid_queue_adapter.rbs +25 -1
- 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: 2a4a82b69372293b7bf689c33125b60b4cbdbcdf4f2e1f468e205cf9bf0c1d85
|
|
4
|
+
data.tar.gz: e2e6e91167d6127e6cee7e2af0339f79ffee244fe6e768d73e50ebb6c2e7b372
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 84356b092c371e103d601576f9fc6ed5cdee9025340948b0d1fe8222b5b1f49f301342f8254a0d858fc89da58e8f828241d7d8ec706103349e63d6f68a426945
|
|
7
|
+
data.tar.gz: b146df16edb3112fa10379437e03ebd6a7f74c46e3a83526bb195320e141e9493319caa96d601622cf1e2f5d8cf18b071cc56c93aac1ad39492b89cfa8e08392
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,25 @@
|
|
|
1
1
|
## [Unreleased]
|
|
2
2
|
|
|
3
|
+
## [0.3.0] - 2026-03-13
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
|
|
7
|
+
- Add `fetch_job_contexts(job_ids)` to queue adapter interface (`Abstract`, `SolidQueueAdapter`, `NullAdapter`) for fetching sub-job context data without direct `SolidQueue::Job` dependency from domain classes
|
|
8
|
+
- Add `persist_job_context(job)` to queue adapter interface for persisting task outputs back to SolidQueue job records after execution
|
|
9
|
+
- Add `without_query_cache` private helper to `SolidQueueAdapter` to bypass ActiveRecord query cache during polling queries
|
|
10
|
+
- Add `"job_workflow_context"` key to `find_job` return hash for direct access to workflow context data
|
|
11
|
+
- Add `AcceptanceNoDependencyWaitJob` and acceptance tests for `depends_on` without `dependency_wait` (polling-only mode)
|
|
12
|
+
- Add acceptance test for output aggregation verification in async workflows
|
|
13
|
+
|
|
14
|
+
### Changed
|
|
15
|
+
|
|
16
|
+
- **Breaking (internal):** Replace `Output#update_task_outputs_from_db` and `Output#update_task_outputs_from_jobs` with `Output#update_task_outputs_from_contexts` — callers now pass context data hashes instead of `SolidQueue::Job` objects
|
|
17
|
+
- `Runner#update_task_outputs` now routes through `QueueAdapter.current.fetch_job_contexts` instead of directly querying `SolidQueue::Job`
|
|
18
|
+
- `Runner#run` now calls `QueueAdapter.current.persist_job_context(job)` after both sub-job and workflow execution
|
|
19
|
+
- `WorkflowStatus.from_job_data` now reads `job_workflow_context` from top-level data first, falling back to `arguments.first.dig("job_workflow_context")`
|
|
20
|
+
- `reschedule_solid_queue_job` now saves full serialized job hash (`active_job.serialize.deep_stringify_keys`) instead of only `["arguments"]`
|
|
21
|
+
- Wrap `find_job`, `fetch_job_statuses`, `job_status`, `reschedule_job`, and `fetch_job_contexts` with `without_query_cache` to prevent stale reads under SolidQueue executor
|
|
22
|
+
|
|
3
23
|
## [0.2.0] - 2026-03-12
|
|
4
24
|
|
|
5
25
|
### Added
|
data/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# JobWorkflow
|
|
2
2
|
|
|
3
|
-
> ⚠️ **Early Stage (v0.
|
|
3
|
+
> ⚠️ **Early Stage (v0.3.0):** This library is in active development. APIs and features may change in breaking ways without notice. Use in production at your own risk and expect potential breaking changes in future releases.
|
|
4
4
|
|
|
5
5
|
## Overview
|
|
6
6
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Production Deployment
|
|
2
2
|
|
|
3
|
-
> ⚠️ **Early Stage (v0.
|
|
3
|
+
> ⚠️ **Early Stage (v0.3.0):** JobWorkflow is still in early development. While this section outlines potential deployment patterns, please thoroughly test in your specific environment and monitor for any issues before relying on JobWorkflow in critical production systems.
|
|
4
4
|
|
|
5
5
|
This section covers suggested settings and patterns for running JobWorkflow in production-like environments.
|
|
6
6
|
|
data/guides/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# JobWorkflow Guides
|
|
2
2
|
|
|
3
|
-
> ⚠️ **Early Stage (v0.
|
|
3
|
+
> ⚠️ **Early Stage (v0.3.0):** JobWorkflow is in active development. APIs and features may change. The following guides provide patterns and examples for building workflows, but be aware that implementations may need adjustment as the library evolves.
|
|
4
4
|
|
|
5
5
|
Welcome to the JobWorkflow documentation! This directory contains comprehensive guides to help you build robust workflows with JobWorkflow.
|
|
6
6
|
|
data/lib/job_workflow/output.rb
CHANGED
|
@@ -50,18 +50,10 @@ module JobWorkflow
|
|
|
50
50
|
task_outputs[task_output.task_name][task_output.each_index] = task_output
|
|
51
51
|
end
|
|
52
52
|
|
|
53
|
-
#: (Array[String], Workflow) -> void
|
|
54
|
-
def
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
update_task_outputs_from_jobs(jobs.to_a, workflow)
|
|
59
|
-
end
|
|
60
|
-
|
|
61
|
-
#: (Array[SolidQueue::Job], Workflow) -> void
|
|
62
|
-
def update_task_outputs_from_jobs(jobs, workflow)
|
|
63
|
-
jobs.each do |job|
|
|
64
|
-
context = Context.deserialize(job.arguments["job_workflow_context"].merge("workflow" => workflow))
|
|
53
|
+
#: (Array[Hash[String, untyped]], Workflow) -> void
|
|
54
|
+
def update_task_outputs_from_contexts(context_data_list, workflow)
|
|
55
|
+
context_data_list.each do |context_data|
|
|
56
|
+
context = Context.deserialize(context_data.merge("workflow" => workflow))
|
|
65
57
|
task_output = context.each_task_output
|
|
66
58
|
next if task_output.nil?
|
|
67
59
|
|
|
@@ -77,10 +77,18 @@ module JobWorkflow
|
|
|
77
77
|
raise NotImplementedError, "#{self.class}#find_job must be implemented"
|
|
78
78
|
end
|
|
79
79
|
|
|
80
|
+
#: (Array[String]) -> Array[Hash[String, untyped]]
|
|
81
|
+
def fetch_job_contexts(_job_ids)
|
|
82
|
+
raise NotImplementedError, "#{self.class}#fetch_job_contexts must be implemented"
|
|
83
|
+
end
|
|
84
|
+
|
|
80
85
|
#: (DSL, Numeric) -> bool
|
|
81
86
|
def reschedule_job(_job, _wait)
|
|
82
87
|
false
|
|
83
88
|
end
|
|
89
|
+
|
|
90
|
+
#: (DSL) -> void
|
|
91
|
+
def persist_job_context(_job); end
|
|
84
92
|
end
|
|
85
93
|
# rubocop:enable Naming/PredicateMethod
|
|
86
94
|
end
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
module JobWorkflow
|
|
4
4
|
module QueueAdapters
|
|
5
|
-
# rubocop:disable Naming/PredicateMethod
|
|
5
|
+
# rubocop:disable Naming/PredicateMethod, Metrics/ClassLength
|
|
6
6
|
class SolidQueueAdapter < Abstract
|
|
7
7
|
# @note
|
|
8
8
|
# - Registry scope: @semaphore_registry is process-scoped (shared across fibers/threads
|
|
@@ -74,16 +74,20 @@ module JobWorkflow
|
|
|
74
74
|
def fetch_job_statuses(job_ids)
|
|
75
75
|
return {} unless defined?(SolidQueue::Job)
|
|
76
76
|
|
|
77
|
-
|
|
77
|
+
without_query_cache do
|
|
78
|
+
SolidQueue::Job.where(active_job_id: job_ids).index_by(&:active_job_id)
|
|
79
|
+
end
|
|
78
80
|
end
|
|
79
81
|
|
|
80
82
|
#: (untyped) -> Symbol
|
|
81
83
|
def job_status(job)
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
84
|
+
without_query_cache do
|
|
85
|
+
return :failed if job.failed?
|
|
86
|
+
return :succeeded if job.finished?
|
|
87
|
+
return :running if job.claimed?
|
|
85
88
|
|
|
86
|
-
|
|
89
|
+
:pending
|
|
90
|
+
end
|
|
87
91
|
end
|
|
88
92
|
|
|
89
93
|
#: () -> bool
|
|
@@ -153,23 +157,40 @@ module JobWorkflow
|
|
|
153
157
|
def find_job(job_id)
|
|
154
158
|
return unless defined?(SolidQueue::Job)
|
|
155
159
|
|
|
156
|
-
job = SolidQueue::Job.find_by(active_job_id: job_id)
|
|
160
|
+
job = without_query_cache { SolidQueue::Job.find_by(active_job_id: job_id) }
|
|
157
161
|
return if job.nil?
|
|
158
162
|
|
|
163
|
+
args = job.arguments
|
|
159
164
|
{
|
|
160
165
|
"job_id" => job.active_job_id,
|
|
161
166
|
"class_name" => job.class_name,
|
|
162
167
|
"queue_name" => job.queue_name,
|
|
163
|
-
"arguments" =>
|
|
168
|
+
"arguments" => args.is_a?(Hash) ? args["arguments"] : args,
|
|
169
|
+
"job_workflow_context" => args.is_a?(Hash) ? args["job_workflow_context"] : nil,
|
|
164
170
|
"status" => job_status(job)
|
|
165
171
|
}
|
|
166
172
|
end
|
|
167
173
|
|
|
174
|
+
# @note
|
|
175
|
+
# - Fetches job_workflow_context hashes for the given job IDs.
|
|
176
|
+
#
|
|
177
|
+
#: (Array[String]) -> Array[Hash[String, untyped]]
|
|
178
|
+
def fetch_job_contexts(job_ids)
|
|
179
|
+
return [] unless defined?(SolidQueue::Job)
|
|
180
|
+
return [] if job_ids.empty?
|
|
181
|
+
|
|
182
|
+
jobs = without_query_cache { SolidQueue::Job.where(active_job_id: job_ids).to_a }
|
|
183
|
+
jobs.filter_map do |job|
|
|
184
|
+
args = job.arguments
|
|
185
|
+
args.is_a?(Hash) ? args["job_workflow_context"] : nil
|
|
186
|
+
end
|
|
187
|
+
end
|
|
188
|
+
|
|
168
189
|
#: (DSL, Numeric) -> bool
|
|
169
190
|
def reschedule_job(job, wait)
|
|
170
191
|
return false unless defined?(SolidQueue::Job)
|
|
171
192
|
|
|
172
|
-
solid_queue_job = SolidQueue::Job.find_by(active_job_id: job.job_id)
|
|
193
|
+
solid_queue_job = without_query_cache { SolidQueue::Job.find_by(active_job_id: job.job_id) }
|
|
173
194
|
return false unless solid_queue_job&.claimed?
|
|
174
195
|
|
|
175
196
|
reschedule_solid_queue_job(solid_queue_job, job, wait)
|
|
@@ -177,17 +198,44 @@ module JobWorkflow
|
|
|
177
198
|
false
|
|
178
199
|
end
|
|
179
200
|
|
|
201
|
+
# @note
|
|
202
|
+
# - Persists the job's updated context (including task outputs) back
|
|
203
|
+
# to the SolidQueue job record after execution completes. Without this,
|
|
204
|
+
# outputs computed during job execution would be lost because
|
|
205
|
+
# SolidQueue does not re-serialize job arguments after perform.
|
|
206
|
+
#
|
|
207
|
+
#: (DSL) -> void
|
|
208
|
+
def persist_job_context(job)
|
|
209
|
+
return unless defined?(SolidQueue::Job)
|
|
210
|
+
|
|
211
|
+
solid_queue_job = SolidQueue::Job.find_by(active_job_id: job.job_id)
|
|
212
|
+
return if solid_queue_job.nil?
|
|
213
|
+
|
|
214
|
+
solid_queue_job.update!(arguments: job.serialize.deep_stringify_keys)
|
|
215
|
+
end
|
|
216
|
+
|
|
180
217
|
private
|
|
181
218
|
|
|
182
219
|
attr_reader :semaphore_registry #: Hash[Object, ^(SolidQueue::Worker) -> void]
|
|
183
220
|
|
|
221
|
+
# @note
|
|
222
|
+
# - Bypasses ActiveRecord query cache for the given block.
|
|
223
|
+
# - When running under SolidQueue's executor, SELECT queries are cached
|
|
224
|
+
# for the entire job execution. Polling queries must bypass this cache
|
|
225
|
+
# to observe status changes made by other threads/processes.
|
|
226
|
+
#
|
|
227
|
+
#: [T] () { () -> T } -> T
|
|
228
|
+
def without_query_cache(&)
|
|
229
|
+
defined?(SolidQueue::Job) ? SolidQueue::Job.uncached(&) : yield
|
|
230
|
+
end
|
|
231
|
+
|
|
184
232
|
#: (SolidQueue::Job, DSL, Numeric) -> bool
|
|
185
233
|
def reschedule_solid_queue_job(solid_queue_job, active_job, wait)
|
|
186
234
|
solid_queue_job.with_lock do
|
|
187
235
|
solid_queue_job.claimed_execution&.destroy!
|
|
188
236
|
solid_queue_job.update!(
|
|
189
237
|
scheduled_at: wait.seconds.from_now,
|
|
190
|
-
arguments: active_job.serialize.deep_stringify_keys
|
|
238
|
+
arguments: active_job.serialize.deep_stringify_keys
|
|
191
239
|
)
|
|
192
240
|
solid_queue_job.prepare_for_execution
|
|
193
241
|
end
|
|
@@ -219,6 +267,6 @@ module JobWorkflow
|
|
|
219
267
|
end
|
|
220
268
|
end
|
|
221
269
|
end
|
|
222
|
-
# rubocop:enable Naming/PredicateMethod
|
|
270
|
+
# rubocop:enable Naming/PredicateMethod, Metrics/ClassLength
|
|
223
271
|
end
|
|
224
272
|
end
|
data/lib/job_workflow/runner.rb
CHANGED
|
@@ -13,9 +13,14 @@ module JobWorkflow
|
|
|
13
13
|
#: () -> void
|
|
14
14
|
def run
|
|
15
15
|
task = context._task_context.task
|
|
16
|
-
|
|
16
|
+
if !task.nil? && context.sub_job?
|
|
17
|
+
run_task(task)
|
|
18
|
+
QueueAdapter.current.persist_job_context(job)
|
|
19
|
+
return
|
|
20
|
+
end
|
|
17
21
|
|
|
18
22
|
catch(:rescheduled) { run_workflow }
|
|
23
|
+
QueueAdapter.current.persist_job_context(job)
|
|
19
24
|
end
|
|
20
25
|
|
|
21
26
|
private
|
|
@@ -167,7 +172,8 @@ module JobWorkflow
|
|
|
167
172
|
#: (Task) -> void
|
|
168
173
|
def update_task_outputs(task)
|
|
169
174
|
finished_job_ids = context.job_status.finished_job_ids(task_name: task.task_name)
|
|
170
|
-
|
|
175
|
+
context_data_list = QueueAdapter.current.fetch_job_contexts(finished_job_ids)
|
|
176
|
+
context.output.update_task_outputs_from_contexts(context_data_list, context.workflow)
|
|
171
177
|
end
|
|
172
178
|
end
|
|
173
179
|
end
|
data/lib/job_workflow/version.rb
CHANGED
|
@@ -34,7 +34,7 @@ module JobWorkflow
|
|
|
34
34
|
job_class = job_class_name.constantize
|
|
35
35
|
workflow = job_class._workflow
|
|
36
36
|
|
|
37
|
-
context_data = data["arguments"]
|
|
37
|
+
context_data = data["job_workflow_context"] || data["arguments"]&.first&.dig("job_workflow_context")
|
|
38
38
|
context = if context_data
|
|
39
39
|
Context.deserialize(context_data.merge("workflow" => workflow))
|
|
40
40
|
else
|
data/rbs_collection.lock.yaml
CHANGED
|
@@ -6,7 +6,7 @@ gems:
|
|
|
6
6
|
source:
|
|
7
7
|
type: git
|
|
8
8
|
name: ruby/gem_rbs_collection
|
|
9
|
-
revision:
|
|
9
|
+
revision: 9bf2eebb1c54b5d6f23f2acb65d4c36f195b4783
|
|
10
10
|
remote: https://github.com/ruby/gem_rbs_collection.git
|
|
11
11
|
repo_dir: gems
|
|
12
12
|
- name: activerecord
|
|
@@ -14,7 +14,7 @@ gems:
|
|
|
14
14
|
source:
|
|
15
15
|
type: git
|
|
16
16
|
name: ruby/gem_rbs_collection
|
|
17
|
-
revision:
|
|
17
|
+
revision: 9bf2eebb1c54b5d6f23f2acb65d4c36f195b4783
|
|
18
18
|
remote: https://github.com/ruby/gem_rbs_collection.git
|
|
19
19
|
repo_dir: gems
|
|
20
20
|
- name: activesupport
|
|
@@ -22,7 +22,7 @@ gems:
|
|
|
22
22
|
source:
|
|
23
23
|
type: git
|
|
24
24
|
name: ruby/gem_rbs_collection
|
|
25
|
-
revision:
|
|
25
|
+
revision: 9bf2eebb1c54b5d6f23f2acb65d4c36f195b4783
|
|
26
26
|
remote: https://github.com/ruby/gem_rbs_collection.git
|
|
27
27
|
repo_dir: gems
|
|
28
28
|
- name: base64
|
|
@@ -34,7 +34,7 @@ gems:
|
|
|
34
34
|
source:
|
|
35
35
|
type: git
|
|
36
36
|
name: ruby/gem_rbs_collection
|
|
37
|
-
revision:
|
|
37
|
+
revision: 9bf2eebb1c54b5d6f23f2acb65d4c36f195b4783
|
|
38
38
|
remote: https://github.com/ruby/gem_rbs_collection.git
|
|
39
39
|
repo_dir: gems
|
|
40
40
|
- name: concurrent-ruby
|
|
@@ -42,7 +42,7 @@ gems:
|
|
|
42
42
|
source:
|
|
43
43
|
type: git
|
|
44
44
|
name: ruby/gem_rbs_collection
|
|
45
|
-
revision:
|
|
45
|
+
revision: 9bf2eebb1c54b5d6f23f2acb65d4c36f195b4783
|
|
46
46
|
remote: https://github.com/ruby/gem_rbs_collection.git
|
|
47
47
|
repo_dir: gems
|
|
48
48
|
- name: connection_pool
|
|
@@ -50,7 +50,7 @@ gems:
|
|
|
50
50
|
source:
|
|
51
51
|
type: git
|
|
52
52
|
name: ruby/gem_rbs_collection
|
|
53
|
-
revision:
|
|
53
|
+
revision: 9bf2eebb1c54b5d6f23f2acb65d4c36f195b4783
|
|
54
54
|
remote: https://github.com/ruby/gem_rbs_collection.git
|
|
55
55
|
repo_dir: gems
|
|
56
56
|
- name: date
|
|
@@ -74,7 +74,7 @@ gems:
|
|
|
74
74
|
source:
|
|
75
75
|
type: git
|
|
76
76
|
name: ruby/gem_rbs_collection
|
|
77
|
-
revision:
|
|
77
|
+
revision: 9bf2eebb1c54b5d6f23f2acb65d4c36f195b4783
|
|
78
78
|
remote: https://github.com/ruby/gem_rbs_collection.git
|
|
79
79
|
repo_dir: gems
|
|
80
80
|
- name: i18n
|
|
@@ -82,7 +82,7 @@ gems:
|
|
|
82
82
|
source:
|
|
83
83
|
type: git
|
|
84
84
|
name: ruby/gem_rbs_collection
|
|
85
|
-
revision:
|
|
85
|
+
revision: 9bf2eebb1c54b5d6f23f2acb65d4c36f195b4783
|
|
86
86
|
remote: https://github.com/ruby/gem_rbs_collection.git
|
|
87
87
|
repo_dir: gems
|
|
88
88
|
- name: json
|
|
@@ -98,7 +98,7 @@ gems:
|
|
|
98
98
|
source:
|
|
99
99
|
type: git
|
|
100
100
|
name: ruby/gem_rbs_collection
|
|
101
|
-
revision:
|
|
101
|
+
revision: 9bf2eebb1c54b5d6f23f2acb65d4c36f195b4783
|
|
102
102
|
remote: https://github.com/ruby/gem_rbs_collection.git
|
|
103
103
|
repo_dir: gems
|
|
104
104
|
- name: monitor
|
|
@@ -138,7 +138,7 @@ gems:
|
|
|
138
138
|
source:
|
|
139
139
|
type: git
|
|
140
140
|
name: ruby/gem_rbs_collection
|
|
141
|
-
revision:
|
|
141
|
+
revision: 9bf2eebb1c54b5d6f23f2acb65d4c36f195b4783
|
|
142
142
|
remote: https://github.com/ruby/gem_rbs_collection.git
|
|
143
143
|
repo_dir: gems
|
|
144
144
|
- name: stringio
|
|
@@ -162,7 +162,7 @@ gems:
|
|
|
162
162
|
source:
|
|
163
163
|
type: git
|
|
164
164
|
name: ruby/gem_rbs_collection
|
|
165
|
-
revision:
|
|
165
|
+
revision: 9bf2eebb1c54b5d6f23f2acb65d4c36f195b4783
|
|
166
166
|
remote: https://github.com/ruby/gem_rbs_collection.git
|
|
167
167
|
repo_dir: gems
|
|
168
168
|
- name: uri
|
|
@@ -23,11 +23,8 @@ module JobWorkflow
|
|
|
23
23
|
# : (TaskOutput) -> void
|
|
24
24
|
def add_task_output: (TaskOutput) -> void
|
|
25
25
|
|
|
26
|
-
# : (Array[String], Workflow) -> void
|
|
27
|
-
def
|
|
28
|
-
|
|
29
|
-
# : (Array[SolidQueue::Job], Workflow) -> void
|
|
30
|
-
def update_task_outputs_from_jobs: (Array[SolidQueue::Job], Workflow) -> void
|
|
26
|
+
# : (Array[Hash[String, untyped]], Workflow) -> void
|
|
27
|
+
def update_task_outputs_from_contexts: (Array[Hash[String, untyped]], Workflow) -> void
|
|
31
28
|
|
|
32
29
|
# : () -> Array[TaskOutput]
|
|
33
30
|
def flat_task_outputs: () -> Array[TaskOutput]
|
|
@@ -49,8 +49,14 @@ module JobWorkflow
|
|
|
49
49
|
# : (String) -> Hash[String, untyped]?
|
|
50
50
|
def find_job: (String) -> Hash[String, untyped]?
|
|
51
51
|
|
|
52
|
+
# : (Array[String]) -> Array[Hash[String, untyped]]
|
|
53
|
+
def fetch_job_contexts: (Array[String]) -> Array[Hash[String, untyped]]
|
|
54
|
+
|
|
52
55
|
# : (DSL, Numeric) -> bool
|
|
53
56
|
def reschedule_job: (DSL, Numeric) -> bool
|
|
57
|
+
|
|
58
|
+
# : (DSL) -> void
|
|
59
|
+
def persist_job_context: (DSL) -> void
|
|
54
60
|
end
|
|
55
61
|
end
|
|
56
62
|
end
|
|
@@ -51,6 +51,9 @@ module JobWorkflow
|
|
|
51
51
|
# : (String) -> Hash[String, untyped]?
|
|
52
52
|
def find_job: (String) -> Hash[String, untyped]?
|
|
53
53
|
|
|
54
|
+
# : (Array[String]) -> Array[Hash[String, untyped]]
|
|
55
|
+
def fetch_job_contexts: (Array[String]) -> Array[Hash[String, untyped]]
|
|
56
|
+
|
|
54
57
|
# : (DSL, Numeric) -> bool
|
|
55
58
|
def reschedule_job: (DSL, Numeric) -> bool
|
|
56
59
|
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
module JobWorkflow
|
|
4
4
|
module QueueAdapters
|
|
5
|
-
# rubocop:disable Naming/PredicateMethod
|
|
5
|
+
# rubocop:disable Naming/PredicateMethod, Metrics/ClassLength
|
|
6
6
|
class SolidQueueAdapter < Abstract
|
|
7
7
|
# @note
|
|
8
8
|
# - Registry scope: @semaphore_registry is process-scoped (shared across fibers/threads
|
|
@@ -82,13 +82,37 @@ module JobWorkflow
|
|
|
82
82
|
# : (String) -> Hash[String, untyped]?
|
|
83
83
|
def find_job: (String) -> Hash[String, untyped]?
|
|
84
84
|
|
|
85
|
+
# @note
|
|
86
|
+
# - Fetches job_workflow_context hashes for the given job IDs.
|
|
87
|
+
#
|
|
88
|
+
# : (Array[String]) -> Array[Hash[String, untyped]]
|
|
89
|
+
def fetch_job_contexts: (Array[String]) -> Array[Hash[String, untyped]]
|
|
90
|
+
|
|
85
91
|
# : (DSL, Numeric) -> bool
|
|
86
92
|
def reschedule_job: (DSL, Numeric) -> bool
|
|
87
93
|
|
|
94
|
+
# @note
|
|
95
|
+
# - Persists the job's updated context (including task outputs) back
|
|
96
|
+
# to the SolidQueue job record after execution completes. Without this,
|
|
97
|
+
# outputs computed during job execution would be lost because
|
|
98
|
+
# SolidQueue does not re-serialize job arguments after perform.
|
|
99
|
+
#
|
|
100
|
+
# : (DSL) -> void
|
|
101
|
+
def persist_job_context: (DSL) -> void
|
|
102
|
+
|
|
88
103
|
private
|
|
89
104
|
|
|
90
105
|
attr_reader semaphore_registry: Hash[Object, ^(SolidQueue::Worker) -> void]
|
|
91
106
|
|
|
107
|
+
# @note
|
|
108
|
+
# - Bypasses ActiveRecord query cache for the given block.
|
|
109
|
+
# - When running under SolidQueue's executor, SELECT queries are cached
|
|
110
|
+
# for the entire job execution. Polling queries must bypass this cache
|
|
111
|
+
# to observe status changes made by other threads/processes.
|
|
112
|
+
#
|
|
113
|
+
# : [T] () { () -> T } -> T
|
|
114
|
+
def without_query_cache: [T] () { () -> T } -> T
|
|
115
|
+
|
|
92
116
|
# : (SolidQueue::Job, DSL, Numeric) -> bool
|
|
93
117
|
def reschedule_solid_queue_job: (SolidQueue::Job, DSL, Numeric) -> bool
|
|
94
118
|
|