tengine_job 1.1.0 → 1.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +15 -0
- data/Gemfile.lock +78 -48
- data/bin/tengine_job +71 -0
- data/examples/0004_retry_one_layer.rb +10 -7
- data/examples/0027_parallel_ssh_job +9 -0
- data/examples/0027_parallel_ssh_jobs.rb +14 -0
- data/lib/tengine/job.rb +19 -49
- data/lib/tengine/job/dsl.rb +13 -0
- data/lib/tengine/job/{dsl_binder.rb → dsl/binder.rb} +4 -4
- data/lib/tengine/job/{dsl_evaluator.rb → dsl/evaluator.rb} +2 -2
- data/lib/tengine/job/{dsl_loader.rb → dsl/loader.rb} +20 -22
- data/lib/tengine/job/runtime.rb +32 -0
- data/lib/tengine/job/{drivers → runtime/drivers}/job_control_driver.rb +46 -92
- data/lib/tengine/job/{drivers → runtime/drivers}/job_execution_driver.rb +14 -10
- data/lib/tengine/job/runtime/drivers/jobnet_control_driver.rb +240 -0
- data/lib/tengine/job/{drivers → runtime/drivers}/schedule_driver.rb +4 -4
- data/lib/tengine/job/{edge.rb → runtime/edge.rb} +79 -25
- data/lib/tengine/job/{executable.rb → runtime/executable.rb} +35 -15
- data/lib/tengine/job/{execution.rb → runtime/execution.rb} +19 -11
- data/lib/tengine/job/runtime/job_base.rb +5 -0
- data/lib/tengine/job/runtime/jobnet.rb +283 -0
- data/lib/tengine/job/runtime/junction.rb +44 -0
- data/lib/tengine/job/runtime/named_vertex.rb +95 -0
- data/lib/tengine/job/runtime/root_jobnet.rb +81 -0
- data/lib/tengine/job/{signal.rb → runtime/signal.rb} +99 -13
- data/lib/tengine/job/runtime/ssh_job.rb +486 -0
- data/lib/tengine/job/{jobnet → runtime}/state_transition.rb +6 -4
- data/lib/tengine/job/runtime/stoppable.rb +64 -0
- data/lib/tengine/job/runtime/vertex.rb +50 -0
- data/lib/tengine/job/structure.rb +20 -0
- data/lib/tengine/job/{category.rb → structure/category.rb} +9 -5
- data/lib/tengine/job/{jobnet/builder.rb → structure/edge_builder.rb} +11 -7
- data/lib/tengine/job/{element_selector_notation.rb → structure/element_selector_notation.rb} +15 -11
- data/lib/tengine/job/structure/jobnet_builder.rb +83 -0
- data/lib/tengine/job/structure/jobnet_finder.rb +60 -0
- data/lib/tengine/job/{name_path.rb → structure/name_path.rb} +2 -2
- data/lib/tengine/job/structure/tree.rb +20 -0
- data/lib/tengine/job/structure/visitor.rb +67 -0
- data/lib/tengine/job/template.rb +24 -0
- data/lib/tengine/job/template/edge.rb +37 -0
- data/lib/tengine/job/template/expansion.rb +24 -0
- data/lib/tengine/job/template/generator.rb +111 -0
- data/lib/tengine/job/template/jobnet.rb +83 -0
- data/lib/tengine/job/template/junction.rb +14 -0
- data/lib/tengine/job/{job.rb → template/named_vertex.rb} +3 -5
- data/lib/tengine/job/{root_jobnet_template.rb → template/root_jobnet.rb} +12 -26
- data/lib/tengine/job/template/ssh_job.rb +80 -0
- data/lib/tengine/job/template/vertex.rb +97 -0
- metadata +127 -93
- data/lib/tengine/job/connectable.rb +0 -43
- data/lib/tengine/job/drivers/jobnet_control_driver.rb +0 -249
- data/lib/tengine/job/end.rb +0 -32
- data/lib/tengine/job/expansion.rb +0 -37
- data/lib/tengine/job/fork.rb +0 -6
- data/lib/tengine/job/jobnet.rb +0 -184
- data/lib/tengine/job/jobnet/job_state_transition.rb +0 -167
- data/lib/tengine/job/jobnet/jobnet_state_transition.rb +0 -110
- data/lib/tengine/job/jobnet_actual.rb +0 -84
- data/lib/tengine/job/jobnet_template.rb +0 -10
- data/lib/tengine/job/join.rb +0 -6
- data/lib/tengine/job/junction.rb +0 -29
- data/lib/tengine/job/killing.rb +0 -30
- data/lib/tengine/job/mm_compatibility.rb +0 -6
- data/lib/tengine/job/mm_compatibility/connectable.rb +0 -13
- data/lib/tengine/job/root.rb +0 -16
- data/lib/tengine/job/root_jobnet_actual.rb +0 -58
- data/lib/tengine/job/script_executable.rb +0 -235
- data/lib/tengine/job/start.rb +0 -20
- data/lib/tengine/job/stoppable.rb +0 -15
- data/lib/tengine/job/vertex.rb +0 -181
@@ -5,7 +5,7 @@ include Tengine::Core::SafeUpdatable
|
|
5
5
|
driver :schedule_driver do
|
6
6
|
|
7
7
|
on :'start.execution.job.tengine' do
|
8
|
-
exec = Tengine::Job::Signal.new(event).execution
|
8
|
+
exec = Tengine::Job::Runtime::Signal.new(event).execution
|
9
9
|
name = exec.name_as_resource
|
10
10
|
status = Tengine::Core::Schedule::SCHEDULED
|
11
11
|
if exec.actual_base_timeout_alert && !exec.actual_base_timeout_alert.zero?
|
@@ -35,7 +35,7 @@ driver :schedule_driver do
|
|
35
35
|
i = h["execution_id"] or next
|
36
36
|
|
37
37
|
orig = Tengine::Core::EventWrapper.new(Tengine::Core::Event.new(g)) # this object shall noe be persisted
|
38
|
-
exec = Tengine::Job::Signal.new(orig).execution
|
38
|
+
exec = Tengine::Job::Runtime::Signal.new(orig).execution
|
39
39
|
name = exec.name_as_resource
|
40
40
|
status = Tengine::Core::Schedule::SCHEDULED
|
41
41
|
if exec.actual_base_timeout_alert && !exec.actual_base_timeout_alert.zero? && Tengine::Core::Schedule.where(event_type_name: "alert.execution.job.tengine", source_name: name).count.zero?
|
@@ -58,7 +58,7 @@ driver :schedule_driver do
|
|
58
58
|
end
|
59
59
|
|
60
60
|
on :'success.execution.job.tengine' do
|
61
|
-
name = Tengine::Job::Signal.new(event).execution.name_as_resource
|
61
|
+
name = Tengine::Job::Runtime::Signal.new(event).execution.name_as_resource
|
62
62
|
Tengine::Core::Schedule.with(
|
63
63
|
safe: safemode(Tengine::Core::Schedule.collection)
|
64
64
|
).where(
|
@@ -68,7 +68,7 @@ driver :schedule_driver do
|
|
68
68
|
end
|
69
69
|
|
70
70
|
on :'error.execution.job.tengine' do
|
71
|
-
name = Tengine::Job::Signal.new(event).execution.name_as_resource
|
71
|
+
name = Tengine::Job::Runtime::Signal.new(event).execution.name_as_resource
|
72
72
|
Tengine::Core::Schedule.with(
|
73
73
|
safe: safemode(Tengine::Core::Schedule.collection)
|
74
74
|
).where(
|
@@ -1,23 +1,27 @@
|
|
1
1
|
# -*- coding: utf-8 -*-
|
2
|
-
require 'tengine/job'
|
2
|
+
require 'tengine/job/runtime'
|
3
3
|
require 'selectable_attr'
|
4
4
|
|
5
5
|
# Vertexとともにジョブネットを構成するグラフの「辺」を表すモデル
|
6
|
-
# Tengine::Job::Jobnetにembeddedされます。
|
7
|
-
class Tengine::Job::Edge
|
6
|
+
# Tengine::Job::Runtime::Jobnetにembeddedされます。
|
7
|
+
class Tengine::Job::Runtime::Edge
|
8
8
|
include Mongoid::Document
|
9
9
|
include Mongoid::Timestamps
|
10
10
|
include Tengine::Core::SelectableAttr
|
11
|
-
include Tengine::Job::Signal::Transmittable
|
11
|
+
include Tengine::Job::Runtime::Signal::Transmittable
|
12
|
+
include Tengine::Job::Structure::Visitor::Accepter
|
12
13
|
|
13
14
|
class StatusError < StandardError
|
14
15
|
end
|
15
16
|
|
16
|
-
embedded_in :owner, :class_name => "Tengine::Job::Jobnet", :inverse_of => :edges
|
17
|
+
embedded_in :owner, :class_name => "Tengine::Job::Runtime::Jobnet", :inverse_of => :edges
|
18
|
+
|
19
|
+
with_options(class_name: "Tengine::Job::Runtime::Vertex", inverse_of: nil) do |c|
|
20
|
+
c.belongs_to :origin , foreign_key: "origin_id"
|
21
|
+
c.belongs_to :destination, foreign_key: "destination_id"
|
22
|
+
end
|
17
23
|
|
18
24
|
field :phase_cd , :type => Integer, :default => 0 # ステータス。とりうる値は後述を参照してください。詳しくはtengine_jobパッケージ設計書の「edge状態遷移」を参照してください。
|
19
|
-
field :origin_id , :type => Moped::BSON::ObjectId # 辺の遷移元となるvertexのid
|
20
|
-
field :destination_id, :type => Moped::BSON::ObjectId # 辺の遷移先となるvertexのid
|
21
25
|
|
22
26
|
validates :origin_id, :presence => true
|
23
27
|
validates :destination_id, :presence => true
|
@@ -55,7 +59,7 @@ class Tengine::Job::Edge
|
|
55
59
|
end
|
56
60
|
|
57
61
|
def inspect
|
58
|
-
"#<#{self.class.name} #{name_for_message}>"
|
62
|
+
"#<#{self.class.name} #{phase_key.inspect} #{name_for_message}>"
|
59
63
|
end
|
60
64
|
|
61
65
|
# https://cacoo.com/diagrams/hdLgrzYsTBBpV3Wj#3E9EA
|
@@ -72,11 +76,18 @@ class Tengine::Job::Edge
|
|
72
76
|
when :suspended then
|
73
77
|
self.phase_key = :keeping
|
74
78
|
when :closing then
|
79
|
+
|
80
|
+
Tengine.logger.debug "c" * 100
|
81
|
+
Tengine.logger.debug "#{object_id} #{inspect}"
|
82
|
+
# Tengine.logger.debug caller[0, 20].join("\n ")
|
83
|
+
|
84
|
+
# binding.pry
|
85
|
+
|
75
86
|
self.phase_key = :closed
|
76
87
|
signal.paths << self
|
77
88
|
signal.with_paths_backup do
|
78
|
-
if destination.is_a?(Tengine::Job::
|
79
|
-
destination.next_edges.first.transmit(signal)
|
89
|
+
if destination.is_a?(Tengine::Job::Runtime::NamedVertex)
|
90
|
+
signal.cache(destination.next_edges.first).transmit(signal)
|
80
91
|
else
|
81
92
|
signal.leave(self)
|
82
93
|
end
|
@@ -92,7 +103,7 @@ class Tengine::Job::Edge
|
|
92
103
|
# IG
|
93
104
|
when :suspended, :keeping then
|
94
105
|
# N/A
|
95
|
-
raise Tengine::Job::Edge::StatusError, "#{self.class.name}#complete not available on #{phase_key.inspect} at #{self.inspect}"
|
106
|
+
raise Tengine::Job::Runtime::Edge::StatusError, "#{self.class.name}#complete not available on #{phase_key.inspect} at #{self.inspect}"
|
96
107
|
end
|
97
108
|
end
|
98
109
|
|
@@ -101,7 +112,10 @@ class Tengine::Job::Edge
|
|
101
112
|
if d = destination
|
102
113
|
if signal.execution.in_scope?(d)
|
103
114
|
self.phase_key = :active
|
104
|
-
|
115
|
+
|
116
|
+
signal.call_later do
|
117
|
+
d.reset(signal)
|
118
|
+
end
|
105
119
|
end
|
106
120
|
else
|
107
121
|
raise "destination not found: #{destination_id.inspect} from #{origin.inspect}"
|
@@ -112,39 +126,79 @@ class Tengine::Job::Edge
|
|
112
126
|
case phase_key
|
113
127
|
when :active, :suspended, :keeping, :transmitting then
|
114
128
|
self.phase_key = :closing
|
129
|
+
when :closing, :closed then
|
130
|
+
# ignored
|
131
|
+
else
|
132
|
+
Tengine.logger.warn "#{object_id} #{inspect} wasn't closed"
|
115
133
|
end
|
116
134
|
end
|
117
135
|
|
118
|
-
def close_followings
|
119
|
-
|
136
|
+
def close_followings(signal, options = {})
|
137
|
+
v = Tengine::Job::Runtime::Edge::Closer.new(signal, options)
|
138
|
+
accept_visitor(v)
|
139
|
+
v.closed_edges
|
120
140
|
end
|
121
141
|
|
122
|
-
|
123
|
-
|
142
|
+
# ownerのupdate_with_lockを使っています。
|
143
|
+
def close_followings_and_trasmit(signal)
|
144
|
+
jobnet = signal.cache(self.owner)
|
145
|
+
closing_edges = nil
|
146
|
+
closed_edges = []
|
147
|
+
jobnet.update_with_lock do
|
148
|
+
closing_edges = self.close_followings(signal)
|
149
|
+
closing_edges.each do |e|
|
150
|
+
next unless e.owner.id == jobnet.id
|
151
|
+
je = signal.cache(e) # jobnet単位で保存するので、jobnetオブエジェクトに紐付けられたものを見つける
|
152
|
+
je.close(nil)
|
153
|
+
closed_edges << e
|
154
|
+
end
|
155
|
+
# jobnetオブジェクトのedgesに含まれないエッジについては、そのowner毎にまとめて保存する
|
156
|
+
self.transmit(signal)
|
157
|
+
|
158
|
+
Tengine.logger.debug "<" * 100
|
159
|
+
Tengine.logger.debug "#{__FILE__}##{__LINE__}"
|
160
|
+
jobnet.edges.each do |edge|
|
161
|
+
Tengine.logger.debug "#{edge.object_id} #{edge.inspect} BEFORE end of block for update_with_lock"
|
162
|
+
end
|
163
|
+
end
|
164
|
+
# signal.cache_list
|
165
|
+
signal.changed_vertecs.each(&:save!)
|
124
166
|
end
|
125
167
|
|
126
168
|
def phase_key=(phase_key)
|
127
|
-
Tengine.logger.debug("edge phase changed. <#{self.id.to_s}> #{self.phase_name} -> #{Tengine::Job::Edge.phase_name_by_key(phase_key)}")
|
128
|
-
|
169
|
+
# Tengine.logger.debug("edge phase changed. <#{self.id.to_s}> #{self.phase_name} -> #{Tengine::Job::Runtime::Edge.phase_name_by_key(phase_key)}")
|
170
|
+
Tengine.logger.debug("#{object_id} edge phase changed. <#{inspect}> #{self.phase_name} -> #{Tengine::Job::Runtime::Edge.phase_name_by_key(phase_key)}")
|
171
|
+
self.write_attribute(:phase_cd, Tengine::Job::Runtime::Edge.phase_id_by_key(phase_key))
|
129
172
|
end
|
130
173
|
|
131
174
|
class Closer
|
175
|
+
attr_reader :closed_edges
|
176
|
+
def initialize(signal, options = {})
|
177
|
+
@signal = signal
|
178
|
+
@closed_edges = []
|
179
|
+
end
|
180
|
+
|
181
|
+
def close(edge)
|
182
|
+
edge.close(@signal)
|
183
|
+
@closed_edges << edge
|
184
|
+
end
|
185
|
+
|
132
186
|
def visit(obj)
|
133
|
-
|
187
|
+
# binding.pry
|
188
|
+
obj = @signal.remember(obj)
|
189
|
+
if obj.is_a?(Tengine::Job::Runtime::End)
|
134
190
|
if parent = obj.parent
|
135
191
|
(parent.next_edges || []).each{|edge| edge.accept_visitor(self)}
|
136
192
|
end
|
137
|
-
elsif obj.is_a?(Tengine::Job::Vertex)
|
193
|
+
elsif obj.is_a?(Tengine::Job::Runtime::Vertex)
|
138
194
|
obj.next_edges.each{|edge| edge.accept_visitor(self)}
|
139
|
-
elsif obj.is_a?(Tengine::Job::Edge)
|
140
|
-
|
141
|
-
obj.destination.accept_visitor(self)
|
195
|
+
elsif obj.is_a?(Tengine::Job::Runtime::Edge)
|
196
|
+
close(obj)
|
197
|
+
@signal.cache(obj.destination).accept_visitor(self)
|
142
198
|
else
|
143
199
|
raise "Unsupported class #{obj.inspect}"
|
144
200
|
end
|
145
201
|
end
|
146
|
-
|
147
202
|
end
|
148
203
|
|
149
|
-
|
150
204
|
end
|
@@ -1,10 +1,10 @@
|
|
1
1
|
# -*- coding: utf-8 -*-
|
2
|
-
require 'tengine/job'
|
2
|
+
require 'tengine/job/runtime'
|
3
3
|
require 'selectable_attr'
|
4
4
|
|
5
5
|
# ジョブ/ジョブネットを実行する際の情報に関するモジュール
|
6
|
-
# Tengine::Job::
|
7
|
-
module Tengine::Job::Executable
|
6
|
+
# Tengine::Job::Runtime::Jobne, Tengine::Job::Runtime::JobBase、Tengine::Job::Runtime::Executionがこのモジュールをincludeします
|
7
|
+
module Tengine::Job::Runtime::Executable
|
8
8
|
extend ActiveSupport::Concern
|
9
9
|
|
10
10
|
class PhaseError < StandardError
|
@@ -28,20 +28,40 @@ module Tengine::Job::Executable
|
|
28
28
|
end
|
29
29
|
|
30
30
|
def phase_key=(phase_key)
|
31
|
-
element_type =
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
31
|
+
element_type =
|
32
|
+
case self.class
|
33
|
+
when Tengine::Job::Runtime::Execution then "execution"
|
34
|
+
when Tengine::Job::Runtime::RootJobnet then "root_jobnet"
|
35
|
+
when Tengine::Job::Runtime::Jobnet then self.jobnet_type_key == :normal ? "jobnet" : self.jobnet_type_name
|
36
|
+
when Tengine::Job::Runtime::SshJob then "job"
|
37
|
+
end
|
38
|
+
caption = respond_to?(:name_path) ? name_path : respond_to?(:name) ? name : inspect
|
39
|
+
Tengine.logger.debug("#{element_type} phase changed. <#{ self.id.to_s}> #{caption} #{self.phase_name} -> #{ self.class.phase_name_by_key(phase_key)}")
|
40
|
+
|
41
|
+
# Tengine.logger.debug(caller.join("\n "))
|
42
|
+
|
42
43
|
self.write_attribute(:phase_cd, self.class.phase_id_by_key(phase_key))
|
43
44
|
end
|
44
45
|
|
46
|
+
after_save :update_children_phase_modified, if: :phase_cd_changed?
|
47
|
+
|
48
|
+
def update_children_phase_modified
|
49
|
+
return unless respond_to?(:children)
|
50
|
+
children.each do |child|
|
51
|
+
if child.respond_to?(:chained_box?) && child.chained_box?
|
52
|
+
child.phase_key = phase_key
|
53
|
+
child.save!
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def update_phase!(phase_key, attrs = {})
|
59
|
+
attrs.each do |key, value|
|
60
|
+
self.send("#{key}=", value)
|
61
|
+
end
|
62
|
+
self.phase_key = phase_key
|
63
|
+
self.save!
|
64
|
+
end
|
45
65
|
|
46
66
|
unless defined?(HUMAN_PHASE_KEYS_HASH)
|
47
67
|
HUMAN_PHASE_KEYS_HASH = {
|
@@ -66,7 +86,7 @@ module Tengine::Job::Executable
|
|
66
86
|
|
67
87
|
# human_phase_keyの表示用の文字列を返します。
|
68
88
|
def human_phase_name
|
69
|
-
I18n.t(human_phase_key, :scope => "selectable_attrs.tengine/job/
|
89
|
+
I18n.t(human_phase_key, :scope => "selectable_attrs.tengine/job/runtime/jobnet.human_phase_name")
|
70
90
|
end
|
71
91
|
|
72
92
|
end
|
@@ -1,10 +1,12 @@
|
|
1
1
|
# -*- coding: utf-8 -*-
|
2
|
+
|
2
3
|
require 'tengine/core'
|
4
|
+
require 'tengine/job/runtime'
|
3
5
|
|
4
|
-
class Tengine::Job::Execution
|
6
|
+
class Tengine::Job::Runtime::Execution
|
5
7
|
include Mongoid::Document
|
6
8
|
include Mongoid::Timestamps
|
7
|
-
include Tengine::Job::Executable
|
9
|
+
include Tengine::Job::Runtime::Executable
|
8
10
|
include Tengine::Core::CollectionAccessible
|
9
11
|
|
10
12
|
field :target_actual_ids, :type => Array
|
@@ -22,7 +24,7 @@ class Tengine::Job::Execution
|
|
22
24
|
field :retry, :type => Boolean, :default => false # 再実行かどうか
|
23
25
|
field :spot , :type => Boolean, :default => false # スポット実行かどうか
|
24
26
|
|
25
|
-
belongs_to :root_jobnet, :class_name => "Tengine::Job::
|
27
|
+
belongs_to :root_jobnet, :class_name => "Tengine::Job::Runtime::RootJobnet", :index => true, :inverse_of => :executions
|
26
28
|
|
27
29
|
attr_accessor :signal # runを実行して、ackを返す際に一時的にsignalを記録しておく属性です。それ以外には使用しないでください。
|
28
30
|
|
@@ -67,13 +69,19 @@ class Tengine::Job::Execution
|
|
67
69
|
def transmit(signal)
|
68
70
|
case phase_key
|
69
71
|
when :initialized then
|
70
|
-
|
71
|
-
|
72
|
-
|
72
|
+
self.phase_key = :ready
|
73
|
+
signal.call_later do
|
74
|
+
if self.retry
|
75
|
+
target_actuals.each do |target|
|
76
|
+
signal.call_later{ signal.cache(target).reset(signal) }
|
77
|
+
end
|
78
|
+
end
|
79
|
+
signal.call_later do
|
80
|
+
Tengine.logger.info("=" * 50)
|
81
|
+
activate(signal)
|
82
|
+
self.save!
|
73
83
|
end
|
74
84
|
end
|
75
|
-
self.phase_key = :ready
|
76
|
-
activate(signal)
|
77
85
|
else
|
78
86
|
raise "Unsupported phase_key for transmit: #{phase_key.inspect}"
|
79
87
|
end
|
@@ -98,7 +106,7 @@ class Tengine::Job::Execution
|
|
98
106
|
def ack(signal)
|
99
107
|
case phase_key
|
100
108
|
when :ready then
|
101
|
-
raise Tengine::Job::Executable::PhaseError, "ack not available on #{phase_key.inspect}"
|
109
|
+
raise Tengine::Job::Runtime::Executable::PhaseError, "ack not available on #{phase_key.inspect}"
|
102
110
|
when :starting then
|
103
111
|
self.phase_key = :running
|
104
112
|
end
|
@@ -107,7 +115,7 @@ class Tengine::Job::Execution
|
|
107
115
|
def succeed(signal)
|
108
116
|
case phase_key
|
109
117
|
when :initialized, :ready, :error then
|
110
|
-
raise Tengine::Job::Executable::PhaseError, "succeed not available on #{phase_key.inspect}"
|
118
|
+
raise Tengine::Job::Runtime::Executable::PhaseError, "succeed not available on #{phase_key.inspect}"
|
111
119
|
when :starting, :running, :dying, :stuck then
|
112
120
|
self.phase_key = :success
|
113
121
|
signal.fire(self, :"success.execution.job.tengine")
|
@@ -117,7 +125,7 @@ class Tengine::Job::Execution
|
|
117
125
|
def fail(signal)
|
118
126
|
case phase_key
|
119
127
|
when :initialized, :ready, :success then
|
120
|
-
raise Tengine::Job::Executable::PhaseError, "fail not available on #{phase_key.inspect}"
|
128
|
+
raise Tengine::Job::Runtime::Executable::PhaseError, "fail not available on #{phase_key.inspect}"
|
121
129
|
when :starting, :running, :dying, :stuck then
|
122
130
|
self.phase_key = :error
|
123
131
|
signal.fire(self, :"error.execution.job.tengine")
|
@@ -0,0 +1,283 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
require 'tengine/job/runtime'
|
3
|
+
|
4
|
+
require 'selectable_attr'
|
5
|
+
|
6
|
+
# ジョブの始端から終端までを持ち、VertexとEdgeを組み合わせてジョブネットを構成することができるVertex。
|
7
|
+
# 自身もジョブネットを構成するVertexの一部として扱われる。
|
8
|
+
class Tengine::Job::Runtime::Jobnet < Tengine::Job::Runtime::NamedVertex
|
9
|
+
include Tengine::Core::SelectableAttr
|
10
|
+
include Tengine::Core::SafeUpdatable
|
11
|
+
|
12
|
+
include Tengine::Job::Structure::JobnetBuilder
|
13
|
+
include Tengine::Job::Structure::JobnetFinder
|
14
|
+
include Tengine::Job::Structure::ElementSelectorNotation
|
15
|
+
|
16
|
+
include Tengine::Job::Runtime::StateTransition
|
17
|
+
|
18
|
+
field :description , :type => String # ジョブネットの説明
|
19
|
+
|
20
|
+
field :was_expansion, :type => Boolean # テンプレートがTenigne::Job::Expansionであった場合にtrueです。
|
21
|
+
|
22
|
+
# was_expansionがtrueなら元々のtemplateへの参照が必要なのでTenigne::Job::Runtime::RootJobnetだけでなく
|
23
|
+
# JobnetActualでもtemplateが必要です。
|
24
|
+
belongs_to :template, :index => true, :class_name => "Tengine::Job::Template::RootJobnet"
|
25
|
+
|
26
|
+
field :jobnet_type_cd, :type => Integer, :default => 1 # ジョブネットの種類。後述の定義を参照してください。
|
27
|
+
|
28
|
+
selectable_attr :jobnet_type_cd do
|
29
|
+
entry 1, :normal , "normal"
|
30
|
+
entry 2, :finally , "finally", :alternative => true
|
31
|
+
# entry 3, :recover , "recover", :alternative => true
|
32
|
+
entry 4, :hadoop_job_run, "hadoop job run"
|
33
|
+
entry 5, :hadoop_job , "hadoop job" , :chained_box => true
|
34
|
+
entry 6, :map_phase , "map phase" , :chained_box => true
|
35
|
+
entry 7, :reduce_phase , "reduce phase" , :chained_box => true
|
36
|
+
end
|
37
|
+
def chained_box?; jobnet_type_entry[:chained_box]; end
|
38
|
+
|
39
|
+
embeds_many :edges, :class_name => "Tengine::Job::Runtime::Edge", :inverse_of => :owner , :validate => false
|
40
|
+
accepts_nested_attributes_for :edges
|
41
|
+
|
42
|
+
before_validation do |r|
|
43
|
+
r.edges.each do |edge|
|
44
|
+
unless edge.valid?
|
45
|
+
edge.errors.each do |f, error|
|
46
|
+
r.errors.add(:base, "#{edge.name_for_message} #{f.to_s.humanize} #{error}")
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
VERTEX_CLASSES = {
|
53
|
+
vertex: "Vertex",
|
54
|
+
start_vertex: "Start",
|
55
|
+
end_vertex: "End",
|
56
|
+
jobnet: "Jobnet",
|
57
|
+
fork: "Fork",
|
58
|
+
join: "Join",
|
59
|
+
}.freeze
|
60
|
+
|
61
|
+
VERTEX_CLASSES.each do |key, value|
|
62
|
+
instance_eval("def #{key}_class; Tengine::Job::Runtime::#{value}; end", __FILE__, __LINE__)
|
63
|
+
end
|
64
|
+
|
65
|
+
attr_reader :stop_modified
|
66
|
+
before_save :check_stop_modified
|
67
|
+
after_save :update_children_stop_modified, if: :stop_modified
|
68
|
+
|
69
|
+
def check_stop_modified
|
70
|
+
@stop_modified = stop_reason_changed? || stopped_at_changed?
|
71
|
+
true
|
72
|
+
end
|
73
|
+
|
74
|
+
def update_children_stop_modified
|
75
|
+
@stop_modified = false
|
76
|
+
children.each do |child|
|
77
|
+
if child.is_a?(Tengine::Job::Runtime::Stoppable) && child.respond_to?(:chained_box?) && child.chained_box?
|
78
|
+
child.stop_reason = stop_reason
|
79
|
+
child.stopped_at = stopped_at
|
80
|
+
child.save!
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
class << self
|
86
|
+
def by_name(name)
|
87
|
+
where({:name => name}).first
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
# @override
|
92
|
+
def ancestors_until_expansion
|
93
|
+
if (parent = self.parent) && !self.was_expansion?
|
94
|
+
parent.ancestors_until_expansion + [parent]
|
95
|
+
else
|
96
|
+
[]
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
# TODO このメソッドは削除するべき。これを使わないで動くようにする。
|
101
|
+
def script_executable?
|
102
|
+
false
|
103
|
+
end
|
104
|
+
|
105
|
+
## 状態遷移アクション
|
106
|
+
|
107
|
+
# ハンドリングするドライバ: ジョブネット制御ドライバ or ジョブ起動ドライバ
|
108
|
+
def transmit(signal)
|
109
|
+
self.phase_key = :ready
|
110
|
+
signal.fire(self, :"start.jobnet.job.tengine", {
|
111
|
+
:target_jobnet_id => self.id,
|
112
|
+
:target_jobnet_name_path => self.name_path,
|
113
|
+
})
|
114
|
+
end
|
115
|
+
available(:transmit, :on => :initialized,
|
116
|
+
:ignored => [:ready, :starting, :running, :dying, :success, :error, :stuck])
|
117
|
+
|
118
|
+
# ハンドリングするドライバ: ジョブネット制御ドライバ
|
119
|
+
def activate(signal)
|
120
|
+
self.phase_key = :starting
|
121
|
+
self.started_at = signal.event.occurred_at
|
122
|
+
complete_origin_edge(signal) if prev_edges && !prev_edges.empty?
|
123
|
+
|
124
|
+
signal.call_later do
|
125
|
+
signal.cache(parent || signal.execution).ack(signal)
|
126
|
+
if root?
|
127
|
+
signal.execution.with(safe: safemode(Tengine::Job::Runtime::Execution.collection)).save!
|
128
|
+
end
|
129
|
+
|
130
|
+
signal.call_later do
|
131
|
+
self.update_with_lock do
|
132
|
+
signal.paths << self
|
133
|
+
signal.cache(self.start_vertex).transmit(signal)
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
138
|
+
available(:activate, :on => :ready,
|
139
|
+
:ignored => [:starting, :running, :dying, :success, :error, :stuck])
|
140
|
+
|
141
|
+
# ハンドリングするドライバ: ジョブネット制御ドライバ
|
142
|
+
# このackは、子要素のTengine::Job::Runtime::Start#activateから呼ばれます
|
143
|
+
def ack(signal)
|
144
|
+
self.phase_key = :running
|
145
|
+
end
|
146
|
+
available(:ack, :on => [:initialized, :ready, :starting],
|
147
|
+
:ignored => [:running, :dying, :success, :error, :stuck])
|
148
|
+
|
149
|
+
# ハンドリングするドライバ: ジョブネット制御ドライバ
|
150
|
+
# このackは、子要素のTengine::Job::End#activateから呼ばれます
|
151
|
+
def finish(signal)
|
152
|
+
|
153
|
+
Tengine.logger.info("#{__FILE__}##{__LINE__} #{self.class}#finish")
|
154
|
+
|
155
|
+
edge = signal.cache(end_vertex.prev_edges.first)
|
156
|
+
edge.closed? ?
|
157
|
+
self.fail(signal) :
|
158
|
+
succeed(signal)
|
159
|
+
end
|
160
|
+
|
161
|
+
# ハンドリングするドライバ: ジョブネット制御ドライバ
|
162
|
+
def succeed(signal)
|
163
|
+
self.phase_key = :success
|
164
|
+
self.finished_at = signal.event.occurred_at
|
165
|
+
signal.fire(self, :"success.jobnet.job.tengine", {
|
166
|
+
:target_jobnet_id => self.id,
|
167
|
+
:target_jobnet_name_path => self.name_path,
|
168
|
+
})
|
169
|
+
end
|
170
|
+
available :succeed, :on => [:starting, :running, :dying, :stuck, :error], :ignored => [:success]
|
171
|
+
|
172
|
+
# ハンドリングするドライバ: ジョブネット制御ドライバ
|
173
|
+
def fail(signal)
|
174
|
+
Tengine.logger.info("#{__FILE__}##{__LINE__} #{self.class}#fail")
|
175
|
+
|
176
|
+
return if signal.cache(self.edges).any?(&:alive?)
|
177
|
+
self.phase_key = :error
|
178
|
+
self.finished_at = signal.event.occurred_at
|
179
|
+
signal.fire(self, :"error.jobnet.job.tengine", {
|
180
|
+
:target_jobnet_id => self.id,
|
181
|
+
:target_jobnet_name_path => self.name_path,
|
182
|
+
})
|
183
|
+
end
|
184
|
+
available :fail, :on => [:starting, :running, :dying, :success], :ignored => [:error, :stuck]
|
185
|
+
|
186
|
+
def fire_stop(signal)
|
187
|
+
return if self.phase_key == :initialized
|
188
|
+
signal.fire(self, :"stop.jobnet.job.tengine", {
|
189
|
+
:target_jobnet_id => self.id,
|
190
|
+
:target_jobnet_name_path => self.name_path,
|
191
|
+
:stop_reason => signal.event[:stop_reason]
|
192
|
+
})
|
193
|
+
end
|
194
|
+
|
195
|
+
def stop(signal)
|
196
|
+
self.phase_key = :dying
|
197
|
+
self.stopped_at = signal.event.occurred_at
|
198
|
+
self.stop_reason = signal.event[:stop_reason]
|
199
|
+
close(signal)
|
200
|
+
children.each do |child|
|
201
|
+
child.fire_stop(signal) if child.respond_to?(:fire_stop)
|
202
|
+
end
|
203
|
+
end
|
204
|
+
available :stop, :on => [:initialized, :ready, :starting, :running], :ignored => [:dying, :success, :error, :stuck]
|
205
|
+
|
206
|
+
def close(signal)
|
207
|
+
self.edges.each{|edge| signal.cache(edge).close(signal)}
|
208
|
+
end
|
209
|
+
|
210
|
+
|
211
|
+
def reset(signal, &block)
|
212
|
+
# children.each{|c| c.reset(signal) }
|
213
|
+
self.phase_key = :initialized
|
214
|
+
edges.each do |edge|
|
215
|
+
signal.paths.push(edge)
|
216
|
+
edge.phase_key = :active
|
217
|
+
end
|
218
|
+
children.each do |child|
|
219
|
+
signal.call_later do
|
220
|
+
signal.paths.push(child)
|
221
|
+
signal.cache(child).reset(signal)
|
222
|
+
end
|
223
|
+
end
|
224
|
+
|
225
|
+
reset_followings(signal)
|
226
|
+
|
227
|
+
rescue Exception => e
|
228
|
+
puts "#{self.name_path} [#{e.class}] #{e.message}"
|
229
|
+
raise
|
230
|
+
end
|
231
|
+
available :reset, :on => [:initialized, :success, :error, :stuck]
|
232
|
+
|
233
|
+
end
|
234
|
+
|
235
|
+
|
236
|
+
|
237
|
+
|
238
|
+
# ジョブネットの始端を表すVertex。特に状態は持たない。
|
239
|
+
class Tengine::Job::Runtime::Start < Tengine::Job::Runtime::Vertex
|
240
|
+
# https://cacoo.com/diagrams/hdLgrzYsTBBpV3Wj#D26C1
|
241
|
+
def transmit(signal)
|
242
|
+
activate(signal)
|
243
|
+
end
|
244
|
+
|
245
|
+
def activate(signal)
|
246
|
+
signal.leave(self)
|
247
|
+
end
|
248
|
+
|
249
|
+
def reset(signal)
|
250
|
+
# signal.leave(self, :reset)
|
251
|
+
end
|
252
|
+
end
|
253
|
+
|
254
|
+
|
255
|
+
# ジョブネットの終端を表すVertex。特に状態は持たない。
|
256
|
+
class Tengine::Job::Runtime::End < Tengine::Job::Runtime::Vertex
|
257
|
+
# https://cacoo.com/diagrams/hdLgrzYsTBBpV3Wj#D26C1
|
258
|
+
def transmit(signal)
|
259
|
+
activate(signal)
|
260
|
+
end
|
261
|
+
|
262
|
+
def activate(signal)
|
263
|
+
complete_origin_edge(signal, :except_closed => true)
|
264
|
+
parent = signal.cache(self.parent) # Endのparentであるジョブネット
|
265
|
+
parent_finally = parent.finally_vertex
|
266
|
+
if parent_finally && (parent.phase_key != :dying)
|
267
|
+
parent_finally.transmit(signal)
|
268
|
+
else
|
269
|
+
parent.finish(signal) unless parent.phase_key == :stuck
|
270
|
+
end
|
271
|
+
end
|
272
|
+
|
273
|
+
def reset(signal)
|
274
|
+
Tengine.logger.info("#{__FILE__}##{__LINE__} #{self.class}#reset")
|
275
|
+
|
276
|
+
parent = signal.cache(self.parent) # Endのparentであるジョブネット
|
277
|
+
if signal.execution.in_scope?(parent)
|
278
|
+
if f = parent.finally_vertex
|
279
|
+
f.reset(signal)
|
280
|
+
end
|
281
|
+
end
|
282
|
+
end
|
283
|
+
end
|