[LRUG] Advice: manipulating ActiveRecord objects [newbie]

Frederick Cheung frederick.cheung at gmail.com
Mon Dec 14 05:50:45 PST 2015




On 14 December 2015 at 12:47:21, Marco Iannone (marco.iannone at gmail.com) wrote:

​Hello,

​ Ruby newbie question alert ​: many apologies if it's too basic, or not the type of questions for this forum.
At least I can say we tried hard before going to StackOverflow ​, and now here.​  

We're trying to ​ensure a webpage loads faster. The main issue ​relates to a few activerecord-generated queries on that page.

If you look at the SQL they generate, the queries are as follows (all code a bit simplified):

select * from feed_details where account_id = 5
select * from feeds where id in (VERY_LONG_LIST_OF_IDS_FROM_FIRST_QUERY)
select * from feeds_metadata where feed_id in (VERY_LONG_LIST_FROM_FIRST_QUERY)
select * from documents where feed_id in (VERY_LONG_LIST_FROM_FIRST_QUERY)

All the indexes are in place, but we verified that they'd take less than half the time by changing ​each of them to:

select * from feeds, feed_details where id = feed_id and account_id = 5
select * from feeds_metadata fm, feed_details fd where fm.feed_id = fd.feed_id and account_id = 5
select * from documents where d, feed_details fd where d.feed_id = fd.feed_id and account_id = 5



That in a nutshell is the difference between .preloads and .eager_load (.includes defaults to preloads), except that .eager_load runs one query
to load all the associations. This can perform badly when there are multiple has_many being loaded (as in your case) , since you end up pretty much with the cartesian  product. Worth a try though.

This generates an active record object with the data from the above-mentioned queries, that is used in the controller to build the JSON that is sent to the webpage as in below (see feeds_data line):

  def index
    consumer          = ConsumerWithAccount.new(current_user.account)
    feed_builder      = FeedBuilder
    feeds_data        = FeedDetail.feeds_data_for_account(current_user.account.id)

    render json: ResponseBuilder.new(consumer, feed_builder, feeds_data)
  end
 
To use the faster queries, we were hoping to build the feeds_data activerecord object ​​by using the faster queries; this gets passed to the JSON builder​. That would ​seem to require getting rid of the "involves", which (seemingly) uses the IN ().

If useful, we have the ​more effective activerecord for the 3 queries, they look like:
Feed.joins(:feed_details).where(feed_document_sets: {account_id: current_account_id})

But we couldn't figure out how to build that ​ ​ ​activerecord object ​, or one that can be parsed in a similar way by the responsebuilder​ : therein lies the proverbial rub.  
Intense googling and referring to rails docs hasn't helped.​  



Sometimes you can crowbar this sort of stuff in: load the objects for each of the tables in whatever way is the fastest, keep them in a hash so that you can easily identify which ones belongs to which feed detail. Then fiddle with some_feed_detail.association(:association_name).target to side load in the data. This may not work if response builder expects to have an active record relation rather than an array (i.e. it wants to chain more stuff onto the scope)

Fred

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.lrug.org/pipermail/chat-lrug.org/attachments/20151214/7ebf435c/attachment.html>


More information about the Chat mailing list