TIL
Killing an N+1 with preload
A hot read endpoint was slow and I could not see why from the code. Watching the query log gave it away: one query to load the records, then one more query per record to load an association the access check needed. The classic N+1.
Before:
records = Record.where(account_id: account.id)
records.select { |r| r.policy.allows?(user) } # loads r.policy per record
After, with preload (includes works too):
records = Record.where(account_id: account.id).preload(:policy)
records.select { |r| r.policy.allows?(user) } # policies already loaded
preload runs one extra query for all the policies at once instead of one per record. The request went from a long list of repeated queries down to two, and it got much faster. The query log is what found it, not the code.