Most of the web applications these days use database replication for their database setup. This involves a master database and multiple replicas known as slaves. The master database and the slaves share the data and the schema, and are always kept in a consistent state. In this architecture, all writes and updates must take place on the master. Reads, however, may take place on one or more slaves. This design makes writes to the database faster and improves the performance of the application. It also provides a highly reliable and highly available setup; if the master database were to go down, reads can still continue from the slaves.
In a Rails application, ActiveRecord is an ORM framework which is used to connect objects of the application to database tables. ActiveRecord provides adapters for almost all relational database systems. However, ActiveRecord does not support replication. This means that, if say, we are using MySQL as the database and the ActiveRecord MySQL adapter over it, although, MySQL supports replication, since ActiveRecord does not, your application will not be able to read from the slave databases.
There are a couple of gems which help you achieve this, Octopus and Makara being the most widely used. The default behaviour after using any of this gem becomes ‘all reads from slaves, writes to master’. Both perform the basic job of allowing reads from slaves pretty well, however, this post compares these two gems on a few intricate parameters and briefly explains which one might be suitable to use when.
For the purpose of comparison, lets assume our application has two databases, A and B. Each of these databases follow master-slave architecture, hence A has 2 slaves; A1 and A2, while B has 2 slaves; B1 and B2.
1. Slave failure
Suppose while making a read query on database A1, if it goes down, the expected behaviour would be that the query would be re-directed to either A2 or master A.
Octopus does not support re-direction to another slave or master. If a slave is being queried and it goes down, it gives a database error to the application. Makara handles slave failure by maintaining a blacklist. When a slave goes down it is blacklisted for a specific duration. In the meanwhile, queries will go to another slave. The blacklisted slave will be retried after the specified duration has passed.
2. Replication lag
Quoting from a MongoLab blog, replication lag is the delay between the time an operation occurs on the primary and the time that same operation gets applied on the secondary. If some data is written to database A, it might take some time before that data is available on slaves A1 and A2. This might be a problem.
Octopus does not provide any solution to this, but advises that queries which might suffer from replication lag should be made on master rather than slaves ( Octopus provides an easy way to do this, more on this in the next point ).
Makara tries to solve the problem of replication lag by using a cookie. It has a “stick_to_master!” method which adds a cookie which will persist the master context for a duration which you can specify ( normally longer than the replication lag ). Suppose I create a new record in one request and immediately want to read it in the next request, the stick_to_master! method ensures that the read request comes to master rather than the slaves.
3. Flexibility to distribute reads across master and slave
The best way to solve replication lag is to query master rather than slave for queries which might suffer from replication lag. In this regard, Octopus easily allows configuring which reads should go to master and which should go to slave. By default all the reads are from slave, but using the “using(:master)” method, you can query from the master. Also, you can change the default, so that all reads can be from master, and then use “using(:slave)” method to query from the slaves when required.
Makara does not have such flexible configuration in-built. Its default is all reads from slave which cannot be changed. However, a good samaritan has written an extension which changes the default, so all reads can be from master and only those specified will be from slave.
4. Reading from slaves of multiple databases
Octopus, although the documentation says otherwise, does not support reading from slaves of multiple databases. Hence, when it needs data from database B, it should ideally query slave B1/B2 but it does not, the query goes to the master B. Makara supports querying slaves of multiple databases.
5. Error handling
Octopus doesn’t handle errors that well. Since Makara positions itself as an ActiveRecord adapter, it can catch errors from the database adaptors and handle them gracefully.
6. Community support
Octopus is more active than Makara and seems to be more in use. It also has a good community presence and questions / problems get replied to quickly. However, Makara is steadily gaining an audience and it attempts to overcome the drawbacks of Octopus.
Based on your requirements and priorities you will need to decide which one of these tools is more suitable for you. Hope this post gives you an insight into making that choice. Do post your suggestions and feedback. Happy coding!
More Reading :