miga-base 0.7.16.0 → 0.7.16.6

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: 2ee2f247a9f26ddbf2fcf0fc85bb0ed51d0d4980e2e40297449665a0befca102
4
- data.tar.gz: 9d2ea26b71d226f0cbddebb214912a82b432886efb72e8bb6523b4b2f5aa588c
3
+ metadata.gz: fcd30efdaa8cf3a8ae87b5cc658363c2d37c0beb3c13491595beb983b1629b32
4
+ data.tar.gz: '0441764548f4199a8f3df253c151dc856aa414f5a91d02126bcb893ee2fe6835'
5
5
  SHA512:
6
- metadata.gz: 673a1f46b1e34e14b58a434be79ec92c2ed31e7225004bd3fe08daebdf1362603353b5dd8473de8923583ed9819b6fe54578a3e18ea33dd51a54e2fad0bb0755
7
- data.tar.gz: 23e44862a13fa293ea72d2260fc9676e2438b4c0c1725c64863a23c144d9b8a27dfefa62da6234fc0e86a8e59ada90498801a5609baaf07357293807801ccf60
6
+ metadata.gz: 18e829f1cb4141df9515565bd19af32e67b9920689a75dd4b5f2f5a79d0a5b7d86ea425f64e7702766e09a14c5b70af4d07ccb16df94aeef3ba4ade54e9506ab
7
+ data.tar.gz: 72ad22fd3386214825c463edc9232f2deeae24d06257a523f73592fd2d9496e452d192d10f37cf493b52a936578c3111a1a8801a78494688d51fc75b01e489f2
data/bin/miga CHANGED
@@ -7,4 +7,5 @@ $LOAD_PATH.push File.expand_path('../../lib', __FILE__)
7
7
  require 'miga'
8
8
  require 'miga/cli'
9
9
 
10
- MiGA::Cli.new(ARGV).launch
10
+ MiGA::Cli.new(ARGV).launch(true)
11
+
@@ -7,6 +7,7 @@ class MiGA::Cli::Action::Doctor < MiGA::Cli::Action
7
7
  include MiGA::Cli::Action::Doctor::Base
8
8
 
9
9
  def parse_cli
10
+ cli.defaults = { threads: 1 }
10
11
  cli.defaults = Hash[@@OPERATIONS.keys.map { |i| [i, true] }]
11
12
  cli.parse do |opt|
12
13
  operation_n = Hash[@@OPERATIONS.map { |k, v| [v[0], k] }]
@@ -24,6 +25,10 @@ class MiGA::Cli::Action::Doctor < MiGA::Cli::Action
24
25
  @@OPERATIONS.each_key { |i| cli[i] = false }
25
26
  cli[op_k] = true
26
27
  end
28
+ opt.on(
29
+ '-t', '--threads INT', Integer,
30
+ "Concurrent threads to use. By default: #{cli[:threads]}"
31
+ ) { |v| cli[:threads] = v }
27
32
  end
28
33
  end
29
34
 
@@ -59,11 +64,19 @@ class MiGA::Cli::Action::Doctor < MiGA::Cli::Action
59
64
  # Perform status operation with MiGA::Cli +cli+
60
65
  def check_status(cli)
61
66
  cli.say 'Updating metadata status'
62
- n, k = cli.load_project.dataset_names.size, 0
63
- cli.load_project.each_dataset do |d|
64
- cli.advance('Datasets:', k += 1, n, false)
65
- d.recalculate_status
67
+ p = cli.load_project
68
+ n = p.dataset_names.size
69
+ (0 .. cli[:threads] - 1).map do |i|
70
+ Process.fork do
71
+ k = 0
72
+ cli.load_project.each_dataset do |d|
73
+ k += 1
74
+ cli.advance('Datasets:', k, n, false) if i == 0
75
+ d.recalculate_status if k % cli[:threads] == i
76
+ end
77
+ end
66
78
  end
79
+ Process.waitall
67
80
  cli.say
68
81
  end
69
82
 
@@ -71,36 +84,59 @@ class MiGA::Cli::Action::Doctor < MiGA::Cli::Action
71
84
  # Perform databases operation with MiGA::Cli +cli+
72
85
  def check_db(cli)
73
86
  cli.say 'Checking integrity of databases'
74
- n, k = cli.load_project.dataset_names.size, 0
75
- cli.load_project.each_dataset do |d|
76
- cli.advance('Datasets:', k += 1, n, false)
77
- each_database_file(d) do |db_file, metric, result|
78
- check_sqlite3_database(db_file, metric) do
79
- cli.say(" > Removing malformed database from #{d.name}:#{result} ")
80
- File.unlink(db_file)
81
- r = d.result(result) or next
82
- [r.path(:done), r.path].each { |f| File.unlink(f) if File.exist?(f) }
87
+ p = cli.load_project
88
+ n = p.dataset_names.size
89
+ (0 .. cli[:threads] - 1).map do |i|
90
+ Process.fork do
91
+ k = 0
92
+ p.each_dataset do |d|
93
+ k += 1
94
+ cli.advance('Datasets:', k, n, false) if i == 0
95
+ next unless k % cli[:threads] == i
96
+ each_database_file(d) do |db_file, metric, result|
97
+ check_sqlite3_database(db_file, metric) do
98
+ cli.say(
99
+ " > Removing malformed database from #{d.name}:#{result} "
100
+ )
101
+ File.unlink(db_file)
102
+ r = d.result(result) or next
103
+ [r.path(:done), r.path].each do |f|
104
+ File.unlink(f) if File.exist?(f)
105
+ end
106
+ end
107
+ end
83
108
  end
84
109
  end
85
110
  end
111
+ Process.waitall
86
112
  cli.say
87
113
  end
88
114
 
89
115
  ##
90
116
  # Perform bidirectional operation with MiGA::Cli +cli+
91
117
  def check_bidir(cli)
92
- cli.say 'Checking that reference distances are bidirectional'
118
+ cli.say 'Checking if reference distances are bidirectional'
93
119
  ref_ds = cli.load_project.each_dataset.select(&:ref?)
94
120
  ref_names = ref_ds.map(&:name)
95
- n, k = ref_ds.size, 0
96
- ref_ds.each do |d|
97
- cli.advance('Datasets:', k += 1, n, false)
98
- saved = saved_targets(d)
99
- next if saved.nil?
121
+ n = ref_ds.size
122
+ (0 .. cli[:threads] - 1).map do |i|
123
+ Process.fork do
124
+ k = 0
125
+ ref_ds.each do |d|
126
+ k += 1
127
+ cli.advance('Datasets:', k, n, false) if i == 0
128
+ next unless k % cli[:threads] == i
100
129
 
101
- to_save = ref_names - saved
102
- to_save.each { |k| save_bidirectional(cli.load_project.dataset(k), d) }
130
+ saved = saved_targets(d)
131
+ next if saved.nil?
132
+
133
+ (ref_names - saved).each do |k|
134
+ save_bidirectional(cli.load_project.dataset(k), d)
135
+ end
136
+ end
137
+ end
103
138
  end
139
+ Process.waitall
104
140
  cli.say
105
141
  end
106
142
 
@@ -134,10 +134,19 @@ module MiGA::Cli::Action::Doctor::Base
134
134
 
135
135
  data[0], data[1] = data[1], data[0]
136
136
  SQLite3::Database.new(db_file_b) do |conn|
137
- conn.execute(
138
- "insert into #{metric} (seq1, seq2, #{metric}, sd, n, omega) " +
139
- "values(?, ?, ?, ?, ?, ?)", data
140
- )
137
+ attempts = 0
138
+ begin
139
+ attempts += 1
140
+ conn.execute(
141
+ "insert into #{metric} (seq1, seq2, #{metric}, sd, n, omega) " +
142
+ "values(?, ?, ?, ?, ?, ?)", data
143
+ )
144
+ rescue SQLite3::BusyException => e
145
+ raise "Cannot populate #{db_file_b}: #{e.message}" if attempts > 3
146
+
147
+ sleep(1)
148
+ retry
149
+ end
141
150
  end
142
151
  end
143
152
  end
@@ -70,7 +70,7 @@ class MiGA::Cli::Action::Get < MiGA::Cli::Action
70
70
  glob = get_sub_cli
71
71
  p = cli.load_project
72
72
  glob.each do |sub_cli|
73
- rd = create_remote_dataset(sub_cli)
73
+ rd = create_remote_dataset(sub_cli, p)
74
74
  next if rd.nil?
75
75
 
76
76
  if sub_cli[:get_md]
@@ -115,7 +115,7 @@ class MiGA::Cli::Action::Get < MiGA::Cli::Action
115
115
  glob
116
116
  end
117
117
 
118
- def create_remote_dataset(sub_cli)
118
+ def create_remote_dataset(sub_cli, p)
119
119
  sub_cli.ensure_par(dataset: '-D', ids: '-I')
120
120
  unless sub_cli[:api_key].nil?
121
121
  ENV["#{sub_cli[:universe].to_s.upcase}_API_KEY"] = sub_cli[:api_key]
@@ -165,7 +165,7 @@ class MiGA::Cli::Action::GetDb < MiGA::Cli::Action
165
165
  def check_target
166
166
  return false if cli[:overwrite]
167
167
 
168
- file = File.expand_path(cli[:database], cli[:local])
168
+ file = File.expand_path(cli[:database].to_s, cli[:local])
169
169
  if Dir.exist? file
170
170
  warn "The target directory already exists: #{file}"
171
171
  true
@@ -8,7 +8,7 @@ class MiGA::Cli::Action::Run < MiGA::Cli::Action
8
8
  def parse_cli
9
9
  cli.defaults = { try_load: false, thr: 1, env: false }
10
10
  cli.parse do |opt|
11
- cli.opt_object(opt, [:project, :dataset_opt, :result])
11
+ cli.opt_object(opt, [:project, :dataset_opt, :result_opt])
12
12
  opt.on(
13
13
  '-t', '--threads INT', Integer,
14
14
  "Threads to use in the local run (by default: #{cli[:thr]})"
@@ -43,6 +43,9 @@ class MiGA::Cli::Action::Run < MiGA::Cli::Action
43
43
  cli[:dataset] = nil
44
44
  end
45
45
 
46
+ # Use virtual result if not explicitly passed
47
+ cli[:result] ||= cli[:dataset] ? :d : :p
48
+
46
49
  # Load project
47
50
  p = cli.load_project
48
51
 
@@ -56,10 +56,11 @@ module MiGA::Cli::OptHelper
56
56
  # - :project_type To allow (optionally) a type of project
57
57
  # - :project_type_req To require a type of project
58
58
  # - :result To require a type of project or dataset result
59
+ # - :result_opt To allow (optionally) a type of result
59
60
  # - :result_dataset To require a type of dataset result
60
61
  # - :result_project To require a type of project result
61
- # The options :result, :result_dataset, and :result_project are mutually
62
- # exclusive
62
+ # The options :result, :result_opt, :result_dataset, and :result_project
63
+ # are mutually exclusive
63
64
  def opt_object(opt, what = [:project, :dataset])
64
65
  what.each do |w|
65
66
  case w
@@ -82,10 +83,10 @@ module MiGA::Cli::OptHelper
82
83
  "#{req}Type of #{obj}. Recognized types include:",
83
84
  *klass.KNOWN_TYPES.map { |k, v| "~ #{k}: #{v[:description]}" }
84
85
  ) { |v| self[:type] = v.downcase.to_sym }
85
- when :result
86
+ when :result, :result_opt
86
87
  opt.on(
87
88
  '-r', '--result STRING',
88
- '(Mandatory) Name of the result',
89
+ "#{"(Mandatory) " if w == :result}Name of the result",
89
90
  'Recognized names for dataset-specific results include:',
90
91
  *MiGA::Dataset.RESULT_DIRS.keys.map { |n| " ~ #{n}" },
91
92
  'Recognized names for project-wide results include:',
@@ -52,10 +52,44 @@ class MiGA::MiGA
52
52
  # 1,000 otherwise.
53
53
  # The report goes to $stderr iff --verborse
54
54
  def advance(step, n = 0, total = nil, bin = true)
55
- adv = total.nil? ? (n == 0 ? '' : num_suffix(n, bin)) :
56
- ('%.1f%% (%s/%s)' % [100.0 * n / total,
57
- num_suffix(n, bin), num_suffix(total, bin)])
58
- $stderr.print("[%s] %s %s \r" % [Time.now, step, adv])
55
+ # Initialize advance timing
56
+ @_advance_time ||= { last: nil, n: 0, avg: nil }
57
+ if n <= 1 || @_advance_time[:n] > n
58
+ @_advance_time[:last] = nil
59
+ @_advance_time[:n] = 0
60
+ @_advance_time[:avg] = nil
61
+ end
62
+
63
+ # Estimate timing
64
+ adv_n = n - @_advance_time[:n]
65
+ unless total.nil? || @_advance_time[:last].nil? || adv_n <= 0
66
+ if adv_n.to_f/n > 0.001
67
+ this_time = Time.now - @_advance_time[:last]
68
+ this_avg = this_time / adv_n
69
+ @_advance_time[:avg] ||= this_avg
70
+ @_advance_time[:avg] = 0.9 * @_advance_time[:avg] + 0.1 * this_avg
71
+ end
72
+ end
73
+ @_advance_time[:last] = Time.now
74
+ @_advance_time[:n] = n
75
+
76
+ # Report
77
+ adv_vals = [100.0 * n / total, num_suffix(n, bin), num_suffix(total, bin)]
78
+ adv =
79
+ total.nil? ? (n == 0 ? '' : num_suffix(n, bin)) :
80
+ ('%.1f%% (%s/%s)' % adv_vals)
81
+ left =
82
+ if @_advance_time[:avg].nil?
83
+ ''
84
+ else
85
+ left_time = @_advance_time[:avg] * (total - n) / 60 # <- in minutes
86
+ left_time < 0.01 ? ' ' :
87
+ left_time < 1 ? ('%.0fs left' % (left_time * 60)) :
88
+ left_time > 1440 ? ('%.1fd left' % (left_time / 1440)) :
89
+ left_time > 60 ? ('%.1fh left' % (left_time / 60)) :
90
+ ('%.1fm left' % left_time)
91
+ end
92
+ $stderr.print("[%s] %s %s %s \r" % [Time.now, step, adv, left])
59
93
  end
60
94
 
61
95
  ##
@@ -77,6 +77,7 @@ module MiGA::Daemon::Base
77
77
  ##
78
78
  # Writing file handler (IO) to the log file
79
79
  def logfh
80
+ @logfh ||= nil
80
81
  return $stderr if show_log?
81
82
  return @logfh if @logfh && !@logfh.closed?
82
83
 
@@ -34,17 +34,29 @@ class MiGA::Json < MiGA::MiGA
34
34
  # +opts+.
35
35
  def parse(path, opts = {})
36
36
  opts = default_opts(opts)
37
- cont = opts[:contents] ? path : File.read(path)
38
- raise "Empty descriptor: #{opts[:contents] ? "''" : path}." if cont.empty?
39
37
 
40
- y = JSON.parse(cont,
41
- symbolize_names: opts[:symbolize],
42
- create_additions: opts[:additions])
38
+ # Read JSON
39
+ cont = path
40
+ 12.times do
41
+ cont = File.read(path)
42
+ break unless cont.empty?
43
+ sleep 1 # Wait up to 12 seconds for racing processes (iff empty file)
44
+ end unless opts[:contents]
45
+ raise "Empty descriptor: #{opts[:contents] ? "''" : path}" if cont.empty?
46
+
47
+ # Parse JSON
48
+ params = { symbolize_names: opts[:symbolize],
49
+ create_additions: opts[:additions] }
50
+ y = JSON.parse(cont, params)
51
+
52
+ # Add defaults
43
53
  unless opts[:default].nil?
44
54
  opts[:default] = parse(opts[:default]) if opts[:default].is_a? String
45
55
  y.each { |k, v| opts[:default][k] = v }
46
56
  y = opts[:default]
47
57
  end
58
+
59
+ # Return
48
60
  y
49
61
  end
50
62
 
@@ -8,7 +8,7 @@ module MiGA
8
8
  # - Float representing the major.minor version.
9
9
  # - Integer representing gem releases of the current version.
10
10
  # - Integer representing minor changes that require new version number.
11
- VERSION = [0.7, 16, 0]
11
+ VERSION = [0.7, 16, 6]
12
12
 
13
13
  ##
14
14
  # Nickname for the current major.minor version.
@@ -16,7 +16,7 @@ module MiGA
16
16
 
17
17
  ##
18
18
  # Date of the current gem release.
19
- VERSION_DATE = Date.new(2020, 10, 13)
19
+ VERSION_DATE = Date.new(2020, 11, 22)
20
20
 
21
21
  ##
22
22
  # Reference of MiGA.
@@ -12,7 +12,7 @@ cd "$DIR"
12
12
  miga date > "miga-project.start"
13
13
 
14
14
  # Execute doctor
15
- miga doctor -P "$PROJECT" -v
15
+ miga doctor -P "$PROJECT" -t "$CORES" -v
16
16
 
17
17
  # Index taxonomy
18
18
  miga tax_index -P "$PROJECT" -i "miga-project.taxonomy.json" --ref --active
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: miga-base
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.7.16.0
4
+ version: 0.7.16.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - Luis M. Rodriguez-R
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-10-13 00:00:00.000000000 Z
11
+ date: 2020-11-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: daemons