job-workflow 0.5.0 → 0.6.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/.agents/instructions/coding-style.md +38 -0
- data/.agents/instructions/domain.md +37 -0
- data/.agents/instructions/environment.md +44 -0
- data/.agents/instructions/general.md +29 -0
- data/.agents/instructions/security.md +20 -0
- data/.agents/instructions/structure.md +43 -0
- data/.agents/instructions/tech-stack.md +40 -0
- data/.agents/instructions/testing.md +46 -0
- data/.agents/instructions/workflow.md +39 -0
- data/.rubocop.yml +1 -2
- data/AGENTS.md +23 -0
- data/CHANGELOG.md +26 -0
- data/README.md +1 -1
- data/app/controllers/job_workflow/monitoring/application_controller.rb +11 -0
- data/app/controllers/job_workflow/monitoring/executions_controller.rb +28 -0
- data/app/controllers/job_workflow/monitoring/workflows_controller.rb +11 -0
- data/app/views/job_workflow/monitoring/executions/index.html.erb +57 -0
- data/app/views/job_workflow/monitoring/executions/show.html.erb +200 -0
- data/app/views/job_workflow/monitoring/workflows/index.html.erb +39 -0
- data/app/views/layouts/job_workflow/monitoring/application.html.erb +117 -0
- data/config/routes.rb +8 -0
- data/guides/API_REFERENCE.md +9 -6
- data/guides/DEPENDENCY_WAIT.md +9 -5
- data/guides/MONITORING_UI.md +74 -0
- data/guides/PARALLEL_PROCESSING.md +33 -21
- data/guides/PRODUCTION_DEPLOYMENT.md +1 -1
- data/guides/README.md +6 -1
- data/guides/THROTTLING.md +24 -0
- data/guides/WORKFLOW_STATUS_QUERY.md +7 -1
- data/lib/job_workflow/context.rb +7 -5
- data/lib/job_workflow/dsl.rb +0 -4
- data/lib/job_workflow/instrumentation/opentelemetry_subscriber.rb +1 -1
- data/lib/job_workflow/instrumentation.rb +14 -14
- data/lib/job_workflow/job_status.rb +16 -1
- data/lib/job_workflow/monitoring/dag_layout.rb +186 -0
- data/lib/job_workflow/monitoring/engine.rb +15 -0
- data/lib/job_workflow/monitoring/execution_page.rb +16 -0
- data/lib/job_workflow/monitoring/execution_registry.rb +50 -0
- data/lib/job_workflow/monitoring/execution_view_model.rb +258 -0
- data/lib/job_workflow/monitoring/parameter_filter.rb +37 -0
- data/lib/job_workflow/monitoring/workflow_definition.rb +24 -0
- data/lib/job_workflow/monitoring/workflow_registry.rb +24 -0
- data/lib/job_workflow/monitoring.rb +120 -0
- data/lib/job_workflow/queue_adapters/abstract.rb +7 -2
- data/lib/job_workflow/queue_adapters/null_adapter.rb +12 -1
- data/lib/job_workflow/queue_adapters/solid_queue_adapter.rb +42 -12
- data/lib/job_workflow/railtie.rb +2 -0
- data/lib/job_workflow/runner.rb +5 -3
- data/lib/job_workflow/sub_task_job.rb +93 -0
- data/lib/job_workflow/task_enqueue.rb +19 -12
- data/lib/job_workflow/version.rb +1 -1
- data/lib/job_workflow/workflow_status.rb +39 -3
- data/lib/job_workflow.rb +2 -0
- data/rbs_collection.lock.yaml +11 -11
- data/sig/generated/job_workflow/context.rbs +7 -7
- data/sig/generated/job_workflow/instrumentation/opentelemetry_subscriber.rbs +0 -1
- data/sig/generated/job_workflow/instrumentation.rbs +28 -28
- data/sig/generated/job_workflow/job_status.rbs +5 -2
- data/sig/generated/job_workflow/monitoring/dag_layout.rbs +80 -0
- data/sig/generated/job_workflow/monitoring/engine.rbs +8 -0
- data/sig/generated/job_workflow/monitoring/execution_page.rbs +14 -0
- data/sig/generated/job_workflow/monitoring/execution_registry.rbs +21 -0
- data/sig/generated/job_workflow/monitoring/execution_view_model.rbs +111 -0
- data/sig/generated/job_workflow/monitoring/parameter_filter.rbs +16 -0
- data/sig/generated/job_workflow/monitoring/workflow_definition.rbs +18 -0
- data/sig/generated/job_workflow/monitoring/workflow_registry.rbs +13 -0
- data/sig/generated/job_workflow/monitoring.rbs +38 -0
- data/sig/generated/job_workflow/queue_adapters/abstract.rbs +7 -4
- data/sig/generated/job_workflow/queue_adapters/null_adapter.rbs +5 -2
- data/sig/generated/job_workflow/queue_adapters/solid_queue_adapter.rbs +18 -6
- data/sig/generated/job_workflow/runner.rbs +1 -1
- data/sig/generated/job_workflow/sub_task_job.rbs +40 -0
- data/sig/generated/job_workflow/task_enqueue.rbs +5 -8
- data/sig/generated/job_workflow/workflow_status.rbs +18 -2
- data/sig-private/job-workflow.rbs +11 -0
- data/sig-private/rails.rbs +5 -0
- metadata +42 -1
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module JobWorkflow
|
|
4
|
+
class SubTaskJob < ActiveJob::Base
|
|
5
|
+
include ActiveJob::Continuable
|
|
6
|
+
|
|
7
|
+
class << self
|
|
8
|
+
#: (context: Context) -> SubTaskJob
|
|
9
|
+
def from_parent_context(context:)
|
|
10
|
+
validate_sub_task_context!(context)
|
|
11
|
+
|
|
12
|
+
new_context = context.dup
|
|
13
|
+
job = new(context.arguments.to_h)
|
|
14
|
+
new_context._job = job
|
|
15
|
+
job._context = new_context
|
|
16
|
+
task = new_context._task_context.task || (raise "task is not set")
|
|
17
|
+
return job if task.enqueue.queue.nil?
|
|
18
|
+
|
|
19
|
+
job.set(queue: task.enqueue.queue)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
private
|
|
23
|
+
|
|
24
|
+
#: (Context) -> void
|
|
25
|
+
def validate_sub_task_context!(context)
|
|
26
|
+
task_context = context._task_context
|
|
27
|
+
raise ArgumentError, "task_context.task is required" if task_context.task.nil?
|
|
28
|
+
raise ArgumentError, "task_context.parent_job_id is required" if task_context.parent_job_id.nil?
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
#: (Hash[untyped, untyped]) -> void
|
|
33
|
+
def perform(arguments)
|
|
34
|
+
payload = arguments.symbolize_keys
|
|
35
|
+
self._context = build_context(payload)
|
|
36
|
+
Runner.new(context: _context).run
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
#: () -> Output
|
|
40
|
+
def output
|
|
41
|
+
context = _context
|
|
42
|
+
raise "context is not set." if context.nil?
|
|
43
|
+
|
|
44
|
+
context.output
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
attr_accessor :_context #: Context?
|
|
48
|
+
|
|
49
|
+
#: () -> Hash[String, untyped]
|
|
50
|
+
def serialize
|
|
51
|
+
super.merge({ "job_workflow_context" => _context&.serialize || serialized_job_workflow_context }.compact)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
#: (Hash[String, untyped]) -> void
|
|
55
|
+
def deserialize(job_data)
|
|
56
|
+
super
|
|
57
|
+
self.serialized_job_workflow_context = job_data["job_workflow_context"]
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
private
|
|
61
|
+
|
|
62
|
+
attr_accessor :serialized_job_workflow_context #: Hash[String, untyped]?
|
|
63
|
+
|
|
64
|
+
#: (Hash[Symbol, untyped]) -> Context
|
|
65
|
+
def build_context(payload)
|
|
66
|
+
context_data = extract_context_data(payload)
|
|
67
|
+
parent_job_id = context_data.fetch("task_context").fetch("parent_job_id")
|
|
68
|
+
parent_job_data = QueueAdapter.current.find_job(parent_job_id)
|
|
69
|
+
raise WorkflowStatus::NotFoundError, "Workflow with job_id '#{parent_job_id}' not found" if parent_job_data.nil?
|
|
70
|
+
|
|
71
|
+
workflow = resolve_workflow(parent_job_data.fetch("class_name"))
|
|
72
|
+
Context.deserialize(context_data.merge("job" => self, "workflow" => workflow))
|
|
73
|
+
._update_arguments(payload.except(:job_workflow_context))
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
#: (job_class_name: String) -> Workflow
|
|
77
|
+
def resolve_workflow(job_class_name)
|
|
78
|
+
job_class = JobWorkflow::DSL._included_classes.to_a.reverse.find { |klass| klass.name == job_class_name }
|
|
79
|
+
job_class ||= job_class_name.safe_constantize
|
|
80
|
+
raise NameError, "uninitialized constant #{job_class_name}" if job_class.nil?
|
|
81
|
+
|
|
82
|
+
job_class._workflow
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
#: (Hash[Symbol, untyped]) -> Hash[String, untyped]
|
|
86
|
+
def extract_context_data(payload)
|
|
87
|
+
context_data = serialized_job_workflow_context || payload[:job_workflow_context]
|
|
88
|
+
raise "job_workflow_context is not set." if context_data.nil?
|
|
89
|
+
|
|
90
|
+
context_data.deep_stringify_keys
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
end
|
|
@@ -4,7 +4,6 @@ module JobWorkflow
|
|
|
4
4
|
class TaskEnqueue
|
|
5
5
|
attr_reader :condition #: true | false | ^(Context) -> bool
|
|
6
6
|
attr_reader :queue #: String?
|
|
7
|
-
attr_reader :concurrency #: Integer?
|
|
8
7
|
|
|
9
8
|
class << self
|
|
10
9
|
#: (true | false | ^(Context) -> bool | Hash[Symbol, untyped] | nil) -> TaskEnqueue
|
|
@@ -13,26 +12,39 @@ module JobWorkflow
|
|
|
13
12
|
when TrueClass, FalseClass, Proc
|
|
14
13
|
new(condition: value)
|
|
15
14
|
when Hash
|
|
15
|
+
validate_hash_keys!(value)
|
|
16
|
+
|
|
16
17
|
new(
|
|
17
18
|
condition: value.fetch(:condition, !value.empty?),
|
|
18
|
-
queue: value[:queue]
|
|
19
|
-
concurrency: value[:concurrency]
|
|
19
|
+
queue: value[:queue]
|
|
20
20
|
)
|
|
21
21
|
else
|
|
22
22
|
new
|
|
23
23
|
end
|
|
24
24
|
end
|
|
25
|
+
|
|
26
|
+
private
|
|
27
|
+
|
|
28
|
+
#: (Hash[Symbol, untyped]) -> void
|
|
29
|
+
def validate_hash_keys!(value)
|
|
30
|
+
unsupported_keys = value.keys - %i[condition queue]
|
|
31
|
+
return if unsupported_keys.empty?
|
|
32
|
+
|
|
33
|
+
if unsupported_keys == %i[concurrency]
|
|
34
|
+
raise ArgumentError, "enqueue does not support :concurrency; use throttle instead"
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
raise ArgumentError, "enqueue supports only :condition and :queue"
|
|
38
|
+
end
|
|
25
39
|
end
|
|
26
40
|
|
|
27
41
|
#: (
|
|
28
42
|
# ?condition: true | false | ^(Context) -> bool,
|
|
29
|
-
# ?queue: String
|
|
30
|
-
# ?concurrency: Integer?
|
|
43
|
+
# ?queue: String?
|
|
31
44
|
# ) -> void
|
|
32
|
-
def initialize(condition: false, queue: nil
|
|
45
|
+
def initialize(condition: false, queue: nil)
|
|
33
46
|
@condition = condition
|
|
34
47
|
@queue = queue
|
|
35
|
-
@concurrency = concurrency
|
|
36
48
|
end
|
|
37
49
|
|
|
38
50
|
#: (Context) -> bool
|
|
@@ -41,10 +53,5 @@ module JobWorkflow
|
|
|
41
53
|
|
|
42
54
|
!!condition
|
|
43
55
|
end
|
|
44
|
-
|
|
45
|
-
#: () -> bool
|
|
46
|
-
def should_limits_concurrency?
|
|
47
|
-
!!condition && !concurrency.nil? && QueueAdapter.current.supports_concurrency_limits?
|
|
48
|
-
end
|
|
49
56
|
end
|
|
50
57
|
end
|
data/lib/job_workflow/version.rb
CHANGED
|
@@ -10,6 +10,7 @@ module JobWorkflow
|
|
|
10
10
|
attr_reader :context #: Context
|
|
11
11
|
attr_reader :job_class_name #: String
|
|
12
12
|
attr_reader :status #: status_type
|
|
13
|
+
attr_reader :completed_task_names #: Array[Symbol]
|
|
13
14
|
|
|
14
15
|
class << self
|
|
15
16
|
#: (String) -> WorkflowStatus
|
|
@@ -24,6 +25,7 @@ module JobWorkflow
|
|
|
24
25
|
def find_by(job_id:)
|
|
25
26
|
data = QueueAdapter.current.find_job(job_id)
|
|
26
27
|
return if data.nil?
|
|
28
|
+
return if data["class_name"] == JobWorkflow::SubTaskJob.name
|
|
27
29
|
|
|
28
30
|
WorkflowStatus.from_job_data(data)
|
|
29
31
|
end
|
|
@@ -33,23 +35,57 @@ module JobWorkflow
|
|
|
33
35
|
job_class_name = data["class_name"]
|
|
34
36
|
job_class = job_class_name.constantize
|
|
35
37
|
workflow = job_class._workflow
|
|
38
|
+
context = context_from_job_data(data, workflow)
|
|
39
|
+
|
|
40
|
+
new(
|
|
41
|
+
context:,
|
|
42
|
+
job_class_name:,
|
|
43
|
+
status: data["status"],
|
|
44
|
+
completed_task_names: completed_task_names_from_job_data(data)
|
|
45
|
+
)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
private
|
|
49
|
+
|
|
50
|
+
#: (Hash[String, untyped]) -> Array[Symbol]
|
|
51
|
+
def completed_task_names_from_job_data(data)
|
|
52
|
+
Array(data.dig("continuation", "completed")).map(&:to_sym)
|
|
53
|
+
end
|
|
36
54
|
|
|
55
|
+
#: (Hash[String, untyped], Workflow) -> Context
|
|
56
|
+
def context_from_job_data(data, workflow)
|
|
37
57
|
context_data = data["job_workflow_context"] || data["arguments"]&.first&.dig("job_workflow_context")
|
|
38
58
|
context = if context_data
|
|
39
59
|
Context.deserialize(context_data.merge("workflow" => workflow))
|
|
40
60
|
else
|
|
41
61
|
Context.from_hash({ workflow: })
|
|
42
62
|
end
|
|
63
|
+
serialized_arguments = workflow_arguments_data(data)
|
|
64
|
+
return context if serialized_arguments.nil?
|
|
65
|
+
|
|
66
|
+
context._update_arguments(ActiveJob::Arguments.deserialize([serialized_arguments]).first)
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
#: (Hash[String, untyped]) -> Hash[String, untyped]?
|
|
70
|
+
def workflow_arguments_data(data)
|
|
71
|
+
serialized_arguments = data["arguments"]&.first
|
|
72
|
+
return if serialized_arguments.nil? || serialized_arguments.key?("job_workflow_context")
|
|
43
73
|
|
|
44
|
-
|
|
74
|
+
serialized_arguments
|
|
45
75
|
end
|
|
46
76
|
end
|
|
47
77
|
|
|
48
|
-
#: (
|
|
49
|
-
|
|
78
|
+
#: (
|
|
79
|
+
# context: Context,
|
|
80
|
+
# job_class_name: String,
|
|
81
|
+
# status: status_type,
|
|
82
|
+
# ?completed_task_names: Array[Symbol]
|
|
83
|
+
# ) -> void
|
|
84
|
+
def initialize(context:, job_class_name:, status:, completed_task_names: [])
|
|
50
85
|
@context = context #: Context
|
|
51
86
|
@job_class_name = job_class_name #: String
|
|
52
87
|
@status = status #: Symbol
|
|
88
|
+
@completed_task_names = completed_task_names
|
|
53
89
|
end
|
|
54
90
|
|
|
55
91
|
#: () -> Symbol?
|
data/lib/job_workflow.rb
CHANGED
|
@@ -31,6 +31,7 @@ require_relative "job_workflow/task"
|
|
|
31
31
|
require_relative "job_workflow/task_graph"
|
|
32
32
|
require_relative "job_workflow/schedule"
|
|
33
33
|
require_relative "job_workflow/dsl"
|
|
34
|
+
require_relative "job_workflow/sub_task_job"
|
|
34
35
|
require_relative "job_workflow/runner"
|
|
35
36
|
require_relative "job_workflow/workflow_status"
|
|
36
37
|
require_relative "job_workflow/workflow"
|
|
@@ -44,6 +45,7 @@ require_relative "job_workflow/output_def"
|
|
|
44
45
|
require_relative "job_workflow/task_output"
|
|
45
46
|
require_relative "job_workflow/output"
|
|
46
47
|
require_relative "job_workflow/queue"
|
|
48
|
+
require_relative "job_workflow/monitoring"
|
|
47
49
|
require_relative "job_workflow/auto_scaling"
|
|
48
50
|
# :nocov:
|
|
49
51
|
require_relative "job_workflow/railtie" if defined?(Rails::Railtie)
|
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: 3f5e8df1ce89ea06067fa42263012c968b4e583e
|
|
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: 3f5e8df1ce89ea06067fa42263012c968b4e583e
|
|
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: 3f5e8df1ce89ea06067fa42263012c968b4e583e
|
|
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: 3f5e8df1ce89ea06067fa42263012c968b4e583e
|
|
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: 3f5e8df1ce89ea06067fa42263012c968b4e583e
|
|
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: 3f5e8df1ce89ea06067fa42263012c968b4e583e
|
|
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: 3f5e8df1ce89ea06067fa42263012c968b4e583e
|
|
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: 3f5e8df1ce89ea06067fa42263012c968b4e583e
|
|
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: 3f5e8df1ce89ea06067fa42263012c968b4e583e
|
|
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: 3f5e8df1ce89ea06067fa42263012c968b4e583e
|
|
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: 3f5e8df1ce89ea06067fa42263012c968b4e583e
|
|
166
166
|
remote: https://github.com/ruby/gem_rbs_collection.git
|
|
167
167
|
repo_dir: gems
|
|
168
168
|
- name: uri
|
|
@@ -25,9 +25,9 @@ module JobWorkflow
|
|
|
25
25
|
# task_context: TaskContext,
|
|
26
26
|
# output: Output,
|
|
27
27
|
# job_status: JobStatus,
|
|
28
|
-
# ?job:
|
|
28
|
+
# ?job: _JobInterface?
|
|
29
29
|
# ) -> void
|
|
30
|
-
def initialize: (workflow: Workflow, arguments: Arguments, task_context: TaskContext, output: Output, job_status: JobStatus, ?job:
|
|
30
|
+
def initialize: (workflow: Workflow, arguments: Arguments, task_context: TaskContext, output: Output, job_status: JobStatus, ?job: _JobInterface?) -> void
|
|
31
31
|
|
|
32
32
|
# : () -> Hash[String, untyped]
|
|
33
33
|
def serialize: () -> Hash[String, untyped]
|
|
@@ -44,11 +44,11 @@ module JobWorkflow
|
|
|
44
44
|
# : () -> void
|
|
45
45
|
def checkpoint!: () -> void
|
|
46
46
|
|
|
47
|
-
# : (
|
|
48
|
-
def _job=: (
|
|
47
|
+
# : (_JobInterface) -> void
|
|
48
|
+
def _job=: (_JobInterface) -> void
|
|
49
49
|
|
|
50
|
-
# : () ->
|
|
51
|
-
def _job: () ->
|
|
50
|
+
# : () -> _JobInterface?
|
|
51
|
+
def _job: () -> _JobInterface?
|
|
52
52
|
|
|
53
53
|
# : () -> String
|
|
54
54
|
def job_id: () -> String
|
|
@@ -115,7 +115,7 @@ module JobWorkflow
|
|
|
115
115
|
|
|
116
116
|
private
|
|
117
117
|
|
|
118
|
-
attr_accessor job:
|
|
118
|
+
attr_accessor job: _JobInterface?
|
|
119
119
|
|
|
120
120
|
attr_writer workflow: Workflow
|
|
121
121
|
|
|
@@ -19,7 +19,6 @@ module JobWorkflow
|
|
|
19
19
|
# @note This subscriber requires the opentelemetry-api gem to be installed.
|
|
20
20
|
# If not available, subscription will be silently skipped.
|
|
21
21
|
class OpenTelemetrySubscriber
|
|
22
|
-
# rubocop:disable Metrics/ClassLength
|
|
23
22
|
module Attributes
|
|
24
23
|
JOB_NAME: String
|
|
25
24
|
|
|
@@ -63,26 +63,26 @@ module JobWorkflow
|
|
|
63
63
|
DRY_RUN_EXECUTE: untyped
|
|
64
64
|
end
|
|
65
65
|
|
|
66
|
-
# : (
|
|
67
|
-
def self.instrument_workflow: (
|
|
66
|
+
# : (_JobInterface) { () -> untyped } -> untyped
|
|
67
|
+
def self.instrument_workflow: (_JobInterface) { () -> untyped } -> untyped
|
|
68
68
|
|
|
69
|
-
# : (
|
|
70
|
-
def self.instrument_task: (
|
|
69
|
+
# : (_JobInterface, Task, Context) { () -> untyped } -> untyped
|
|
70
|
+
def self.instrument_task: (_JobInterface, Task, Context) { () -> untyped } -> untyped
|
|
71
71
|
|
|
72
|
-
# : (
|
|
73
|
-
def self.notify_task_skip: (
|
|
72
|
+
# : (_JobInterface, Task, String) -> void
|
|
73
|
+
def self.notify_task_skip: (_JobInterface, Task, String) -> void
|
|
74
74
|
|
|
75
|
-
# : (
|
|
76
|
-
def self.notify_task_enqueue: (
|
|
75
|
+
# : (_JobInterface, Task, Integer) -> void
|
|
76
|
+
def self.notify_task_enqueue: (_JobInterface, Task, Integer) -> void
|
|
77
77
|
|
|
78
78
|
# : (Task, Context, String, Integer, Float, StandardError) -> void
|
|
79
79
|
def self.notify_task_retry: (Task, Context, String, Integer, Float, StandardError) -> void
|
|
80
80
|
|
|
81
|
-
# : (
|
|
82
|
-
def self.instrument_dependent_wait: (
|
|
81
|
+
# : (_JobInterface, Task) { () -> untyped } -> untyped
|
|
82
|
+
def self.instrument_dependent_wait: (_JobInterface, Task) { () -> untyped } -> untyped
|
|
83
83
|
|
|
84
|
-
# : (
|
|
85
|
-
def self.notify_dependent_reschedule: (
|
|
84
|
+
# : (_JobInterface, Task, Numeric, Integer) -> void
|
|
85
|
+
def self.notify_dependent_reschedule: (_JobInterface, Task, Numeric, Integer) -> void
|
|
86
86
|
|
|
87
87
|
# : (Semaphore) { () -> untyped } -> untyped
|
|
88
88
|
def self.instrument_throttle: (Semaphore) { () -> untyped } -> untyped
|
|
@@ -99,32 +99,32 @@ module JobWorkflow
|
|
|
99
99
|
# : (String, Hash[Symbol, untyped]) { () -> untyped } -> untyped
|
|
100
100
|
def self.instrument_custom: (String, Hash[Symbol, untyped]) { () -> untyped } -> untyped
|
|
101
101
|
|
|
102
|
-
# : (
|
|
103
|
-
def self.instrument_dry_run: (
|
|
102
|
+
# : (_JobInterface, Context, Symbol?, Integer, bool) { () -> untyped } -> untyped
|
|
103
|
+
def self.instrument_dry_run: (_JobInterface, Context, Symbol?, Integer, bool) { () -> untyped } -> untyped
|
|
104
104
|
|
|
105
105
|
# : (String, Hash[Symbol, untyped]) ?{ () -> untyped } -> untyped
|
|
106
106
|
private def self.instrument: (String, Hash[Symbol, untyped]) ?{ () -> untyped } -> untyped
|
|
107
107
|
|
|
108
|
-
# : (
|
|
109
|
-
private def self.build_workflow_payload: (
|
|
108
|
+
# : (_JobInterface) -> Hash[Symbol, untyped]
|
|
109
|
+
private def self.build_workflow_payload: (_JobInterface) -> Hash[Symbol, untyped]
|
|
110
110
|
|
|
111
|
-
# : (
|
|
112
|
-
private def self.build_task_payload: (
|
|
111
|
+
# : (_JobInterface, Task, Context) -> Hash[Symbol, untyped]
|
|
112
|
+
private def self.build_task_payload: (_JobInterface, Task, Context) -> Hash[Symbol, untyped]
|
|
113
113
|
|
|
114
|
-
# : (
|
|
115
|
-
private def self.build_task_skip_payload: (
|
|
114
|
+
# : (_JobInterface, Task, String) -> Hash[Symbol, untyped]
|
|
115
|
+
private def self.build_task_skip_payload: (_JobInterface, Task, String) -> Hash[Symbol, untyped]
|
|
116
116
|
|
|
117
|
-
# : (
|
|
118
|
-
private def self.build_task_enqueue_payload: (
|
|
117
|
+
# : (_JobInterface, Task, Integer) -> Hash[Symbol, untyped]
|
|
118
|
+
private def self.build_task_enqueue_payload: (_JobInterface, Task, Integer) -> Hash[Symbol, untyped]
|
|
119
119
|
|
|
120
120
|
# : (Task, Context, String, Integer, Float, StandardError) -> Hash[Symbol, untyped]
|
|
121
121
|
private def self.build_task_retry_payload: (Task, Context, String, Integer, Float, StandardError) -> Hash[Symbol, untyped]
|
|
122
122
|
|
|
123
|
-
# : (
|
|
124
|
-
private def self.build_dependent_payload: (
|
|
123
|
+
# : (_JobInterface, Task) -> Hash[Symbol, untyped]
|
|
124
|
+
private def self.build_dependent_payload: (_JobInterface, Task) -> Hash[Symbol, untyped]
|
|
125
125
|
|
|
126
|
-
# : (
|
|
127
|
-
private def self.build_dependent_reschedule_payload: (
|
|
126
|
+
# : (_JobInterface, Task, Numeric, Integer) -> Hash[Symbol, untyped]
|
|
127
|
+
private def self.build_dependent_reschedule_payload: (_JobInterface, Task, Numeric, Integer) -> Hash[Symbol, untyped]
|
|
128
128
|
|
|
129
129
|
# : (Semaphore) -> Hash[Symbol, untyped]
|
|
130
130
|
private def self.build_throttle_payload: (Semaphore) -> Hash[Symbol, untyped]
|
|
@@ -132,7 +132,7 @@ module JobWorkflow
|
|
|
132
132
|
# : (String) -> Hash[Symbol, untyped]
|
|
133
133
|
private def self.build_queue_payload: (String) -> Hash[Symbol, untyped]
|
|
134
134
|
|
|
135
|
-
# : (
|
|
136
|
-
private def self.build_skip_in_dry_run_payload: (
|
|
135
|
+
# : (_JobInterface, Context, Symbol?, Integer, bool) -> Hash[Symbol, untyped]
|
|
136
|
+
private def self.build_skip_in_dry_run_payload: (_JobInterface, Context, Symbol?, Integer, bool) -> Hash[Symbol, untyped]
|
|
137
137
|
end
|
|
138
138
|
end
|
|
@@ -33,12 +33,15 @@ module JobWorkflow
|
|
|
33
33
|
# : (TaskJobStatus) -> void
|
|
34
34
|
def update_task_job_status: (TaskJobStatus) -> void
|
|
35
35
|
|
|
36
|
-
# : (task_name: Symbol, jobs: Array[
|
|
37
|
-
def update_task_job_statuses_from_jobs: (task_name: Symbol, jobs: Array[
|
|
36
|
+
# : (task_name: Symbol, jobs: Array[_JobInterface]) -> void
|
|
37
|
+
def update_task_job_statuses_from_jobs: (task_name: Symbol, jobs: Array[_JobInterface]) -> void
|
|
38
38
|
|
|
39
39
|
# : (Symbol) -> void
|
|
40
40
|
def update_task_job_statuses_from_db: (Symbol) -> void
|
|
41
41
|
|
|
42
|
+
# : () -> void
|
|
43
|
+
def refresh_from_db!: () -> void
|
|
44
|
+
|
|
42
45
|
private
|
|
43
46
|
|
|
44
47
|
attr_accessor task_job_statuses: Hash[Symbol, Array[TaskJobStatus]]
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
# Generated from lib/job_workflow/monitoring/dag_layout.rb with RBS::Inline
|
|
2
|
+
|
|
3
|
+
module JobWorkflow
|
|
4
|
+
module Monitoring
|
|
5
|
+
class DagLayout
|
|
6
|
+
NODE_WIDTH: ::Integer
|
|
7
|
+
|
|
8
|
+
NODE_HEIGHT: ::Integer
|
|
9
|
+
|
|
10
|
+
COLUMN_GAP: ::Integer
|
|
11
|
+
|
|
12
|
+
ROW_GAP: ::Integer
|
|
13
|
+
|
|
14
|
+
PADDING: ::Integer
|
|
15
|
+
|
|
16
|
+
LABEL_LIMIT: ::Integer
|
|
17
|
+
|
|
18
|
+
# : (tasks: Array[Hash[Symbol, untyped]]) -> void
|
|
19
|
+
def initialize: (tasks: Array[Hash[Symbol, untyped]]) -> void
|
|
20
|
+
|
|
21
|
+
# : () -> Hash[Symbol, untyped]
|
|
22
|
+
def to_h: () -> Hash[Symbol, untyped]
|
|
23
|
+
|
|
24
|
+
private
|
|
25
|
+
|
|
26
|
+
attr_reader tasks: Array[Hash[Symbol, untyped]]
|
|
27
|
+
|
|
28
|
+
# : (Array[Hash[Symbol, untyped]]) -> void
|
|
29
|
+
def validate_tasks!: (Array[Hash[Symbol, untyped]]) -> void
|
|
30
|
+
|
|
31
|
+
# : (Hash[Symbol, untyped], Hash[Symbol, bool]) -> void
|
|
32
|
+
def validate_dependencies!: (Hash[Symbol, untyped], Hash[Symbol, bool]) -> void
|
|
33
|
+
|
|
34
|
+
# : () -> Array[Hash[Symbol, untyped]]
|
|
35
|
+
def nodes: () -> Array[Hash[Symbol, untyped]]
|
|
36
|
+
|
|
37
|
+
# : () -> Array[Hash[Symbol, untyped]]
|
|
38
|
+
def edges: () -> Array[Hash[Symbol, untyped]]
|
|
39
|
+
|
|
40
|
+
# : () -> Hash[Symbol, Hash[Symbol, Integer]]
|
|
41
|
+
def node_positions: () -> Hash[Symbol, Hash[Symbol, Integer]]
|
|
42
|
+
|
|
43
|
+
# : (Hash[Symbol, untyped], Hash[Symbol, Hash[Symbol, Integer]]) -> Integer
|
|
44
|
+
def dependency_column: (Hash[Symbol, untyped], Hash[Symbol, Hash[Symbol, Integer]]) -> Integer
|
|
45
|
+
|
|
46
|
+
# : (Symbol, Symbol) -> Hash[Symbol, untyped]
|
|
47
|
+
def edge_view: (Symbol, Symbol) -> Hash[Symbol, untyped]
|
|
48
|
+
|
|
49
|
+
# : (Hash[Symbol, untyped], Hash[Symbol, untyped]) -> String
|
|
50
|
+
def edge_path: (Hash[Symbol, untyped], Hash[Symbol, untyped]) -> String
|
|
51
|
+
|
|
52
|
+
# : (Symbol) -> Hash[Symbol, untyped]
|
|
53
|
+
def node_view: (Symbol) -> Hash[Symbol, untyped]
|
|
54
|
+
|
|
55
|
+
# : () -> Integer
|
|
56
|
+
def canvas_width: () -> Integer
|
|
57
|
+
|
|
58
|
+
# : () -> Integer
|
|
59
|
+
def canvas_height: () -> Integer
|
|
60
|
+
|
|
61
|
+
# : (Integer) -> Integer
|
|
62
|
+
def x_for: (Integer) -> Integer
|
|
63
|
+
|
|
64
|
+
# : (Integer) -> Integer
|
|
65
|
+
def y_for: (Integer) -> Integer
|
|
66
|
+
|
|
67
|
+
# : (Symbol) -> String
|
|
68
|
+
def truncate_label: (Symbol) -> String
|
|
69
|
+
|
|
70
|
+
# : (Hash[Symbol, untyped]) -> String?
|
|
71
|
+
def node_meta_label: (Hash[Symbol, untyped]) -> String?
|
|
72
|
+
|
|
73
|
+
# : () -> String
|
|
74
|
+
def root_task_label: () -> String
|
|
75
|
+
|
|
76
|
+
# : (Hash[Symbol, Integer]) -> String
|
|
77
|
+
def each_progress_label: (Hash[Symbol, Integer]) -> String
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
end
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# Generated from lib/job_workflow/monitoring/execution_page.rb with RBS::Inline
|
|
2
|
+
|
|
3
|
+
module JobWorkflow
|
|
4
|
+
module Monitoring
|
|
5
|
+
class ExecutionPage
|
|
6
|
+
attr_reader executions: Array[ExecutionViewModel]
|
|
7
|
+
|
|
8
|
+
attr_reader next_cursor: String?
|
|
9
|
+
|
|
10
|
+
# : (executions: Array[ExecutionViewModel], next_cursor: String?) -> void
|
|
11
|
+
def initialize: (executions: Array[ExecutionViewModel], next_cursor: String?) -> void
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# Generated from lib/job_workflow/monitoring/execution_registry.rb with RBS::Inline
|
|
2
|
+
|
|
3
|
+
module JobWorkflow
|
|
4
|
+
module Monitoring
|
|
5
|
+
class ExecutionRegistry
|
|
6
|
+
DEFAULT_LIMIT: ::Integer
|
|
7
|
+
|
|
8
|
+
# : (job_class_name: String, ?limit: Integer, ?cursor: String?) -> ExecutionPage
|
|
9
|
+
def self.page_for: (job_class_name: String, ?limit: Integer, ?cursor: String?) -> ExecutionPage
|
|
10
|
+
|
|
11
|
+
# : (String) -> ExecutionViewModel?
|
|
12
|
+
def self.find: (String) -> ExecutionViewModel?
|
|
13
|
+
|
|
14
|
+
# : (Hash[String, untyped], ?hydrate_sub_tasks: bool) -> ExecutionViewModel?
|
|
15
|
+
private def self.build_view_model: (Hash[String, untyped], ?hydrate_sub_tasks: bool) -> ExecutionViewModel?
|
|
16
|
+
|
|
17
|
+
# : (WorkflowStatus) -> void
|
|
18
|
+
private def self.hydrate_sub_task_state: (WorkflowStatus) -> void
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|