Rails and Database Session Variables
Ruby's ActiveRecord provides a very robust means of manipulating database table structures especially in the context of automated deployments such as Capistrano. When there is a master-slave database configuration, it may not always be prudent to perform DDL statements on the master and let those propagate through to the slave, especially in high-volume sites where Rails deployments may involve multiple migrations since replication lag may occur and present some significant problems.
In addition, when no downtime is specified, a DDL rollout may break the current application code, so a targeted deployment may be more prudent.
Managing DDL Propagation in Master-Slave Environments
In MySQL, the solution would be to set the session variable SQL_LOG_BIN = 0 which causes subsequent statements not to get written to the binary log and therefore won't get replicated to slaves. Unfortunately, the given connection options of ActiveRecord do not accommodate the setting of database options.
Manual Implementation: Explicit Session Setting in Migrations
However, one way to accomplish setting this variable would be to have developers explicitly set it in the migration files:
class AddAccounts < ActiveRecord::Migration def self.up execute("set SESSION sql_log_bin=0") create_table :api_accounts do |t| t.column "user_id", :int t.column "name", :text t.column "account",:text t.column "created_at", :datetime t.column "updated_at", :datetime end end def self.down execute("set SESSION sql_log_bin=0") drop_table :api_accounts end end
Automating Control with ActiveRecord Monkey Patching
But since this would be a hassle for developers and is easily overlooked, we can leverage Capistrano's architecture to monkey patch the ActiveRecord::Migration class so that this variable is set whenever the migrations are invoked.
We constructed a file, config/initializers/active_record.rb:
#opens the ActiveRecord::Migration class #use alias_method to add functionality to the ActiveRecord::Migration.migrate method class ActiveRecord::Migration class << self alias_method :migrate_conn, :migrate def migrate(direction) ActiveRecord::Base.connection_pool.with_connection do |conn| @connection = conn connection.execute("SET SQL_LOG_BIN=0") @connection = nil end migrate_conn(direction) end end end
Privilege Requirements and Real-World Application
Note that the database user that invokes the migrations needs to have the SUPER privilege granted to it in order to be able to set this variable.
We successfully developed and implemented this technique with the devops team at SlideShare last month to build rolling DDL scripting to multiple databases using Capistrano. It allowed them to have explicit control over which database was being updated, thereby giving them the means necessary to update one database while the other served the current application code.
MySQL Database Consulting Services
Ready to optimize your MySQL Database for the future?
Share this
Share this
More resources
Learn more about Pythian by reading the following blogs and articles.
pt-online -schema corner case: When you don’t want to replicate your alter activities across the whole cluster.
MySQL Back to Basics: Analyze, Check, Optimize, and Repair
Change of database passwords through OEM
Ready to unlock value from your data?
With Pythian, you can accomplish your data transformation goals and more.