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.
Files changed (70) hide show
  1. checksums.yaml +15 -0
  2. data/Gemfile.lock +78 -48
  3. data/bin/tengine_job +71 -0
  4. data/examples/0004_retry_one_layer.rb +10 -7
  5. data/examples/0027_parallel_ssh_job +9 -0
  6. data/examples/0027_parallel_ssh_jobs.rb +14 -0
  7. data/lib/tengine/job.rb +19 -49
  8. data/lib/tengine/job/dsl.rb +13 -0
  9. data/lib/tengine/job/{dsl_binder.rb → dsl/binder.rb} +4 -4
  10. data/lib/tengine/job/{dsl_evaluator.rb → dsl/evaluator.rb} +2 -2
  11. data/lib/tengine/job/{dsl_loader.rb → dsl/loader.rb} +20 -22
  12. data/lib/tengine/job/runtime.rb +32 -0
  13. data/lib/tengine/job/{drivers → runtime/drivers}/job_control_driver.rb +46 -92
  14. data/lib/tengine/job/{drivers → runtime/drivers}/job_execution_driver.rb +14 -10
  15. data/lib/tengine/job/runtime/drivers/jobnet_control_driver.rb +240 -0
  16. data/lib/tengine/job/{drivers → runtime/drivers}/schedule_driver.rb +4 -4
  17. data/lib/tengine/job/{edge.rb → runtime/edge.rb} +79 -25
  18. data/lib/tengine/job/{executable.rb → runtime/executable.rb} +35 -15
  19. data/lib/tengine/job/{execution.rb → runtime/execution.rb} +19 -11
  20. data/lib/tengine/job/runtime/job_base.rb +5 -0
  21. data/lib/tengine/job/runtime/jobnet.rb +283 -0
  22. data/lib/tengine/job/runtime/junction.rb +44 -0
  23. data/lib/tengine/job/runtime/named_vertex.rb +95 -0
  24. data/lib/tengine/job/runtime/root_jobnet.rb +81 -0
  25. data/lib/tengine/job/{signal.rb → runtime/signal.rb} +99 -13
  26. data/lib/tengine/job/runtime/ssh_job.rb +486 -0
  27. data/lib/tengine/job/{jobnet → runtime}/state_transition.rb +6 -4
  28. data/lib/tengine/job/runtime/stoppable.rb +64 -0
  29. data/lib/tengine/job/runtime/vertex.rb +50 -0
  30. data/lib/tengine/job/structure.rb +20 -0
  31. data/lib/tengine/job/{category.rb → structure/category.rb} +9 -5
  32. data/lib/tengine/job/{jobnet/builder.rb → structure/edge_builder.rb} +11 -7
  33. data/lib/tengine/job/{element_selector_notation.rb → structure/element_selector_notation.rb} +15 -11
  34. data/lib/tengine/job/structure/jobnet_builder.rb +83 -0
  35. data/lib/tengine/job/structure/jobnet_finder.rb +60 -0
  36. data/lib/tengine/job/{name_path.rb → structure/name_path.rb} +2 -2
  37. data/lib/tengine/job/structure/tree.rb +20 -0
  38. data/lib/tengine/job/structure/visitor.rb +67 -0
  39. data/lib/tengine/job/template.rb +24 -0
  40. data/lib/tengine/job/template/edge.rb +37 -0
  41. data/lib/tengine/job/template/expansion.rb +24 -0
  42. data/lib/tengine/job/template/generator.rb +111 -0
  43. data/lib/tengine/job/template/jobnet.rb +83 -0
  44. data/lib/tengine/job/template/junction.rb +14 -0
  45. data/lib/tengine/job/{job.rb → template/named_vertex.rb} +3 -5
  46. data/lib/tengine/job/{root_jobnet_template.rb → template/root_jobnet.rb} +12 -26
  47. data/lib/tengine/job/template/ssh_job.rb +80 -0
  48. data/lib/tengine/job/template/vertex.rb +97 -0
  49. metadata +127 -93
  50. data/lib/tengine/job/connectable.rb +0 -43
  51. data/lib/tengine/job/drivers/jobnet_control_driver.rb +0 -249
  52. data/lib/tengine/job/end.rb +0 -32
  53. data/lib/tengine/job/expansion.rb +0 -37
  54. data/lib/tengine/job/fork.rb +0 -6
  55. data/lib/tengine/job/jobnet.rb +0 -184
  56. data/lib/tengine/job/jobnet/job_state_transition.rb +0 -167
  57. data/lib/tengine/job/jobnet/jobnet_state_transition.rb +0 -110
  58. data/lib/tengine/job/jobnet_actual.rb +0 -84
  59. data/lib/tengine/job/jobnet_template.rb +0 -10
  60. data/lib/tengine/job/join.rb +0 -6
  61. data/lib/tengine/job/junction.rb +0 -29
  62. data/lib/tengine/job/killing.rb +0 -30
  63. data/lib/tengine/job/mm_compatibility.rb +0 -6
  64. data/lib/tengine/job/mm_compatibility/connectable.rb +0 -13
  65. data/lib/tengine/job/root.rb +0 -16
  66. data/lib/tengine/job/root_jobnet_actual.rb +0 -58
  67. data/lib/tengine/job/script_executable.rb +0 -235
  68. data/lib/tengine/job/start.rb +0 -20
  69. data/lib/tengine/job/stoppable.rb +0 -15
  70. 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::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
- d.reset(signal)
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
- accept_visitor(Tengine::Job::Edge::Closer.new)
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
- def accept_visitor(visitor)
123
- visitor.visit(self)
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
- self.write_attribute(:phase_cd, Tengine::Job::Edge.phase_id_by_key(phase_key))
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
- if obj.is_a?(Tengine::Job::End)
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
- obj.close(nil)
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::JobnetActual, Tengine::Job::JobnetTemplateがこのモジュールをincludeします
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 = nil
32
- case self.class
33
- when Tengine::Job::Execution then element_type = "execution"
34
- when Tengine::Job::RootJobnetActual then element_type = "root_jobnet"
35
- when Tengine::Job::JobnetActual then element_type = self.script_executable? ? "job" :
36
- self.jobnet_type_key == :normal ? "jobnet" : self.jobnet_type_name
37
- end
38
- Tengine.logger.debug("#{element_type} phase changed. <#{ self.id.to_s}> #{self.phase_name} -> #{ self.class.phase_name_by_key(phase_key)}")
39
- if is_a?(Tengine::Job::JobnetActual)
40
- children.each{|child| child.phase_key = phase_key if child.respond_to?(:chained_box?) && child.chained_box?}
41
- end
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/jobnet_actual.human_phase_name")
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::RootJobnetActual", :index => true, :inverse_of => :executions
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
- if self.retry
71
- target_actuals.each do |target|
72
- target.reset(signal)
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,5 @@
1
+ require 'tengine/job/runtime'
2
+
3
+ class Tengine::Job::Runtime::JobBase < Tengine::Job::Runtime::NamedVertex
4
+
5
+ end
@@ -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