aboutsummaryrefslogtreecommitdiffstats
path: root/activerecord/test/cases/database_selector_test.rb
Commit message (Collapse)AuthorAgeFilesLines
* Fix DatabaseSelector test that fails sometimeseileencodes2019-02-121-0/+4
| | | | | | | | | | | | | | | | On CI we've seen a few test failures when the DatabaseSelectorTest runs before the ConnectionHandlersMultiDbTest. This is because it's creating 2 handlers but not properly tearing them down. Example failure: ``` Failure: ActiveRecord::ConnectionAdapters::ConnectionHandlersMultiDbTest#test_connects_to_with_single_configuration [/rails/activerecord/test/cases/connection_adapters/connection_handlers_multi_db_test.rb:241]: Expected: 1 Actual: 2 ```
* Refactor options for middlewareEileen Uchitelle2019-02-011-0/+48
| | | | | | | | | | Right now we only have one option that's supported, the delay. However I can see us supporting other options in the future. This PR refactors the options to get passed into the resolver so whether you're using middleware or using the config options you can pass options to the resolver. This will also make it easy to add new options in the future.
* Write update_last_write_timestamp after requestJohn Hawthorn2019-01-301-0/+37
| | | | | | | | | We need to update using the timestamp from the end of the request, not the start. For example, if a request spends 5+ seconds writing, we still want to wait another 5 seconds for replication lag. Since we now run the update after we yield, we need to use ensure to make sure we update the timestamp even if there is an exception.
* Adds basic automatic database switching to RailsEileen Uchitelle2019-01-301-0/+77
The following PR adds behavior to Rails to allow an application to automatically switch it's connection from the primary to the replica. A request will be sent to the replica if: * The request is a read request (`GET` or `HEAD`) * AND It's been 2 seconds since the last write to the database (because we don't want to send a user to a replica if the write hasn't made it to the replica yet) A request will be sent to the primary if: * It's not a GET/HEAD request (ie is a POST, PATCH, etc) * Has been less than 2 seconds since the last write to the database The implementation that decides when to switch reads (the 2 seconds) is "safe" to use in production but not recommended without adequate testing with your infrastructure. At GitHub in addition to the a 5 second delay we have a curcuit breaker that checks the replication delay and will send the query to a replica before the 5 seconds has passed. This is specific to our application and therefore not something Rails should be doing for you. You'll need to test and implement more robust handling of when to switch based on your infrastructure. The auto switcher in Rails is meant to be a basic implementation / API that acts as a guide for how to implement autoswitching. The impementation here is meant to be strict enough that you know how to implement your own resolver and operations classes but flexible enough that we're not telling you how to do it. The middleware is not included automatically and can be installed in your application with the classes you want to use for the resolver and operations passed in. If you don't pass any classes into the middleware the Rails default Resolver and Session classes will be used. The Resolver decides what parameters define when to switch, Operations sets timestamps for the Resolver to read from. For example you may want to use cookies instead of a session so you'd implement a Resolver::Cookies class and pass that into the middleware via configuration options. ``` config.active_record.database_selector = { delay: 2.seconds } config.active_record.database_resolver = MyResolver config.active_record.database_operations = MyResolver::MyCookies ``` Your classes can inherit from the existing classes and reimplment the methods (or implement more methods) that you need to do the switching. You only need to implement methods that you want to change. For example if you wanted to set the session token for the last read from a replica you would reimplement the `read_from_replica` method in your resolver class and implement a method that updates a new timestamp in your operations class.