db_sucker 3.0.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 +7 -0
- data/.gitignore +16 -0
- data/CHANGELOG.md +45 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +193 -0
- data/Rakefile +1 -0
- data/VERSION +1 -0
- data/bin/db_sucker +12 -0
- data/bin/db_sucker.sh +14 -0
- data/db_sucker.gemspec +29 -0
- data/doc/config_example.rb +53 -0
- data/doc/container_example.yml +150 -0
- data/lib/db_sucker/adapters/mysql2.rb +103 -0
- data/lib/db_sucker/application/colorize.rb +28 -0
- data/lib/db_sucker/application/container/accessors.rb +60 -0
- data/lib/db_sucker/application/container/ssh.rb +225 -0
- data/lib/db_sucker/application/container/validations.rb +53 -0
- data/lib/db_sucker/application/container/variation/accessors.rb +45 -0
- data/lib/db_sucker/application/container/variation/helpers.rb +21 -0
- data/lib/db_sucker/application/container/variation/worker_api.rb +65 -0
- data/lib/db_sucker/application/container/variation.rb +60 -0
- data/lib/db_sucker/application/container.rb +70 -0
- data/lib/db_sucker/application/container_collection.rb +47 -0
- data/lib/db_sucker/application/core.rb +222 -0
- data/lib/db_sucker/application/dispatch.rb +364 -0
- data/lib/db_sucker/application/evented_resultset.rb +149 -0
- data/lib/db_sucker/application/fake_channel.rb +22 -0
- data/lib/db_sucker/application/output_helper.rb +197 -0
- data/lib/db_sucker/application/sklaven_treiber/log_spool.rb +57 -0
- data/lib/db_sucker/application/sklaven_treiber/worker/accessors.rb +105 -0
- data/lib/db_sucker/application/sklaven_treiber/worker/core.rb +168 -0
- data/lib/db_sucker/application/sklaven_treiber/worker/helpers.rb +144 -0
- data/lib/db_sucker/application/sklaven_treiber/worker/io/base.rb +240 -0
- data/lib/db_sucker/application/sklaven_treiber/worker/io/file_copy.rb +81 -0
- data/lib/db_sucker/application/sklaven_treiber/worker/io/file_gunzip.rb +58 -0
- data/lib/db_sucker/application/sklaven_treiber/worker/io/file_import_sql.rb +80 -0
- data/lib/db_sucker/application/sklaven_treiber/worker/io/file_shasum.rb +49 -0
- data/lib/db_sucker/application/sklaven_treiber/worker/io/pv_wrapper.rb +73 -0
- data/lib/db_sucker/application/sklaven_treiber/worker/io/sftp_download.rb +57 -0
- data/lib/db_sucker/application/sklaven_treiber/worker/io/throughput.rb +219 -0
- data/lib/db_sucker/application/sklaven_treiber/worker/routines.rb +313 -0
- data/lib/db_sucker/application/sklaven_treiber/worker.rb +48 -0
- data/lib/db_sucker/application/sklaven_treiber.rb +281 -0
- data/lib/db_sucker/application/slot_pool.rb +137 -0
- data/lib/db_sucker/application/tie.rb +25 -0
- data/lib/db_sucker/application/window/core.rb +185 -0
- data/lib/db_sucker/application/window/dialog.rb +142 -0
- data/lib/db_sucker/application/window/keypad/core.rb +85 -0
- data/lib/db_sucker/application/window/keypad.rb +174 -0
- data/lib/db_sucker/application/window/prompt.rb +124 -0
- data/lib/db_sucker/application/window.rb +329 -0
- data/lib/db_sucker/application.rb +168 -0
- data/lib/db_sucker/patches/beta-warning.rb +374 -0
- data/lib/db_sucker/patches/developer.rb +29 -0
- data/lib/db_sucker/patches/net-sftp.rb +20 -0
- data/lib/db_sucker/patches/thread-count.rb +30 -0
- data/lib/db_sucker/version.rb +4 -0
- data/lib/db_sucker.rb +81 -0
- metadata +217 -0
@@ -0,0 +1,149 @@
|
|
1
|
+
module DbSucker
|
2
|
+
class Application
|
3
|
+
class EventedResultset
|
4
|
+
SetAlreadyClosedError = Class.new(::RuntimeError)
|
5
|
+
include Enumerable
|
6
|
+
|
7
|
+
def initialize
|
8
|
+
@store = []
|
9
|
+
@monitor = Monitor.new
|
10
|
+
@closed = false
|
11
|
+
@close_signal = @monitor.new_cond
|
12
|
+
@value_signal = @monitor.new_cond
|
13
|
+
end
|
14
|
+
|
15
|
+
def sync &block
|
16
|
+
@monitor.synchronize(&block)
|
17
|
+
end
|
18
|
+
|
19
|
+
def enq data, group = nil
|
20
|
+
sync do
|
21
|
+
raise SetAlreadyClosedError, "failed to enqueue data: resultset is already closed" if closed?
|
22
|
+
@store << [group.try(:to_sym), data]
|
23
|
+
@value_signal.broadcast
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def push *args
|
28
|
+
sync do
|
29
|
+
args.each {|a| enq(a) }
|
30
|
+
@store
|
31
|
+
end
|
32
|
+
end
|
33
|
+
alias_method :<<, :push
|
34
|
+
|
35
|
+
def close!
|
36
|
+
sync do
|
37
|
+
@closed = true
|
38
|
+
@value_signal.broadcast
|
39
|
+
@close_signal.broadcast
|
40
|
+
end
|
41
|
+
true
|
42
|
+
end
|
43
|
+
|
44
|
+
def closed?
|
45
|
+
sync { @closed }
|
46
|
+
end
|
47
|
+
|
48
|
+
def empty?
|
49
|
+
sync { @store.empty? }
|
50
|
+
end
|
51
|
+
|
52
|
+
def wait
|
53
|
+
sync do
|
54
|
+
return if closed?
|
55
|
+
@close_signal.wait
|
56
|
+
end
|
57
|
+
true
|
58
|
+
end
|
59
|
+
|
60
|
+
def for_group group
|
61
|
+
@store.map{|grp, data| grp == group.try(:to_sym) ? data : nil }.compact
|
62
|
+
end
|
63
|
+
|
64
|
+
def each &block
|
65
|
+
wait
|
66
|
+
if block
|
67
|
+
@store.each do |group, data|
|
68
|
+
block.call(data)
|
69
|
+
end
|
70
|
+
else
|
71
|
+
@store.map(&:second)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def join *a
|
76
|
+
wait
|
77
|
+
each.join(*a)
|
78
|
+
end
|
79
|
+
|
80
|
+
def [] which
|
81
|
+
wait
|
82
|
+
@store[which].try(:last)
|
83
|
+
end
|
84
|
+
|
85
|
+
def eachx &block
|
86
|
+
wait
|
87
|
+
@store.each(&block)
|
88
|
+
end
|
89
|
+
|
90
|
+
def each_line &block
|
91
|
+
Thread.current[self.object_id.to_s] = nil
|
92
|
+
loop do
|
93
|
+
data = gets
|
94
|
+
if !data
|
95
|
+
break if closed?
|
96
|
+
next
|
97
|
+
end
|
98
|
+
block.call(data)
|
99
|
+
end
|
100
|
+
ensure
|
101
|
+
Thread.current[self.object_id.to_s] = nil
|
102
|
+
end
|
103
|
+
|
104
|
+
def each_linex &block
|
105
|
+
Thread.current[self.object_id.to_s] = nil
|
106
|
+
loop do
|
107
|
+
group, entry = getx
|
108
|
+
unless entry
|
109
|
+
break if closed?
|
110
|
+
next
|
111
|
+
end
|
112
|
+
block.call(group, entry)
|
113
|
+
end
|
114
|
+
ensure
|
115
|
+
Thread.current[self.object_id.to_s] = nil
|
116
|
+
end
|
117
|
+
|
118
|
+
def gets
|
119
|
+
sync do
|
120
|
+
Thread.current[self.object_id.to_s] ||= -1
|
121
|
+
if !closed? && !@store[Thread.current[self.object_id.to_s]+1]
|
122
|
+
@value_signal.wait
|
123
|
+
end
|
124
|
+
if @store[Thread.current[self.object_id.to_s]+1]
|
125
|
+
Thread.current[self.object_id.to_s] += 1
|
126
|
+
@store[Thread.current[self.object_id.to_s]].try(:last)
|
127
|
+
else
|
128
|
+
false
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
def getx
|
134
|
+
sync do
|
135
|
+
Thread.current[self.object_id.to_s] ||= -1
|
136
|
+
if !closed? && !@store[Thread.current[self.object_id.to_s]+1]
|
137
|
+
@value_signal.wait
|
138
|
+
end
|
139
|
+
if @store[Thread.current[self.object_id.to_s]+1]
|
140
|
+
Thread.current[self.object_id.to_s] += 1
|
141
|
+
@store[Thread.current[self.object_id.to_s]]
|
142
|
+
else
|
143
|
+
false
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|
149
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module DbSucker
|
2
|
+
class Application
|
3
|
+
class FakeChannel
|
4
|
+
def initialize &block
|
5
|
+
@storage = {}
|
6
|
+
@termination = block
|
7
|
+
end
|
8
|
+
|
9
|
+
def [] k
|
10
|
+
@storage[k]
|
11
|
+
end
|
12
|
+
|
13
|
+
def []= k, v
|
14
|
+
@storage[k] = v
|
15
|
+
end
|
16
|
+
|
17
|
+
def alive?
|
18
|
+
!@termination.try(:call, self)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,197 @@
|
|
1
|
+
module DbSucker
|
2
|
+
class Application
|
3
|
+
module OutputHelper
|
4
|
+
def self.hook ctx
|
5
|
+
[:puts, :print, :warn, :debug, :log, :warning, :error, :abort, :decolorize, :db_table_listing, :print_db_table_list, :decolorize, :render_table, :f_percentage, :human_bytes, :human_number, :human_percentage, :human_seconds, :human_seconds2, :rll, :c, :strbool].each do |meth|
|
6
|
+
ctx.__send__(:define_method, meth) do |*a|
|
7
|
+
Thread.main[:app].__send__(meth, *a)
|
8
|
+
end
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
def puts *a
|
13
|
+
outsync { @opts[:stdout].send(:puts, *a) }
|
14
|
+
end
|
15
|
+
|
16
|
+
def print *a
|
17
|
+
outsync { @opts[:stdout].send(:print, *a) }
|
18
|
+
end
|
19
|
+
|
20
|
+
def warn *a
|
21
|
+
outsync { @opts[:stdout].send(:warn, *a) }
|
22
|
+
end
|
23
|
+
|
24
|
+
def debug msg, lvl = 1
|
25
|
+
puts c("[DEBUG] #{msg}", :black) if @opts[:debug] && @opts[:debug] >= lvl
|
26
|
+
end
|
27
|
+
|
28
|
+
def log msg
|
29
|
+
puts c("#{msg}")
|
30
|
+
end
|
31
|
+
|
32
|
+
def warning msg
|
33
|
+
warn c("[WARN] #{msg}", :red)
|
34
|
+
end
|
35
|
+
|
36
|
+
def error msg
|
37
|
+
warn c("[ERROR] #{msg}", :red)
|
38
|
+
end
|
39
|
+
|
40
|
+
def abort msg, exit_code = 1
|
41
|
+
warn c("[ABORT] #{msg}", :red)
|
42
|
+
exit(exit_code) if exit_code
|
43
|
+
end
|
44
|
+
|
45
|
+
def rll
|
46
|
+
print "\033[A"
|
47
|
+
print "\033[2K\r"
|
48
|
+
end
|
49
|
+
|
50
|
+
def render_table table, headers = []
|
51
|
+
[].tap do |r|
|
52
|
+
col_sizes = table.map{|col| col.map{|i| decolorize(i.to_s) }.map(&:length).max }
|
53
|
+
headers.map{|i| decolorize(i.to_s) }.map(&:length).each_with_index do |length, header|
|
54
|
+
col_sizes[header] = [col_sizes[header] || 0, length || 0].max
|
55
|
+
end
|
56
|
+
|
57
|
+
# header
|
58
|
+
if headers.any?
|
59
|
+
r << [].tap do |line|
|
60
|
+
col_sizes.count.times do |col|
|
61
|
+
line << headers[col].ljust(col_sizes[col] + (headers[col].length - decolorize(headers[col]).length))
|
62
|
+
end
|
63
|
+
end.join(" | ")
|
64
|
+
r << "".ljust(col_sizes.inject(&:+) + ((col_sizes.count - 1) * 3), "-")
|
65
|
+
end
|
66
|
+
|
67
|
+
# records
|
68
|
+
table[0].count.times do |row|
|
69
|
+
r << [].tap do |line|
|
70
|
+
col_sizes.count.times do |col|
|
71
|
+
line << "#{table[col][row]}".ljust(col_sizes[col] + (table[col][row].to_s.length - decolorize(table[col][row]).length))
|
72
|
+
end
|
73
|
+
end.join(" | ")
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def db_table_listing col
|
79
|
+
col.each do |id, ccfg|
|
80
|
+
log ""
|
81
|
+
log "====================="
|
82
|
+
log "=== #{c id, :magenta}"
|
83
|
+
log "====================="
|
84
|
+
a, b = [], []
|
85
|
+
ccfg.variations.map do |name, vd|
|
86
|
+
a << c(name, :blue)
|
87
|
+
b << (vd.label.present? ? c(vd.label) : c("no label", :black))
|
88
|
+
end
|
89
|
+
render_table([a, b], [c("variation"), c("label")]).each{|l| log("#{l}") }
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def print_db_table_list host, dbs
|
94
|
+
log ""
|
95
|
+
log c(host, :red)
|
96
|
+
|
97
|
+
dbs.each_with_index do |db, i|
|
98
|
+
if db.is_a?(Array)
|
99
|
+
d = c(db[0], :magenta) << c(" (#{db[1].length})", :black)
|
100
|
+
if i == dbs.count - 1
|
101
|
+
log("#{db[1].any? ? "├──" : "└──"} #{d}")
|
102
|
+
else
|
103
|
+
log("├── #{d}")
|
104
|
+
end
|
105
|
+
|
106
|
+
table = render_table([db[1].map{|r| c(r[1], :cyan) }, db[1].map{|r| c(r[0], :green) }])
|
107
|
+
table.each_with_index do |l, i2|
|
108
|
+
if i2 == table.count - 1
|
109
|
+
log("│ └── #{l}")
|
110
|
+
else
|
111
|
+
log("│ ├── #{l}")
|
112
|
+
end
|
113
|
+
end
|
114
|
+
else
|
115
|
+
log " #{c db, :blue}"
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
def strbool v
|
121
|
+
v = true if ["true", "t", "1", "y", "yes", "on"].include?(v)
|
122
|
+
v = false if ["false", "f", "0", "n", "no", "off"].include?(v)
|
123
|
+
v
|
124
|
+
end
|
125
|
+
|
126
|
+
def f_percentage have, total, nn = 2
|
127
|
+
human_percentage(total == 0 ? 100 : have == 0 ? 0 : (have.to_d / total.to_d * 100.to_d), nn)
|
128
|
+
end
|
129
|
+
|
130
|
+
def human_bytes bytes
|
131
|
+
return false unless bytes
|
132
|
+
{
|
133
|
+
'B' => 1024,
|
134
|
+
'KB' => 1024 * 1024,
|
135
|
+
'MB' => 1024 * 1024 * 1024,
|
136
|
+
'GB' => 1024 * 1024 * 1024 * 1024,
|
137
|
+
'TB' => 1024 * 1024 * 1024 * 1024 * 1024
|
138
|
+
}.each_pair { |e, s| return "#{"%.2f" % (bytes.to_d / (s / 1024)).round(2)} #{e}" if bytes < s }
|
139
|
+
end
|
140
|
+
|
141
|
+
def human_number(n)
|
142
|
+
n.to_s.reverse.gsub(/...(?=.)/,'\&,').reverse
|
143
|
+
end
|
144
|
+
|
145
|
+
def human_percentage(n, nn = 2)
|
146
|
+
"%.#{nn}f%%" % n
|
147
|
+
end
|
148
|
+
|
149
|
+
def human_seconds secs, sprec = 0
|
150
|
+
t_minute = 60
|
151
|
+
t_hour = t_minute * 60
|
152
|
+
t_day = t_hour * 24
|
153
|
+
t_week = t_day * 7
|
154
|
+
t_month = t_day * 30
|
155
|
+
t_year = t_month * 12
|
156
|
+
"".tap do |r|
|
157
|
+
if secs >= t_year
|
158
|
+
r << "#{"%i" % (secs / t_year)}y "
|
159
|
+
secs = secs % t_year
|
160
|
+
end
|
161
|
+
|
162
|
+
if secs >= t_month
|
163
|
+
r << "#{"%i" % (secs / t_month)}m "
|
164
|
+
secs = secs % t_month
|
165
|
+
end
|
166
|
+
|
167
|
+
if secs >= t_week
|
168
|
+
r << "#{"%i" % (secs / t_week)}w "
|
169
|
+
secs = secs % t_week
|
170
|
+
end
|
171
|
+
|
172
|
+
if secs >= t_day || !r.blank?
|
173
|
+
r << "#{"%i" % (secs / t_day)}d "
|
174
|
+
secs = secs % t_day
|
175
|
+
end
|
176
|
+
|
177
|
+
if secs >= t_hour || !r.blank?
|
178
|
+
r << "#{"%i" % (secs / t_hour)}h "
|
179
|
+
secs = secs % t_hour
|
180
|
+
end
|
181
|
+
|
182
|
+
if secs >= t_minute || !r.blank?
|
183
|
+
r << "#{"%i" % (secs / t_minute)}m "
|
184
|
+
secs = secs % t_minute
|
185
|
+
end
|
186
|
+
|
187
|
+
r << "#{"%.#{sprec}f" % secs.round(sprec)}s" unless r.include?("d")
|
188
|
+
end.strip
|
189
|
+
end
|
190
|
+
|
191
|
+
def human_seconds2 secs
|
192
|
+
return "?:¿?:¿?" if secs.try(:infinite?)
|
193
|
+
Time.at(secs.round).utc.strftime("%k:%M:%S").strip
|
194
|
+
end
|
195
|
+
end
|
196
|
+
end
|
197
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
module DbSucker
|
2
|
+
class Application
|
3
|
+
class SklavenTreiber
|
4
|
+
class LogSpool
|
5
|
+
attr_reader :original, :spool
|
6
|
+
|
7
|
+
def initialize original
|
8
|
+
@original = original
|
9
|
+
@enabled = true
|
10
|
+
@spool = []
|
11
|
+
@monitor = Monitor.new
|
12
|
+
end
|
13
|
+
|
14
|
+
def sync
|
15
|
+
@monitor.synchronize { yield }
|
16
|
+
end
|
17
|
+
|
18
|
+
def enable
|
19
|
+
sync do
|
20
|
+
@enabled = true
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def clear
|
25
|
+
sync { @spool.clear }
|
26
|
+
end
|
27
|
+
|
28
|
+
def disable void_spool = false, &block
|
29
|
+
sync do
|
30
|
+
@enabled = false
|
31
|
+
(void_spool && clear) || (block && spooldown(&block))
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def spooldown
|
36
|
+
sync do
|
37
|
+
while e = @spool.shift
|
38
|
+
yield(e + [original])
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def puts *args
|
44
|
+
sync { @enabled ? (@spool << [:puts, args, Time.current]) : @original.send(:puts, *args) }
|
45
|
+
end
|
46
|
+
|
47
|
+
def print *args
|
48
|
+
sync { @enabled ? (@spool << [:print, args, Time.current]) : @original.send(:print, *args) }
|
49
|
+
end
|
50
|
+
|
51
|
+
def warn *args
|
52
|
+
sync { @enabled ? (@spool << [:warn, args, Time.current]) : @original.send(:warn, *args) }
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,105 @@
|
|
1
|
+
module DbSucker
|
2
|
+
class Application
|
3
|
+
class SklavenTreiber
|
4
|
+
class Worker
|
5
|
+
module Accessors
|
6
|
+
def pending?
|
7
|
+
@state == :pending
|
8
|
+
end
|
9
|
+
|
10
|
+
def done?
|
11
|
+
succeeded? || failed? || canceled?
|
12
|
+
end
|
13
|
+
|
14
|
+
def sshing?
|
15
|
+
@sshing
|
16
|
+
end
|
17
|
+
|
18
|
+
def failed?
|
19
|
+
@state == :failed
|
20
|
+
end
|
21
|
+
|
22
|
+
def succeeded?
|
23
|
+
@state == :done
|
24
|
+
end
|
25
|
+
|
26
|
+
def canceled?
|
27
|
+
@state == :canceled
|
28
|
+
end
|
29
|
+
|
30
|
+
def running?
|
31
|
+
@state == :running
|
32
|
+
end
|
33
|
+
|
34
|
+
def paused?
|
35
|
+
@state == :paused
|
36
|
+
end
|
37
|
+
|
38
|
+
def pausing?
|
39
|
+
@state == :pausing
|
40
|
+
end
|
41
|
+
|
42
|
+
def deferred?
|
43
|
+
@deferred
|
44
|
+
end
|
45
|
+
|
46
|
+
def status
|
47
|
+
@status
|
48
|
+
end
|
49
|
+
|
50
|
+
def state
|
51
|
+
@state
|
52
|
+
end
|
53
|
+
|
54
|
+
def trxid
|
55
|
+
sklaventreiber.trxid
|
56
|
+
end
|
57
|
+
|
58
|
+
def app
|
59
|
+
sklaventreiber.app
|
60
|
+
end
|
61
|
+
|
62
|
+
def descriptive
|
63
|
+
"#{ctn.source["database"]}-#{table}"
|
64
|
+
end
|
65
|
+
|
66
|
+
def identifier
|
67
|
+
"#{trxid}_table"
|
68
|
+
end
|
69
|
+
|
70
|
+
def to_s
|
71
|
+
"#<#{self.class}:#{self.object_id}-#{descriptive}(#{@state})>"
|
72
|
+
end
|
73
|
+
|
74
|
+
def tmp_filename tmp_suffix = false
|
75
|
+
"#{ctn.tmp_path}/#{trxid}_#{ctn.source["database"]}_#{table}.dbsc#{".tmp" if tmp_suffix}"
|
76
|
+
end
|
77
|
+
|
78
|
+
def local_tmp_path
|
79
|
+
sklaventreiber.app.core_tmp_path
|
80
|
+
end
|
81
|
+
|
82
|
+
def local_tmp_file file
|
83
|
+
"#{local_tmp_path}/#{file}"
|
84
|
+
end
|
85
|
+
|
86
|
+
def spinner_frame
|
87
|
+
@spinner_frames.unshift(@spinner_frames.pop)[0]
|
88
|
+
end
|
89
|
+
|
90
|
+
def copy_file_destination dstfile
|
91
|
+
d, dt = Time.current.strftime("%Y-%m-%d"), Time.current.strftime("%H-%M-%S")
|
92
|
+
|
93
|
+
File.expand_path dstfile.dup
|
94
|
+
.gsub!(":combined", ":datetime_-_:table")
|
95
|
+
.gsub!(":datetime", "#{d}_#{dt}")
|
96
|
+
.gsub!(":date", d)
|
97
|
+
.gsub!(":time", dt)
|
98
|
+
.gsub!(":table", table)
|
99
|
+
.gsub!(":id", sklaventreiber.trxid)
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
@@ -0,0 +1,168 @@
|
|
1
|
+
module DbSucker
|
2
|
+
class Application
|
3
|
+
class SklavenTreiber
|
4
|
+
class Worker
|
5
|
+
module Core
|
6
|
+
def sync
|
7
|
+
@monitor.synchronize { yield }
|
8
|
+
end
|
9
|
+
|
10
|
+
def aquire thread
|
11
|
+
@thread = thread
|
12
|
+
thread[:current_task] = descriptive
|
13
|
+
thread[:current_worker] = self
|
14
|
+
if m = thread[:managed_worker]
|
15
|
+
debug "Consumer thread ##{m} aquired worker #{descriptive}"
|
16
|
+
else
|
17
|
+
debug "Main thread aquired worker #{descriptive}"
|
18
|
+
end
|
19
|
+
@status = ["initializing...", "gray"]
|
20
|
+
@state = :aquired
|
21
|
+
self
|
22
|
+
end
|
23
|
+
|
24
|
+
def pause wait = false
|
25
|
+
sync do
|
26
|
+
return if done?
|
27
|
+
return if @state == :pausing || @state == :paused
|
28
|
+
@pause_data = { state_was: @state, signal: @monitor.new_cond }
|
29
|
+
if @state == :pending
|
30
|
+
@state = :paused
|
31
|
+
else
|
32
|
+
@state = :pausing
|
33
|
+
@pause_data[:signal].wait if wait
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def _pausepoint
|
39
|
+
sync do
|
40
|
+
return if !(@state == :pausing || @state == :paused)
|
41
|
+
return unless @pause_data
|
42
|
+
return unless @thread == Thread.current
|
43
|
+
@state = :paused
|
44
|
+
@pause_data[:signal].broadcast
|
45
|
+
end
|
46
|
+
@thread[:paused] = true
|
47
|
+
Thread.stop
|
48
|
+
@thread[:paused] = false
|
49
|
+
_cancelpoint
|
50
|
+
end
|
51
|
+
|
52
|
+
def unpause
|
53
|
+
sync do
|
54
|
+
return if !(@state == :pausing || @state == :paused)
|
55
|
+
return unless @pause_data
|
56
|
+
@state = @pause_data[:state_was]
|
57
|
+
@pause_data = false
|
58
|
+
@thread.wakeup if @thread
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def cancel! reason = nil, now = false
|
63
|
+
return if done?
|
64
|
+
@should_cancel = reason || true
|
65
|
+
unpause
|
66
|
+
sync { _cancelpoint(reason) if pending? || now }
|
67
|
+
end
|
68
|
+
|
69
|
+
def fail! reason, now = false
|
70
|
+
@status = ["FAILED(#{@current_perform}) #{reason}", "red"]
|
71
|
+
@state = :failed
|
72
|
+
throw :abort_execution, true if now
|
73
|
+
end
|
74
|
+
|
75
|
+
def _cancelpoint reason = nil
|
76
|
+
if @should_cancel
|
77
|
+
reason ||= @should_cancel if @should_cancel.is_a?(String)
|
78
|
+
reason ||= @status[0]
|
79
|
+
@should_cancel = false
|
80
|
+
@state = :canceled
|
81
|
+
@status = ["CANCELED#{" (was #{reason.to_s.gsub("[CLOSING] ", "")})" if reason}", "red"]
|
82
|
+
throw :abort_execution, true
|
83
|
+
true
|
84
|
+
elsif @state == :failed
|
85
|
+
throw :abort_execution, true
|
86
|
+
true
|
87
|
+
end
|
88
|
+
_pausepoint
|
89
|
+
end
|
90
|
+
|
91
|
+
def priority
|
92
|
+
100 - ({
|
93
|
+
running: 50,
|
94
|
+
aquired: 50,
|
95
|
+
pausing: 50,
|
96
|
+
paused: 30,
|
97
|
+
canceled: 35,
|
98
|
+
pending: 30,
|
99
|
+
failed: 20,
|
100
|
+
done: 10,
|
101
|
+
}[@state] || 0)
|
102
|
+
end
|
103
|
+
|
104
|
+
def run
|
105
|
+
@state = :running
|
106
|
+
@sshing = true
|
107
|
+
@started = Time.current
|
108
|
+
@download_state = { state: :idle, offset: 0 }
|
109
|
+
@remote_files_to_remove = []
|
110
|
+
@local_files_to_remove = []
|
111
|
+
@current_perform = nil
|
112
|
+
|
113
|
+
app.fire(:worker_routine_before_all, self)
|
114
|
+
catch :abort_execution do
|
115
|
+
perform.each_with_index do |m, i|
|
116
|
+
@current_perform = m
|
117
|
+
_cancelpoint
|
118
|
+
@step = i + 1
|
119
|
+
r = catch(:abort_execution) {
|
120
|
+
aquire_slots(*app.opts[:routine_pools][m.to_sym]) do
|
121
|
+
begin
|
122
|
+
r0 = Time.current
|
123
|
+
app.fire(:worker_routine_before, self, @current_perform)
|
124
|
+
send(:"_#{m}")
|
125
|
+
ensure
|
126
|
+
app.fire(:worker_routine_after, self, @current_perform)
|
127
|
+
@timings[m] = Time.current - r0
|
128
|
+
end
|
129
|
+
end
|
130
|
+
nil
|
131
|
+
}
|
132
|
+
throw :abort_execution if r
|
133
|
+
_cancelpoint
|
134
|
+
end
|
135
|
+
@status = ["DONE (#{runtime})", "green"]
|
136
|
+
end
|
137
|
+
rescue StandardError => ex
|
138
|
+
@exception = ex
|
139
|
+
fail! "#{ex.class}: #{ex.message}"
|
140
|
+
Thread.main[:app].notify_exception("SklavenTreiber::Worker encountered an error in `#{@current_perform}' (ctn: #{ctn.name}, var: #{var.name}, db: #{ctn.source["database"]}, table: #{table})", ex)
|
141
|
+
rescue Interrupt => ex
|
142
|
+
@state = :failed
|
143
|
+
ensure
|
144
|
+
# cleanup temp files
|
145
|
+
ctn.sftp_start do |sftp|
|
146
|
+
@remote_files_to_remove.each do |file|
|
147
|
+
sftp.remove!(file) rescue false
|
148
|
+
end
|
149
|
+
end if @remote_files_to_remove.any?
|
150
|
+
|
151
|
+
# cleanup local temp files
|
152
|
+
@local_files_to_remove.each do |file|
|
153
|
+
File.unlink(file) rescue false
|
154
|
+
end
|
155
|
+
|
156
|
+
app.fire(:worker_routine_after_all, self)
|
157
|
+
@state = :done if !canceled? && !failed?
|
158
|
+
@ended = Time.current
|
159
|
+
@sshing = false
|
160
|
+
|
161
|
+
# debug timings
|
162
|
+
debug "[Timings(#{table})] all: #{human_seconds(@timings.values.sum, 3)}, #{@timings.map{|a,t| "#{a}: #{human_seconds(t, 3)}" } * ", "}", 50
|
163
|
+
end
|
164
|
+
end
|
165
|
+
end
|
166
|
+
end
|
167
|
+
end
|
168
|
+
end
|