rbbt-util 5.23.14 → 5.23.15
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/lib/rbbt/persist.rb +6 -4
- data/lib/rbbt/util/cmd.rb +2 -2
- data/lib/rbbt/util/concurrency/processes.rb +23 -15
- data/lib/rbbt/util/concurrency/processes/socket.rb +2 -2
- data/lib/rbbt/util/concurrency/processes/worker.rb +6 -2
- data/lib/rbbt/util/log.rb +3 -3
- data/lib/rbbt/util/log/progress.rb +4 -4
- data/lib/rbbt/util/log/progress/report.rb +79 -21
- data/lib/rbbt/util/misc/pipes.rb +2 -2
- data/lib/rbbt/util/open.rb +9 -1
- data/lib/rbbt/workflow/accessor.rb +15 -2
- data/lib/rbbt/workflow/step.rb +9 -0
- data/lib/rbbt/workflow/step/dependencies.rb +24 -17
- data/lib/rbbt/workflow/step/run.rb +182 -174
- data/test/rbbt/test_workflow.rb +6 -2
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c7019b7a1b634937fbf75f958245dca674834f76
|
4
|
+
data.tar.gz: 7541763d2c390591b153b3d95c37277d63bdf6ea
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 86ac8dd6da1e81e8c9a801cda18728c736b91c4540c9a8d9b60b7c0e83885f3d501d290e19afea81392c0ebf57c96d50afb89f21ffc239ca47ba8cee3817d10f
|
7
|
+
data.tar.gz: 81afe72fbfd01c65c67722d71d58b0444a9e78ba178149a2ebc9a3f708fdc6e46937e751d6995ef499b248adb4a9355ad6fac4b1d4dfccd7fa6ab14fd89fd56a
|
data/lib/rbbt/persist.rb
CHANGED
@@ -197,6 +197,9 @@ module Persist
|
|
197
197
|
def self.tee_stream_thread(stream, path, type, callback = nil, abort_callback = nil, lockfile = nil)
|
198
198
|
file, out = Misc.tee_stream(stream)
|
199
199
|
|
200
|
+
out.pair = file
|
201
|
+
file.pair = out
|
202
|
+
|
200
203
|
saver_thread = Thread.new do
|
201
204
|
begin
|
202
205
|
file.threads = []
|
@@ -220,13 +223,12 @@ module Persist
|
|
220
223
|
threads += stream.threads if stream.respond_to?(:threads) && stream.threads
|
221
224
|
ConcurrentStream.setup(out, :threads => threads, :filename => path)
|
222
225
|
|
223
|
-
out.callback = callback
|
226
|
+
#out.callback = callback
|
224
227
|
out.abort_callback = abort_callback
|
225
228
|
out.lockfile = stream.lockfile if stream.respond_to? :lockfile and stream.lockfile
|
226
229
|
|
227
|
-
stream.callback = callback
|
228
|
-
stream.abort_callback = abort_callback
|
229
|
-
|
230
|
+
#stream.callback = callback
|
231
|
+
#stream.abort_callback = abort_callback
|
230
232
|
|
231
233
|
out
|
232
234
|
end
|
data/lib/rbbt/util/cmd.rb
CHANGED
@@ -138,7 +138,7 @@ module CMD
|
|
138
138
|
if pipe
|
139
139
|
err_thread = Thread.new do
|
140
140
|
while line = serr.gets
|
141
|
-
Log.log "STDERR
|
141
|
+
Log.log "STDERR [#{pid}]: " + line, stderr if Integer === stderr and log
|
142
142
|
end
|
143
143
|
serr.close
|
144
144
|
end
|
@@ -183,7 +183,7 @@ module CMD
|
|
183
183
|
pid = io.pids.first
|
184
184
|
while line = io.gets
|
185
185
|
if pid
|
186
|
-
Log.debug "STDOUT
|
186
|
+
Log.debug "STDOUT [#{pid}]: " + line
|
187
187
|
else
|
188
188
|
Log.debug "STDOUT: " + line
|
189
189
|
end
|
@@ -158,12 +158,17 @@ class RbbtProcessQueue
|
|
158
158
|
begin
|
159
159
|
while processes.any?
|
160
160
|
raise Aborted if @abort_monitor
|
161
|
-
processes[0].join
|
162
|
-
Log.debug "Joined process #{processes[0].pid} of queue #{Process.pid}"
|
163
|
-
processes.shift
|
161
|
+
#processes[0].join
|
162
|
+
#Log.debug "Joined process #{processes[0].pid} of queue #{Process.pid}"
|
163
|
+
#processes.shift
|
164
|
+
pid, status = Process.wait2
|
165
|
+
Log.debug "Joined process #{pid} of queue #{Process.pid} (status: #{status})"
|
166
|
+
processes.reject!{|p| p.pid == pid}
|
167
|
+
raise ProcessFailed.new pid if not status.success?
|
164
168
|
end
|
165
169
|
Log.low "All processes completed #{Process.pid}"
|
166
170
|
rescue Aborted
|
171
|
+
Log.exception $!
|
167
172
|
Log.low "Aborting process monitor #{Process.pid}"
|
168
173
|
processes.each{|p| p.abort_and_join}
|
169
174
|
Log.low "Processes aborted #{Process.pid}"
|
@@ -250,25 +255,28 @@ class RbbtProcessQueue
|
|
250
255
|
begin
|
251
256
|
pid, @status = Process.waitpid2 @master_pid unless @status
|
252
257
|
error = true unless @status.success?
|
253
|
-
|
254
|
-
|
258
|
+
begin
|
259
|
+
@callback_thread.join if @callback_thread
|
260
|
+
raise ProcessFailed.new @master_pid unless @status.success?
|
261
|
+
rescue
|
262
|
+
exception = $!
|
263
|
+
raise $!
|
264
|
+
end
|
255
265
|
error = false
|
256
266
|
rescue Aborted, Interrupt
|
257
267
|
exception = $!
|
258
268
|
Log.exception $!
|
259
269
|
error = true
|
260
|
-
|
261
|
-
|
262
|
-
|
270
|
+
if @aborted
|
271
|
+
raise $!
|
272
|
+
else
|
273
|
+
self.abort
|
274
|
+
Log.high "Process queue #{@master_pid} aborted"
|
275
|
+
retry
|
276
|
+
end
|
263
277
|
rescue Errno::ESRCH, Errno::ECHILD
|
264
278
|
retry if Misc.pid_exists? @master_pid
|
265
279
|
error = ! @status.success?
|
266
|
-
rescue ProcessFailed
|
267
|
-
exception = $!
|
268
|
-
rescue Exception
|
269
|
-
exception = $!
|
270
|
-
Log.exception $!
|
271
|
-
retry
|
272
280
|
ensure
|
273
281
|
begin
|
274
282
|
begin
|
@@ -346,7 +354,7 @@ class RbbtProcessQueue
|
|
346
354
|
ensure
|
347
355
|
@queue.clean if @queue
|
348
356
|
#@callback_thread.push ClosedStream if @callback_thread && @callback_thread.alive?
|
349
|
-
@callback_queue.clean if @
|
357
|
+
@callback_queue.clean if @callback_queue
|
350
358
|
end
|
351
359
|
end
|
352
360
|
|
@@ -53,7 +53,6 @@ class RbbtProcessQueue
|
|
53
53
|
break
|
54
54
|
end
|
55
55
|
end
|
56
|
-
Kernel.exit! 0
|
57
56
|
rescue Respawn
|
58
57
|
Kernel.exit! 28
|
59
58
|
rescue ClosedStream
|
@@ -63,6 +62,7 @@ class RbbtProcessQueue
|
|
63
62
|
retry unless @stop
|
64
63
|
Log.high "Worker #{Process.pid} leaving"
|
65
64
|
rescue Exception
|
65
|
+
Log.high "Worker #{Process.pid} had exception: #{$!.message}"
|
66
66
|
begin
|
67
67
|
@callback_queue.push($!) if @callback_queue
|
68
68
|
rescue
|
@@ -74,6 +74,8 @@ class RbbtProcessQueue
|
|
74
74
|
rescue Aborted
|
75
75
|
Log.high "Worker #{Process.pid} aborted"
|
76
76
|
end
|
77
|
+
|
78
|
+
Log.high "Worker #{Process.pid} completed"
|
77
79
|
Kernel.exit! 0
|
78
80
|
end
|
79
81
|
|
@@ -92,7 +94,6 @@ class RbbtProcessQueue
|
|
92
94
|
|
93
95
|
initial = Misc.memory_use(Process.pid)
|
94
96
|
memory_cap = multiplier * initial
|
95
|
-
Log.debug "Worker for #{Process.pid} started with pid #{@current} -- initial: #{initial} - multiplier: #{multiplier} - cap: #{memory_cap}"
|
96
97
|
|
97
98
|
@asked = false
|
98
99
|
@monitored = false
|
@@ -153,6 +154,8 @@ class RbbtProcessQueue
|
|
153
154
|
run
|
154
155
|
end
|
155
156
|
|
157
|
+
Log.debug "Worker for #{Process.pid} started with pid #{@current} -- initial: #{initial} - multiplier: #{multiplier} - cap: #{memory_cap}"
|
158
|
+
|
156
159
|
while true
|
157
160
|
@prev = @current
|
158
161
|
pid, status = Process.waitpid2 @current
|
@@ -178,6 +181,7 @@ class RbbtProcessQueue
|
|
178
181
|
end
|
179
182
|
|
180
183
|
if status
|
184
|
+
Log.high "Worker respawner with #{Process.pid} (#{@current}) completed with status #{status}"
|
181
185
|
Kernel.exit! status.to_i >> 8
|
182
186
|
else
|
183
187
|
Kernel.exit! -1
|
data/lib/rbbt/util/log.rb
CHANGED
@@ -133,12 +133,12 @@ module Log
|
|
133
133
|
end
|
134
134
|
end
|
135
135
|
|
136
|
-
def self.up_lines(num =
|
136
|
+
def self.up_lines(num = 1)
|
137
137
|
"\033[#{num+1}F\033[2K"
|
138
138
|
end
|
139
139
|
|
140
|
-
def self.down_lines(num =
|
141
|
-
"\
|
140
|
+
def self.down_lines(num = 1)
|
141
|
+
"\033[#{num+1}E"
|
142
142
|
end
|
143
143
|
|
144
144
|
def self.return_line
|
@@ -6,12 +6,12 @@ module Log
|
|
6
6
|
|
7
7
|
attr_accessor :max, :ticks, :frequency, :depth, :desc, :file, :bytes
|
8
8
|
def initialize(max = nil, options = {})
|
9
|
-
options = Misc.add_defaults options, :depth => 0, :num_reports => 100, :desc => "Progress", :io => STDERR, :severity => Log.severity
|
10
|
-
depth, num_reports, desc, io, severity, file, bytes = Misc.process_options options, :depth, :num_reports, :desc, :io, :severity, :file, :bytes
|
9
|
+
options = Misc.add_defaults options, :depth => 0, :num_reports => 100, :desc => "Progress", :io => STDERR, :severity => Log.severity, :frequency => 2
|
10
|
+
depth, num_reports, desc, io, severity, file, bytes, frequency = Misc.process_options options, :depth, :num_reports, :desc, :io, :severity, :file, :bytes, :frequency
|
11
11
|
|
12
12
|
@max = max
|
13
13
|
@ticks = 0
|
14
|
-
@frequency =
|
14
|
+
@frequency = frequency
|
15
15
|
@last_time = nil
|
16
16
|
@last_count = nil
|
17
17
|
@last_percent = nil
|
@@ -46,7 +46,7 @@ module Log
|
|
46
46
|
end
|
47
47
|
|
48
48
|
diff = time - @last_time
|
49
|
-
report and return if diff
|
49
|
+
report and return if diff >= @frequency
|
50
50
|
return unless max and max > 0
|
51
51
|
|
52
52
|
percent = self.percent
|
@@ -10,22 +10,73 @@ module Log
|
|
10
10
|
end
|
11
11
|
|
12
12
|
attr_accessor :history, :mean_max, :max_history
|
13
|
-
def thr
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
end
|
13
|
+
#def thr
|
14
|
+
# count = (@ticks - @last_count).to_f
|
15
|
+
# if @last_time.nil?
|
16
|
+
# seconds = 0.001
|
17
|
+
# else
|
18
|
+
# seconds = Time.now - @last_time
|
19
|
+
# end
|
20
|
+
# thr = count / seconds
|
21
|
+
#end
|
22
|
+
|
23
|
+
|
24
|
+
#def thr_msg
|
25
|
+
# thr = self.thr
|
26
|
+
# if @history.nil?
|
27
|
+
# @history ||= [thr]
|
28
|
+
# else
|
29
|
+
# @history << thr
|
30
|
+
# max_history ||= case
|
31
|
+
# when @ticks > 20
|
32
|
+
# count = @ticks - @last_count
|
33
|
+
# if @max
|
34
|
+
# times = @max / count
|
35
|
+
# num = times / 20
|
36
|
+
# num = 2 if num < 2
|
37
|
+
# else
|
38
|
+
# num = 10
|
39
|
+
# end
|
40
|
+
# count * num
|
41
|
+
# else
|
42
|
+
# 20
|
43
|
+
# end
|
44
|
+
# max_history = 30 if max_history > 30
|
45
|
+
# max_history = 100
|
46
|
+
# @history.shift if @history.length > max_history
|
47
|
+
# end
|
48
|
+
|
49
|
+
# @mean_max ||= 0
|
50
|
+
# if @history.length > 3
|
51
|
+
|
52
|
+
# w_mean = 0
|
53
|
+
# total_w = 0
|
54
|
+
# @history.each_with_index do |v,i|
|
55
|
+
# c = i ** 10
|
56
|
+
# w_mean += v * c
|
57
|
+
# total_w += c
|
58
|
+
# end
|
59
|
+
# mean = @mean = w_mean.to_f / total_w
|
60
|
+
|
61
|
+
# @mean_max = mean if mean > @mean_max
|
62
|
+
# end
|
63
|
+
|
64
|
+
# if mean.nil? or mean.to_i > 1
|
65
|
+
# str = "#{ Log.color :blue, thr.to_i.to_s } per sec."
|
66
|
+
# str << " #{ Log.color :yellow, mean.to_i.to_s } avg. #{Log.color :yellow, @mean_max.to_i.to_s} max." if @mean_max > 0
|
67
|
+
# else
|
68
|
+
# str = "#{ Log.color :blue, (1/thr).ceil.to_s } secs each"
|
69
|
+
# str << " #{ Log.color :yellow, (1/mean).ceil.to_s } avg. #{Log.color :yellow, (1/@mean_max).ceil.to_s} min." if @mean_max > 0
|
70
|
+
# end
|
71
|
+
|
72
|
+
# str
|
73
|
+
#end
|
22
74
|
|
23
75
|
def thr_msg
|
24
|
-
thr = self.thr
|
25
76
|
if @history.nil?
|
26
|
-
@history ||= [
|
77
|
+
@history ||= [[@ticks, Time.now] ]
|
27
78
|
else
|
28
|
-
@history <<
|
79
|
+
@history << [@ticks, Time.now]
|
29
80
|
max_history ||= case
|
30
81
|
when @ticks > 20
|
31
82
|
count = @ticks - @last_count
|
@@ -47,18 +98,23 @@ module Log
|
|
47
98
|
@mean_max ||= 0
|
48
99
|
if @history.length > 3
|
49
100
|
|
50
|
-
|
51
|
-
|
52
|
-
@history.
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
end
|
57
|
-
mean = @mean = w_mean.to_f / total_w
|
101
|
+
sticks, stime = @history.first
|
102
|
+
ssticks, sstime = @history[3]
|
103
|
+
lticks, ltime = @history.last
|
104
|
+
|
105
|
+
mean = @mean = (lticks - sticks).to_f / (ltime - stime)
|
106
|
+
short_mean = (ssticks - sticks).to_f / (sstime - stime)
|
58
107
|
|
59
108
|
@mean_max = mean if mean > @mean_max
|
60
109
|
end
|
61
110
|
|
111
|
+
if short_mean
|
112
|
+
thr = short_mean
|
113
|
+
else
|
114
|
+
thr = @ticks / (Time.now - @start)
|
115
|
+
end
|
116
|
+
iii [short_mean, thr]
|
117
|
+
|
62
118
|
if mean.nil? or mean.to_i > 1
|
63
119
|
str = "#{ Log.color :blue, thr.to_i.to_s } per sec."
|
64
120
|
str << " #{ Log.color :yellow, mean.to_i.to_s } avg. #{Log.color :yellow, @mean_max.to_i.to_s} max." if @mean_max > 0
|
@@ -70,6 +126,7 @@ module Log
|
|
70
126
|
str
|
71
127
|
end
|
72
128
|
|
129
|
+
|
73
130
|
def eta_msg
|
74
131
|
percent = self.percent
|
75
132
|
time = Time.now
|
@@ -140,8 +197,9 @@ module Log
|
|
140
197
|
else
|
141
198
|
bars = BARS
|
142
199
|
end
|
200
|
+
bars << self unless BARS.include? self
|
143
201
|
|
144
|
-
print(io, Log.up_lines(bars.length) << Log.color(:yellow, "...Progress\n") << Log.down_lines(bars.length))
|
202
|
+
print(io, Log.up_lines(bars.length+1) << Log.color(:yellow, "...Progress\n") << Log.down_lines(bars.length+1))
|
145
203
|
print(io, Log.up_lines(@depth) << report_msg << Log.down_lines(@depth))
|
146
204
|
@last_time = Time.now
|
147
205
|
@last_count = ticks
|
data/lib/rbbt/util/misc/pipes.rb
CHANGED
@@ -203,7 +203,7 @@ module Misc
|
|
203
203
|
end
|
204
204
|
|
205
205
|
stream.close unless stream.closed?
|
206
|
-
stream.join if stream.respond_to? :join
|
206
|
+
#stream.join if stream.respond_to? :join
|
207
207
|
in_pipes.first.close
|
208
208
|
#Log.medium "Tee done #{Misc.fingerprint stream}"
|
209
209
|
rescue Aborted, Interrupt
|
@@ -608,7 +608,7 @@ module Misc
|
|
608
608
|
end
|
609
609
|
|
610
610
|
def self.save_stream(file, stream)
|
611
|
-
|
611
|
+
save, out = Misc.tee_stream stream
|
612
612
|
out.filename = file
|
613
613
|
save.filename = file
|
614
614
|
|
data/lib/rbbt/util/open.rb
CHANGED
@@ -192,7 +192,7 @@ module Open
|
|
192
192
|
if (dir_sub_path = find_repo_dir(file))
|
193
193
|
remove_from_repo(*dir_sub_path)
|
194
194
|
else
|
195
|
-
FileUtils.rm(file)
|
195
|
+
FileUtils.rm(file) if File.exists?(file)
|
196
196
|
end
|
197
197
|
end
|
198
198
|
|
@@ -563,4 +563,12 @@ module Open
|
|
563
563
|
end
|
564
564
|
notify_write(file)
|
565
565
|
end
|
566
|
+
|
567
|
+
def self.writable?(path)
|
568
|
+
if File.exists?(path)
|
569
|
+
File.writable?(path)
|
570
|
+
else
|
571
|
+
File.writable?(File.dirname(File.expand_path(path)))
|
572
|
+
end
|
573
|
+
end
|
566
574
|
end
|
@@ -111,6 +111,14 @@ class Step
|
|
111
111
|
@info_lock
|
112
112
|
end
|
113
113
|
|
114
|
+
def status_lock
|
115
|
+
@status_lock = begin
|
116
|
+
path = Persist.persistence_path(info_file + '.status.lock', {:dir => Step.lock_dir})
|
117
|
+
Lockfile.new path, :refresh => false, :dont_use_lock_id => true
|
118
|
+
end if @status_lock.nil?
|
119
|
+
@status_lock
|
120
|
+
end
|
121
|
+
|
114
122
|
def info(check_lock = true)
|
115
123
|
return {:status => :noinfo} if info_file.nil? or not Open.exists? info_file
|
116
124
|
begin
|
@@ -163,7 +171,7 @@ class Step
|
|
163
171
|
|
164
172
|
def set_info(key, value)
|
165
173
|
return nil if @exec or info_file.nil?
|
166
|
-
return nil if !
|
174
|
+
return nil if ! writable?
|
167
175
|
value = Annotated.purge value if defined? Annotated
|
168
176
|
Open.lock(info_file, :lock => info_lock) do
|
169
177
|
i = info(false).dup
|
@@ -435,7 +443,7 @@ class Step
|
|
435
443
|
end
|
436
444
|
|
437
445
|
def stalled?
|
438
|
-
started? && ! (done? || error? || aborted?
|
446
|
+
started? && ! (done? || error? || aborted? || running? || waiting?)
|
439
447
|
end
|
440
448
|
|
441
449
|
def missing?
|
@@ -626,6 +634,11 @@ class Step
|
|
626
634
|
out
|
627
635
|
end
|
628
636
|
end
|
637
|
+
|
638
|
+
def relocated?
|
639
|
+
done? && info[:path] && info[:path] != path
|
640
|
+
end
|
641
|
+
|
629
642
|
end
|
630
643
|
|
631
644
|
module Workflow
|
data/lib/rbbt/workflow/step.rb
CHANGED
@@ -273,6 +273,11 @@ class Step
|
|
273
273
|
pid_file = Step.pid_file path
|
274
274
|
files_dir = Step.files_dir path
|
275
275
|
|
276
|
+
if ! (Open.writable?(path) && Open.writable?(info_file))
|
277
|
+
Log.warn "Could not clean #{path}: not writable"
|
278
|
+
return
|
279
|
+
end
|
280
|
+
|
276
281
|
if Open.exists?(path) or Open.exists?(pid_file) or Open.exists?(info_file) or Open.exists?(files_dir)
|
277
282
|
|
278
283
|
@result = nil
|
@@ -324,6 +329,10 @@ class Step
|
|
324
329
|
new_dependencies.uniq
|
325
330
|
end
|
326
331
|
|
332
|
+
def writable?
|
333
|
+
Open.writable?(self.path) && Open.writable?(self.info_file)
|
334
|
+
end
|
335
|
+
|
327
336
|
def recursive_clean
|
328
337
|
dependencies.each do |step|
|
329
338
|
step.recursive_clean
|
@@ -80,18 +80,23 @@ class Step
|
|
80
80
|
return if status == 'streaming' and job.running?
|
81
81
|
end
|
82
82
|
|
83
|
-
|
84
|
-
job.
|
85
|
-
(job.done? && ! job.updated?) || (job.error? && ! job.updated?) ||
|
86
|
-
(job.done? && job.dirty?) || (job.error? && job.dirty?) ||
|
87
|
-
(!(job.noinfo? || job.done? || job.error? || job.aborted? || job.running?))
|
83
|
+
job.status_lock.synchronize do
|
84
|
+
status = job.status.to_s
|
88
85
|
|
89
|
-
job.
|
90
|
-
|
86
|
+
if (status == 'error' && (job.recoverable_error? || job.dirty?)) ||
|
87
|
+
job.aborted? ||
|
88
|
+
(job.done? && ! job.updated?) || (job.error? && ! job.updated?) ||
|
89
|
+
(job.done? && job.dirty?) || (job.error? && job.dirty?) ||
|
90
|
+
(!(job.noinfo? || job.done? || job.error? || job.aborted? || job.running?))
|
91
|
+
|
92
|
+
job.clean
|
93
|
+
end
|
94
|
+
|
95
|
+
(job.init_info and job.dup_inputs) unless status == 'done' or job.started?
|
91
96
|
|
92
|
-
|
97
|
+
canfail = ComputeDependency === job && job.canfail?
|
98
|
+
end
|
93
99
|
|
94
|
-
canfail = ComputeDependency === job && job.canfail?
|
95
100
|
raise DependencyError, job if job.error? and not canfail
|
96
101
|
end
|
97
102
|
|
@@ -125,16 +130,18 @@ class Step
|
|
125
130
|
return
|
126
131
|
end
|
127
132
|
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
133
|
+
dependency.status_lock.synchronize do
|
134
|
+
if dependency.aborted? or (dependency.error? and dependency.recoverable_error?) or (!Open.remote?(dependency.path) && dependency.missing?)
|
135
|
+
Log.warn "Cleaning dep. on exec #{Log.color :blue, dependency.path} (missing: #{dependency.missing?}; error #{dependency.error?})"
|
136
|
+
dependency.clean
|
137
|
+
raise TryAgain
|
138
|
+
end
|
132
139
|
end
|
133
140
|
|
134
141
|
if ! dependency.started? && ! dependency.error?
|
135
142
|
log_dependency_exec(dependency, :starting)
|
136
143
|
dependency.run(true)
|
137
|
-
|
144
|
+
raise TryAgain
|
138
145
|
end
|
139
146
|
|
140
147
|
dependency.grace
|
@@ -158,7 +165,7 @@ class Step
|
|
158
165
|
end
|
159
166
|
|
160
167
|
rescue TryAgain
|
161
|
-
Log.low "Retrying dep. #{Log.color :yellow, dependency.task_name.to_s} -- [#{dependency.status}]
|
168
|
+
Log.low "Retrying dep. #{Log.color :yellow, dependency.task_name.to_s} -- [#{dependency.status}] #{(dependency.messages || ["No message"]).last}"
|
162
169
|
retry
|
163
170
|
rescue Aborted, Interrupt
|
164
171
|
Log.error "Aborted dep. #{Log.color :red, dependency.task_name.to_s}"
|
@@ -417,8 +424,8 @@ class Step
|
|
417
424
|
dangling_deps = all_deps.reject{|dep| dep.done? || canfail_paths.include?(dep.path) }.
|
418
425
|
select{|dep| dep.waiting? }
|
419
426
|
|
420
|
-
Log.medium "Aborting waiting dangling dependencies #{Misc.fingerprint dangling_deps}" if dangling_deps.any?
|
421
|
-
dangling_deps.each{|dep| dep.abort }
|
427
|
+
Log.medium "Aborting (actually not) waiting dangling dependencies #{Misc.fingerprint dangling_deps}" if dangling_deps.any?
|
428
|
+
#dangling_deps.each{|dep| dep.abort }
|
422
429
|
|
423
430
|
end
|
424
431
|
|
@@ -108,7 +108,7 @@ class Step
|
|
108
108
|
rec_dependencies.
|
109
109
|
select{|dependency| ! (defined? WorkflowRESTClient and WorkflowRESTClient::RemoteStep === dependency) }.
|
110
110
|
select{|dependency| ! Open.remote?(dependency.path) }.
|
111
|
-
select{|dependency| Open.exists?(dependency.info_file) }.
|
111
|
+
select{|dependency| Open.exists?(dependency.info_file) && ! dependency.relocated? }.
|
112
112
|
select{|dependency| ! dependency.error? }
|
113
113
|
end
|
114
114
|
|
@@ -120,7 +120,6 @@ class Step
|
|
120
120
|
(dependency_checks + input_checks).uniq
|
121
121
|
end
|
122
122
|
|
123
|
-
|
124
123
|
def out_of_date
|
125
124
|
|
126
125
|
checks = self.checks
|
@@ -130,6 +129,7 @@ class Step
|
|
130
129
|
canfail_paths = self.canfail_paths
|
131
130
|
checks.each do |dep|
|
132
131
|
next unless Open.exists?(dep.info_file)
|
132
|
+
next if dep.relocated?
|
133
133
|
|
134
134
|
begin
|
135
135
|
if dep.done? && self.done? && Open.exists?(dep.path) && Open.exists?(self.path) && (File.mtime(dep.path) > File.mtime(self.path))
|
@@ -150,7 +150,7 @@ class Step
|
|
150
150
|
end
|
151
151
|
|
152
152
|
def updated?
|
153
|
-
return true unless (done? || error?)
|
153
|
+
return true unless (done? || error? || ! writable?)
|
154
154
|
|
155
155
|
@updated ||= out_of_date.empty?
|
156
156
|
end
|
@@ -182,201 +182,203 @@ class Step
|
|
182
182
|
res = @mutex.synchronize do
|
183
183
|
no_load = :stream if no_load
|
184
184
|
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
Open.write(pid_file, Process.pid.to_s) unless Open.exists? pid_file
|
192
|
-
|
193
|
-
@exec = false
|
194
|
-
init_info
|
185
|
+
self.status_lock.synchronize do
|
186
|
+
Open.write(pid_file, Process.pid.to_s) unless Open.exists?(path) or Open.exists?(pid_file)
|
187
|
+
result = Persist.persist "Job", @task.result_type, :file => path, :check => checks.collect{|dep| dep.path}, :no_load => no_load do
|
188
|
+
if Step === Step.log_relay_step and not self == Step.log_relay_step
|
189
|
+
relay_log(Step.log_relay_step) unless self.respond_to? :relay_step and self.relay_step
|
190
|
+
end
|
195
191
|
|
196
|
-
|
192
|
+
Open.write(pid_file, Process.pid.to_s) unless Open.exists? pid_file
|
197
193
|
|
198
|
-
|
199
|
-
|
200
|
-
:name => name,
|
201
|
-
:clean_name => clean_name,
|
202
|
-
:workflow => (@workflow || @task.workflow).to_s,
|
203
|
-
:task_name => @task.name,
|
204
|
-
:result_type => @task.result_type,
|
205
|
-
:result_description => @task.result_description,
|
206
|
-
:dependencies => dependencies.collect{|dep| [dep.task_name, dep.name, dep.path]}
|
207
|
-
})
|
194
|
+
@exec = false
|
195
|
+
init_info
|
208
196
|
|
197
|
+
log :setup, "#{Log.color :green, "Setup"} step #{Log.color :yellow, task.name.to_s || ""}"
|
209
198
|
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
199
|
+
merge_info({
|
200
|
+
:issued => (issue_time = Time.now),
|
201
|
+
:name => name,
|
202
|
+
:clean_name => clean_name,
|
203
|
+
:workflow => (@workflow || @task.workflow).to_s,
|
204
|
+
:task_name => @task.name,
|
205
|
+
:result_type => @task.result_type,
|
206
|
+
:result_description => @task.result_description,
|
207
|
+
:dependencies => dependencies.collect{|dep| [dep.task_name, dep.name, dep.path]}
|
208
|
+
})
|
217
209
|
|
218
|
-
new_inputs = []
|
219
|
-
@inputs.each_with_index do |input,i|
|
220
|
-
name = @task.inputs[i]
|
221
|
-
type = @task.input_types[name]
|
222
210
|
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
211
|
+
begin
|
212
|
+
run_dependencies
|
213
|
+
rescue Exception
|
214
|
+
FileUtils.rm pid_file if File.exist?(pid_file)
|
215
|
+
stop_dependencies
|
216
|
+
raise $!
|
217
|
+
end
|
227
218
|
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
219
|
+
new_inputs = []
|
220
|
+
@inputs.each_with_index do |input,i|
|
221
|
+
name = @task.inputs[i]
|
222
|
+
type = @task.input_types[name]
|
223
|
+
|
224
|
+
if type == :directory
|
225
|
+
directory_inputs = file('directory_inputs')
|
226
|
+
input_source = directory_inputs['.source'][name].find
|
227
|
+
input_dir = directory_inputs[name].find
|
228
|
+
|
229
|
+
case input
|
230
|
+
when Path
|
231
|
+
if input.directory?
|
232
|
+
new_inputs << input
|
233
|
+
else
|
234
|
+
input.open do |io|
|
235
|
+
begin
|
236
|
+
Misc.untar(io, input_source)
|
237
|
+
rescue
|
238
|
+
raise ParameterException, "Error unpackaging tar directory input '#{name}':\n\n#{$!.message}"
|
239
|
+
end
|
238
240
|
end
|
241
|
+
tar_1 = input_source.glob("*")
|
242
|
+
raise ParameterException, "When using tar.gz files for directories, the directory must be the single first level entry" if tar_1.length != 1
|
243
|
+
FileUtils.ln_s Misc.path_relative_to(directory_inputs, tar_1.first), input_dir
|
244
|
+
new_inputs << input_dir
|
245
|
+
end
|
246
|
+
when File, IO, Tempfile
|
247
|
+
begin
|
248
|
+
Misc.untar(Open.gunzip(input), input_source)
|
249
|
+
rescue
|
250
|
+
raise ParameterException, "Error unpackaging tar directory input '#{name}':\n\n#{$!.message}"
|
239
251
|
end
|
240
252
|
tar_1 = input_source.glob("*")
|
241
253
|
raise ParameterException, "When using tar.gz files for directories, the directory must be the single first level entry" if tar_1.length != 1
|
242
|
-
|
254
|
+
FileUtils.ln_s Misc.path_relative_to(directory_inputs, tar_1.first), input_dir
|
243
255
|
new_inputs << input_dir
|
256
|
+
else
|
257
|
+
raise ParameterException, "Format of directory input '#{name}' not understood: #{Misc.fingerprint input}"
|
244
258
|
end
|
245
|
-
when File, IO, Tempfile
|
246
|
-
begin
|
247
|
-
Misc.untar(Open.gunzip(input), input_source)
|
248
|
-
rescue
|
249
|
-
raise ParameterException, "Error unpackaging tar directory input '#{name}':\n\n#{$!.message}"
|
250
|
-
end
|
251
|
-
tar_1 = input_source.glob("*")
|
252
|
-
raise ParameterException, "When using tar.gz files for directories, the directory must be the single first level entry" if tar_1.length != 1
|
253
|
-
FileUtils.ln_s Misc.path_relative_to(directory_inputs, tar_1.first), input_dir
|
254
|
-
new_inputs << input_dir
|
255
259
|
else
|
256
|
-
|
257
|
-
end
|
258
|
-
else
|
259
|
-
new_inputs << input
|
260
|
-
end
|
261
|
-
end if @inputs
|
262
|
-
|
263
|
-
@inputs = new_inputs if @inputs
|
264
|
-
|
265
|
-
if not task.inputs.nil?
|
266
|
-
info_inputs = @inputs.collect do |i|
|
267
|
-
if Path === i
|
268
|
-
i.to_s
|
269
|
-
else
|
270
|
-
i
|
260
|
+
new_inputs << input
|
271
261
|
end
|
272
|
-
end
|
273
|
-
set_info :inputs, Misc.remove_long_items(Misc.zip2hash(task.inputs, info_inputs))
|
274
|
-
end
|
262
|
+
end if @inputs
|
275
263
|
|
276
|
-
|
277
|
-
log :started, "Starting step #{Log.color :yellow, task.name.to_s || ""}"
|
264
|
+
@inputs = new_inputs if @inputs
|
278
265
|
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
backtrace.each{|l| l.force_encoding("UTF-8")} if String.instance_methods.include? :force_encoding
|
290
|
-
set_info :backtrace, backtrace
|
291
|
-
log(:error, "#{$!.class}: #{$!.message}")
|
292
|
-
stop_dependencies
|
293
|
-
raise $!
|
294
|
-
end
|
266
|
+
if not task.inputs.nil?
|
267
|
+
info_inputs = @inputs.collect do |i|
|
268
|
+
if Path === i
|
269
|
+
i.to_s
|
270
|
+
else
|
271
|
+
i
|
272
|
+
end
|
273
|
+
end
|
274
|
+
set_info :inputs, Misc.remove_long_items(Misc.zip2hash(task.inputs, info_inputs))
|
275
|
+
end
|
295
276
|
|
296
|
-
|
297
|
-
|
298
|
-
result = prepare_result result.stream, @task.description, info if TSV::Dumper === result
|
299
|
-
end
|
277
|
+
set_info :started, (start_time = Time.now)
|
278
|
+
log :started, "Starting step #{Log.color :yellow, task.name.to_s || ""}"
|
300
279
|
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
280
|
+
begin
|
281
|
+
result = _exec
|
282
|
+
rescue Aborted, Interrupt
|
283
|
+
log(:aborted, "Aborted")
|
284
|
+
raise $!
|
285
|
+
rescue Exception
|
286
|
+
backtrace = $!.backtrace
|
287
|
+
|
288
|
+
# HACK: This fixes an strange behaviour in 1.9.3 where some
|
289
|
+
# backtrace strings are coded in ASCII-8BIT
|
290
|
+
backtrace.each{|l| l.force_encoding("UTF-8")} if String.instance_methods.include? :force_encoding
|
291
|
+
set_info :backtrace, backtrace
|
292
|
+
log(:error, "#{$!.class}: #{$!.message}")
|
293
|
+
stop_dependencies
|
294
|
+
raise $!
|
295
|
+
end
|
307
296
|
|
308
|
-
|
309
|
-
|
297
|
+
if not no_load or ENV["RBBT_NO_STREAM"] == "true"
|
298
|
+
result = prepare_result result, @task.description, info if IO === result
|
299
|
+
result = prepare_result result.stream, @task.description, info if TSV::Dumper === result
|
300
|
+
end
|
310
301
|
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
302
|
+
stream = case result
|
303
|
+
when IO
|
304
|
+
result
|
305
|
+
when TSV::Dumper
|
306
|
+
result.stream
|
307
|
+
end
|
308
|
+
|
309
|
+
if stream
|
310
|
+
log :streaming, "Streaming step #{Log.color :yellow, task.name.to_s || ""}"
|
311
|
+
|
312
|
+
callback = Proc.new do
|
313
|
+
if AbortedStream === stream
|
314
|
+
if stream.exception
|
315
|
+
raise stream.exception
|
316
|
+
else
|
317
|
+
raise Aborted
|
318
|
+
end
|
317
319
|
end
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
320
|
+
begin
|
321
|
+
status = self.status
|
322
|
+
if status != :done and status != :error and status != :aborted
|
323
|
+
Misc.insist do
|
324
|
+
merge_info({
|
325
|
+
:done => (done_time = Time.now),
|
326
|
+
:total_time_elapsed => (total_time_elapsed = done_time - issue_time),
|
327
|
+
:time_elapsed => (time_elapsed = done_time - start_time)
|
328
|
+
})
|
329
|
+
log :done, "Completed step #{Log.color :yellow, task.name.to_s || ""} in #{time_elapsed.to_i}+#{(total_time_elapsed - time_elapsed).to_i} sec."
|
330
|
+
end
|
329
331
|
end
|
332
|
+
rescue
|
333
|
+
Log.exception $!
|
334
|
+
ensure
|
335
|
+
Step.purge_stream_cache
|
336
|
+
set_info :pid, nil
|
337
|
+
FileUtils.rm pid_file if File.exist?(pid_file)
|
330
338
|
end
|
331
|
-
rescue
|
332
|
-
Log.exception $!
|
333
|
-
ensure
|
334
|
-
Step.purge_stream_cache
|
335
|
-
set_info :pid, nil
|
336
|
-
FileUtils.rm pid_file if File.exist?(pid_file)
|
337
339
|
end
|
338
|
-
end
|
339
340
|
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
341
|
+
abort_callback = Proc.new do |exception|
|
342
|
+
begin
|
343
|
+
if exception
|
344
|
+
self.exception exception
|
345
|
+
else
|
346
|
+
log :aborted, "#{Log.color :red, "Aborted"} step #{Log.color :yellow, task.name.to_s || ""}" if status == :streaming
|
347
|
+
end
|
348
|
+
_clean_finished
|
349
|
+
rescue
|
350
|
+
stop_dependencies
|
351
|
+
set_info :pid, nil
|
352
|
+
FileUtils.rm pid_file if File.exist?(pid_file)
|
346
353
|
end
|
347
|
-
_clean_finished
|
348
|
-
rescue
|
349
|
-
stop_dependencies
|
350
|
-
set_info :pid, nil
|
351
|
-
FileUtils.rm pid_file if File.exist?(pid_file)
|
352
354
|
end
|
353
|
-
end
|
354
355
|
|
355
|
-
|
356
|
+
ConcurrentStream.setup stream, :callback => callback, :abort_callback => abort_callback
|
356
357
|
|
357
|
-
|
358
|
-
|
359
|
-
|
360
|
-
|
361
|
-
|
358
|
+
if AbortedStream === stream
|
359
|
+
exception = stream.exception || Aborted.new("Aborted stream: #{Misc.fingerprint stream}")
|
360
|
+
self.exception exception
|
361
|
+
_clean_finished
|
362
|
+
raise exception
|
363
|
+
end
|
364
|
+
else
|
365
|
+
merge_info({
|
366
|
+
:done => (done_time = Time.now),
|
367
|
+
:total_time_elapsed => (total_time_elapsed = done_time - issue_time),
|
368
|
+
:time_elapsed => (time_elapsed = done_time - start_time)
|
369
|
+
})
|
370
|
+
log :ending
|
371
|
+
Step.purge_stream_cache
|
372
|
+
FileUtils.rm pid_file if File.exist?(pid_file)
|
362
373
|
end
|
363
|
-
else
|
364
|
-
merge_info({
|
365
|
-
:done => (done_time = Time.now),
|
366
|
-
:total_time_elapsed => (total_time_elapsed = done_time - issue_time),
|
367
|
-
:time_elapsed => (time_elapsed = done_time - start_time)
|
368
|
-
})
|
369
|
-
log :ending
|
370
|
-
Step.purge_stream_cache
|
371
|
-
FileUtils.rm pid_file if File.exist?(pid_file)
|
372
|
-
end
|
373
374
|
|
374
|
-
|
375
|
+
set_info :dependencies, dependencies.collect{|dep| [dep.task_name, dep.name, dep.path]}
|
375
376
|
|
376
|
-
|
377
|
-
|
377
|
+
if result.nil? && File.exists?(self.tmp_path) && ! File.exists?(self.path)
|
378
|
+
FileUtils.mv self.tmp_path, self.path
|
379
|
+
end
|
380
|
+
result
|
378
381
|
end
|
379
|
-
result
|
380
382
|
end
|
381
383
|
|
382
384
|
if no_load
|
@@ -407,16 +409,22 @@ class Step
|
|
407
409
|
def produce(force=false, dofork=false)
|
408
410
|
return self if done? and not dirty?
|
409
411
|
|
410
|
-
|
411
|
-
|
412
|
-
if
|
413
|
-
|
414
|
-
|
415
|
-
|
416
|
-
|
417
|
-
|
412
|
+
|
413
|
+
self.status_lock.synchronize do
|
414
|
+
if error? or aborted? or stalled?
|
415
|
+
if stalled?
|
416
|
+
Log.warn "Aborting stalled job #{self.path}"
|
417
|
+
abort
|
418
|
+
end
|
419
|
+
if force or aborted? or recoverable_error?
|
420
|
+
clean
|
418
421
|
else
|
419
|
-
|
422
|
+
e = get_exception
|
423
|
+
if e
|
424
|
+
raise e
|
425
|
+
else
|
426
|
+
raise "Error in job: #{self.path}"
|
427
|
+
end
|
420
428
|
end
|
421
429
|
end
|
422
430
|
end
|
@@ -662,7 +670,7 @@ class Step
|
|
662
670
|
begin
|
663
671
|
set_info :joined, true
|
664
672
|
rescue
|
665
|
-
end if File.exists?(info_file) &&
|
673
|
+
end if File.exists?(info_file) && writable?
|
666
674
|
@result = nil
|
667
675
|
end
|
668
676
|
end
|
data/test/rbbt/test_workflow.rb
CHANGED
@@ -146,18 +146,22 @@ class TestWorkflow < Test::Unit::TestCase
|
|
146
146
|
end
|
147
147
|
|
148
148
|
def test_update_on_input_dependency_update
|
149
|
+
Log.severity = 0
|
149
150
|
send_input_dep_to_reverse_job = TestWF.job(:send_input_dep_to_reverse, nil, :name => "Miguel")
|
150
151
|
send_input_dep_to_reverse_job.clean
|
151
152
|
send_input_dep_to_reverse_job.run
|
153
|
+
|
152
154
|
input_dep_job = send_input_dep_to_reverse_job.step(:input_dep)
|
155
|
+
mtime_orig = File.mtime send_input_dep_to_reverse_job.step(:reverse_input_text).path
|
156
|
+
|
157
|
+
sleep 1
|
153
158
|
input_dep_job.clean
|
154
159
|
input_dep_job.run
|
155
160
|
send_input_dep_to_reverse_job = TestWF.job(:send_input_dep_to_reverse, nil, :name => "Miguel")
|
156
|
-
|
161
|
+
|
157
162
|
send_input_dep_to_reverse_job.run
|
158
163
|
mtime_new = File.mtime send_input_dep_to_reverse_job.step(:reverse_input_text).path
|
159
164
|
assert mtime_orig < mtime_new
|
160
|
-
|
161
165
|
end
|
162
166
|
|
163
167
|
def test_helper
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rbbt-util
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 5.23.
|
4
|
+
version: 5.23.15
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Miguel Vazquez
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2018-08-
|
11
|
+
date: 2018-08-14 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rake
|