class Alterity
Constants
- Configuration
- CurrentState
- VERSION
Public Class Methods
after_running_migrations()
click to toggle source
# File lib/alterity.rb, line 46 def after_running_migrations state.migrating = false end
before_running_migrations()
click to toggle source
hooks
# File lib/alterity.rb, line 39 def before_running_migrations require "open3" state.migrating = true set_database_config prepare_replicas_dsns_table end
configure() { |config| ... }
click to toggle source
# File lib/alterity/configuration.rb, line 38 def configure yield config end
disable() { || ... }
click to toggle source
# File lib/alterity/configuration.rb, line 42 def disable state.disabled = true yield ensure state.disabled = false end
process_sql_query(sql, &block)
click to toggle source
# File lib/alterity.rb, line 10 def process_sql_query(sql, &block) return block.call if state.disabled case sql.tr("\n", " ").strip when /^alter\s+table\s+(?<table>.+?)\s+(?<updates>.+)/i table = $~[:table] updates = $~[:updates] if updates.split(",").all? { |s| s =~ /^\s*drop\s+foreign\s+key/i } || updates.split(",").all? { |s| s =~ /^\s*add\s+constraint/i } block.call elsif updates =~ /drop\s+foreign\s+key/i || updates =~ /add\s+constraint/i # ADD CONSTRAINT / DROP FOREIGN KEY have to go to the original table, # other alterations need to got to the new table. raise "[Alterity] Can't change a FK and do something else in the same query. Split it." else execute_alter(table, updates) end when /^create\s+index\s+(?<index>.+?)\s+on\s+(?<table>.+?)\s+(?<updates>.+)/i execute_alter($~[:table], "ADD INDEX #{$~[:index]} #{$~[:updates]}") when /^create\s+unique\s+index\s+(?<index>.+?)\s+on\s+(?<table>.+?)\s+(?<updates>.+)/i execute_alter($~[:table], "ADD UNIQUE INDEX #{$~[:index]} #{$~[:updates]}") when /^drop\s+index\s+(?<index>.+?)\s+on\s+(?<table>.+)/i execute_alter($~[:table], "DROP INDEX #{$~[:index]}") else block.call end end
reset_state_and_configuration()
click to toggle source
# File lib/alterity/configuration.rb, line 17 def reset_state_and_configuration self.config = Configuration.new class << config def replicas(database:, table:, dsns:) return ArgumentError.new("database & table must be present") if database.blank? || table.blank? self.replicas_dsns_database = database self.replicas_dsns_table = table self.replicas_dsns = dsns.uniq.map do |dsn| parts = dsn.split(",") # automatically add default port parts << "P=3306" unless parts.any? { |part| part.start_with?("P=") } parts.join(",") end.compact end end self.state = CurrentState.new load "#{__dir__}/default_configuration.rb" end
Private Class Methods
execute_alter(table, updates)
click to toggle source
# File lib/alterity.rb, line 52 def execute_alter(table, updates) altered_table = table.delete("`") alter_argument = %("#{updates.gsub('"', '\\"').gsub('`', '\\\`')}") prepared_command = config.command.call(altered_table, alter_argument).to_s.gsub(/\n/, "\\\n") puts "[Alterity] Will execute: #{prepared_command}" config.before_command&.call(prepared_command) result_str = +"" exit_status = nil Open3.popen2e(prepared_command) do |_stdin, stdout_and_stderr, wait_thr| stdout_and_stderr.each do |line| puts line result_str << line config.on_command_output&.call(line) end exit_status = wait_thr.value config.after_command&.call(wait_thr.value.to_i) end raise("[Alterity] Command failed. Full output: #{result_str}") unless exit_status.success? end
prepare_replicas_dsns_table()
click to toggle source
Optional: Automatically set up table PT-OSC will monitor for replica lag.
# File lib/alterity.rb, line 82 def prepare_replicas_dsns_table return if config.replicas_dsns_table.blank? dsns = config.replicas_dsns.dup # Automatically remove master dsns.reject! { |dsn| dsn.split(",").include?("h=#{config.host}") } database = config.replicas_dsns_database table = "#{database}.#{config.replicas_dsns_table}" connection = ActiveRecord::Base.connection connection.execute "CREATE DATABASE IF NOT EXISTS #{database}" connection.execute <<~SQL CREATE TABLE IF NOT EXISTS #{table} ( id INT(11) NOT NULL AUTO_INCREMENT, parent_id INT(11) DEFAULT NULL, dsn VARCHAR(255) NOT NULL, PRIMARY KEY (id) ) ENGINE=InnoDB SQL connection.execute "TRUNCATE #{table}" return if dsns.empty? connection.execute <<~SQL INSERT INTO #{table} (dsn) VALUES #{dsns.map { |dsn| "('#{dsn}')" }.join(',')} SQL end
set_database_config()
click to toggle source
# File lib/alterity.rb, line 74 def set_database_config db_config_hash = ActiveRecord::Base.connection_db_config.configuration_hash %i[host port database username password].each do |key| config[key] = db_config_hash[key] end end