miga-base 0.7.16.7 → 0.7.18.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/bin/miga +2 -0
- data/lib/miga/cli/action/daemon.rb +1 -0
- data/lib/miga/cli/action/doctor/base.rb +11 -31
- data/lib/miga/cli/action/run.rb +3 -3
- data/lib/miga/cli/action/wf.rb +1 -1
- data/lib/miga/daemon.rb +18 -8
- data/lib/miga/daemon/base.rb +7 -1
- data/lib/miga/dataset.rb +2 -3
- data/lib/miga/dataset/result.rb +4 -4
- data/lib/miga/remote_dataset.rb +6 -4
- data/lib/miga/sqlite.rb +49 -0
- data/lib/miga/version.rb +2 -2
- data/scripts/maintenance.bash +9 -0
- data/scripts/project_stats.bash +3 -0
- data/test/daemon_test.rb +21 -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: 78b020bda6120b5a284100b9544d7a488d385630f21b5d6d5e2bb9b365c832fb
|
4
|
+
data.tar.gz: d8332711fdf86308726700654ee8db91bb097f022a26e1240e62d107efa833a1
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8b766a40acf37193204e6369e07f2ecba1a67aa6141133654ab69f89b2ba9a63a959bd3f5b7fd21c56542eea7e3174cc342b05dc76b73ed27c6a2b273c546ca9
|
7
|
+
data.tar.gz: 6f8d791b9bf443a1eb43198625ebce48501ebaa3680be0c4cef55931b176fd2365407865e6e43cd549217ada6c67e5c86b156bd0816b80f91c388af812af810c
|
data/bin/miga
CHANGED
@@ -73,6 +73,7 @@ class MiGA::Cli::Action::Daemon < MiGA::Cli::Action
|
|
73
73
|
end
|
74
74
|
|
75
75
|
def perform
|
76
|
+
cli.operation or raise 'Please specify a daemon operation'
|
76
77
|
p = cli.load_project
|
77
78
|
d = MiGA::Daemon.new(p, cli[:json])
|
78
79
|
dopts = %i[latency maxjobs nodelist ppn shutdown_when_done]
|
@@ -1,5 +1,5 @@
|
|
1
1
|
require 'miga/cli/action'
|
2
|
-
require '
|
2
|
+
require 'miga/sqlite'
|
3
3
|
|
4
4
|
class MiGA::Cli::Action::Doctor < MiGA::Cli::Action
|
5
5
|
end
|
@@ -10,9 +10,7 @@ module MiGA::Cli::Action::Doctor::Base
|
|
10
10
|
# tables saving +metric+ (:ani or :aai) and call +blk+ if the
|
11
11
|
# file is corrupt or doesn't contain the expected structure
|
12
12
|
def check_sqlite3_database(db_file, metric, &blk)
|
13
|
-
|
14
|
-
conn.execute("select count(*) from #{metric}").first
|
15
|
-
end
|
13
|
+
MiGA::SQLite.new(db_file).run("select count(*) from #{metric}")
|
16
14
|
rescue SQLite3::SQLException, SQLite3::CorruptException
|
17
15
|
blk.call
|
18
16
|
end
|
@@ -107,11 +105,7 @@ module MiGA::Cli::Action::Doctor::Base
|
|
107
105
|
dist = dataset.result(:distances) or return
|
108
106
|
path = dist.file_path(:aai_db) or return
|
109
107
|
|
110
|
-
|
111
|
-
SQLite3::Database.new(path) do |conn|
|
112
|
-
o = conn.execute('select seq2 from aai').map(&:first)
|
113
|
-
end
|
114
|
-
o
|
108
|
+
MiGA::SQLite.new(path).run('select seq2 from aai').map(&:first)
|
115
109
|
end
|
116
110
|
|
117
111
|
##
|
@@ -120,34 +114,20 @@ module MiGA::Cli::Action::Doctor::Base
|
|
120
114
|
def save_bidirectional(a, b)
|
121
115
|
each_database_file(a) do |db_file, metric, result|
|
122
116
|
data = nil
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
"from #{metric} where seq2 = ? limit 1", b.name
|
128
|
-
).first
|
129
|
-
end
|
117
|
+
data = MiGA::SQLite.new(db_file).run(
|
118
|
+
"select seq1, seq2, #{metric}, sd, n, omega " +
|
119
|
+
"from #{metric} where seq2 = ? limit 1", b.name
|
120
|
+
).first
|
130
121
|
next if data.nil? || data.empty?
|
131
122
|
|
132
123
|
db_file_b = File.join(File.dirname(db_file), "#{b.name}.db")
|
133
124
|
next unless File.exist?(db_file_b)
|
134
125
|
|
135
126
|
data[0], data[1] = data[1], data[0]
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
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
|
150
|
-
end
|
127
|
+
MiGA::SQLite.new(db_file_b).run(
|
128
|
+
"insert into #{metric} (seq1, seq2, #{metric}, sd, n, omega) " +
|
129
|
+
"values(?, ?, ?, ?, ?, ?)", data
|
130
|
+
)
|
151
131
|
end
|
152
132
|
end
|
153
133
|
end
|
data/lib/miga/cli/action/run.rb
CHANGED
@@ -37,9 +37,9 @@ class MiGA::Cli::Action::Run < MiGA::Cli::Action
|
|
37
37
|
cli[:thr] ||= ENV['CORES'].to_i unless ENV['CORES'].nil?
|
38
38
|
cli[:result] = File.basename(cli[:result].to_s, '.bash').to_sym
|
39
39
|
end
|
40
|
-
|
41
|
-
|
42
|
-
|
40
|
+
%i[project dataset result].each do |i|
|
41
|
+
cli[i] = nil if cli[i].nil? || cli[i].empty?
|
42
|
+
end
|
43
43
|
|
44
44
|
# Unset dataset if the requested result is for projects
|
45
45
|
if (MiGA::Project.RESULT_DIRS.keys + [:p]).include? cli[:result]
|
data/lib/miga/cli/action/wf.rb
CHANGED
@@ -184,7 +184,7 @@ module MiGA::Cli::Action::Wf
|
|
184
184
|
def call_cli(cmd)
|
185
185
|
cmd << '-v' if cli[:verbose]
|
186
186
|
MiGA::MiGA.DEBUG "Cli::Action::Wf.call_cli #{cmd}"
|
187
|
-
MiGA::Cli.new(cmd.map(&:to_s)).launch
|
187
|
+
MiGA::Cli.new(cmd.map(&:to_s)).launch(true)
|
188
188
|
end
|
189
189
|
|
190
190
|
def run_daemon
|
data/lib/miga/daemon.rb
CHANGED
@@ -73,7 +73,7 @@ class MiGA::Daemon < MiGA::MiGA
|
|
73
73
|
say 'MiGA:%s launched' % project.name
|
74
74
|
say '-----------------------------------'
|
75
75
|
miga_say "Saving log to: #{output_file}" unless show_log?
|
76
|
-
|
76
|
+
queue_maintenance
|
77
77
|
load_status
|
78
78
|
say 'Configuration options:'
|
79
79
|
say @runopts.to_s
|
@@ -92,8 +92,7 @@ class MiGA::Daemon < MiGA::MiGA
|
|
92
92
|
flush!
|
93
93
|
if (loop_i % 12).zero?
|
94
94
|
purge!
|
95
|
-
|
96
|
-
# recalculate_status!
|
95
|
+
queue_maintenance
|
97
96
|
end
|
98
97
|
save_status
|
99
98
|
sleep(latency)
|
@@ -101,9 +100,13 @@ class MiGA::Daemon < MiGA::MiGA
|
|
101
100
|
true
|
102
101
|
end
|
103
102
|
|
104
|
-
|
105
|
-
|
106
|
-
|
103
|
+
##
|
104
|
+
# Queue maintenance tasks as an analysis job
|
105
|
+
def queue_maintenance
|
106
|
+
return if bypass_maintenance?
|
107
|
+
|
108
|
+
say 'Queueing maintenance tasks'
|
109
|
+
queue_job(:maintenance)
|
107
110
|
end
|
108
111
|
|
109
112
|
##
|
@@ -271,6 +274,11 @@ class MiGA::Daemon < MiGA::MiGA
|
|
271
274
|
# Avoid single datasets hogging resources
|
272
275
|
@jobs_to_run.rotate! rand(jobs_to_run.size)
|
273
276
|
|
277
|
+
# Prioritize project-wide jobs
|
278
|
+
project_jobs = @jobs_to_run.select { |i| i[:ds].nil? }
|
279
|
+
@jobs_to_run.delete_if { |i| i[:ds].nil? }
|
280
|
+
@jobs_to_run.prepend(*project_jobs)
|
281
|
+
|
274
282
|
# Launch as many +jobs_to_run+ as possible
|
275
283
|
while (hostk = next_host)
|
276
284
|
break if jobs_to_run.empty?
|
@@ -309,11 +317,13 @@ class MiGA::Daemon < MiGA::MiGA
|
|
309
317
|
job[:hostk] = hostk
|
310
318
|
job[:cmd] = job[:cmd].miga_variables(host: nodelist[hostk])
|
311
319
|
job[:pid] = spawn job[:cmd]
|
312
|
-
|
320
|
+
MiGA::MiGA.DEBUG "Detaching PID: #{job[:pid]}"
|
321
|
+
Process.detach(job[:pid]) unless [nil, '', 0].include?(job[:pid])
|
313
322
|
when 'bash'
|
314
323
|
# Local job
|
315
324
|
job[:pid] = spawn job[:cmd]
|
316
|
-
|
325
|
+
MiGA::MiGA.DEBUG "Detaching PID: #{job[:pid]}"
|
326
|
+
Process.detach(job[:pid]) unless [nil, '', 0].include?(job[:pid])
|
317
327
|
else
|
318
328
|
# Schedule cluster job (qsub, msub, slurm)
|
319
329
|
job[:pid] = `#{job[:cmd]}`.chomp
|
data/lib/miga/daemon/base.rb
CHANGED
@@ -17,7 +17,7 @@ module MiGA::Daemon::Base
|
|
17
17
|
if !force && v == 0 && k != :verbosity
|
18
18
|
raise "Daemon's #{k} cannot be set to zero"
|
19
19
|
end
|
20
|
-
when :shutdown_when_done, :show_log
|
20
|
+
when :shutdown_when_done, :show_log, :bypass_maintenance
|
21
21
|
v = !!v
|
22
22
|
when :nodelist
|
23
23
|
if v =~ /^\$/
|
@@ -63,6 +63,12 @@ module MiGA::Daemon::Base
|
|
63
63
|
!!runopts(:shutdown_when_done)
|
64
64
|
end
|
65
65
|
|
66
|
+
##
|
67
|
+
# Should the daemon ignore regular maintenance steps?
|
68
|
+
def bypass_maintenance?
|
69
|
+
!!runopts(:bypass_maintenance)
|
70
|
+
end
|
71
|
+
|
66
72
|
##
|
67
73
|
# Returns the level of verbosity for the daemon as an Integer, or 1 if unset.
|
68
74
|
# Verbosity levels are:
|
data/lib/miga/dataset.rb
CHANGED
@@ -7,7 +7,7 @@ require 'miga/metadata'
|
|
7
7
|
require 'miga/dataset/result'
|
8
8
|
require 'miga/dataset/status'
|
9
9
|
require 'miga/dataset/hooks'
|
10
|
-
require '
|
10
|
+
require 'miga/sqlite'
|
11
11
|
|
12
12
|
##
|
13
13
|
# Dataset representation in MiGA
|
@@ -190,8 +190,7 @@ class MiGA::Dataset < MiGA::MiGA
|
|
190
190
|
r = result(ref_project ? :taxonomy : :distances)
|
191
191
|
return nil if r.nil?
|
192
192
|
|
193
|
-
|
194
|
-
db.execute(
|
193
|
+
MiGA::SQLite.new(r.file_path(:aai_db)).run(
|
195
194
|
'SELECT seq2, aai FROM aai WHERE seq2 != ? ' \
|
196
195
|
'GROUP BY seq2 ORDER BY aai DESC LIMIT ?', [name, how_many]
|
197
196
|
)
|
data/lib/miga/dataset/result.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
require '
|
1
|
+
require 'miga/sqlite'
|
2
2
|
require 'miga/result'
|
3
3
|
require 'miga/dataset/base'
|
4
4
|
require 'miga/common/with_result'
|
@@ -151,13 +151,13 @@ module MiGA::Dataset::Result
|
|
151
151
|
db = r.file_path(db_type)
|
152
152
|
next if db.nil? || !File.size?(db)
|
153
153
|
|
154
|
-
sqlite_db =
|
154
|
+
sqlite_db = MiGA::SQLite.new(db)
|
155
155
|
table = db_type[-6..-4]
|
156
|
-
val = sqlite_db.
|
156
|
+
val = sqlite_db.run("select seq2 from #{table}")
|
157
157
|
next if val.empty?
|
158
158
|
|
159
159
|
(val.map(&:first) - ref).each do |extra|
|
160
|
-
sqlite_db.
|
160
|
+
sqlite_db.run("delete from #{table} where seq2=?", extra)
|
161
161
|
end
|
162
162
|
end
|
163
163
|
end
|
data/lib/miga/remote_dataset.rb
CHANGED
@@ -156,10 +156,12 @@ class MiGA::RemoteDataset < MiGA::MiGA
|
|
156
156
|
return nil unless metadata[:ncbi_asm]
|
157
157
|
|
158
158
|
ncbi_asm_id = self.class.ncbi_asm_acc2id metadata[:ncbi_asm]
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
159
|
+
txt = nil
|
160
|
+
3.times do
|
161
|
+
txt = self.class.download(:ncbi_summary, :assembly, ncbi_asm_id, :json)
|
162
|
+
txt.empty? ? sleep(1) : break
|
163
|
+
end
|
164
|
+
doc = MiGA::Json.parse(txt, symbolize: false, contents: true)
|
163
165
|
@_ncbi_asm_json_doc = doc['result'][ doc['result']['uids'].first ]
|
164
166
|
end
|
165
167
|
|
data/lib/miga/sqlite.rb
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
# @package MiGA
|
2
|
+
# @license Artistic-2.0
|
3
|
+
|
4
|
+
require 'sqlite3'
|
5
|
+
|
6
|
+
##
|
7
|
+
# SQLite3 wrapper for MiGA.
|
8
|
+
class MiGA::SQLite < MiGA::MiGA
|
9
|
+
class << self
|
10
|
+
##
|
11
|
+
# Default parsing options. Supported +opts+ keys:
|
12
|
+
# - +:busy_attempts+: Number of times to retry when database is busy
|
13
|
+
# (default: 3)
|
14
|
+
def default_opts(opts = {})
|
15
|
+
opts[:busy_attempts] ||= 3
|
16
|
+
opts
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
##
|
21
|
+
# Options hash
|
22
|
+
attr :opts
|
23
|
+
|
24
|
+
##
|
25
|
+
# Database absolute path
|
26
|
+
attr :path
|
27
|
+
|
28
|
+
##
|
29
|
+
# Create MiGA::SQLite with database in +path+ (without opening a connection)
|
30
|
+
# and options +opts+ (see +.default_opts+)
|
31
|
+
def initialize(path, opts = {})
|
32
|
+
@opts = MiGA::SQLite.default_opts(opts)
|
33
|
+
@path = File.absolute_path(path)
|
34
|
+
end
|
35
|
+
|
36
|
+
##
|
37
|
+
# Executes +cmd+ and returns the result
|
38
|
+
def run(*cmd)
|
39
|
+
busy_attempts ||= 0
|
40
|
+
conn = SQLite3::Database.new(path)
|
41
|
+
conn.execute(*cmd)
|
42
|
+
rescue SQLite3::BusyException => e
|
43
|
+
busy_attempts += 1
|
44
|
+
raise "Database busy #{path}: #{e.message}" if busy_attempts >= 3
|
45
|
+
|
46
|
+
sleep(1)
|
47
|
+
retry
|
48
|
+
end
|
49
|
+
end
|
data/lib/miga/version.rb
CHANGED
@@ -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,
|
11
|
+
VERSION = [0.7, 18, 0]
|
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,
|
19
|
+
VERSION_DATE = Date.new(2020, 12, 27)
|
20
20
|
|
21
21
|
##
|
22
22
|
# Reference of MiGA.
|
data/scripts/project_stats.bash
CHANGED
@@ -12,12 +12,15 @@ cd "$DIR"
|
|
12
12
|
miga date > "miga-project.start"
|
13
13
|
|
14
14
|
# Execute doctor
|
15
|
+
echo "# Doctor"
|
15
16
|
miga doctor -P "$PROJECT" -t "$CORES" -v
|
16
17
|
|
17
18
|
# Index taxonomy
|
19
|
+
echo "# Index taxonomy"
|
18
20
|
miga tax_index -P "$PROJECT" -i "miga-project.taxonomy.json" --ref --active
|
19
21
|
|
20
22
|
# Index metadata
|
23
|
+
echo "# Index metadata"
|
21
24
|
ruby -I "$MIGA/lib" \
|
22
25
|
"$MIGA/utils/index_metadata.rb" "$PROJECT" "miga-project.metadata.db"
|
23
26
|
|
data/test/daemon_test.rb
CHANGED
@@ -93,7 +93,7 @@ class DaemonTest < Test::Unit::TestCase
|
|
93
93
|
0 => /-{20}\n/,
|
94
94
|
1 => /MiGA:#{p.name} launched/,
|
95
95
|
2 => /-{20}\n/,
|
96
|
-
|
96
|
+
8 => /Probing running jobs\n/
|
97
97
|
}.each { |k, v| assert_match(v, l[k], "unexpected line: #{k}") }
|
98
98
|
ensure
|
99
99
|
begin
|
@@ -160,8 +160,9 @@ class DaemonTest < Test::Unit::TestCase
|
|
160
160
|
assert_equal(0, d1.jobs_running.size)
|
161
161
|
assert_equal(0, d1.jobs_to_run.size)
|
162
162
|
capture_stderr { d1.in_loop }
|
163
|
+
# 3 dataset jobs + 1 maintenance job:
|
163
164
|
assert_equal(1, d1.jobs_running.size)
|
164
|
-
assert_equal(
|
165
|
+
assert_equal(3, d1.jobs_to_run.size)
|
165
166
|
end
|
166
167
|
|
167
168
|
def test_maxjobs_runopts
|
@@ -172,7 +173,7 @@ class DaemonTest < Test::Unit::TestCase
|
|
172
173
|
assert_equal(0, d1.jobs_to_run.size)
|
173
174
|
capture_stderr { d1.in_loop }
|
174
175
|
assert_equal(2, d1.jobs_running.size)
|
175
|
-
assert_equal(
|
176
|
+
assert_equal(2, d1.jobs_to_run.size)
|
176
177
|
end
|
177
178
|
|
178
179
|
def test_load_status
|
@@ -232,6 +233,7 @@ class DaemonTest < Test::Unit::TestCase
|
|
232
233
|
end
|
233
234
|
|
234
235
|
def test_shutdown_when_done
|
236
|
+
daemon.runopts(:bypass_maintenance, true)
|
235
237
|
daemon.runopts(:shutdown_when_done, true)
|
236
238
|
out = capture_stderr { assert { !daemon.in_loop } }.string
|
237
239
|
assert_match(/Nothing else to do/, out)
|
@@ -317,4 +319,20 @@ class DaemonTest < Test::Unit::TestCase
|
|
317
319
|
out = capture_stderr { d1.in_loop }.string
|
318
320
|
assert_match(/Daemon loop start/, out)
|
319
321
|
end
|
322
|
+
|
323
|
+
def test_bypass_maintenance
|
324
|
+
# Default (run maintenance)
|
325
|
+
d = daemon(0)
|
326
|
+
d.runopts(:latency, 0, true)
|
327
|
+
capture_stderr { d.in_loop }
|
328
|
+
assert_equal(1, d.jobs_running.size)
|
329
|
+
assert_equal(:maintenance, d.jobs_running.first[:job])
|
330
|
+
|
331
|
+
# Bypassing maintenance
|
332
|
+
d = daemon(1)
|
333
|
+
d.runopts(:latency, 0, true)
|
334
|
+
d.runopts(:bypass_maintenance, true)
|
335
|
+
capture_stderr { d.in_loop }
|
336
|
+
assert_equal([], d.jobs_running)
|
337
|
+
end
|
320
338
|
end
|
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.
|
4
|
+
version: 0.7.18.0
|
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-
|
11
|
+
date: 2020-12-27 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: daemons
|
@@ -196,6 +196,7 @@ files:
|
|
196
196
|
- lib/miga/result/dates.rb
|
197
197
|
- lib/miga/result/source.rb
|
198
198
|
- lib/miga/result/stats.rb
|
199
|
+
- lib/miga/sqlite.rb
|
199
200
|
- lib/miga/tax_dist.rb
|
200
201
|
- lib/miga/tax_index.rb
|
201
202
|
- lib/miga/taxonomy.rb
|
@@ -211,6 +212,7 @@ files:
|
|
211
212
|
- scripts/essential_genes.bash
|
212
213
|
- scripts/haai_distances.bash
|
213
214
|
- scripts/init.bash
|
215
|
+
- scripts/maintenance.bash
|
214
216
|
- scripts/miga.bash
|
215
217
|
- scripts/mytaxa.bash
|
216
218
|
- scripts/mytaxa_scan.bash
|