Skip to content

Don't force a connection to be established in the query cache middleware#25573

Closed
sgrif wants to merge 2 commits into
rails:masterfrom
sgrif:sg-dont-force-connection-in-query-cache
Closed

Don't force a connection to be established in the query cache middleware#25573
sgrif wants to merge 2 commits into
rails:masterfrom
sgrif:sg-dont-force-connection-in-query-cache

Conversation

@sgrif

@sgrif sgrif commented Jun 29, 2016

Copy link
Copy Markdown
Contributor

We shouldn't assume the environment in which the executor is being used,
especially now that this is no longer a middleware. Active Record is
meant to establish its connection lazily as needed, but the query cache
middleware currently is forcing it, which can cause things like boot
time to slow down as a database connection is needed to load routes,
etc.

The way query caching is handled is actually pretty leaky. If the result
of ActiveRecord::Base.connection is different in the run and
complete stages, we will have leaked enabling the query cache in the
first connection, and potentially changed the state of the latter.

This is equally leaky, but will enable the query cache on the new
connection when it is established during the course of the block. I
think the long term solution here is to have query caching decoupled
from the connection adapter, and instead handle it at a higher level.
That's a much larger refactoring though, and I need to explore whether
its worthwhile and if there's the expectation of the query cache living
on the connection adapter in the public API.

In the short term, this solves the concrete problem at hand.

Alternatives

We could simply not enable the query cache if Active Record isn't
already connected. I'm concerened that this would actually cause the
query cache to be actually disabled in some applications though, and
adds a very subtle implicit contract to the executor which I don't want
to have.

We shouldn't assume the environment in which the executor is being used,
*especially* now that this is no longer a middleware. Active Record is
meant to establish its connection lazily as needed, but the query cache
middleware currently is forcing it, which can cause things like boot
time to slow down as a database connection is needed to load routes,
etc.

The way query caching is handled is actually pretty leaky. If the result
of `ActiveRecord::Base.connection` is different in the `run` and
`complete` stages, we will have leaked enabling the query cache in the
first connection, and potentially changed the state of the latter.

This is equally leaky, but will enable the query cache on the new
connection when it is established during the course of the block. I
think the long term solution here is to have query caching decoupled
from the connection adapter, and instead handle it at a higher level.
That's a much larger refactoring though, and I need to explore whether
its worthwhile and if there's the expectation of the query cache living
on the connection adapter in the public API.

In the short term, this solves the concrete problem at hand.

Alternatives
------------

We could simply not enable the query cache if Active Record isn't
already connected. I'm concerened that this would actually cause the
query cache to be actually disabled in some applications though, and
adds a very subtle implicit contract to the executor which I don't want
to have.
@sgrif

sgrif commented Jun 29, 2016

Copy link
Copy Markdown
Contributor Author

r? @matthewd, @rafaelfranca

I wanted to get some feedback on this before I merge it.

@matthewd

Copy link
Copy Markdown
Member

I think the long term solution here is to have query caching decoupled from the connection adapter, and instead handle it at a higher level.

Isn't this PR 90% of the way to doing that? Having the connection defer back to the registry for the truth of the enablement seems a small step; the only notable hurdles seem to be 1) a "global" clear_query_cache, and 2) the super-obscure possibility that someone's currently deliberately configuring the query cache differently for each of 2+ connections.

But... don't we have to solve both of those to properly address the "what if connection is different" concern anyway?


💯 to doing something about this, though... I felt very dirty leaving it in its current connection-hogging state -- just didn't want to distract the executor change.

@matthewd

Copy link
Copy Markdown
Member

(agree the "only if connected" alternative is untenable: I'd guess 99% of applications are not connected when this runs... which only makes the forced-connection behaviour more egregious.)

@sgrif

sgrif commented Jun 29, 2016

Copy link
Copy Markdown
Contributor Author

When I mean decouple it, I mean further than just having the connection point at the global registry -- I mean actually not have it be a thing on the connection at all, either w/ some sort of decorator or by handling it at the appropriate place in Relation or Base. Either way, this is in that general direction and I want to explore it further.

@sgrif

sgrif commented Jun 29, 2016

Copy link
Copy Markdown
Contributor Author

the super-obscure possibility that someone's currently deliberately configuring the query cache differently for each of 2+ connections.

Yeah as long as the cache isn't actually shared, I'm not terribly concerned there. I can't think of a reasonable use case for only caching queries on one connection.

@rafaelfranca

Copy link
Copy Markdown
Member

This looks good to me. :shipit:

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants