Skip to content

Commit

Permalink
Merging PR #7
Browse files Browse the repository at this point in the history
  • Loading branch information
anthonyalberto committed Jan 27, 2015
2 parents 5602d1c + 0bbd105 commit e5bc87d
Show file tree
Hide file tree
Showing 5 changed files with 35 additions and 10 deletions.
14 changes: 11 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
mysql_online_migrations
=======================

Patch Rails migrations to enforce MySQL 5.6 online migrations
Patch Rails migrations to enforce MySQL 5.6 online migrations
Prior to MySQL 5.6, when adding / removing / renaming indexes and columns, MySQL would lock the writes of the whole table.
MySQL 5.6 by default will try to apply the least locking possible. You however don't know what kind of locking it applies and there's situations where it can't allow writes during a migration (See Caveats).
MySQL 5.6 by default will try to apply the least locking possible. You however don't know what kind of locking it applies and there's situations where it can't allow writes during a migration (See Caveats).
This gem enforces `LOCK=NONE` in all migration statements of Rails. Therefore, you're getting an error when MySQL cannot write during the migration so there's no surprise when rolling out in production.


Expand Down Expand Up @@ -54,7 +54,15 @@ with_lock do
end
`````

The `with_lock` method will be useful when hitting the caveats of `LOCK=NONE`. Please read the following section.
The `with_lock` method will be useful when hitting the caveats of `LOCK=NONE`. Please read the 'Caveats' section.

### Enable verbose output
To enable an 'ONLINE MIGRATION' debug statement whenever an online migration is
run, simply set the `MysqlOnlineMigrations.verbose` module variable to true.
Example (in a Rails app's config/initializers/mysql_online_migrations.rb):
````
MysqlOnlineMigrations.verbose = true
````

Caveats
=======================
Expand Down
8 changes: 6 additions & 2 deletions lib/mysql_online_migrations.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@
end

module MysqlOnlineMigrations

class << self; attr_accessor :verbose; end

def self.prepended(base)
ActiveRecord::Base.send(:class_attribute, :mysql_online_migrations, :instance_writer => false)
ActiveRecord::Base.send("mysql_online_migrations=", true)
Expand All @@ -22,7 +25,7 @@ def connection
original_connection.instance_variable_get(:@delegate)
end

@no_lock_adapter ||= ActiveRecord::ConnectionAdapters::Mysql2AdapterWithoutLock.new(@original_adapter)
@no_lock_adapter ||= ActiveRecord::ConnectionAdapters::Mysql2AdapterWithoutLock.new(@original_adapter, MysqlOnlineMigrations.verbose)

if adapter_mode
@no_lock_adapter
Expand All @@ -38,6 +41,7 @@ def with_lock
yield
ActiveRecord::Base.mysql_online_migrations = original_value
end

end

ActiveRecord::Migration.send(:prepend, MysqlOnlineMigrations)
ActiveRecord::Migration.send(:prepend, MysqlOnlineMigrations)
8 changes: 5 additions & 3 deletions lib/mysql_online_migrations/mysql2_adapter_without_lock.rb
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
module ActiveRecord
module ConnectionAdapters
class Mysql2AdapterWithoutLock < Mysql2Adapter

OPTIMIZABLE_DDL_REGEX = /^(alter|create (unique )? ?index|drop index) /i
DDL_WITH_COMMA_REGEX = /^alter /i
DDL_WITH_LOCK_NONE_REGEX = / LOCK=NONE\s*$/i

def initialize(mysql2_adapter)
def initialize(mysql2_adapter, verbose = false)
@verbose = verbose
params = [:@connection, :@logger, :@connection_options, :@config].map do |sym|
mysql2_adapter.instance_variable_get(sym)
end
Expand All @@ -24,9 +26,9 @@ def lock_none_statement(sql)
return "" unless ActiveRecord::Base.mysql_online_migrations
return "" if sql =~ DDL_WITH_LOCK_NONE_REGEX
comma_delimiter = (sql =~ DDL_WITH_COMMA_REGEX ? "," : "")
puts "ONLINE MIGRATION"
puts "ONLINE MIGRATION" if @verbose
"#{comma_delimiter} LOCK=NONE"
end
end
end
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@
it "successfully instantiates a working adapter" do
expect(ActiveRecord::ConnectionAdapters::Mysql2AdapterWithoutLock.new(@adapter)).to be_active
end

it "successfully instantiates a working adapter with verbose output" do
instance = ActiveRecord::ConnectionAdapters::Mysql2AdapterWithoutLock.new(@adapter, true)
expect(instance.instance_variable_get(:@verbose)).to be_truthy
end
end

context "#lock_none_statement" do
Expand Down
10 changes: 8 additions & 2 deletions spec/lib/mysql_online_migrations_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,12 @@
end

context "#connection" do
shared_examples_for "Mysql2AdapterWithoutLock created" do
shared_examples_for "Mysql2AdapterWithoutLock created" do |verbose|
it "memoizes an instance of Mysql2AdapterWithoutLock" do
MysqlOnlineMigrations.verbose = verbose

expect(ActiveRecord::ConnectionAdapters::Mysql2AdapterWithoutLock).to receive(:new)
.with(an_instance_of(ActiveRecord::ConnectionAdapters::Mysql2Adapter)).once.and_call_original
.with(an_instance_of(ActiveRecord::ConnectionAdapters::Mysql2Adapter), verbose).once.and_call_original
3.times { migration.connection }
end
end
Expand All @@ -26,6 +28,10 @@
it_behaves_like "Mysql2AdapterWithoutLock created"
end

context 'when migrating with verbose output' do
it_behaves_like "Mysql2AdapterWithoutLock created", true
end

context 'when rolling back' do
before do
migration.instance_variable_set(:@connection, ActiveRecord::Migration::CommandRecorder.new(ActiveRecord::Base.connection))
Expand Down

0 comments on commit e5bc87d

Please sign in to comment.