fasten 0.2.0 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/Gemfile.lock +1 -1
- data/lib/fasten.rb +4 -16
- data/lib/fasten/dag.rb +15 -7
- data/lib/fasten/executor.rb +13 -6
- data/lib/fasten/load_save.rb +69 -0
- data/lib/fasten/log_support.rb +1 -0
- data/lib/fasten/stats.rb +39 -0
- data/lib/fasten/ui.rb +4 -3
- data/lib/fasten/version.rb +1 -1
- data/lib/fasten/worker.rb +3 -3
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3b5fa7853f4b7c0e9a187bb5f76f59019e7db4defdbc68d059ca71a90ad38b8b
|
4
|
+
data.tar.gz: 91db6845b40c890928b3f217e5d91ab88f127187b95257a4863533db92dc0840
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 184e4868d0cc1b5e24ae365e636e7fb988c656665c05420abce2551024143b53857cf32ebb1e717cadedfba655cae42079d1044aa5ae8f82872478d762be8190
|
7
|
+
data.tar.gz: c7f0c1c4d1a8a23ee97be8916de501eacda6a70b526b387fbabbac130743b6aadcfccba1d626581cf0ebe860006cecb58cb7729cd48a4d8aa9d86633ef9a61ab
|
data/.gitignore
CHANGED
data/Gemfile.lock
CHANGED
data/lib/fasten.rb
CHANGED
@@ -6,11 +6,15 @@ require 'binding_of_caller'
|
|
6
6
|
require 'logger'
|
7
7
|
require 'ostruct'
|
8
8
|
require 'curses'
|
9
|
+
require 'fileutils'
|
10
|
+
require 'csv'
|
9
11
|
|
10
12
|
require 'fasten/log_support'
|
13
|
+
require 'fasten/stats'
|
11
14
|
require 'fasten/task'
|
12
15
|
require 'fasten/ui'
|
13
16
|
require 'fasten/dag'
|
17
|
+
require 'fasten/load_save'
|
14
18
|
require 'fasten/executor'
|
15
19
|
require 'fasten/worker'
|
16
20
|
require 'fasten/version'
|
@@ -26,20 +30,4 @@ module Fasten
|
|
26
30
|
executor
|
27
31
|
end
|
28
32
|
end
|
29
|
-
|
30
|
-
class Executor
|
31
|
-
def load(path)
|
32
|
-
items = YAML.safe_load(File.read(path)).each do |name, params|
|
33
|
-
params.each do |key, val|
|
34
|
-
next unless val.is_a?(String) && (match = %r{^/(.+)/$}.match(val))
|
35
|
-
|
36
|
-
params[key] = Regexp.new(match[1])
|
37
|
-
end
|
38
|
-
|
39
|
-
add Fasten::Task.new({ name: name }.merge(params))
|
40
|
-
end
|
41
|
-
|
42
|
-
log_info "Loaded #{items.count} tasks from #{path}"
|
43
|
-
end
|
44
|
-
end
|
45
33
|
end
|
data/lib/fasten/dag.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
module Fasten
|
2
2
|
module DAG
|
3
|
-
attr_reader :task_list, :task_done_list, :task_error_list, :task_pending_list, :task_running_list
|
3
|
+
attr_reader :task_map, :task_list, :task_done_list, :task_error_list, :task_pending_list, :task_running_list
|
4
4
|
|
5
5
|
def initialize_dag
|
6
6
|
@task_map = {}
|
@@ -20,11 +20,13 @@ module Fasten
|
|
20
20
|
end
|
21
21
|
|
22
22
|
def update_task(task)
|
23
|
-
if task.
|
24
|
-
update_error_task task
|
25
|
-
else
|
23
|
+
if task.state == :DONE
|
26
24
|
update_done_task task
|
25
|
+
else
|
26
|
+
update_error_task task
|
27
27
|
end
|
28
|
+
|
29
|
+
stats_add_entry(self, task.state, task)
|
28
30
|
end
|
29
31
|
|
30
32
|
def update_done_task(task)
|
@@ -43,7 +45,7 @@ module Fasten
|
|
43
45
|
end
|
44
46
|
|
45
47
|
def next_task
|
46
|
-
task_waiting_list.pop
|
48
|
+
task_waiting_list.sort_by!(&:run_score).pop
|
47
49
|
end
|
48
50
|
|
49
51
|
def task_waiting_list
|
@@ -51,6 +53,7 @@ module Fasten
|
|
51
53
|
|
52
54
|
reset_tasks
|
53
55
|
setup_tasks_dependencies
|
56
|
+
setup_tasks_scores
|
54
57
|
move_pending_to_waiting
|
55
58
|
end
|
56
59
|
|
@@ -72,7 +75,7 @@ module Fasten
|
|
72
75
|
@task_list.each do |task|
|
73
76
|
task.dependants = []
|
74
77
|
task.depends = []
|
75
|
-
task.
|
78
|
+
task.run_score = 0
|
76
79
|
task.done ? @task_done_list << task : @task_pending_list << task
|
77
80
|
end
|
78
81
|
end
|
@@ -86,12 +89,17 @@ module Fasten
|
|
86
89
|
raise "Dependency task '#{after}' not found on task '#{task.name}'." unless after_task
|
87
90
|
|
88
91
|
task.depends << after_task
|
89
|
-
task.level += 1
|
90
92
|
after_task.dependants << task
|
91
93
|
end
|
92
94
|
end
|
93
95
|
end
|
94
96
|
|
97
|
+
def setup_tasks_scores
|
98
|
+
@task_pending_list.each do |task|
|
99
|
+
task.run_score += task.dependants.count
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
95
103
|
def no_waiting_tasks?
|
96
104
|
task_waiting_list.empty?
|
97
105
|
end
|
data/lib/fasten/executor.rb
CHANGED
@@ -3,8 +3,11 @@ module Fasten
|
|
3
3
|
include Fasten::LogSupport
|
4
4
|
include Fasten::DAG
|
5
5
|
include Fasten::UI
|
6
|
+
include Fasten::LoadSave
|
7
|
+
include Fasten::Stats
|
6
8
|
|
7
9
|
def initialize(name: nil, workers: 8, worker_class: Fasten::Worker, fasten_dir: '.fasten')
|
10
|
+
setup_stats(name)
|
8
11
|
super name: name || "#{self.class} #{$PID}", workers: workers, pid: $PID, state: :IDLE, worker_class: worker_class, fasten_dir: fasten_dir
|
9
12
|
initialize_dag
|
10
13
|
|
@@ -16,22 +19,26 @@ module Fasten
|
|
16
19
|
end
|
17
20
|
|
18
21
|
def perform
|
19
|
-
log_ini self,
|
22
|
+
log_ini self, running_counters
|
20
23
|
self.state = :RUNNING
|
24
|
+
load_stats
|
21
25
|
|
22
26
|
run_ui do
|
23
27
|
perform_loop
|
24
28
|
end
|
25
29
|
|
26
|
-
self.state = :
|
27
|
-
log_fin self,
|
30
|
+
self.state = task_list.map(&:state).all?(:DONE) ? :DONE : :FAIL
|
31
|
+
log_fin self, running_counters
|
32
|
+
|
33
|
+
stats_add_entry(self, state, self)
|
34
|
+
save_stats
|
28
35
|
end
|
29
36
|
|
30
|
-
def
|
37
|
+
def done_counters
|
31
38
|
"#{task_done_list.count}/#{task_list.count}"
|
32
39
|
end
|
33
40
|
|
34
|
-
def
|
41
|
+
def running_counters
|
35
42
|
"#{task_done_list.count + task_running_list.count}/#{task_list.count}"
|
36
43
|
end
|
37
44
|
|
@@ -68,7 +75,7 @@ module Fasten
|
|
68
75
|
|
69
76
|
update_task task
|
70
77
|
|
71
|
-
log_fin task,
|
78
|
+
log_fin task, done_counters
|
72
79
|
end
|
73
80
|
end
|
74
81
|
|
@@ -0,0 +1,69 @@
|
|
1
|
+
module Fasten
|
2
|
+
module LoadSave
|
3
|
+
attr_reader :stats_path
|
4
|
+
|
5
|
+
def load(path)
|
6
|
+
items = YAML.safe_load(File.read(path)).each do |name, params|
|
7
|
+
params.each do |key, val|
|
8
|
+
next unless val.is_a?(String) && (match = %r{^/(.+)/$}.match(val))
|
9
|
+
|
10
|
+
params[key] = Regexp.new(match[1])
|
11
|
+
end
|
12
|
+
|
13
|
+
add Fasten::Task.new({ name: name }.merge(params))
|
14
|
+
end
|
15
|
+
|
16
|
+
log_info "Loaded #{items.count} tasks from #{path}"
|
17
|
+
end
|
18
|
+
|
19
|
+
def save(path)
|
20
|
+
keys = %i[after shell]
|
21
|
+
|
22
|
+
items = task_list.map do |task|
|
23
|
+
data = task.to_h.select do |key, _val|
|
24
|
+
keys.include? key
|
25
|
+
end
|
26
|
+
|
27
|
+
[task.name, data]
|
28
|
+
end.to_h
|
29
|
+
|
30
|
+
File.write path, items.to_yaml
|
31
|
+
|
32
|
+
log_info "Loaded #{items.count} tasks into #{path}"
|
33
|
+
end
|
34
|
+
|
35
|
+
def load_stats
|
36
|
+
return unless @stats_path && File.exist?(@stats_path)
|
37
|
+
|
38
|
+
self.stats_data = []
|
39
|
+
CSV.foreach(@stats_path, headers: true) do |row|
|
40
|
+
stats_data << row.to_h
|
41
|
+
end
|
42
|
+
|
43
|
+
@task_waiting_list = nil
|
44
|
+
rescue StandardError
|
45
|
+
nil
|
46
|
+
ensure
|
47
|
+
self.stats ||= {}
|
48
|
+
end
|
49
|
+
|
50
|
+
def save_stats
|
51
|
+
return unless @stats_path && stats_data
|
52
|
+
|
53
|
+
CSV.open(@stats_path, 'wb') do |csv|
|
54
|
+
csv << stats_data.first.keys
|
55
|
+
|
56
|
+
stats_data.each do |data|
|
57
|
+
csv << data.values
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def setup_stats(name)
|
63
|
+
@stats_path = "#{ENV['HOME']}/.fasten/stats/#{name}.csv" if ENV['HOME'] && name
|
64
|
+
FileUtils.mkdir_p File.dirname(@stats_path)
|
65
|
+
rescue StandardError
|
66
|
+
@stats_path = nil
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
data/lib/fasten/log_support.rb
CHANGED
data/lib/fasten/stats.rb
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
module Fasten
|
2
|
+
module Stats
|
3
|
+
def stats_create_entry(state, target)
|
4
|
+
{
|
5
|
+
'state' => state.to_s,
|
6
|
+
'kind' => target.is_a?(Fasten::Executor) ? 'executor' : 'task',
|
7
|
+
'name' => target.name,
|
8
|
+
'ini' => target.ini.to_f,
|
9
|
+
'fin' => target.fin.to_f,
|
10
|
+
'run' => target.fin - target.ini
|
11
|
+
}
|
12
|
+
end
|
13
|
+
|
14
|
+
def stats_add_entry(object, state, target)
|
15
|
+
return unless target.ini && target.fin
|
16
|
+
|
17
|
+
entry = stats_create_entry(state, target)
|
18
|
+
object.stats_data ||= []
|
19
|
+
object.stats_data << entry
|
20
|
+
|
21
|
+
history = stats_history(object, entry)
|
22
|
+
|
23
|
+
update_avg(history, entry)
|
24
|
+
update_std(history, entry)
|
25
|
+
end
|
26
|
+
|
27
|
+
def stats_history(object, entry)
|
28
|
+
object.stats_data.select { |e| e['state'] == entry['state'] && e['kind'] == entry['kind'] && e['name'] == entry['name'] }
|
29
|
+
end
|
30
|
+
|
31
|
+
def update_avg(history, entry)
|
32
|
+
entry['avg'] = history.inject(0.0) { |s, x| s + x['run'].to_f } / history.size
|
33
|
+
end
|
34
|
+
|
35
|
+
def update_std(history, entry)
|
36
|
+
entry['std'] = Math.sqrt(history.inject(0.0) { |v, x| v + (x['run'].to_f - entry['avg'])**2 })
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
data/lib/fasten/ui.rb
CHANGED
@@ -108,7 +108,7 @@ module Fasten
|
|
108
108
|
when :RUNNING
|
109
109
|
attrs = color_pair(1) | A_TOP
|
110
110
|
icon = SPINNER_STR[task.worker&.spinner] if icon
|
111
|
-
when :
|
111
|
+
when :FAIL
|
112
112
|
attrs = color_pair(3)
|
113
113
|
icon = '✘︎' if icon
|
114
114
|
when :DONE
|
@@ -143,14 +143,15 @@ module Fasten
|
|
143
143
|
ui_progressbar(2, col_ini, col_fin, count_done, count_total)
|
144
144
|
|
145
145
|
max = 2
|
146
|
-
task_list.
|
146
|
+
list = task_list.sort_by(&:run_score)
|
147
|
+
list.each_with_index do |task, index|
|
147
148
|
next if 3 + index >= ui_rows
|
148
149
|
|
149
150
|
x = ui_task_string(task, 3 + index, 2, icon: true)
|
150
151
|
max = x if x > max
|
151
152
|
end
|
152
153
|
|
153
|
-
|
154
|
+
list.each_with_index do |task, index|
|
154
155
|
next if 3 + index >= ui_rows || task.depends.nil? || task.depends.empty?
|
155
156
|
|
156
157
|
setpos 3 + index, max
|
data/lib/fasten/version.rb
CHANGED
data/lib/fasten/worker.rb
CHANGED
@@ -30,13 +30,13 @@ module Fasten
|
|
30
30
|
end
|
31
31
|
|
32
32
|
def receive
|
33
|
-
updated_task = Marshal.load(parent_read) # rubocop:disable Security/MarshalLoad
|
33
|
+
updated_task = Marshal.load(parent_read) # rubocop:disable Security/MarshalLoad because pipe is a secure channel
|
34
34
|
|
35
35
|
%i[ini fin response error].each { |key| running_task[key] = updated_task[key] }
|
36
36
|
|
37
37
|
task = running_task
|
38
38
|
self.running_task = nil
|
39
|
-
task.state = task.error ? :
|
39
|
+
task.state = task.error ? :FAIL : :DONE
|
40
40
|
|
41
41
|
task
|
42
42
|
end
|
@@ -77,7 +77,7 @@ module Fasten
|
|
77
77
|
def process_incoming_tasks
|
78
78
|
log_ini self, 'process_incoming_tasks'
|
79
79
|
|
80
|
-
while (object = Marshal.load(child_read)) # rubocop:disable Security/MarshalLoad
|
80
|
+
while (object = Marshal.load(child_read)) # rubocop:disable Security/MarshalLoad because pipe is a secure channel
|
81
81
|
run_task(object) if object.is_a? Fasten::Task
|
82
82
|
end
|
83
83
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: fasten
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Aldrin Martoq
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2018-10-
|
11
|
+
date: 2018-10-16 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -133,7 +133,9 @@ files:
|
|
133
133
|
- lib/fasten.rb
|
134
134
|
- lib/fasten/dag.rb
|
135
135
|
- lib/fasten/executor.rb
|
136
|
+
- lib/fasten/load_save.rb
|
136
137
|
- lib/fasten/log_support.rb
|
138
|
+
- lib/fasten/stats.rb
|
137
139
|
- lib/fasten/task.rb
|
138
140
|
- lib/fasten/ui.rb
|
139
141
|
- lib/fasten/version.rb
|