fasten 0.2.0 → 0.4.0
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/.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
|