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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1ca1b131f4ee26ffb7f5dcaa5408e314988c3568d155b9cb804daba84c588f90
4
- data.tar.gz: 6bd08febfd39b5e0ab11c95e6e687acdb6778c67f493469033e49364f47b6012
3
+ metadata.gz: 3b5fa7853f4b7c0e9a187bb5f76f59019e7db4defdbc68d059ca71a90ad38b8b
4
+ data.tar.gz: 91db6845b40c890928b3f217e5d91ab88f127187b95257a4863533db92dc0840
5
5
  SHA512:
6
- metadata.gz: 5fa5a15d9c430cbbe53fee25917c9dcaba4dfb40e70b6b5323d1a46036d0e2a53fdc1d4960226bddf14a76946499fcc8e777b9431596c7c92bfd261093476ed3
7
- data.tar.gz: 4d0596481e975f9c4406f1c46ce707cde8f1f9d11b4089e94a825fe5281b3139e6a9fdf82ac3e905b606ef16a6157019aa04ee3bdd7a3687ebe5310d2303ede3
6
+ metadata.gz: 184e4868d0cc1b5e24ae365e636e7fb988c656665c05420abce2551024143b53857cf32ebb1e717cadedfba655cae42079d1044aa5ae8f82872478d762be8190
7
+ data.tar.gz: c7f0c1c4d1a8a23ee97be8916de501eacda6a70b526b387fbabbac130743b6aadcfccba1d626581cf0ebe860006cecb58cb7729cd48a4d8aa9d86633ef9a61ab
data/.gitignore CHANGED
@@ -10,3 +10,4 @@
10
10
  # rspec failure tracking
11
11
  .rspec_status
12
12
  .fasten
13
+ *.testfile
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- fasten (0.2.0)
4
+ fasten (0.4.0)
5
5
  binding_of_caller
6
6
  curses
7
7
 
@@ -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
@@ -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.error
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.level = 0
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
@@ -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, running_stats
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 = :IDLE
27
- log_fin self, running_stats
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 done_stats
37
+ def done_counters
31
38
  "#{task_done_list.count}/#{task_list.count}"
32
39
  end
33
40
 
34
- def running_stats
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, done_stats
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
@@ -32,6 +32,7 @@ module Fasten
32
32
 
33
33
  FileUtils.mkdir_p File.dirname(path)
34
34
  log = File.new path, 'a'
35
+ log.sync = true
35
36
 
36
37
  $stdout.reopen log
37
38
  $stderr.reopen log
@@ -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
@@ -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 :ERROR
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.each_with_index do |task, index|
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
- task_list.each_with_index do |task, index|
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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Fasten
4
- VERSION = '0.2.0'
4
+ VERSION = '0.4.0'
5
5
  end
@@ -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 ? :ERROR : :DONE
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.2.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-14 00:00:00.000000000 Z
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