@@ -90,16 +90,16 @@ This gem will publish an `n_plus_one_query` event via ActiveSupport::Notificatio
90
90
91
91
You could implement some basic tracking. This will let you measure the extent of the N+1 query problems in your app:
92
92
``` ruby
93
- ActiveSupport ::Notifications .subscribe(" n_plus_one_query" ) do |event , data |
93
+ ActiveSupport ::Notifications .subscribe(' n_plus_one_query' ) do |event , data |
94
94
statsd.increment " web.#{ Rails .env} .n_plus_one_queries.global"
95
95
end
96
96
```
97
97
98
98
You could log the N+1 queries. In your development environment, you could throw N+1 queries into the logs along with a stack trace:
99
99
``` ruby
100
- ActiveSupport ::Notifications .subscribe(" n_plus_one_query" ) do |event , data |
100
+ ActiveSupport ::Notifications .subscribe(' n_plus_one_query' ) do |event , data |
101
101
message = " N+1 Query detected: #{ data[:association ] } on #{ data[:source ].class } "
102
- backtrace = caller .select { |r | r.starts_with?(Rails .root.to_s) }
102
+ backtrace = caller .select { |r | r.starts_with?(Rails .root.to_s) }
103
103
Rails .logger.debug(" \n\n #{ message } \n #{ backtrace.join(" \n " ) } \n " .red)
104
104
end
105
105
```
@@ -113,7 +113,7 @@ config.around(:each) do |example|
113
113
raise QueryError .new (message)
114
114
end
115
115
end
116
- ActiveSupport ::Notifications .subscribed(callback, " n_plus_one_query" ) do
116
+ ActiveSupport ::Notifications .subscribed(callback, ' n_plus_one_query' ) do
117
117
example.run
118
118
end
119
119
end
@@ -144,7 +144,7 @@ There is now a `has_many_aggregate` method available for ActiveRecord::Base. Thi
144
144
``` ruby
145
145
# old
146
146
Contact .all.each do |contact |
147
- contact.addresses.maximum(" LENGTH(street)" )
147
+ contact.addresses.maximum(' LENGTH(street)' )
148
148
contact.addresses.count
149
149
end
150
150
# SELECT * FROM contacts
159
159
# new
160
160
class Contact < ActiveRecord ::Base
161
161
has_many :addresses
162
- has_many_aggregate :addresses , :max_street_length , :maximum , " LENGTH(street)" , default: nil
163
- has_many_aggregate :addresses , :count_all , :count , " * "
162
+ has_many_aggregate :addresses , :max_street_length , :maximum , ' LENGTH(street)' , default: nil
163
+ has_many_aggregate :addresses , :count_all , :count , ' * '
164
164
end
165
165
166
166
Contact .jit_preload.each do |contact |
@@ -177,7 +177,7 @@ Furthermore, there is an argument `max_ids_per_query` setting max ids per query.
177
177
``` ruby
178
178
class Contact < ActiveRecord ::Base
179
179
has_many :addresses
180
- has_many_aggregate :addresses , :count_all , :count , " * " , max_ids_per_query: 10
180
+ has_many_aggregate :addresses , :count_all , :count , ' * ' , max_ids_per_query: 10
181
181
end
182
182
183
183
Contact .jit_preload.each do |contact |
@@ -197,26 +197,26 @@ This is a method `preload_scoped_relation` that is available that can handle thi
197
197
# old
198
198
class Contact < ActiveRecord ::Base
199
199
has_many :addresses
200
- has_many :usa_addresses , -> { where(country: Country .find_by_name(" USA" )) }
200
+ has_many :usa_addresses , -> { where(country: Country .find_by_name(' USA' )) }
201
201
end
202
202
203
203
Contact .jit_preload.all.each do |contact |
204
204
# This will preload the association as expected, but it must be defined as an association in advance
205
205
contact.usa_addresses
206
206
207
207
# This will preload as the entire addresses association, and filters it in memory
208
- contact.addresses.select { |address | address.country == Country .find_by_name(" USA" ) }
208
+ contact.addresses.select { |address | address.country == Country .find_by_name(' USA' ) }
209
209
210
210
# This is an N+1 query
211
- contact.addresses.where(country: Country .find_by_name(" USA" ))
211
+ contact.addresses.where(country: Country .find_by_name(' USA' ))
212
212
end
213
213
214
214
# New
215
215
Contact .jit_preload.all.each do |contact |
216
216
contact.preload_scoped_relation(
217
- name: " USA Addresses" ,
217
+ name: ' USA Addresses' ,
218
218
base_association: :addresses ,
219
- preload_scope: Address .where(country: Country .find_by_name(" USA" ))
219
+ preload_scope: Address .where(country: Country .find_by_name(' USA' ))
220
220
)
221
221
end
222
222
# SELECT * FROM contacts
@@ -236,14 +236,14 @@ JitPreloader.globally_enabled = true
236
236
# Can also be given anything that responds to `call`.
237
237
# You could build a kill switch with Redis (or whatever you'd like)
238
238
# so that you can turn it on or off dynamically.
239
- JitPreloader .globally_enabled = -> { $redis .get(' always_jit_preload' ) == ' on' }
239
+ JitPreloader .globally_enabled = -> { $redis .get(' always_jit_preload' ) == ' on' }
240
240
241
241
# Setting global max ids constraint on all aggregation methods.
242
242
JitPreloader .max_ids_per_query = 10
243
243
244
244
class Contact < ActiveRecord ::Base
245
245
has_many :emails
246
- has_many_aggregate :emails , :count_all , :count , " * "
246
+ has_many_aggregate :emails , :count_all , :count , ' * '
247
247
end
248
248
249
249
# When enabled globally, this would not generate an N+1 query.
0 commit comments