ArMysqlFlexmaster
Mysql Flexmaster is an adapter for ActiveRecord that allows an application node to choose among a list of potential masters at runtime. It trades some properties of a more traditional HA solution (load balancing, middleware) for simplicity of operation.
Installation
Add this line to your application's Gemfile:
gem 'ar_mysql_flexmaster'
And then execute:
$ bundle
Or install it yourself as:
$ gem install ar_mysql_flexmaster
Configuration:
database.yml contains a list of hosts -- all of them potential masters, all of them potential replicas. It should look like this:
production:
adapter: mysql_flexmaster
username: flex
hosts: ["db01:3306", "db02:3306"]
production_slave:
adapter: mysql_flexmaster
username: flex
slave: true
hosts: ["db01:3306", "db02:3306"]
How it works
Overview
The mysql "READ_ONLY" flag is used to indicate a current master amongst the cluster. Only one member of the replication chain may be read-write at any given time. The application picks in run time, based on the read_only flag, which host is correct.
boot time
Your activerecord application will pick a correct mysql host for the given configuration by probing hosts until it finds the correct host.
For master configurations (slave: true is not specified):
The application will probe each host in turn, and find the mysql candidate among these nodes that is read-write (SET GLOBAL READ_ONLY=0). If it finds more than one node where READ_ONLY == 0, it will abort.
For slave configurations (slave: true specified):
The application will choose a replica at random from amongst those where READ_ONLY == 1.
run time
Before each transaction is begun on the master, the application checks the status of the READ_ONLY variable. If READ_ONLY == 0, it will proceed with the transaction as normal. If READ_ONLY == 1, it will drop the current connection and re-poll the cluster for the current master, sleeping up to a default of 5 seconds to wait for the new master to be promoted. When it finds the new master, it will begin the transaction there.
promoting a new master
The bin/master_cut script in this project will perform steps 3-5 for you.
The process of promoting a new master to head the cluster should be as follows:
- identify a new candidate master
- ensure that all other replicas in the cluster are chained off the candidate master; you want the chain to look like this:
<existing master> -> <candidate master> -> <other replicas>
-> <other replicas>
- set the old master to READ_ONLY = 1
- record the master-bin-log position of the candidate master (if you want to re-use the old master)
- set the new master to READ_ONLY = 0
The application will eventually shift slave traffic to another node in the cluster, if available, and will drop their connection to the old master whenever a transaction is attempted, or after a certain number of queries.
caveats, gotchas
- Any explicit ( BEGIN ... END ) transaction that are in-flight when the old master goes READ_ONLY will crash. In theory there's a workaround for this problem, in pratice it's rather unwieldy due to a lack of shared global variables in mysql.
Contributing
- Fork it
- Create your feature branch (
git checkout -b my-new-feature) - Commit your changes (
git commit -am 'Added some feature') - Push to the branch (
git push origin my-new-feature) - Create new Pull Request