1
0
Fork 0
mirror of https://github.com/rails/rails.git synced 2022-11-09 12:12:34 -05:00
Commit graph

15 commits

Author SHA1 Message Date
eileencodes
cd881ab169 Move while_preventing_writes from conn to handler
If we put the `while_preventing_writes` on the connection then the
middleware that sends reads to the primary and ensures they can't write
will not work. The `while_preventing_writes` will only be applied to the
connection which it's called on - which in the case of the middleware is
Ar::Base.

This worked fine if you called it directly like
`OtherDbConn.connection.while_preventing_writes` but Rails didn't have a
way of knowing you wanted to call it on all the connections.

The change here moves the `while_preventing_writes` method from the
connection to the handler so that it can block writes to all queries for
that handler. This will apply to all the connections associated with
that handler.
2019-06-14 16:11:36 -04:00
Ryuta Kamizono
c81af6ae72 Enable Layout/EmptyLinesAroundAccessModifier cop
We sometimes say "✂️ newline after `private`" in a code review (e.g.
https://github.com/rails/rails/pull/18546#discussion_r23188776,
https://github.com/rails/rails/pull/34832#discussion_r244847195).

Now `Layout/EmptyLinesAroundAccessModifier` cop have new enforced style
`EnforcedStyle: only_before` (https://github.com/rubocop-hq/rubocop/pull/7059).

That cop and enforced style will reduce the our code review cost.
2019-06-13 12:00:45 +09:00
Akira Matsuda
cecbc2340a Properly give defaults for DatabaseSelector options
The initializer receives `nil` for these options when no cofigurations were given:
https://github.com/rails/rails/blob/v6.0.0.rc1/activerecord/lib/active_record/railtie.rb#L91-L97
2019-05-07 08:46:13 +09:00
Ryuta Kamizono
713cee01a5 Fix typo a -> an, an -> a [ci skip] 2019-02-11 17:03:10 +09:00
John Hawthorn
a68bcde506 Rename database selector operations to context 2019-02-07 14:32:54 -08:00
John Hawthorn
ed1f392ea6 Rename resolver ivar to operations in Resolver
We're already in the resolver, we call this class "operations" in the
middleware, so we should use the same naming here.
2019-02-06 11:57:30 -08:00
John Hawthorn
6aaf01385e Rename Session.build to Session.call
This is more consistent with Resolver, which has build called. This
allows using a Proc instead of a class, which could be nice if you need
to vary switching logic based on the request in a more ad-hoc way (ie.
check if it is an API request).
2019-02-06 11:55:53 -08:00
Eileen M. Uchitelle
f4aed53e44
Merge pull request #35132 from eileencodes/allow-application-to-change-handler-names
Add ability to change the names of the default handlers
2019-02-04 08:45:48 -05:00
Eileen Uchitelle
8d32346cde Add ability to change the names of the default handlers
When I wrote the `connected_to` and `connects_to` API's I wrote them
with the idea in mind that it didn't really matter what the
handlers/roles were called as long as those connecting to the roles knew
which one wrote and which one read.

With the introduction of the middleware Rails begins to assume it's
`writing` and `reading` and there's no room for other roles. At GitHub
we've been using this method for a long time so we have a ton of legacy
code that uses different handler names `default` and `readonly`. We
could rename all our code but I think this is better for a few reasons:

- Legacy apps that have been using multiple databases for a long time
  can have an eaiser time switching.
- If we later find this to cause more issues than it's worth we can
  easily deprecate.
- We won't force old apps to rewrite the resolver middleware just to use
  a different handler.

Adding the writing_role/reading_role required that I move the code that
creates the first handler for writing to the railtie. If I didn't move
this the core class would assign the handler before I was able to assign
a new one in my configuration and I'd end up with 3 handlers instead of
2.
2019-02-01 14:11:35 -05:00
Eileen Uchitelle
31f205234a Refactor options for middleware
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.
2019-02-01 11:34:50 -05:00
alkesh26
379354572f ActiveRecord typo fixe. 2019-01-31 16:31:51 +05:30
Abhay Nikam
15db608f3e Fixed typo for DatabaseSelector::Resolver documentation 2019-01-31 07:57:38 +05:30
Ryuta Kamizono
1f5e21bcbc Remove redundant begin block
We enabled `Style/RedundantBegin` cop at #34764, but it is hard to
detect an offence if returning value put after the block.
2019-01-31 10:18:34 +09:00
John Hawthorn
ab952b1949 Write update_last_write_timestamp after request
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.
2019-01-30 13:25:09 -08:00
Eileen Uchitelle
0abcec416b Adds basic automatic database switching to Rails
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.
2019-01-30 13:37:25 -05:00