-
-
Notifications
You must be signed in to change notification settings - Fork 708
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
Rails 4 prep #4667
Rails 4 prep #4667
Conversation
2cd052c
to
6ed7af9
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.
nice!
f213f86
to
04b55cc
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.
Nice work.
Can you link me to some information about the issue with subqueries in Rails 4? Subqueries can be much more efficient, right?
app/models/enterprise.rb
Outdated
@@ -117,7 +117,7 @@ class Enterprise < ActiveRecord::Base | |||
# checkout. | |||
ready_enterprises = Enterprise.ready_for_checkout.select('enterprises.id') | |||
if ready_enterprises.present? | |||
where("enterprises.id NOT IN (?)", ready_enterprises) | |||
where("enterprises.id NOT IN (?)", ready_enterprises.map(&:id)) |
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.
I think that we can use pluck(:id)
here to be more efficient. The select
isn't needed then. But this could have a performance impact, the array can be big.
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.
pluck could create an extra query, right?
I think this should be select :-)
#3709
I think we should agree on some rules for using map vs select vs pluck :-)
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.
I think we should agree on some rules for using map vs select vs pluck :-)
That sounds good! And we could add it to the wiki
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.
Thank you for the clarification. I now think that I know how it works.
In this particular case, there was a select that would lead to a sub-query. The present?
call creates a separate query just looking if anything is there without loading the enterprises. Then applying map
would first load all the enterprise objects and then copy the ids into an array. A pluck
call would do that without creating the enterprise objects (less Ruby work).
If we used the enterprise objects somewhere else, we should use map to re-use the same array and not load the enterprises again. But I don't think we do it here, so pluck would be better than map.
Matt, you explained that select *
is a problem with sub-queries in Rails 4. That's logical. We should definitely avoid that. In this case, we do select('enterprises.id')
in line 118 which should allow us to do an efficient sub-query. That should be no problem with Rails 4, right?
With my current knowledge, we don't need map
here and should use a sub-query. We could move the select
from line 118 down to the 120 to make it clearer that we are selecting the ids for the IN
query part.
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.
awesome Maikel 👌
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.
Aha! Yeah it looks like it's mostly the prepared statements in our scopes that are causing issues. In the #managed_by
scope in enterprise_fee.rb
this line throws a "subquery has too many columns" error:
where('enterprise_id IN (?)', user.enterprises)
but this does not:
where(enterprise_id: user.enterprises)
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.
nice investigation Matt!!
is there a reason you are not satisfied with how we have been fixing things so far with:
where('enterprise_id IN (?)', user.enterprises.select(&:id))
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.
I need to redo the other subquery PR and check some things in Rails 4. It looks like it will be better if we avoid prepared statements and favour the hash syntax, passing an ActiveRecord::Relation
object (as mentioned above). And in that case I think we generally don't need the select
, as ActiveRecord
should in theory figure it out and build the subquery correctly from the hash parameters?
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.
I am not sure I agree.
Prepared statements are a good thing in general. I dont think we should plan to just avoid them.
I dont think we should let active record control things, we should control things and we should name the exact fields we want in the query.
These are general rules I have seen used in other projects: as much prepared statements as possible and lists of fields in a query as specific as possible.
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.
In general, the new Rails 4 way where.not(id: ready_enterprises)
sounds better than what we have at the moment. But for compatibility with Rails 3 and 4, I would prefer where("enterprises.id NOT IN (?)", ready_enterprises.select("enterprises.id"))
for now. The reason I prefer this version is that it's very explicit what's happening, it's compatible with many versions and avoids the Rails 3 bug where multiple where(id: collection)
statements override each other.
Instead of optimising the way we use the ActiveRecord interface further, I would also suggest looking at the underlying problem and changing our logic here. This negation is really expensive. It would be better to find criteria for enterprises that make them not being ready for checkout and using those in a direct query.
One problem is that there can be several reasons why an enterprise is not ready. And we are querying them all together. Instead, we could actually query those individually and change the UI to say:
- These enterprises are missing a shipping method: ...
- These enterprises are missing a payment method: ...
- ...
@Matt-Yorkley I noticed that your most recent version still uses pluck
. Does select('enterprises.id')
still raise the too-many-column error? I don't understand that because enterprises.id
is only one column, isn't it? Or are there many more in JOIN clauses that count as well?
It's difficult to find anything concrete for this specific error. Basically when a subquery is not specifying which columns to fetch and defaulting to |
there's this issue for it: |
04b55cc
to
8b70165
Compare
has_and_belongs_to_many relationships now require a join_table
return if distributors.empty? || flash[:notice].present? | ||
|
||
flash[:notice] = active_distributors_not_ready_for_checkout_message(distributors) | ||
warning = OrderCycleWarning.new(spree_current_user).call |
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.
With this abstraction the intent is much clearer now 👌
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.
Nice work, @Matt-Yorkley! 😃
@mkllnk Are you happy with this PR already? 🙂 |
I'm still concerned about the performance of |
I'll try it again in Rails 4 |
6c7979e
to
2df09e3
Compare
Aha! The resulting query here looked like this in Rails 4: ...NOT IN (SELECT DISTINCT enterprises.*, enterprises.id FROM "enterprises" JOIN... The scope that's being negated here uses |
Great work @Matt-Yorkley! Only Code Climate is the usual grinch. 😉 |
2df09e3
to
7d71f21
Compare
All green so merging! |
What? Why?
Pulls across some commits for upgrading to Rails 4. Mostly minor changes in syntax.
What should we test?
I think a green build will be enough to validate these changes.
Release notes
Adapted some syntax in preparation for Rails 4.
Changelog Category: Changed