New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add ability to prevent access to a database #51354
base: main
Are you sure you want to change the base?
Conversation
c438333
to
5dabbdc
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the PR @stevecrozz!
I personally don't love how close yet different prevent_writes
and prevent_access
are. It feels like a confusing API and it would let an application set both when that just doesn't really make sense. It only makes sense to use one or the other.
I would rather rewrite the prevent_writes
API than have two arguments that are this similar. I think it would be better to do the following:
- Deprecate
prevent_writes
and friends - Add a new argument called something like
prevent_queries
. This would take a symbol of:writes
and:all
...maybe also:reads
(I'm on the fence whether we need reads). - Raise the appropriate error based on the symbol set by
prevent_queries
.
In use the API would look like this:
ActiveRecord::Base.connected_to(role: :writing, prevent_queries: :all) do
...
end
ActiveRecord::Base.while_preventing_queries(:writes) do
...
end
This would make current_preventing_access
more complicated but it's better for Rails to have clean APIs and messy internals than an API that doesn't feel right.
An alternative option is to not lean on or change the connected_to
API and make a new API for preventing access. I feel like preventing access is somewhat of a separate concern from multi-db connection management that requires prevent_writes
to be a feature (we use it for connections to act like they are on a replica for dev/test). So I could see this being an entirely separate API.
Let me know if I can clarify anything.
This was bugging me too and I think it's worth exploring. Currently Ultimately, what do you think should be appended to that stack when we're preventing access? |
3c1bce0
to
b803e92
Compare
b803e92
to
9e61852
Compare
@eileencodes I have prepared a new implementation of the same feature. The new implementation is now separate from the multi-connection handling concerns. It stores a value on the current thread as opposed to a specific connection using thread_mattr_accessor (thank you for this suggestion e_a). |
Motivation / Background
From discussion: https://discuss.rubyonrails.org/t/proposal-prevent-activerecord-access/85322
N+1 queries can be a real performance concern. It isn't always immediately obvious what the impacts could be of adding a new query. I wish engineers would be constantly watching the query log as they work. But things can get out of hand quickly, and even if you are watching the wall of queries being logged, N+1s can sometimes disappear into the noise.
Also, there are times when I want a pure in-memory algorithm. An API like this would allow me to both express and enforce the intention to not be hitting the database in a branch of code.
Just today, I was working with some code that broke a test that asserts for a given controller action, exactly 118 queries are dispatched. This is a terrible test to have, but we have it because we have had real problems with query explosions emerging unexpectedly. I believe strict loading could help, but it would not have prevented all of the problems that led to write and maintain this test.
This is indeed a heavy hammer to use. But I don't see an alternative that can achieve the same level of assurance, and the implementation was pretty easy thanks to the prior art of
while_preventing_writes
.Detail
This Pull Request adds
while_preventing_access
to the public API of ActiveRecord::Base much like was done forwhile_preventing_writes
in #34505Additional information
Checklist
Before submitting the PR make sure the following are checked:
[Fix #issue-number]