percona_migrator 0.1.0.rc.4 → 0.1.0.rc.5
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 +4 -4
- data/CHANGELOG.md +12 -2
- data/Gemfile +2 -0
- data/README.md +1 -1
- data/Rakefile +2 -2
- data/lib/active_record/connection_adapters/percona_adapter.rb +8 -5
- data/lib/percona_migrator/cli_generator.rb +3 -3
- data/lib/percona_migrator/runner.rb +56 -35
- data/lib/percona_migrator/version.rb +1 -1
- data/test_database.rb +32 -6
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e7301e478b66a555e4abe723cd9429914fba7820
|
4
|
+
data.tar.gz: fbf40c010ac9272f913b1e79285cb5f61fb11839
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 39543206254ddc6f383446adca091ead90f3ac4a879dad0f7b246b8443d3bbeadc5eb95500fa26040fb8c28ef6182ad94e0620a1a4e8dd593f885f057555ed41
|
7
|
+
data.tar.gz: 011386f3f3e8866fb9db4c19c98f9e050c4899168bb5c0eb9d78ddd56451ad5eeb89caaa3973f7574f21038b89dbcda4f511353103c760f577007043e8f82af0
|
data/CHANGELOG.md
CHANGED
@@ -6,13 +6,23 @@ Please follow the format in [Keep a Changelog](http://keepachangelog.com/)
|
|
6
6
|
|
7
7
|
## [Unreleased]
|
8
8
|
|
9
|
+
## [0.1.0.rc.5] - 2016-03-29
|
10
|
+
|
11
|
+
### Changed
|
12
|
+
|
13
|
+
- Raise a ActiveRecord::StatementInvalid on failed migration. It also provides
|
14
|
+
more detailed error message when possible such as pt-onlin-schema-change
|
15
|
+
being missing.
|
16
|
+
|
9
17
|
## [0.1.0.rc.4] - 2016-03-15
|
10
18
|
|
11
19
|
### Added
|
12
20
|
|
13
21
|
- Support #drop_table
|
14
|
-
- Support
|
15
|
-
|
22
|
+
- Support for foreing keys in db/schema.rb when using [Foreigner
|
23
|
+
gem](https://github.com/matthuhiggins/foreigner) in Rails 3 apps. This allows to
|
24
|
+
define foreign keys with #execute, but does not provide support for
|
25
|
+
#add_foreign_key yet.
|
16
26
|
|
17
27
|
## [0.1.0.rc.3] - 2016-03-10
|
18
28
|
|
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# PerconaMigrator [](https://travis-ci.org/redbooth/percona_migrator)
|
1
|
+
# PerconaMigrator [](https://travis-ci.org/redbooth/percona_migrator) [](https://codeclimate.com/github/redbooth/percona_migrator)
|
2
2
|
|
3
3
|
Percona Migrator is a tool for running online and non-blocking
|
4
4
|
DDL `ActiveRecord::Migrations` using `pt-online-schema-change` command-line tool of
|
data/Rakefile
CHANGED
@@ -6,12 +6,12 @@ require './test_database'
|
|
6
6
|
|
7
7
|
RSpec::Core::RakeTask.new(:spec)
|
8
8
|
|
9
|
-
task :
|
9
|
+
task default: :spec
|
10
10
|
|
11
11
|
namespace :db do
|
12
12
|
desc 'Create the test database'
|
13
13
|
task :create do
|
14
14
|
config = Configuration.new
|
15
|
-
TestDatabase.new(config).
|
15
|
+
TestDatabase.new(config).setup_test_database
|
16
16
|
end
|
17
17
|
end
|
@@ -83,7 +83,7 @@ module ActiveRecord
|
|
83
83
|
def add_column(table_name, column_name, type, options = {})
|
84
84
|
super
|
85
85
|
command = cli_generator.generate(table_name, @sql)
|
86
|
-
runner.execute(command)
|
86
|
+
log(@sql, nil) { runner.execute(command) }
|
87
87
|
end
|
88
88
|
|
89
89
|
# Removes the column(s) from the table definition
|
@@ -93,7 +93,7 @@ module ActiveRecord
|
|
93
93
|
def remove_column(table_name, *column_names)
|
94
94
|
super
|
95
95
|
command = cli_generator.generate(table_name, @sql)
|
96
|
-
runner.execute(command)
|
96
|
+
log(@sql, nil) { runner.execute(command) }
|
97
97
|
end
|
98
98
|
|
99
99
|
# Adds a new index to the table
|
@@ -106,7 +106,7 @@ module ActiveRecord
|
|
106
106
|
execute "ADD #{index_type} INDEX #{quote_column_name(index_name)} (#{index_columns})#{index_options}"
|
107
107
|
|
108
108
|
command = cli_generator.generate(table_name, @sql)
|
109
|
-
runner.execute(command)
|
109
|
+
log(@sql, nil) { runner.execute(command) }
|
110
110
|
end
|
111
111
|
|
112
112
|
# Remove the given index from the table.
|
@@ -118,7 +118,7 @@ module ActiveRecord
|
|
118
118
|
execute "DROP INDEX #{quote_column_name(index_name)}"
|
119
119
|
|
120
120
|
command = cli_generator.generate(table_name, @sql)
|
121
|
-
runner.execute(command)
|
121
|
+
log(@sql, nil) { runner.execute(command) }
|
122
122
|
end
|
123
123
|
|
124
124
|
# Records the SQL statement to be executed. This is used to then delegate
|
@@ -139,7 +139,7 @@ module ActiveRecord
|
|
139
139
|
def percona_execute(sql, name)
|
140
140
|
if alter_statement?(sql)
|
141
141
|
command = cli_generator.parse_statement(sql)
|
142
|
-
runner.execute(command)
|
142
|
+
log(sql, nil) { runner.execute(command) }
|
143
143
|
else
|
144
144
|
mysql_adapter.execute(sql, name)
|
145
145
|
end
|
@@ -154,6 +154,9 @@ module ActiveRecord
|
|
154
154
|
yield mysql_adapter.execute(sql, name)
|
155
155
|
end
|
156
156
|
|
157
|
+
def error_number(exception)
|
158
|
+
end
|
159
|
+
|
157
160
|
private
|
158
161
|
|
159
162
|
attr_reader :mysql_adapter, :logger, :runner, :cli_generator
|
@@ -47,7 +47,7 @@ module PerconaMigrator
|
|
47
47
|
end
|
48
48
|
|
49
49
|
# Generates the percona command. Fills all the connection credentials from
|
50
|
-
# the current AR connection, but that can amended via ENV-vars:
|
50
|
+
# the current AR connection, but that can be amended via ENV-vars:
|
51
51
|
# PERCONA_DB_HOST, PERCONA_DB_USER, PERCONA_DB_PASSWORD, PERCONA_DB_NAME
|
52
52
|
# Table name can't not be amended, it populates automatically from the
|
53
53
|
# migration data
|
@@ -59,7 +59,7 @@ module PerconaMigrator
|
|
59
59
|
alter_argument = AlterArgument.new(statement)
|
60
60
|
dsn = DSN.new(database, table_name)
|
61
61
|
|
62
|
-
"#{
|
62
|
+
"#{self} #{dsn} #{alter_argument}"
|
63
63
|
end
|
64
64
|
|
65
65
|
# Generates the percona command for a raw MySQL statement. Fills all the
|
@@ -74,7 +74,7 @@ module PerconaMigrator
|
|
74
74
|
alter_argument = AlterArgument.new(statement)
|
75
75
|
dsn = DSN.new(database, alter_argument.table_name)
|
76
76
|
|
77
|
-
"#{
|
77
|
+
"#{self} #{dsn} #{alter_argument}"
|
78
78
|
end
|
79
79
|
|
80
80
|
private
|
@@ -1,23 +1,48 @@
|
|
1
1
|
require 'open3'
|
2
2
|
|
3
3
|
module PerconaMigrator
|
4
|
+
class Error < StandardError; end
|
5
|
+
|
6
|
+
# Used when for whatever reason we couldn't get the spawned process'
|
7
|
+
# status.
|
8
|
+
class NoStatusError < Error
|
9
|
+
def message
|
10
|
+
'Status could not be retrieved'.freeze
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
# Used when the spawned process failed by receiving a signal.
|
15
|
+
# pt-online-schema-change returns "SIGSEGV (signal 11)" on failures.
|
16
|
+
class SignalError < Error
|
17
|
+
attr_reader :status
|
18
|
+
|
19
|
+
# Constructor
|
20
|
+
#
|
21
|
+
# @param status [Process::Status]
|
22
|
+
def initialize(status)
|
23
|
+
super
|
24
|
+
@status = status
|
25
|
+
end
|
26
|
+
|
27
|
+
def message
|
28
|
+
status.to_s
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
class CommandNotFoundError < Error
|
33
|
+
def message
|
34
|
+
'Please install pt-online-schema-change. Check: https://www.percona.com/doc/percona-toolkit for further details'
|
35
|
+
end
|
36
|
+
end
|
4
37
|
|
5
38
|
# It executes pt-online-schema-change commands in a new process and gets its
|
6
39
|
# output and status
|
7
40
|
class Runner
|
41
|
+
COMMAND_NOT_FOUND = 127
|
8
42
|
|
9
43
|
NONE = "\e[0m"
|
10
44
|
CYAN = "\e[38;5;86m"
|
11
45
|
GREEN = "\e[32m"
|
12
|
-
RED = "\e[31m"
|
13
|
-
|
14
|
-
# Executes the given command printing the output to the logger
|
15
|
-
#
|
16
|
-
# @param command [String]
|
17
|
-
# @param logger [IO]
|
18
|
-
def self.execute(command, logger)
|
19
|
-
new(command, logger).execute
|
20
|
-
end
|
21
46
|
|
22
47
|
# Constructor
|
23
48
|
#
|
@@ -33,11 +58,7 @@ module PerconaMigrator
|
|
33
58
|
# @return [Boolean]
|
34
59
|
def execute(command)
|
35
60
|
@command = command
|
36
|
-
|
37
|
-
log_started
|
38
|
-
run_command
|
39
|
-
log_finished
|
40
|
-
|
61
|
+
logging { run_command }
|
41
62
|
status
|
42
63
|
end
|
43
64
|
|
@@ -45,6 +66,15 @@ module PerconaMigrator
|
|
45
66
|
|
46
67
|
attr_reader :command, :logger, :status
|
47
68
|
|
69
|
+
# Logs the start and end of the execution
|
70
|
+
#
|
71
|
+
# @yield
|
72
|
+
def logging
|
73
|
+
log_started
|
74
|
+
yield
|
75
|
+
log_finished
|
76
|
+
end
|
77
|
+
|
48
78
|
# TODO: log as a migration logger subitem
|
49
79
|
#
|
50
80
|
# Logs when the execution started
|
@@ -56,33 +86,24 @@ module PerconaMigrator
|
|
56
86
|
#
|
57
87
|
# @raise [Errno::ENOENT] if pt-online-schema-change can't be found
|
58
88
|
def run_command
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
if status.nil?
|
65
|
-
Kernel.warn("Error running '#{command}': status could not be retrieved")
|
89
|
+
message = nil
|
90
|
+
Open3.popen3(command) do |_stdin, stdout, stderr, waith_thr|
|
91
|
+
@status = waith_thr.value
|
92
|
+
message = stderr.read
|
93
|
+
logger.info(stdout.read)
|
66
94
|
end
|
67
95
|
|
68
|
-
|
69
|
-
|
70
|
-
|
96
|
+
raise NoStatusError if status.nil?
|
97
|
+
raise SignalError.new(status) if status.signaled?
|
98
|
+
raise CommandNotFoundError if status.exitstatus == COMMAND_NOT_FOUND
|
71
99
|
|
72
|
-
|
73
|
-
raise Errno::ENOENT, "Please install pt-online-schema-change. Check: https://www.percona.com/doc/percona-toolkit"
|
100
|
+
raise Error, message unless status.success?
|
74
101
|
end
|
75
102
|
|
76
|
-
# Logs the status of the execution once it's finished
|
103
|
+
# Logs the status of the execution once it's finished. At this point we
|
104
|
+
# know it's a success
|
77
105
|
def log_finished
|
78
|
-
|
79
|
-
|
80
|
-
value = status.exitstatus
|
81
|
-
return unless value
|
82
|
-
|
83
|
-
message = value.zero? ? "#{GREEN}Done!#{NONE}" : "#{RED}Failed!#{NONE}"
|
84
|
-
|
85
|
-
logger.info("\n#{message}")
|
106
|
+
logger.info("\n#{GREEN}Done!#{NONE}")
|
86
107
|
end
|
87
108
|
end
|
88
109
|
end
|
data/test_database.rb
CHANGED
@@ -1,17 +1,33 @@
|
|
1
|
+
# Setups the test database with the schema_migrations table that ActiveRecord
|
2
|
+
# requires for the migrations, plus a table for the Comment model used throught
|
3
|
+
# the tests.
|
4
|
+
#
|
1
5
|
class TestDatabase
|
6
|
+
|
7
|
+
# Constructor
|
8
|
+
#
|
9
|
+
# @param config [Hash]
|
2
10
|
def initialize(config)
|
3
11
|
@config = config
|
4
12
|
end
|
5
13
|
|
6
|
-
# Creates the
|
7
|
-
|
8
|
-
|
9
|
-
|
14
|
+
# Creates the test database, the schema_migrations and the comments tables.
|
15
|
+
# It drops any of them if they already exist
|
16
|
+
def setup
|
17
|
+
setup_test_database
|
18
|
+
drop_and_create_schema_migrations_table
|
19
|
+
end
|
20
|
+
|
21
|
+
# Creates the percona_migrator_test database and the comments table in it.
|
22
|
+
# Before, it drops both if they already exist
|
23
|
+
def setup_test_database
|
24
|
+
drop_and_create_test_database
|
25
|
+
drop_and_create_comments_table
|
10
26
|
end
|
11
27
|
|
12
28
|
# Creates the ActiveRecord's schema_migrations table required for
|
13
|
-
# migrations to work
|
14
|
-
def
|
29
|
+
# migrations to work. Before, it drops the table if it already exists
|
30
|
+
def drop_and_create_schema_migrations_table
|
15
31
|
%x(#{mysql_command} "USE percona_migrator_test; DROP TABLE IF EXISTS schema_migrations; CREATE TABLE schema_migrations ( version varchar(255) COLLATE utf8_unicode_ci NOT NULL, UNIQUE KEY unique_schema_migrations (version)) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci")
|
16
32
|
end
|
17
33
|
|
@@ -19,6 +35,16 @@ class TestDatabase
|
|
19
35
|
|
20
36
|
attr_reader :config
|
21
37
|
|
38
|
+
def drop_and_create_test_database
|
39
|
+
%x(#{mysql_command} "DROP DATABASE IF EXISTS percona_migrator_test; CREATE DATABASE percona_migrator_test DEFAULT CHARACTER SET utf8 DEFAULT COLLATE utf8_unicode_ci;")
|
40
|
+
end
|
41
|
+
|
42
|
+
def drop_and_create_comments_table
|
43
|
+
%x(#{mysql_command} "USE percona_migrator_test; DROP TABLE IF EXISTS comments; CREATE TABLE comments ( id int(12) NOT NULL AUTO_INCREMENT, PRIMARY KEY (id)) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;")
|
44
|
+
end
|
45
|
+
|
46
|
+
# Returns the command to run the mysql client. It uses the crendentials from
|
47
|
+
# the provided config
|
22
48
|
def mysql_command
|
23
49
|
"mysql --user=#{config['username']} --password=#{config['password']} -e"
|
24
50
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: percona_migrator
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.0.rc.
|
4
|
+
version: 0.1.0.rc.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ilya Zayats
|
@@ -10,7 +10,7 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
|
-
date: 2016-03-
|
13
|
+
date: 2016-03-29 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: rails
|