mysql_manager 1.0.3 → 1.0.4

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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 7db617b7015f5a675990bef408681d12e9333c1e
4
+ data.tar.gz: 6c130b35e26811fd5defcdfba76efb38147aef0f
5
+ SHA512:
6
+ metadata.gz: ee25ebbb9fc2c754fb96b5edbe9ef157e6483be8954ab928ba008a77f1491686e1e523d9fb1afa2dbae7bb18df5cb6855115e2487f782f6dc068151db7a5eaa3
7
+ data.tar.gz: cb8de0cc6774c0762e963ccbd92fa2709e8d4c13c8a4528455992299e4c11ea853aad089eb4f4a74f677ef4b32ed99bcc5612392a7ea0ea4d30a343440832ae2
data/README.md CHANGED
@@ -22,7 +22,7 @@ Or install it yourself as:
22
22
 
23
23
  ## Usage
24
24
 
25
- Usage: mysql-manager options
25
+ Usage: bin/mysql-manager options
26
26
  --kill Kill queries based on specified criteria
27
27
  --kill:max-query-time TIME Kill queries that have been running for more than TIME
28
28
  --kill:user USER Kill queries matching USER (repeatable)
@@ -48,10 +48,17 @@ Or install it yourself as:
48
48
  --log:file FILE Write logs to FILE (default: STDERR)
49
49
  --log:age DAYS Rotate logs after DAYS pass (default: 7)
50
50
  --log:size SIZE Rotate logs after the grow past SIZE bytes
51
+ --hotcopy Perform a hotcopy
52
+ --hotcopy:data-dir PATH Rsync mysql data dir from PATH (default: /var/lib/mysql)
53
+ --hotcopy:backup-dir PATH Rsync to PATH (default: /tmp/mysql)
54
+ --hotcopy:rsync-args ARGS Arguments to pass to rsync (default: -av)
55
+ --hotcopy:rsync-bin bin Rsync executable path (default: rsync)
56
+ --hotcopy:rsync-ttl ttl Acceptable rsync execution time before performing table locks (default: 60)
51
57
  --dry-run Do not run statements which affect the state of the database when executed
52
58
  -V, --version Display version information
53
59
  -h, --help Display this screen
54
60
 
61
+
55
62
  ## How matching is done
56
63
 
57
64
  If you specify a simple string as a parameter (e.g. `--kill:user api`), this is interpreted as an exactly matching
@@ -78,6 +85,16 @@ Reload `/etc/my.cnf` without restarting MySQL:
78
85
 
79
86
  mysql-manager --reload-my-cnf --reload-my-cnf:config /etc/my.cnf --log:level DEBUG
80
87
 
88
+ Perform a hotcopy backup of the running mysql database using rsync to a remote server. It will repeated rsync the folder without locks until syncs take less than 30 seconds.
89
+
90
+ mysql-manager --hotcopy \
91
+ --hotcopy:data-dir /var/lib/mysql/ \
92
+ --hotcopy:backup-dir user@remote.host.com:/tmp/mysql/ \
93
+ --hotcopy:rsync-args "-av --exclude=*.err" \
94
+ --hotcopy:rsync-ttl 30 \
95
+ --db:user root \
96
+ --db:pass $MYSQL_ROOT_PASSWORD
97
+
81
98
  ## Contributors
82
99
 
83
100
  https://github.com/osterman/mysql_manager/graphs/contributors
@@ -40,6 +40,7 @@ module MysqlManager
40
40
  @options[:kill] = {}
41
41
  @options[:reload_my_cnf] = {}
42
42
  @options[:skip_replication_errors] = {}
43
+ @options[:hotcopy] = {}
43
44
 
44
45
  begin
45
46
  @optparse = OptionParser.new do |opts|
@@ -178,6 +179,40 @@ module MysqlManager
178
179
  @options[:log][:size] = size.to_i
179
180
  end
180
181
 
182
+ #
183
+ # Hotcopy
184
+ #
185
+
186
+ @options[:hotcopy][:execute] = false
187
+ opts.on( '--hotcopy', 'Perform a hotcopy') do
188
+ @options[:hotcopy][:execute] = true
189
+ end
190
+
191
+ @options[:hotcopy][:data_dir] = '/var/lib/mysql'
192
+ opts.on( '--hotcopy:data-dir PATH', "Rsync mysql data dir from PATH (default: #{@options[:hotcopy][:data_dir]})") do |path|
193
+ @options[:hotcopy][:data_dir] = path.to_s
194
+ end
195
+
196
+ @options[:hotcopy][:backup_dir] = '/tmp/mysql'
197
+ opts.on( '--hotcopy:backup-dir PATH', "Rsync to PATH (default: #{@options[:hotcopy][:backup_dir]})") do |path|
198
+ @options[:hotcopy][:backup_dir] = path.to_s
199
+ end
200
+
201
+ @options[:hotcopy][:rsync_args] = '-av'
202
+ opts.on( '--hotcopy:rsync-args ARGS', "Arguments to pass to rsync (default: #{@options[:hotcopy][:rsync_args]})") do |args|
203
+ @options[:hotcopy][:rsync_args] = Shellwords.split(args.to_s)
204
+ end
205
+
206
+ @options[:hotcopy][:rsync_bin] = 'rsync'
207
+ opts.on( '--hotcopy:rsync-bin bin', "Rsync executable path (default: #{@options[:hotcopy][:rsync_bin]})") do |bin|
208
+ @options[:hotcopy][:rsync_bin] = bin.to_s
209
+ end
210
+
211
+ @options[:hotcopy][:rsync_ttl] = 60
212
+ opts.on( '--hotcopy:rsync-ttl ttl', "Acceptable rsync execution time before performing table locks (default: #{@options[:hotcopy][:rsync_ttl]})") do |ttl|
213
+ @options[:hotcopy][:rsync_ttl] = ttl.to_i
214
+ end
215
+
181
216
  #
182
217
  # General options
183
218
  #
@@ -234,6 +269,9 @@ module MysqlManager
234
269
  when :reload_my_cnf
235
270
  @log.debug("about to call reload_my_cnf")
236
271
  @utilities.reload_my_cnf(options)
272
+ when :hotcopy
273
+ @log.debug("about to call hotcopy")
274
+ @utilities.hotcopy(options)
237
275
  end
238
276
  rescue DBI::DatabaseError => e
239
277
  @log.fatal(e.message)
@@ -20,6 +20,8 @@
20
20
  require 'dbi'
21
21
  require 'logger'
22
22
  require 'parseconfig'
23
+ require 'shellwords'
24
+ require 'open3'
23
25
 
24
26
  module MysqlManager
25
27
  class FileNotFoundException < Exception; end
@@ -214,5 +216,72 @@ module MysqlManager
214
216
  @log.error("Error code: #{e.err}: #{e.errstr}")
215
217
  end
216
218
  end
219
+
220
+ def run(cmd)
221
+ stdout, stderr, status = Open3.capture3(Shellwords.join(cmd))
222
+ if status != 0
223
+ puts stderr
224
+ raise Exception.new("execution of command failed")
225
+ end
226
+ return status
227
+ end
228
+
229
+ def hotcopy(options = {})
230
+ begin
231
+ t_start = Time.now.to_f
232
+ # ensure mysql directory exists
233
+ unless File.directory?(options[:data_dir])
234
+ raise FileNotFoundException.new("Unable to open mysql data dir #{options[:data_dir]}")
235
+ end
236
+
237
+ rsync = []
238
+ rsync << options[:rsync_bin]
239
+ rsync << options[:rsync_args]
240
+ rsync << options[:data_dir] + '/'
241
+ rsync << options[:backup_dir] + '/'
242
+
243
+ # create target directory if it does not already exist
244
+ unless File.directory?(options[:backup_dir])
245
+ @log.info("Creating #{options[:backup_dir]}")
246
+ run ['mkdir', '-p', options[:backup_dir]]
247
+ end
248
+
249
+ rsync.flatten!
250
+
251
+ t_rsync = Time.now.to_f
252
+ t_rsync_elapsed = Time.now.to_f
253
+ while t_rsync_elapsed > options[:rsync_ttl]
254
+ # first do a raw rsync without table locks
255
+ @log.info("Performing first-pass rsync without table locks")
256
+ run rsync
257
+ t_rsync_elapsed = Time.now.to_f - t_rsync
258
+ @log.info("Rsync took #{t_rsync_elapsed} seconds")
259
+ sleep 1
260
+ t_rsync = Time.now.to_f
261
+ end
262
+
263
+ # then flush tables with read lock
264
+ @log.info("Locking tables")
265
+ t_lock = Time.now.to_f
266
+ @dbh.do("FLUSH TABLES WITH READ LOCK") unless @dry_run
267
+
268
+ # re-sync the delats
269
+ @log.info("Performing second-pass rsync with table locks")
270
+ run rsync
271
+
272
+ # release locks
273
+ @log.info("Unlocking tables")
274
+ @dbh.do('UNLOCK TABLES')
275
+ @log.info("Total lock time #{Time.now.to_f - t_lock} seconds")
276
+ @log.info("Total elapsed time #{Time.now.to_f - t_start} seconds")
277
+
278
+ rescue DBI::DatabaseError => e
279
+ @log.debug(@dbh.last_statement)
280
+ @log.warn(e.message)
281
+ end
282
+ end
283
+
284
+
285
+
217
286
  end
218
287
  end
@@ -18,5 +18,5 @@
18
18
  # along with MySQL Manager. If not, see <http://www.gnu.org/licenses/>.
19
19
  #
20
20
  module MysqlManager
21
- VERSION = "1.0.3"
21
+ VERSION = "1.0.4"
22
22
  end
@@ -16,6 +16,7 @@ Gem::Specification.new do |gem|
16
16
  gem.require_paths = ["lib"]
17
17
  gem.version = MysqlManager::VERSION
18
18
  gem.add_runtime_dependency "parseconfig", ">= 1.0.2"
19
+ gem.add_runtime_dependency "mysql2", ">= 0.3.18"
19
20
  gem.add_runtime_dependency "dbi", ">= 0.4.5"
20
21
  gem.add_runtime_dependency "dbd-mysql", ">= 0.4.4"
21
22
  end
metadata CHANGED
@@ -1,77 +1,79 @@
1
- --- !ruby/object:Gem::Specification
1
+ --- !ruby/object:Gem::Specification
2
2
  name: mysql_manager
3
- version: !ruby/object:Gem::Version
4
- prerelease: false
5
- segments:
6
- - 1
7
- - 0
8
- - 3
9
- version: 1.0.3
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.4
10
5
  platform: ruby
11
- authors:
6
+ authors:
12
7
  - Erik Osterman
13
8
  autorequire:
14
9
  bindir: bin
15
10
  cert_chain: []
16
-
17
- date: 2012-08-09 00:00:00 -07:00
18
- default_executable:
19
- dependencies:
20
- - !ruby/object:Gem::Dependency
11
+ date: 2015-05-25 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
21
14
  name: parseconfig
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - '>='
18
+ - !ruby/object:Gem::Version
19
+ version: 1.0.2
20
+ type: :runtime
22
21
  prerelease: false
23
- requirement: &id001 !ruby/object:Gem::Requirement
24
- none: false
25
- requirements:
26
- - - ">="
27
- - !ruby/object:Gem::Version
28
- segments:
29
- - 1
30
- - 0
31
- - 2
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - '>='
25
+ - !ruby/object:Gem::Version
32
26
  version: 1.0.2
27
+ - !ruby/object:Gem::Dependency
28
+ name: mysql2
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - '>='
32
+ - !ruby/object:Gem::Version
33
+ version: 0.3.18
33
34
  type: :runtime
34
- version_requirements: *id001
35
- - !ruby/object:Gem::Dependency
36
- name: dbi
37
35
  prerelease: false
38
- requirement: &id002 !ruby/object:Gem::Requirement
39
- none: false
40
- requirements:
41
- - - ">="
42
- - !ruby/object:Gem::Version
43
- segments:
44
- - 0
45
- - 4
46
- - 5
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - '>='
39
+ - !ruby/object:Gem::Version
40
+ version: 0.3.18
41
+ - !ruby/object:Gem::Dependency
42
+ name: dbi
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - '>='
46
+ - !ruby/object:Gem::Version
47
47
  version: 0.4.5
48
48
  type: :runtime
49
- version_requirements: *id002
50
- - !ruby/object:Gem::Dependency
51
- name: dbd-mysql
52
49
  prerelease: false
53
- requirement: &id003 !ruby/object:Gem::Requirement
54
- none: false
55
- requirements:
56
- - - ">="
57
- - !ruby/object:Gem::Version
58
- segments:
59
- - 0
60
- - 4
61
- - 4
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - '>='
53
+ - !ruby/object:Gem::Version
54
+ version: 0.4.5
55
+ - !ruby/object:Gem::Dependency
56
+ name: dbd-mysql
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - '>='
60
+ - !ruby/object:Gem::Version
62
61
  version: 0.4.4
63
62
  type: :runtime
64
- version_requirements: *id003
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - '>='
67
+ - !ruby/object:Gem::Version
68
+ version: 0.4.4
65
69
  description: MySQL Manager is a utility to perform routine tasks on a MySQL database.
66
- email:
70
+ email:
67
71
  - e@osterman.com
68
- executables:
72
+ executables:
69
73
  - mysql-manager
70
74
  extensions: []
71
-
72
75
  extra_rdoc_files: []
73
-
74
- files:
76
+ files:
75
77
  - .gitignore
76
78
  - Gemfile
77
79
  - LICENSE
@@ -83,37 +85,30 @@ files:
83
85
  - lib/mysql_manager/utilities.rb
84
86
  - lib/mysql_manager/version.rb
85
87
  - mysql_manager.gemspec
86
- has_rdoc: true
87
88
  homepage: https://github.com/osterman/mysql_manager
88
- licenses:
89
+ licenses:
89
90
  - GPL3
91
+ metadata: {}
90
92
  post_install_message:
91
93
  rdoc_options: []
92
-
93
- require_paths:
94
+ require_paths:
94
95
  - lib
95
- required_ruby_version: !ruby/object:Gem::Requirement
96
- none: false
97
- requirements:
98
- - - ">="
99
- - !ruby/object:Gem::Version
100
- segments:
101
- - 0
102
- version: "0"
103
- required_rubygems_version: !ruby/object:Gem::Requirement
104
- none: false
105
- requirements:
106
- - - ">="
107
- - !ruby/object:Gem::Version
108
- segments:
109
- - 0
110
- version: "0"
96
+ required_ruby_version: !ruby/object:Gem::Requirement
97
+ requirements:
98
+ - - '>='
99
+ - !ruby/object:Gem::Version
100
+ version: '0'
101
+ required_rubygems_version: !ruby/object:Gem::Requirement
102
+ requirements:
103
+ - - '>='
104
+ - !ruby/object:Gem::Version
105
+ version: '0'
111
106
  requirements: []
112
-
113
107
  rubyforge_project:
114
- rubygems_version: 1.3.7
108
+ rubygems_version: 2.0.14
115
109
  signing_key:
116
- specification_version: 3
117
- summary: MySQL Manager is a utility to perform routine tasks such as restoring replication after errors, killing queries based on a set of criteria, or reloading my.cnf without restarting the database.
110
+ specification_version: 4
111
+ summary: MySQL Manager is a utility to perform routine tasks such as restoring replication
112
+ after errors, killing queries based on a set of criteria, or reloading my.cnf without
113
+ restarting the database.
118
114
  test_files: []
119
-