mysql_truck 0.5.7.1 → 0.6.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.
data/.gitignore CHANGED
@@ -2,3 +2,4 @@
2
2
  .bundle
3
3
  Gemfile.lock
4
4
  pkg/*
5
+ tags
data/bin/mysql_truck CHANGED
@@ -64,11 +64,20 @@ parser = OptionParser.new do |opts|
64
64
  options[:dump_dir] = dir
65
65
  end
66
66
 
67
+ opts.on("--grantees GRANTEE", "List of comma separated S3 account ids that can access this data") do |g|
68
+ options[:grantees] = g
69
+ end
70
+
67
71
  opts.on("-t", "--skip-tables TABLES",
68
72
  "List of tables to skip separated by commas.") do |tables|
69
73
  options[:skip_data_for_tables] = tables.split(",")
70
74
  end
71
75
 
76
+ opts.on("-o", "--only-tables TABLES",
77
+ "List of tables to dump/load.") do |tables|
78
+ options[:only_tables] = tables.split(",")
79
+ end
80
+
72
81
  opts.on("-e", "--exec-smartly",
73
82
  "On dumping, do not dump tables that have already been dumped. On loading, if the files were already downloaded, do not redownload. This option allows for resuming a previous load/dump that failed.") do
74
83
  options[:smartly] = true
@@ -22,11 +22,9 @@ module MysqlTruck
22
22
  def dump_data
23
23
  tables.each do |table|
24
24
  puts "Dumping #{table}..."
25
- next if gzip_files_exist?(table) && smartly?
25
+ next if compressed_files_exist?(table) && smartly?
26
26
 
27
27
  if dump_table?(table)
28
-
29
- # This command creates a table_name.sql and a table_name.txt file
30
28
  base_cmd = "mysqldump --quick"
31
29
 
32
30
  # Dump schema
@@ -37,11 +35,23 @@ module MysqlTruck
37
35
  `#{schema_cmd}`
38
36
 
39
37
  # Dump data
40
- data_cmd = "#{base_cmd} --no-create-info "
41
- data_cmd += "#{db_connection_options} #{table}"
42
- data_cmd += " > #{filename(table)[:data_file]}"
43
- puts data_cmd
44
- `#{data_cmd}`
38
+ puts "Writing data for #{table} ..."
39
+ mysql = Mysql.init
40
+ mysql.options(Mysql::SET_CHARSET_NAME, "utf8")
41
+ mysql.connect(
42
+ config[:host],
43
+ config[:username],
44
+ config[:password],
45
+ config[:database],
46
+ config[:port]
47
+ )
48
+ mysql.query("SET NAMES utf8")
49
+ CSV.open(filename(table)[:data_file], 'wb', :col_sep => "\t") do |csv|
50
+ result = mysql.query("SELECT * FROM #{table}")
51
+ result.each do |row|
52
+ csv << row.collect { |col| col && col.match(/\t/) ? " " : col }
53
+ end
54
+ end
45
55
  end
46
56
 
47
57
  if split_schema_file?(table)
@@ -64,15 +74,17 @@ module MysqlTruck
64
74
  end
65
75
  end
66
76
 
67
- if gzip_files?(table)
68
- puts "gzipping #{filename(table)[:no_index_schema_file]}."
69
- `gzip #{filename(table)[:no_index_schema_file]}`
77
+ if compress_files?(table)
78
+ puts "compressing #{filename(table)[:no_index_schema_file]}."
70
79
 
71
- puts "gzipping #{filename(table)[:alter_table_file]}."
72
- `gzip #{filename(table)[:alter_table_file]}`
80
+ compression_cmd = "lzop -8 -U"
81
+ `#{compression_cmd} #{filename(table)[:no_index_schema_file]}`
73
82
 
74
- puts "gziping #{filename(table)[:data_file]}."
75
- `gzip #{filename(table)[:data_file]}`
83
+ puts "compressing #{filename(table)[:alter_table_file]}."
84
+ `#{compression_cmd} #{filename(table)[:alter_table_file]}`
85
+
86
+ puts "compressing #{filename(table)[:data_file]}."
87
+ `#{compression_cmd} #{filename(table)[:data_file]}`
76
88
  end
77
89
 
78
90
  puts "#{table} dumped.\n\n"
@@ -81,7 +93,7 @@ module MysqlTruck
81
93
 
82
94
  def upload
83
95
  Dir["#{tmp_path}/*"].each do |file|
84
- next if File.extname(file) != ".gz"
96
+ next if File.extname(file) != ".lzo"
85
97
  puts "Uploading #{file} ..."
86
98
  upload_file file
87
99
  end
@@ -96,10 +108,31 @@ module MysqlTruck
96
108
 
97
109
  def upload_file(local_file)
98
110
  path = Pathname.new(local_file)
99
- s3_path = bucket_path.join(path.basename)
111
+
112
+ # Store all schema files in the <DATE>/schema/ dir
113
+ if local_file.match(/\.indices\.sql/) || local_file.match(/\.no_index\.sql/)
114
+ s3_path = bucket_path.join("_schema", path.basename)
115
+
116
+ # Store data files in <DATE>/<TABLE>/<TABLE>.data.tsv.lzo
117
+ elsif local_file.match(/\.data\.tsv/)
118
+ table_name = File.basename(local_file).match(/(.*)\.data\.tsv\.lzo/)[1]
119
+ s3_path = bucket_path.join(table_name, path.basename)
120
+ puts "Path: #{s3_path}"
121
+
122
+ else
123
+ puts "** Couldn't upload #{local_file}!!"
124
+ puts "** Couldn't upload #{local_file}!!"
125
+ return
126
+ end
127
+ puts s3_path
100
128
  @bucket.put(s3_path, open(path), {}, nil, {
101
129
  'x-amz-storage-class' => 'REDUCED_REDUNDANCY'
102
130
  })
131
+
132
+ key = RightAws::S3::Key.create(@bucket, s3_path)
133
+ config[:grantees].split(",").each do |grantee|
134
+ RightAws::S3::Grantee.new(key, grantee, "FULL_CONTROL", :apply)
135
+ end
103
136
  end
104
137
 
105
138
  def tables
@@ -124,19 +157,21 @@ module MysqlTruck
124
157
 
125
158
  # altered mysql dump files
126
159
  :alter_table_file => tmp_path.join("#{table}.indices.sql"),
127
- :data_file => tmp_path.join("#{table}.data.sql"),
128
160
 
129
- # Gzip filenames
130
- :gz_no_index_schema_file => tmp_path.join("#{table}.no_index.sql.gz"),
131
- :gz_alter_table_file => tmp_path.join("#{table}.indices.sql.gz"),
132
- :gz_data_file => tmp_path.join("#{table}.data.sql.gz"),
161
+ # Tab seperated data
162
+ :data_file => tmp_path.join("#{table}.data.tsv"),
163
+
164
+ # compressed filenames
165
+ :compressed_no_index_schema_file => tmp_path.join("#{table}.no_index.sql.lzo"),
166
+ :compressed_alter_table_file => tmp_path.join("#{table}.indices.sql.lzo"),
167
+ :compressed_data_file => tmp_path.join("#{table}.data.tsv.lzo"),
133
168
  }
134
169
  end
135
170
 
136
- def gzip_files_exist?(table)
137
- filename(table)[:gz_no_index_schema_file].file? &&
138
- filename(table)[:gz_alter_table_file].file? &&
139
- filename(table)[:gz_data_file].file?
171
+ def compressed_files_exist?(table)
172
+ filename(table)[:compressed_no_index_schema_file].file? &&
173
+ filename(table)[:compressed_alter_table_file].file? &&
174
+ filename(table)[:compressed_data_file].file?
140
175
  end
141
176
 
142
177
 
@@ -160,14 +195,14 @@ module MysqlTruck
160
195
  )
161
196
  end
162
197
 
163
- def gzip_files?(table)
198
+ def compress_files?(table)
164
199
  !smartly? ||
165
200
  (
166
201
  smartly? &&
167
202
  filename(table)[:data_file].exist? &&
168
203
  filename(table)[:no_index_schema_file].exist? &&
169
204
  filename(table)[:alter_table_file].exist? &&
170
- !gzip_files_exist?(table)
205
+ !compressed_files_exist?(table)
171
206
  )
172
207
  end
173
208
  end # class Dumper
@@ -18,6 +18,7 @@ module MysqlTruck
18
18
  opts = %Q[ -u #{config[:username]} ]
19
19
  opts += %Q[ -p"#{config[:password]}" ] unless config[:password].nil?
20
20
  opts += %Q[ -h #{config[:host]} --default-character-set=utf8 ]
21
+ opts += %Q[ -P #{config[:port]} ] if config[:port]
21
22
  opts += %Q[ #{config[:database]} ]
22
23
  opts
23
24
  end
@@ -27,17 +27,23 @@ module MysqlTruck
27
27
  @time = Time.new(*backup_date_str.split("-"))
28
28
  initialize_directories
29
29
 
30
- puts "Download & gunzip backups"
30
+ puts "Download & decompressing backups"
31
31
  puts "-------------------"
32
32
 
33
33
 
34
34
  @bucket.keys(:prefix => s3_path).each do |key|
35
+ next if key.to_s.match(/\/$/)
35
36
  next unless (filename = download_file(key))
36
37
 
37
- # gunzip file
38
38
  if tmp_path.join(filename).exist?
39
39
  print " - Inflating #{filename} ... "
40
- time = benchmark { `gunzip -f #{tmp_path.join(filename)}` }
40
+
41
+ if File.extname(filename).match(".lzo")
42
+ decompress_cmd = "lzop -d -f -U"
43
+ else
44
+ decompress_cmd = "gunzip -f"
45
+ end
46
+ time = benchmark { `#{decompress_cmd} #{tmp_path.join(filename)}` }
41
47
  print "complete (#{formatted_time time}).\n"
42
48
  end
43
49
  end
@@ -61,6 +67,7 @@ module MysqlTruck
61
67
  index_file = tmp_path.join("#{table}.indices.sql")
62
68
  data_file = tmp_path.join("#{table}.data.sql")
63
69
  csv_data_file = tmp_path.join("#{table}.csv")
70
+ tsv_data_file = tmp_path.join("#{table}.data.tsv")
64
71
 
65
72
 
66
73
  if schema_file.exist?
@@ -88,6 +95,11 @@ module MysqlTruck
88
95
  print " - Importing #{File.basename(csv_data_file)} ... "
89
96
  csv_data_file = import_csv_file(table, backup_date_str, csv_data_file)
90
97
  File.delete(csv_data_file)
98
+
99
+ elsif tsv_data_file.exist?
100
+ print " - Importing #{File.basename(tsv_data_file)} ... "
101
+ tsv_data_file = import_tsv_file(table, backup_date_str, tsv_data_file)
102
+ File.delete(tsv_data_file)
91
103
  end
92
104
 
93
105
  if index_file.exist?
@@ -95,7 +107,7 @@ module MysqlTruck
95
107
  execute_sql_file(table, backup_date_str, index_file)
96
108
  index_file.delete
97
109
  end
98
-
110
+
99
111
  end
100
112
 
101
113
  puts "Backup loaded."
@@ -138,6 +150,23 @@ module MysqlTruck
138
150
  file_path
139
151
  end
140
152
 
153
+ def import_tsv_file(table, backup_date_str, file_path)
154
+ if config[:date_suffix]
155
+ old_file_path = file_path
156
+ file_path = file_path.to_s.gsub("/#{table}.data.", "/#{table}_#{backup_date_str.gsub(/-/, "")}.")
157
+ # move file
158
+ `mv #{old_file_path} #{file_path}`
159
+ end
160
+
161
+ time = benchmark do
162
+ `mysqlimport --local --compress #{db_connection_options} #{file_path}`
163
+ end
164
+
165
+ print "complete (#{formatted_time time}).\n"
166
+
167
+ file_path
168
+ end
169
+
141
170
  def download_file(key)
142
171
  filename = File.basename(key.name)
143
172
 
@@ -1,3 +1,3 @@
1
1
  module MysqlTruck
2
- VERSION = "0.5.7.1"
2
+ VERSION = "0.6.0"
3
3
  end
data/lib/mysql_truck.rb CHANGED
@@ -1,4 +1,6 @@
1
1
  require "right_aws"
2
+ require "mysql"
3
+ require "csv"
2
4
  require "fileutils"
3
5
  require "pathname"
4
6
 
data/mysql_truck.gemspec CHANGED
@@ -16,5 +16,6 @@ Gem::Specification.new do |s|
16
16
  s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
17
17
  s.require_paths = ["lib"]
18
18
 
19
- s.add_runtime_dependency "right_aws"
19
+ s.add_runtime_dependency "right_aws", "~> 2.1.0"
20
+ s.add_runtime_dependency "mysql", "~> 2.8.1"
20
21
  end
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.5.7.1
4
+ version: 0.6.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -11,24 +11,40 @@ authors:
11
11
  autorequire:
12
12
  bindir: bin
13
13
  cert_chain: []
14
- date: 2012-12-15 00:00:00.000000000 Z
14
+ date: 2013-01-25 00:00:00.000000000 Z
15
15
  dependencies:
16
16
  - !ruby/object:Gem::Dependency
17
17
  name: right_aws
18
18
  requirement: !ruby/object:Gem::Requirement
19
19
  none: false
20
20
  requirements:
21
- - - ! '>='
21
+ - - ~>
22
22
  - !ruby/object:Gem::Version
23
- version: '0'
23
+ version: 2.1.0
24
24
  type: :runtime
25
25
  prerelease: false
26
26
  version_requirements: !ruby/object:Gem::Requirement
27
27
  none: false
28
28
  requirements:
29
- - - ! '>='
29
+ - - ~>
30
30
  - !ruby/object:Gem::Version
31
- version: '0'
31
+ version: 2.1.0
32
+ - !ruby/object:Gem::Dependency
33
+ name: mysql
34
+ requirement: !ruby/object:Gem::Requirement
35
+ none: false
36
+ requirements:
37
+ - - ~>
38
+ - !ruby/object:Gem::Version
39
+ version: 2.8.1
40
+ type: :runtime
41
+ prerelease: false
42
+ version_requirements: !ruby/object:Gem::Requirement
43
+ none: false
44
+ requirements:
45
+ - - ~>
46
+ - !ruby/object:Gem::Version
47
+ version: 2.8.1
32
48
  description: Mysql database backup tool. Dumps/Loads to/from S3.
33
49
  email:
34
50
  - peter@paydrotalks.com