module Lhm

Large hadron migrator - online schema change tool

@example

Lhm.change_table(:users) do |m|
  m.add_column(:arbitrary, "INT(12)")
  m.add_index([:arbitrary, :created_at])
  m.ddl("alter table %s add column flag tinyint(1)" % m.name)
end

Copyright © 2011 - 2013, SoundCloud Ltd., Rany Keddo, Tobias Bielohlawek, Tobias Schmidt

Copyright © 2011 - 2013, SoundCloud Ltd., Rany Keddo, Tobias Bielohlawek, Tobias Schmidt

Copyright © 2011 - 2013, SoundCloud Ltd., Rany Keddo, Tobias Bielohlawek, Tobias Schmidt

Copyright © 2011 - 2013, SoundCloud Ltd., Rany Keddo, Tobias Bielohlawek, Tobias Schmidt

Constants

DEFAULT_LOGGER_OPTIONS
VERSION

Public Class Methods

execute_inline!() click to toggle source

Patch LHM to execute ALTER TABLE directly on original tables, without the online migration dance. This mode is designed for local/CI environments where we can speed things up by not invoking “real” LHM logic.

# File lib/lhm/test_support.rb, line 31
def self.execute_inline!
  Lhm::Migrator.prepend(TestMigrator)
  Lhm::Invoker.prepend(TestInvoker)
end
logger() click to toggle source
# File lib/lhm.rb, line 100
def self.logger
  @@logger ||=
    begin
      logger = Logger.new(DEFAULT_LOGGER_OPTIONS[:file])
      logger.level = DEFAULT_LOGGER_OPTIONS[:level]
      logger.formatter = nil
      logger
    end
end
logger=(new_logger) click to toggle source
# File lib/lhm.rb, line 96
def self.logger=(new_logger)
  @@logger = new_logger
end

Public Instance Methods

change_table(table_name, options = {}, &block) click to toggle source

Alters a table with the changes described in the block

@param [String, Symbol] table_name Name of the table @param [Hash] options Optional options to alter the chunk / switch behavior @option options [Integer] :stride

Size of a chunk (defaults to: 2,000)

@option options [Integer] :throttle

Time to wait between chunks in milliseconds (defaults to: 100)

@option options [Integer] :start

Primary Key position at which to start copying chunks

@option options [Integer] :limit

Primary Key position at which to stop copying chunks

@option options [Boolean] :atomic_switch

Use atomic switch to rename tables (defaults to: true)
If using a version of mysql affected by atomic switch bug, LHM forces user
to set this option (see SqlHelper#supports_atomic_switch?)

@yield [Migrator] Yielded Migrator object records the changes @return [Boolean] Returns true if the migration finishes @raise [Error] Raises Lhm::Error in case of a error and aborts the migration

# File lib/lhm.rb, line 50
def change_table(table_name, options = {}, &block)
  origin = Table.parse(table_name, connection)
  invoker = Invoker.new(origin, connection)
  block.call(invoker.migrator)
  invoker.run(options)
  true
end
cleanup(run = false, options = {}) click to toggle source

Cleanup tables and triggers

@param [Boolean] run execute now or just display information @param [Hash] options Optional options to alter cleanup behaviour @option options [Time] :until

Filter to only remove tables up to specified time (defaults to: nil)
# File lib/lhm.rb, line 64
def cleanup(run = false, options = {})
  lhm_tables = connection.select_values('show tables').select { |name| name =~ /^lhm(a|n)_/ }
  if options[:until]
    lhm_tables.select! do |table|
      table_date_time = Time.strptime(table, 'lhma_%Y_%m_%d_%H_%M_%S')
      table_date_time <= options[:until]
    end
  end

  lhm_triggers = connection.select_values('show triggers').collect do |trigger|
    trigger.respond_to?(:trigger) ? trigger.trigger : trigger
  end.select { |name| name =~ /^lhmt/ }

  drop_tables_and_triggers(run, lhm_triggers, lhm_tables)
end
cleanup_current_run(run, table_name, options = {}) click to toggle source
# File lib/lhm.rb, line 80
def cleanup_current_run(run, table_name, options = {})
  Lhm::Cleanup::Current.new(run, table_name, connection, options).execute
end
connection() click to toggle source
# File lib/lhm.rb, line 88
def connection
  @@connection ||=
    begin
      raise 'Please call Lhm.setup' unless defined?(ActiveRecord)
      ActiveRecord::Base.connection
    end
end
setup(connection) click to toggle source
# File lib/lhm.rb, line 84
def setup(connection)
  @@connection = connection
end

Private Instance Methods

drop_tables_and_triggers(run = false, triggers, tables) click to toggle source
# File lib/lhm.rb, line 112
def drop_tables_and_triggers(run = false, triggers, tables)
  if run
    triggers.each do |trigger|
      connection.execute("drop trigger if exists #{trigger}")
    end
    tables.each do |table|
      connection.execute("drop table if exists #{table}")
    end
    true
  elsif tables.empty? && triggers.empty?
    puts 'Everything is clean. Nothing to do.'
    true
  else
    puts "Would drop LHM backup tables: #{tables.join(', ')}."
    puts "Would drop LHM triggers: #{triggers.join(', ')}."
    puts 'Run with Lhm.cleanup(true) to drop all LHM triggers and tables, or Lhm.cleanup_current_run(true, table_name) to clean up a specific LHM.'
    false
  end
end