db_sucker 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (60) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +16 -0
  3. data/CHANGELOG.md +45 -0
  4. data/Gemfile +4 -0
  5. data/LICENSE.txt +22 -0
  6. data/README.md +193 -0
  7. data/Rakefile +1 -0
  8. data/VERSION +1 -0
  9. data/bin/db_sucker +12 -0
  10. data/bin/db_sucker.sh +14 -0
  11. data/db_sucker.gemspec +29 -0
  12. data/doc/config_example.rb +53 -0
  13. data/doc/container_example.yml +150 -0
  14. data/lib/db_sucker/adapters/mysql2.rb +103 -0
  15. data/lib/db_sucker/application/colorize.rb +28 -0
  16. data/lib/db_sucker/application/container/accessors.rb +60 -0
  17. data/lib/db_sucker/application/container/ssh.rb +225 -0
  18. data/lib/db_sucker/application/container/validations.rb +53 -0
  19. data/lib/db_sucker/application/container/variation/accessors.rb +45 -0
  20. data/lib/db_sucker/application/container/variation/helpers.rb +21 -0
  21. data/lib/db_sucker/application/container/variation/worker_api.rb +65 -0
  22. data/lib/db_sucker/application/container/variation.rb +60 -0
  23. data/lib/db_sucker/application/container.rb +70 -0
  24. data/lib/db_sucker/application/container_collection.rb +47 -0
  25. data/lib/db_sucker/application/core.rb +222 -0
  26. data/lib/db_sucker/application/dispatch.rb +364 -0
  27. data/lib/db_sucker/application/evented_resultset.rb +149 -0
  28. data/lib/db_sucker/application/fake_channel.rb +22 -0
  29. data/lib/db_sucker/application/output_helper.rb +197 -0
  30. data/lib/db_sucker/application/sklaven_treiber/log_spool.rb +57 -0
  31. data/lib/db_sucker/application/sklaven_treiber/worker/accessors.rb +105 -0
  32. data/lib/db_sucker/application/sklaven_treiber/worker/core.rb +168 -0
  33. data/lib/db_sucker/application/sklaven_treiber/worker/helpers.rb +144 -0
  34. data/lib/db_sucker/application/sklaven_treiber/worker/io/base.rb +240 -0
  35. data/lib/db_sucker/application/sklaven_treiber/worker/io/file_copy.rb +81 -0
  36. data/lib/db_sucker/application/sklaven_treiber/worker/io/file_gunzip.rb +58 -0
  37. data/lib/db_sucker/application/sklaven_treiber/worker/io/file_import_sql.rb +80 -0
  38. data/lib/db_sucker/application/sklaven_treiber/worker/io/file_shasum.rb +49 -0
  39. data/lib/db_sucker/application/sklaven_treiber/worker/io/pv_wrapper.rb +73 -0
  40. data/lib/db_sucker/application/sklaven_treiber/worker/io/sftp_download.rb +57 -0
  41. data/lib/db_sucker/application/sklaven_treiber/worker/io/throughput.rb +219 -0
  42. data/lib/db_sucker/application/sklaven_treiber/worker/routines.rb +313 -0
  43. data/lib/db_sucker/application/sklaven_treiber/worker.rb +48 -0
  44. data/lib/db_sucker/application/sklaven_treiber.rb +281 -0
  45. data/lib/db_sucker/application/slot_pool.rb +137 -0
  46. data/lib/db_sucker/application/tie.rb +25 -0
  47. data/lib/db_sucker/application/window/core.rb +185 -0
  48. data/lib/db_sucker/application/window/dialog.rb +142 -0
  49. data/lib/db_sucker/application/window/keypad/core.rb +85 -0
  50. data/lib/db_sucker/application/window/keypad.rb +174 -0
  51. data/lib/db_sucker/application/window/prompt.rb +124 -0
  52. data/lib/db_sucker/application/window.rb +329 -0
  53. data/lib/db_sucker/application.rb +168 -0
  54. data/lib/db_sucker/patches/beta-warning.rb +374 -0
  55. data/lib/db_sucker/patches/developer.rb +29 -0
  56. data/lib/db_sucker/patches/net-sftp.rb +20 -0
  57. data/lib/db_sucker/patches/thread-count.rb +30 -0
  58. data/lib/db_sucker/version.rb +4 -0
  59. data/lib/db_sucker.rb +81 -0
  60. 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