fasten 0.5.4 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
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.5.4
4
+ version: 0.6.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-30 00:00:00.000000000 Z
11
+ date: 2018-11-06 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -16,14 +16,28 @@ dependencies:
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: '1.16'
19
+ version: 1.17.1
20
20
  type: :development
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: '1.16'
26
+ version: 1.17.1
27
+ - !ruby/object:Gem::Dependency
28
+ name: curses
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
27
41
  - !ruby/object:Gem::Dependency
28
42
  name: pry
29
43
  requirement: !ruby/object:Gem::Requirement
@@ -95,7 +109,7 @@ dependencies:
95
109
  - !ruby/object:Gem::Version
96
110
  version: '0'
97
111
  - !ruby/object:Gem::Dependency
98
- name: curses
112
+ name: hirb
99
113
  requirement: !ruby/object:Gem::Requirement
100
114
  requirements:
101
115
  - - ">="
@@ -109,7 +123,7 @@ dependencies:
109
123
  - !ruby/object:Gem::Version
110
124
  version: '0'
111
125
  - !ruby/object:Gem::Dependency
112
- name: hirb
126
+ name: os
113
127
  requirement: !ruby/object:Gem::Requirement
114
128
  requirements:
115
129
  - - ">="
@@ -139,7 +153,8 @@ dependencies:
139
153
  description: Fasten your seatbelts! Run jobs in parallel, intelligently.
140
154
  email:
141
155
  - contacto@a0.cl
142
- executables: []
156
+ executables:
157
+ - fasten
143
158
  extensions: []
144
159
  extra_rdoc_files: []
145
160
  files:
@@ -157,20 +172,25 @@ files:
157
172
  - Rakefile
158
173
  - bin/console
159
174
  - bin/setup
175
+ - exe/fasten
160
176
  - fasten.gemspec
161
177
  - lib/fasten.rb
162
- - lib/fasten/dag.rb
163
- - lib/fasten/executor.rb
164
- - lib/fasten/logger.rb
165
- - lib/fasten/state.rb
166
- - lib/fasten/stats.rb
178
+ - lib/fasten/runner.rb
179
+ - lib/fasten/std_thread_proxy.rb
180
+ - lib/fasten/support/dag.rb
181
+ - lib/fasten/support/fork_worker.rb
182
+ - lib/fasten/support/logger.rb
183
+ - lib/fasten/support/state.rb
184
+ - lib/fasten/support/stats.rb
185
+ - lib/fasten/support/thread_worker.rb
186
+ - lib/fasten/support/ui.rb
187
+ - lib/fasten/support/yaml.rb
167
188
  - lib/fasten/task.rb
168
- - lib/fasten/ui.rb
189
+ - lib/fasten/timeout_queue.rb
169
190
  - lib/fasten/ui/console.rb
170
191
  - lib/fasten/ui/curses.rb
171
192
  - lib/fasten/version.rb
172
193
  - lib/fasten/worker.rb
173
- - lib/fasten/yaml.rb
174
194
  homepage: https://github.com/a0/fasten/
175
195
  licenses:
176
196
  - MIT
@@ -1,137 +0,0 @@
1
- module Fasten
2
- module DAG
3
- attr_reader :task_map, :task_list, :task_done_list, :task_error_list, :task_pending_list, :task_running_list
4
-
5
- def initialize_dag
6
- @task_map = {}
7
- @task_list = []
8
- @task_done_list = []
9
- @task_error_list = []
10
- @task_pending_list = []
11
- @task_running_list = []
12
- end
13
-
14
- def add(task)
15
- raise "Task '#{task.name}' already defined" if @task_map[task.name]
16
-
17
- @task_map[task.name] = task
18
- @task_list << task
19
- @task_waiting_list = nil
20
- end
21
-
22
- def update_task(task)
23
- if task.state == :DONE
24
- update_done_task task
25
- else
26
- update_error_task task
27
- end
28
-
29
- stats_add_entry(task.state, task)
30
- end
31
-
32
- def update_done_task(task)
33
- @task_done_list << task
34
- @task_pending_list.delete task
35
- task.dependants.each do |dependant_task|
36
- dependant_task.depends.delete task
37
- end
38
-
39
- move_pending_to_waiting
40
- end
41
-
42
- def update_error_task(task)
43
- @task_error_list << task
44
- @task_pending_list.delete task
45
- end
46
-
47
- def next_task
48
- task_waiting_list.shift
49
- end
50
-
51
- def task_waiting_list
52
- return @task_waiting_list if @task_waiting_list
53
-
54
- reset_tasks
55
- setup_tasks_dependencies
56
- setup_tasks_scores
57
- move_pending_to_waiting
58
- end
59
-
60
- protected
61
-
62
- def move_pending_to_waiting
63
- move_list = task_pending_list.select do |task|
64
- task.depends.count.zero?
65
- end
66
-
67
- @task_waiting_list ||= []
68
- @task_pending_list -= move_list
69
- @task_waiting_list += move_list
70
- @task_waiting_list.sort_by!.with_index do |x, index|
71
- x.state = :WAIT
72
- [-x.run_score, index]
73
- end
74
- end
75
-
76
- def reset_tasks
77
- @task_pending_list.clear
78
- @task_done_list.clear
79
- @task_error_list.clear
80
-
81
- @task_list.each do |task|
82
- task.dependants = []
83
- task.depends = []
84
- task.run_score = 0
85
-
86
- if task.state == :DONE
87
- @task_done_list << task
88
- elsif task.state == :FAIL
89
- @task_error_list << task
90
- else
91
- task.state = :IDLE
92
- @task_pending_list << task
93
- end
94
- end
95
- end
96
-
97
- def setup_tasks_dependencies
98
- @task_pending_list.each do |task|
99
- next unless task.after
100
-
101
- [task.after].flatten.each do |after|
102
- after_task = after.is_a?(Task) ? after : @task_map[after]
103
- raise "Dependency task '#{after}' not found on task '#{task.name}'." unless after_task
104
-
105
- task.depends << after_task
106
- after_task.dependants << task
107
- end
108
- end
109
- end
110
-
111
- def setup_tasks_scores
112
- @task_pending_list.each do |task|
113
- task.run_score += task.dependants.count
114
- end
115
- end
116
-
117
- def no_waiting_tasks?
118
- task_waiting_list.empty?
119
- end
120
-
121
- def no_running_tasks?
122
- task_running_list.empty?
123
- end
124
-
125
- def tasks_waiting?
126
- !task_waiting_list.empty?
127
- end
128
-
129
- def tasks_running?
130
- !task_running_list.empty?
131
- end
132
-
133
- def tasks_failed?
134
- !task_error_list.empty?
135
- end
136
- end
137
- end
@@ -1,62 +0,0 @@
1
- module Fasten
2
- class << self
3
- attr_accessor :logger
4
- end
5
-
6
- module Logger
7
- attr_accessor :log_file
8
-
9
- %w[debug info error].each do |method|
10
- define_method "log_#{method}" do |msg|
11
- return unless Fasten.logger.respond_to?(method)
12
-
13
- caller_name = name if respond_to? :name
14
- caller_name ||= Kernel.binding.of_caller(1).eval('self').class
15
- Fasten.logger.send(method, caller_name) { msg }
16
- end
17
- end
18
-
19
- def initialize_logger
20
- log_path = "#{fasten_dir}/log/executor/#{name}.log"
21
- FileUtils.mkdir_p File.dirname(log_path)
22
- self.log_file = File.new(log_path, 'a')
23
- Fasten.logger.reopen log_file
24
- end
25
-
26
- def log_ini(object, message = nil)
27
- object.ini ||= Time.new
28
- log_info "Ini #{object.state} #{object.class} #{object} #{message}"
29
- end
30
-
31
- def log_fin(object, message = nil)
32
- object.fin ||= Time.new
33
- object.dif = object.fin - object.ini
34
-
35
- log_info "Fin #{object.state} #{object.class} #{object} #{message} in #{object.dif}"
36
- end
37
-
38
- def redirect_std(path)
39
- @saved_stdout = $stdout.clone
40
- @saved_stderr = $stderr.clone
41
-
42
- FileUtils.mkdir_p File.dirname(path)
43
- log = File.new path, 'a'
44
- log.sync = true
45
-
46
- $stdout.reopen log
47
- $stderr.reopen log
48
- end
49
-
50
- def restore_std
51
- $stdout.reopen(@saved_stdout)
52
- $stderr.reopen(@saved_stderr)
53
- end
54
- end
55
- end
56
-
57
- Fasten.logger ||=
58
- begin
59
- Logger.new(STDOUT, level: Logger::DEBUG, progname: $PROGRAM_NAME)
60
- rescue StandardError
61
- nil
62
- end
@@ -1,30 +0,0 @@
1
- module Fasten
2
- module State
3
- attr_accessor :error, :ini, :fin, :dif, :last
4
- attr_writer :state
5
-
6
- def state
7
- @state || :IDLE
8
- end
9
-
10
- def running?
11
- state == :RUNNING
12
- end
13
-
14
- def idle?
15
- state == :IDLE
16
- end
17
-
18
- def pausing?
19
- state == :PAUSING
20
- end
21
-
22
- def paused?
23
- state == :PAUSED
24
- end
25
-
26
- def quitting?
27
- state == :QUITTING
28
- end
29
- end
30
- end
@@ -1,137 +0,0 @@
1
- module Fasten
2
- module Stats
3
- attr_writer :stats_data, :stats_entries
4
- attr_reader :stats_path
5
-
6
- def initialize_stats
7
- return unless stats
8
-
9
- @stats_path = "#{ENV['HOME']}/.fasten/stats/#{name}.csv" if ENV['HOME']
10
- FileUtils.mkdir_p File.dirname(@stats_path)
11
- rescue StandardError
12
- @stats_path = nil
13
- end
14
-
15
- def load_stats
16
- return unless @stats_path && File.exist?(@stats_path)
17
-
18
- self.stats_data = []
19
- CSV.foreach(@stats_path, headers: true) do |row|
20
- stats_data << row.to_h
21
- end
22
-
23
- @task_waiting_list = nil
24
- rescue StandardError
25
- nil
26
- ensure
27
- self.stats ||= {}
28
- end
29
-
30
- def save_stats
31
- return unless @stats_path && stats_data
32
-
33
- keys = %w[state kind name run cnt avg std err]
34
-
35
- CSV.open(@stats_path, 'wb') do |csv|
36
- csv << keys
37
-
38
- stats_data.each do |data|
39
- csv << keys.map { |i| data[i] }
40
- end
41
- end
42
- end
43
-
44
- def stats_create_entry(state, target)
45
- { 'state' => state.to_s,
46
- 'kind' => stats_kind_for(target),
47
- 'name' => target.name,
48
- 'ini' => target.ini.to_f,
49
- 'fin' => target.fin.to_f,
50
- 'run' => target.fin - target.ini,
51
- 'worker' => target.respond_to?(:worker) ? target.worker.name : nil }
52
- end
53
-
54
- def stats_data
55
- @stats_data ||= []
56
- end
57
-
58
- def stats_entries
59
- @stats_entries ||= []
60
- end
61
-
62
- def stats_kind_for(object)
63
- object.is_a?(Fasten::Executor) ? 'executor' : 'task'
64
- end
65
-
66
- def stats_add_entry(state, target)
67
- return unless target.ini && target.fin
68
-
69
- entry = stats_create_entry(state, target)
70
- stats_data << entry
71
- stats_entries << entry
72
-
73
- history = stats_history(entry)
74
-
75
- update_stats(history, entry)
76
- end
77
-
78
- FLOAT_FORMATTER = ->(f) { format('%7.3f', f) }
79
-
80
- def stats_table_run
81
- sub = stats_entries.select { |x| x['kind'] == 'task' }.map { |x| x['run'] }.sum
82
- tot = stats_entries.select { |x| x['kind'] == 'executor' }.map { |x| x['run'] }.sum
83
-
84
- [sub, tot]
85
- end
86
-
87
- def split_time(time)
88
- sign = time.negative? ? '-' : ''
89
- time = -time if time.negative?
90
-
91
- hours, seconds = time.divmod(3600)
92
- minutes, seconds = seconds.divmod(60)
93
- seconds, decimal = seconds.divmod(1)
94
- milliseconds, _ignored = (decimal.round(4) * 1000).divmod(1)
95
-
96
- [sign, hours, minutes, seconds, milliseconds]
97
- end
98
-
99
- def hformat(time, total = nil)
100
- sign, hours, minutes, seconds, milliseconds = split_time time
101
-
102
- str = hours.zero? ? format('%.1s%02d:%02d.%03d', sign, minutes, seconds, milliseconds) : format('%.1s%02d:%02d:%02d.%03d', sign, hours, minutes, seconds, milliseconds)
103
- str += format(' (%.1f%%)', 100.0 * time / total) if total
104
-
105
- str
106
- end
107
-
108
- def stats_table
109
- sub, tot = stats_table_run
110
-
111
- Hirb::Console.render_output(stats_entries,
112
- fields: %w[state kind name run cnt avg std err worker], unicode: true, class: 'Hirb::Helpers::AutoTable',
113
- filters: { 'run' => FLOAT_FORMATTER, 'avg' => FLOAT_FORMATTER, 'std' => FLOAT_FORMATTER, 'err' => FLOAT_FORMATTER },
114
- description: false)
115
-
116
- puts format('∑tasks: %<task>s ∑executed: %<executed>s saved: %<saved>s workers: %<workers>s',
117
- task: hformat(sub), executed: hformat(tot, sub), saved: hformat(sub - tot, sub), workers: workers.to_s)
118
- end
119
-
120
- def stats_history(entry)
121
- stats_data.select { |e| e['state'] == entry['state'] && e['kind'] == entry['kind'] && e['name'] == entry['name'] }
122
- end
123
-
124
- def stats_last(item)
125
- return item.last if item.last
126
-
127
- item.last = stats_data.select { |e| e['kind'] == stats_kind_for(item) && e['name'] == item.name }.last || {}
128
- end
129
-
130
- def update_stats(history, entry)
131
- entry['cnt'] = count = history.size
132
- entry['avg'] = avg = history.inject(0.0) { |s, x| s + x['run'].to_f } / count
133
- entry['std'] = std = Math.sqrt(history.inject(0.0) { |v, x| v + (x['run'].to_f - avg)**2 })
134
- entry['err'] = std / Math.sqrt(count) if count.positive?
135
- end
136
- end
137
- end