alterity 0.0.0 → 0.9.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.
- checksums.yaml +4 -4
- data/lib/alterity.rb +77 -1
- data/lib/alterity/configuration.rb +63 -0
- data/lib/alterity/mysql_client_additions.rb +11 -0
- data/lib/alterity/railtie.rb +30 -0
- data/lib/alterity/version.rb +2 -2
- data/spec/alterity_spec.rb +35 -1
- data/spec/bin/rails_app_migration_test.sh +7 -0
- data/spec/spec_helper.rb +4 -0
- metadata +13 -20
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 61d1573038462b1d92925a95f8b67d8c0d53ffc250c429a742bb9a3e19fe715a
|
4
|
+
data.tar.gz: e6cf943aae6b2ead8e6c04d335a63c1c1438c296381e066c513d0aefb99e6d13
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ea2e19e99394e1cacd831855e509363c4d164dabfe564ef7ac47945c61c9fc94071d965a3bc16efdd80530108fecd50ee582900d962f3fd63571ae1e6d71203f
|
7
|
+
data.tar.gz: bd8f34566ced8252a587804d966c02e850d41a4d0faaf88bd0279886ea58286c4d70a7ca15b7ceafc17934bdce34f69b459b7546a1603acaee4b91ab26f89933
|
data/lib/alterity.rb
CHANGED
@@ -1,4 +1,80 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
3
|
+
require "rails"
|
4
|
+
require "alterity/configuration"
|
5
|
+
require "alterity/mysql_client_additions"
|
6
|
+
require "alterity/railtie"
|
7
|
+
|
8
|
+
class Alterity
|
9
|
+
class << self
|
10
|
+
def process_sql_query(sql, &block)
|
11
|
+
case sql.strip
|
12
|
+
when /^alter table (?<table>.+?) (?<updates>.+)/i
|
13
|
+
execute_alter($~[:table], $~[:updates])
|
14
|
+
when /^create index (?<index>.+?) on (?<table>.+?) (?<updates>.+)/i
|
15
|
+
execute_alter($~[:table], "ADD INDEX #{$~[:index]} #{$~[:updates]}")
|
16
|
+
when /^create unique index (?<index>.+?) on (?<table>.+?) (?<updates>.+)/i
|
17
|
+
execute_alter($~[:table], "ADD UNIQUE INDEX #{$~[:index]} #{$~[:updates]}")
|
18
|
+
when /^drop index (?<index>.+?) on (?<table>.+)/i
|
19
|
+
execute_alter($~[:table], "DROP INDEX #{$~[:index]}")
|
20
|
+
else
|
21
|
+
block.call
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
# hooks
|
26
|
+
def before_running_migrations
|
27
|
+
state.migrating = true
|
28
|
+
set_database_config
|
29
|
+
prepare_replicas_dsns_table
|
30
|
+
end
|
31
|
+
|
32
|
+
def after_running_migrations
|
33
|
+
state.migrating = false
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
def execute_alter(table, updates)
|
39
|
+
altered_table = table.delete("`")
|
40
|
+
alter_argument = %("#{updates.gsub('"', '\\"').gsub('`', '\\\`')}")
|
41
|
+
prepared_command = config.command.call(config, altered_table, alter_argument).gsub(/\n/, "\\\n")
|
42
|
+
puts "[Alterity] Will execute: #{prepared_command}"
|
43
|
+
system(prepared_command)
|
44
|
+
end
|
45
|
+
|
46
|
+
def set_database_config
|
47
|
+
db_config_hash = ActiveRecord::Base.connection_db_config.configuration_hash
|
48
|
+
%i[host port database username password].each do |key|
|
49
|
+
config[key] = db_config_hash[key]
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
# Optional: Automatically set up table PT-OSC will monitor for replica lag.
|
54
|
+
def prepare_replicas_dsns_table
|
55
|
+
return if config.replicas_dsns_table.blank?
|
56
|
+
|
57
|
+
database = config.replicas_dsns_database
|
58
|
+
table = "#{database}.#{config.replicas_dsns_table}"
|
59
|
+
connection = ActiveRecord::Base.connection
|
60
|
+
connection.execute "CREATE DATABASE IF NOT EXISTS #{database}"
|
61
|
+
connection.execute <<~SQL
|
62
|
+
CREATE TABLE IF NOT EXISTS #{table} (
|
63
|
+
id INT(11) NOT NULL AUTO_INCREMENT,
|
64
|
+
parent_id INT(11) DEFAULT NULL,
|
65
|
+
dsn VARCHAR(255) NOT NULL,
|
66
|
+
PRIMARY KEY (id)
|
67
|
+
) ENGINE=InnoDB
|
68
|
+
SQL
|
69
|
+
connection.execute "TRUNCATE #{table}"
|
70
|
+
return if config.replicas_dsns.empty?
|
71
|
+
|
72
|
+
connection.execute <<~SQL
|
73
|
+
INSERT INTO #{table} (dsn)
|
74
|
+
#{config.replicas_dsns.map { |dsn| "('#{dsn}')" }.join(',')}
|
75
|
+
SQL
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
reset_state_and_configuration
|
4
80
|
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Alterity
|
4
|
+
Configuration = Struct.new(
|
5
|
+
:command,
|
6
|
+
:host, :port, :database, :username, :password,
|
7
|
+
:replicas_dsns_database, :replicas_dsns_table, :replicas_dsns
|
8
|
+
)
|
9
|
+
CurrentState = Struct.new(:migrating, :disabled)
|
10
|
+
cattr_accessor :state
|
11
|
+
cattr_accessor :config
|
12
|
+
|
13
|
+
class << self
|
14
|
+
def reset_state_and_configuration
|
15
|
+
self.config = Configuration.new
|
16
|
+
self.state = CurrentState.new
|
17
|
+
|
18
|
+
config.command = lambda { |config, altered_table, alter_argument|
|
19
|
+
<<~SHELL.squish
|
20
|
+
pt-online-schema-change
|
21
|
+
-h #{config.host}
|
22
|
+
-P #{config.port}
|
23
|
+
-u #{config.username}
|
24
|
+
--password=#{config.password}
|
25
|
+
--execute
|
26
|
+
D=#{config.database},t=#{altered_table}
|
27
|
+
--alter #{alter_argument}
|
28
|
+
SHELL
|
29
|
+
}
|
30
|
+
end
|
31
|
+
|
32
|
+
def configure
|
33
|
+
yield self
|
34
|
+
end
|
35
|
+
|
36
|
+
def command=(new_command)
|
37
|
+
config.command = new_command
|
38
|
+
end
|
39
|
+
|
40
|
+
def replicas_dsns_table(database:, table:, dsns:)
|
41
|
+
return ArgumentError.new("database & table must be present") if database.blank? || table.blank?
|
42
|
+
|
43
|
+
config.replicas_dsns_database = database
|
44
|
+
config.replicas_dsns_table = table
|
45
|
+
config.replicas_dsns = dsns.uniq.map do |dsn|
|
46
|
+
parts = dsn.split(",")
|
47
|
+
# automatically add default port
|
48
|
+
parts << "P=3306" unless parts.any? { |part| part.start_with?("P=") }
|
49
|
+
# automatically remove master
|
50
|
+
next if parts.include?("h=#{config.host}") && parts.include?("P=#{config.port}")
|
51
|
+
|
52
|
+
parts.join(",")
|
53
|
+
end.compact
|
54
|
+
end
|
55
|
+
|
56
|
+
def disable
|
57
|
+
state.disabled = true
|
58
|
+
yield
|
59
|
+
ensure
|
60
|
+
state.disabled = false
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Alterity
|
4
|
+
class Railtie < Rails::Railtie
|
5
|
+
railtie_name :alterity
|
6
|
+
|
7
|
+
rake_tasks do
|
8
|
+
namespace :alterity do
|
9
|
+
task :intercept_table_alterations do
|
10
|
+
Alterity.before_running_migrations
|
11
|
+
Rake::Task["alterity:stop_intercepting_table_alterations"].reenable
|
12
|
+
::Mysql2::Client.prepend(Alterity::MysqlClientAdditions)
|
13
|
+
end
|
14
|
+
|
15
|
+
task :stop_intercepting_table_alterations do
|
16
|
+
Rake::Task["alterity:intercept_table_alterations"].reenable
|
17
|
+
Alterity.after_running_migrations
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
unless %w[1 true].include?(ENV["DISABLE_ALTERITY"])
|
22
|
+
["migrate", "migrate:up", "migrate:down", "migrate:redo", "rollback"].each do |task|
|
23
|
+
Rake::Task["db:#{task}"].enhance(["alterity:intercept_table_alterations"]) do
|
24
|
+
Rake::Task["alterity:stop_intercepting_table_alterations"].invoke
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
data/lib/alterity/version.rb
CHANGED
data/spec/alterity_spec.rb
CHANGED
@@ -1,5 +1,39 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
RSpec.describe Alterity do
|
4
|
-
|
4
|
+
describe ".process_sql_query" do
|
5
|
+
it "executes command on table altering queries" do
|
6
|
+
[
|
7
|
+
["ALTER TABLE `users` ADD `col` VARCHAR(255)", "`users`", "ADD `col` VARCHAR(255)"],
|
8
|
+
["ALTER TABLE `users` ADD `col0` INT(11), DROP `col1`", "`users`", "ADD `col0` INT(11), DROP `col1`"],
|
9
|
+
["CREATE INDEX `idx_users_on_col` ON `users` (col)", "`users`", "ADD INDEX `idx_users_on_col` (col)"],
|
10
|
+
["CREATE UNIQUE INDEX `idx_users_on_col` ON `users` (col)", "`users`", "ADD UNIQUE INDEX `idx_users_on_col` (col)"],
|
11
|
+
["DROP INDEX `idx_users_on_col` ON `users`", "`users`", "DROP INDEX `idx_users_on_col`"],
|
12
|
+
["alter table users drop col", "users", "drop col"]
|
13
|
+
].each do |(query, expected_table, expected_updates)|
|
14
|
+
expected_block = proc {}
|
15
|
+
expect(expected_block).not_to receive(:call)
|
16
|
+
expect(Alterity).to receive(:execute_alter).with(expected_table, expected_updates)
|
17
|
+
Alterity.process_sql_query(query, &expected_block)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
it "ignores non-altering queries" do
|
22
|
+
[
|
23
|
+
"select * from users",
|
24
|
+
"insert into users values (1)",
|
25
|
+
"delete from users",
|
26
|
+
"begin",
|
27
|
+
"SHOW CREATE TABLE `users`",
|
28
|
+
"SHOW TABLE STATUS LIKE `users`",
|
29
|
+
"SHOW KEYS FROM `users`",
|
30
|
+
"SHOW FULL FIELDS FROM `users`"
|
31
|
+
].each do |query|
|
32
|
+
expected_block = proc {}
|
33
|
+
expect(expected_block).to receive(:call)
|
34
|
+
expect(Alterity).not_to receive(:execute_alter)
|
35
|
+
Alterity.process_sql_query(query, &expected_block)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
5
39
|
end
|
data/spec/spec_helper.rb
CHANGED
metadata
CHANGED
@@ -1,55 +1,43 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: alterity
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.9.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Chris Maximin
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-
|
11
|
+
date: 2021-04-05 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
|
-
name:
|
14
|
+
name: mysql2
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
17
|
- - ">="
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: '
|
20
|
-
- - "<"
|
21
|
-
- !ruby/object:Gem::Version
|
22
|
-
version: '8'
|
19
|
+
version: '0.3'
|
23
20
|
type: :runtime
|
24
21
|
prerelease: false
|
25
22
|
version_requirements: !ruby/object:Gem::Requirement
|
26
23
|
requirements:
|
27
24
|
- - ">="
|
28
25
|
- !ruby/object:Gem::Version
|
29
|
-
version: '
|
30
|
-
- - "<"
|
31
|
-
- !ruby/object:Gem::Version
|
32
|
-
version: '8'
|
26
|
+
version: '0.3'
|
33
27
|
- !ruby/object:Gem::Dependency
|
34
|
-
name:
|
28
|
+
name: rails
|
35
29
|
requirement: !ruby/object:Gem::Requirement
|
36
30
|
requirements:
|
37
|
-
- - "~>"
|
38
|
-
- !ruby/object:Gem::Version
|
39
|
-
version: '0.5'
|
40
31
|
- - ">="
|
41
32
|
- !ruby/object:Gem::Version
|
42
|
-
version:
|
33
|
+
version: '5'
|
43
34
|
type: :runtime
|
44
35
|
prerelease: false
|
45
36
|
version_requirements: !ruby/object:Gem::Requirement
|
46
37
|
requirements:
|
47
|
-
- - "~>"
|
48
|
-
- !ruby/object:Gem::Version
|
49
|
-
version: '0.5'
|
50
38
|
- - ">="
|
51
39
|
- !ruby/object:Gem::Version
|
52
|
-
version:
|
40
|
+
version: '5'
|
53
41
|
description: Execute your ActiveRecord migrations with Percona's pt-online-schema-change.
|
54
42
|
email:
|
55
43
|
- gems@chrismaximin.com
|
@@ -58,8 +46,12 @@ extensions: []
|
|
58
46
|
extra_rdoc_files: []
|
59
47
|
files:
|
60
48
|
- lib/alterity.rb
|
49
|
+
- lib/alterity/configuration.rb
|
50
|
+
- lib/alterity/mysql_client_additions.rb
|
51
|
+
- lib/alterity/railtie.rb
|
61
52
|
- lib/alterity/version.rb
|
62
53
|
- spec/alterity_spec.rb
|
54
|
+
- spec/bin/rails_app_migration_test.sh
|
63
55
|
- spec/spec_helper.rb
|
64
56
|
homepage: https://github.com/gumroad/alterity
|
65
57
|
licenses:
|
@@ -89,4 +81,5 @@ specification_version: 4
|
|
89
81
|
summary: Execute your ActiveRecord migrations with Percona's pt-online-schema-change.
|
90
82
|
test_files:
|
91
83
|
- spec/alterity_spec.rb
|
84
|
+
- spec/bin/rails_app_migration_test.sh
|
92
85
|
- spec/spec_helper.rb
|