mysql_truck 0.6.4 → 0.6.6
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +6 -0
- data/bin/mysql_truck +4 -0
- data/lib/mysql_truck/dumper.rb +1 -1
- data/lib/mysql_truck/loader.rb +26 -22
- data/lib/mysql_truck/version.rb +1 -1
- data/test/fake_aws.rb +69 -0
- data/test/lib/mysql_truck/loader_test.rb +82 -0
- data/test/test_helper.rb +4 -0
- metadata +9 -3
data/Gemfile
CHANGED
data/bin/mysql_truck
CHANGED
@@ -73,6 +73,10 @@ parser = OptionParser.new do |opts|
|
|
73
73
|
options[:skip_data_for_tables] = tables.split(",")
|
74
74
|
end
|
75
75
|
|
76
|
+
opts.on("--skip-all-data", "Only load schema") do
|
77
|
+
options[:skip_all_data] = true
|
78
|
+
end
|
79
|
+
|
76
80
|
opts.on("-o", "--only-tables TABLES",
|
77
81
|
"List of tables to dump/load.") do |tables|
|
78
82
|
options[:only_tables] = tables.split(",")
|
data/lib/mysql_truck/dumper.rb
CHANGED
@@ -49,7 +49,7 @@ module MysqlTruck
|
|
49
49
|
# Dump data
|
50
50
|
puts "Writing data for #{table} ..."
|
51
51
|
benchmark do
|
52
|
-
puts `mysql #{db_connection_options} --skip-column-names --batch -e "select * from #{table}" | lzop -6 > #{filename(table)[:compressed_data_file]} 2>/var/log/mysql_truck_dump.log`
|
52
|
+
puts `mysql #{db_connection_options} --skip-column-names --batch -e "select * from #{table}" | sed -e 's/\tNULL/\t\\\\N/g'| lzop -6 > #{filename(table)[:compressed_data_file]} 2>/var/log/mysql_truck_dump.log`
|
53
53
|
if $?.exitstatus != 0
|
54
54
|
puts "Command did not execute successfully"
|
55
55
|
end
|
data/lib/mysql_truck/loader.rb
CHANGED
@@ -19,7 +19,6 @@ module MysqlTruck
|
|
19
19
|
end
|
20
20
|
|
21
21
|
def load_backup
|
22
|
-
|
23
22
|
# Default to latest backup
|
24
23
|
backup_date_str = config[:backup_date] || backups.first.split("/").last
|
25
24
|
|
@@ -30,7 +29,6 @@ module MysqlTruck
|
|
30
29
|
puts "Download & decompressing backups"
|
31
30
|
puts "-------------------"
|
32
31
|
|
33
|
-
|
34
32
|
@bucket.keys(:prefix => s3_path).each do |key|
|
35
33
|
next if key.to_s.match(/\/$/)
|
36
34
|
next unless (filename = download_file(key))
|
@@ -68,7 +66,7 @@ module MysqlTruck
|
|
68
66
|
csv_data_file = tmp_path.join("#{table}.csv")
|
69
67
|
tsv_data_file = tmp_path.join("#{table}.data.tsv")
|
70
68
|
|
71
|
-
|
69
|
+
# Create table
|
72
70
|
if schema_file.exist?
|
73
71
|
print " - Loading schema: #{File.basename(schema_file)} ... "
|
74
72
|
execute_sql_file(table, backup_date_str, schema_file)
|
@@ -84,23 +82,26 @@ module MysqlTruck
|
|
84
82
|
next
|
85
83
|
end
|
86
84
|
|
87
|
-
|
88
|
-
if
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
85
|
+
# Load data
|
86
|
+
if !config[:skip_all_data]
|
87
|
+
if data_file.exist?
|
88
|
+
print " - Importing #{File.basename(data_file)} ... "
|
89
|
+
execute_sql_file(table, backup_date_str, data_file)
|
90
|
+
data_file.delete
|
91
|
+
|
92
|
+
elsif csv_data_file.exist?
|
93
|
+
print " - Importing #{File.basename(csv_data_file)} ... "
|
94
|
+
csv_data_file = import_csv_file(table, backup_date_str, csv_data_file)
|
95
|
+
File.delete(csv_data_file)
|
96
|
+
|
97
|
+
elsif tsv_data_file.exist?
|
98
|
+
print " - Importing #{File.basename(tsv_data_file)} ... "
|
99
|
+
tsv_data_file = import_tsv_file(table, backup_date_str, tsv_data_file)
|
100
|
+
File.delete(tsv_data_file)
|
101
|
+
end
|
102
102
|
end
|
103
103
|
|
104
|
+
# Add indices
|
104
105
|
if index_file.exist?
|
105
106
|
print " - Adding indices: #{File.basename(index_file)} ... "
|
106
107
|
execute_sql_file(table, backup_date_str, index_file)
|
@@ -191,8 +192,7 @@ module MysqlTruck
|
|
191
192
|
|
192
193
|
def should_download_file?(filename)
|
193
194
|
table_name = filename.gsub(/\..*\..*$/, '')
|
194
|
-
|
195
|
-
if only_tables.empty? and skip_data_for_tables.empty?
|
195
|
+
if only_tables.empty? and skip_data_for_tables.empty? and !config[:skip_all_data]
|
196
196
|
return true
|
197
197
|
end
|
198
198
|
|
@@ -202,7 +202,8 @@ module MysqlTruck
|
|
202
202
|
return only_tables.include?(table_name)
|
203
203
|
end
|
204
204
|
|
205
|
-
if filename.match(/\.data\.sql\.gz$/)
|
205
|
+
if filename.match(/\.data\.(sql|tsv)\.(lzo|gz)$/)
|
206
|
+
return false if config[:skip_all_data]
|
206
207
|
is_data = true
|
207
208
|
is_schema = false
|
208
209
|
else
|
@@ -210,8 +211,10 @@ module MysqlTruck
|
|
210
211
|
is_schema = true
|
211
212
|
end
|
212
213
|
|
214
|
+
return true if is_schema && config[:skip_all_data]
|
215
|
+
|
213
216
|
if !skip_data_for_tables.empty?
|
214
|
-
if is_schema
|
217
|
+
if is_schema || (is_data && !skip_data_for_tables.include?(table_name))
|
215
218
|
return true
|
216
219
|
end
|
217
220
|
end
|
@@ -237,6 +240,7 @@ module MysqlTruck
|
|
237
240
|
end
|
238
241
|
@backups = @backups.sort { |a,b| b <=> a }
|
239
242
|
end
|
243
|
+
|
240
244
|
@backups
|
241
245
|
end
|
242
246
|
|
data/lib/mysql_truck/version.rb
CHANGED
data/test/fake_aws.rb
ADDED
@@ -0,0 +1,69 @@
|
|
1
|
+
require 'hashie'
|
2
|
+
|
3
|
+
class FakeAws
|
4
|
+
|
5
|
+
def initialize
|
6
|
+
@files = []
|
7
|
+
end
|
8
|
+
|
9
|
+
def bucket(bucket_name)
|
10
|
+
BucketContent.new(bucket_name, @files)
|
11
|
+
end
|
12
|
+
|
13
|
+
def add_fake_file(path)
|
14
|
+
@files << path
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
18
|
+
|
19
|
+
class BucketContent
|
20
|
+
|
21
|
+
def initialize(name, data)
|
22
|
+
@data = data
|
23
|
+
@name = name
|
24
|
+
end
|
25
|
+
|
26
|
+
def name
|
27
|
+
@name
|
28
|
+
end
|
29
|
+
|
30
|
+
def s3
|
31
|
+
FakeS3.new(@data)
|
32
|
+
end
|
33
|
+
|
34
|
+
def keys(configs)
|
35
|
+
prefix = configs[:prefix]
|
36
|
+
@data.map do |file|
|
37
|
+
Hashie::Mash.new({name: file})
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
|
43
|
+
class FakeS3
|
44
|
+
|
45
|
+
def initialize(data)
|
46
|
+
@data = data
|
47
|
+
end
|
48
|
+
|
49
|
+
def interface
|
50
|
+
#TODO calculate common_prefixes
|
51
|
+
Interface.new(@data)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
class Interface
|
56
|
+
|
57
|
+
def initialize(data)
|
58
|
+
@data = data
|
59
|
+
end
|
60
|
+
|
61
|
+
def incrementally_list_bucket(bucket_name, options)
|
62
|
+
yield common_prefixes: [@data.first.split('/')[0..2].join('/') ]
|
63
|
+
end
|
64
|
+
|
65
|
+
def get(bucket_name, key_name)
|
66
|
+
yield 'content'
|
67
|
+
end
|
68
|
+
|
69
|
+
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
module MysqlTruck
|
4
|
+
class LoaderTest < Minitest::Unit::TestCase
|
5
|
+
|
6
|
+
def setup
|
7
|
+
mock_aws_with_fake_backups('2013-01-01', ['users', 'mixes'])
|
8
|
+
end
|
9
|
+
|
10
|
+
def test_loads_old_backups_with_only_tables
|
11
|
+
config = { s3_access_key: 'key', s3_secret_access_key: 'password', :only_tables => ['users'] }
|
12
|
+
loader = MysqlTruck::Loader.new(config)
|
13
|
+
|
14
|
+
assert loader.should_download_file?('users.data.sql.gz')
|
15
|
+
assert loader.should_download_file?('users.indices.sql.gz')
|
16
|
+
assert loader.should_download_file?('users.no_index.sql.gz')
|
17
|
+
|
18
|
+
|
19
|
+
refute loader.should_download_file?('mixes.data.sql.gz')
|
20
|
+
refute loader.should_download_file?('mixes.indices.sql.gz')
|
21
|
+
refute loader.should_download_file?('mixes.no_index.sql.gz')
|
22
|
+
end
|
23
|
+
|
24
|
+
def test_old_backups_with_skip_data_for_tables
|
25
|
+
config = { s3_access_key: 'key', s3_secret_access_key: 'password', :skip_data_for_tables => ['users'] }
|
26
|
+
loader = MysqlTruck::Loader.new(config)
|
27
|
+
|
28
|
+
|
29
|
+
#refutes data files for the ones you want to ignore
|
30
|
+
refute loader.should_download_file?('users.data.sql.gz')
|
31
|
+
assert loader.should_download_file?('users.indices.sql.gz')
|
32
|
+
assert loader.should_download_file?('users.no_index.sql.gz')
|
33
|
+
|
34
|
+
assert loader.should_download_file?('mixes.data.sql.gz')
|
35
|
+
assert loader.should_download_file?('mixes.indices.sql.gz')
|
36
|
+
assert loader.should_download_file?('mixes.no_index.sql.gz')
|
37
|
+
end
|
38
|
+
|
39
|
+
def test_new_backups_with_only_tables
|
40
|
+
config = { s3_access_key: 'key', s3_secret_access_key: 'password', :only_tables => ['users'] }
|
41
|
+
loader = MysqlTruck::Loader.new(config)
|
42
|
+
|
43
|
+
assert loader.should_download_file?('users.data.tsv.lzo')
|
44
|
+
assert loader.should_download_file?('users.indices.sql.lzo')
|
45
|
+
assert loader.should_download_file?('users.no_index.sql.lzo')
|
46
|
+
|
47
|
+
|
48
|
+
refute loader.should_download_file?('mixes.data.tsv.lzo')
|
49
|
+
refute loader.should_download_file?('mixes.indices.sql.lzo')
|
50
|
+
refute loader.should_download_file?('mixes.no_index.sql.lzo')
|
51
|
+
end
|
52
|
+
|
53
|
+
def test_new_backups_with_with_skip_data_for_tables
|
54
|
+
config = { s3_access_key: 'key', s3_secret_access_key: 'password', :skip_data_for_tables => ['users'] }
|
55
|
+
loader = MysqlTruck::Loader.new(config)
|
56
|
+
|
57
|
+
#refutes data files for the ones you want to ignore
|
58
|
+
refute loader.should_download_file?('users.data.tsv.lzo')
|
59
|
+
assert loader.should_download_file?('users.indices.sql.lzo')
|
60
|
+
assert loader.should_download_file?('users.no_index.sql.lzo')
|
61
|
+
|
62
|
+
assert loader.should_download_file?('mixes.data.tsv.lzo')
|
63
|
+
assert loader.should_download_file?('mixes.indices.sql.lzo')
|
64
|
+
assert loader.should_download_file?('mixes.no_index.sql.lzo')
|
65
|
+
end
|
66
|
+
|
67
|
+
def mock_aws_with_fake_backups(date, tables)
|
68
|
+
fake_aws = FakeAws.new
|
69
|
+
|
70
|
+
tables.each do |table|
|
71
|
+
base_name = "mysql/8trackscom/#{date}/#{table}"
|
72
|
+
|
73
|
+
fake_aws.add_fake_file("#{base_name}.data.sql.gz")
|
74
|
+
fake_aws.add_fake_file("#{base_name}.indices.sql.gz")
|
75
|
+
fake_aws.add_fake_file("#{base_name}.no_index.sql.gz")
|
76
|
+
end
|
77
|
+
|
78
|
+
RightAws::S3.expects(:new).returns(fake_aws)
|
79
|
+
end
|
80
|
+
|
81
|
+
end
|
82
|
+
end
|
data/test/test_helper.rb
ADDED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: mysql_truck
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.6.
|
4
|
+
version: 0.6.6
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -11,7 +11,7 @@ authors:
|
|
11
11
|
autorequire:
|
12
12
|
bindir: bin
|
13
13
|
cert_chain: []
|
14
|
-
date: 2013-02-
|
14
|
+
date: 2013-02-27 00:00:00.000000000 Z
|
15
15
|
dependencies:
|
16
16
|
- !ruby/object:Gem::Dependency
|
17
17
|
name: right_aws
|
@@ -66,6 +66,9 @@ files:
|
|
66
66
|
- lib/mysql_truck/loader.rb
|
67
67
|
- lib/mysql_truck/version.rb
|
68
68
|
- mysql_truck.gemspec
|
69
|
+
- test/fake_aws.rb
|
70
|
+
- test/lib/mysql_truck/loader_test.rb
|
71
|
+
- test/test_helper.rb
|
69
72
|
homepage: ''
|
70
73
|
licenses: []
|
71
74
|
post_install_message:
|
@@ -90,4 +93,7 @@ rubygems_version: 1.8.23
|
|
90
93
|
signing_key:
|
91
94
|
specification_version: 3
|
92
95
|
summary: Mysql database backup tool. Dumps/Loads to/from S3.
|
93
|
-
test_files:
|
96
|
+
test_files:
|
97
|
+
- test/fake_aws.rb
|
98
|
+
- test/lib/mysql_truck/loader_test.rb
|
99
|
+
- test/test_helper.rb
|