bullet 4.14.5 → 4.14.6
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +5 -0
- data/README.md +2 -0
- data/lib/bullet.rb +3 -1
- data/lib/bullet/active_record3.rb +63 -35
- data/lib/bullet/active_record3x.rb +55 -33
- data/lib/bullet/active_record4.rb +51 -31
- data/lib/bullet/active_record41.rb +52 -32
- data/lib/bullet/active_record42.rb +79 -50
- data/lib/bullet/detector/association.rb +16 -16
- data/lib/bullet/detector/counter_cache.rb +13 -13
- data/lib/bullet/detector/n_plus_one_query.rb +30 -30
- data/lib/bullet/version.rb +1 -1
- data/spec/bullet/detector/counter_cache_spec.rb +8 -8
- data/spec/bullet/detector/n_plus_one_query_spec.rb +27 -27
- data/spec/integration/active_record3/association_spec.rb +9 -0
- data/spec/integration/active_record4/association_spec.rb +9 -0
- metadata +3 -3
@@ -8,13 +8,15 @@ module Bullet
|
|
8
8
|
# if select only one object, then the only one object has impossible to cause N+1 query.
|
9
9
|
def to_a
|
10
10
|
records = origin_to_a
|
11
|
-
if
|
12
|
-
if records.
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
11
|
+
if Bullet.start?
|
12
|
+
if records.first.class.name !~ /^HABTM_/
|
13
|
+
if records.size > 1
|
14
|
+
Bullet::Detector::NPlusOneQuery.add_possible_objects(records)
|
15
|
+
Bullet::Detector::CounterCache.add_possible_objects(records)
|
16
|
+
elsif records.size == 1
|
17
|
+
Bullet::Detector::NPlusOneQuery.add_impossible_object(records.first)
|
18
|
+
Bullet::Detector::CounterCache.add_impossible_object(records.first)
|
19
|
+
end
|
18
20
|
end
|
19
21
|
end
|
20
22
|
records
|
@@ -25,12 +27,14 @@ module Bullet
|
|
25
27
|
alias_method :origin_preloaders_on, :preloaders_on
|
26
28
|
|
27
29
|
def preloaders_on(association, records, scope)
|
28
|
-
|
29
|
-
|
30
|
-
records.
|
31
|
-
|
30
|
+
if Bullet.start?
|
31
|
+
records.compact!
|
32
|
+
if records.first.class.name !~ /^HABTM_/
|
33
|
+
records.each do |record|
|
34
|
+
Bullet::Detector::Association.add_object_associations(record, association)
|
35
|
+
end
|
36
|
+
Bullet::Detector::UnusedEagerLoading.add_eager_loadings(records, association)
|
32
37
|
end
|
33
|
-
Bullet::Detector::UnusedEagerLoading.add_eager_loadings(records, association)
|
34
38
|
end
|
35
39
|
origin_preloaders_on(association, records, scope)
|
36
40
|
end
|
@@ -42,11 +46,13 @@ module Bullet
|
|
42
46
|
def find_with_associations
|
43
47
|
return origin_find_with_associations { |r| yield r } if block_given?
|
44
48
|
records = origin_find_with_associations
|
45
|
-
|
46
|
-
|
47
|
-
|
49
|
+
if Bullet.start?
|
50
|
+
associations = (eager_load_values + includes_values).uniq
|
51
|
+
records.each do |record|
|
52
|
+
Bullet::Detector::Association.add_object_associations(record, associations)
|
53
|
+
end
|
54
|
+
Bullet::Detector::UnusedEagerLoading.add_eager_loadings(records, associations)
|
48
55
|
end
|
49
|
-
Bullet::Detector::UnusedEagerLoading.add_eager_loadings(records, associations)
|
50
56
|
records
|
51
57
|
end
|
52
58
|
end
|
@@ -59,9 +65,11 @@ module Bullet
|
|
59
65
|
@bullet_eager_loadings = {}
|
60
66
|
records = origin_instantiate(result_set, aliases)
|
61
67
|
|
62
|
-
|
63
|
-
|
64
|
-
|
68
|
+
if Bullet.start?
|
69
|
+
@bullet_eager_loadings.each do |klazz, eager_loadings_hash|
|
70
|
+
objects = eager_loadings_hash.keys
|
71
|
+
Bullet::Detector::UnusedEagerLoading.add_eager_loadings(objects, eager_loadings_hash[objects.first].to_a)
|
72
|
+
end
|
65
73
|
end
|
66
74
|
records
|
67
75
|
end
|
@@ -70,12 +78,14 @@ module Bullet
|
|
70
78
|
def construct_model(record, node, row, model_cache, id, aliases)
|
71
79
|
result = origin_construct_model(record, node, row, model_cache, id, aliases)
|
72
80
|
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
81
|
+
if Bullet.start?
|
82
|
+
associations = node.reflection.name
|
83
|
+
Bullet::Detector::Association.add_object_associations(record, associations)
|
84
|
+
Bullet::Detector::NPlusOneQuery.call_association(record, associations)
|
85
|
+
@bullet_eager_loadings[record.class] ||= {}
|
86
|
+
@bullet_eager_loadings[record.class][record] ||= Set.new
|
87
|
+
@bullet_eager_loadings[record.class][record] << associations
|
88
|
+
end
|
79
89
|
|
80
90
|
result
|
81
91
|
end
|
@@ -85,19 +95,25 @@ module Bullet
|
|
85
95
|
# call one to many associations
|
86
96
|
alias_method :origin_load_target, :load_target
|
87
97
|
def load_target
|
88
|
-
Bullet
|
98
|
+
if Bullet.start?
|
99
|
+
Bullet::Detector::NPlusOneQuery.call_association(@owner, @reflection.name) unless @inversed
|
100
|
+
end
|
89
101
|
origin_load_target
|
90
102
|
end
|
91
103
|
|
92
104
|
alias_method :origin_empty?, :empty?
|
93
105
|
def empty?
|
94
|
-
Bullet
|
106
|
+
if Bullet.start?
|
107
|
+
Bullet::Detector::NPlusOneQuery.call_association(@owner, @reflection.name)
|
108
|
+
end
|
95
109
|
origin_empty?
|
96
110
|
end
|
97
111
|
|
98
112
|
alias_method :origin_include?, :include?
|
99
113
|
def include?(object)
|
100
|
-
Bullet
|
114
|
+
if Bullet.start?
|
115
|
+
Bullet::Detector::NPlusOneQuery.call_association(@owner, @reflection.name)
|
116
|
+
end
|
101
117
|
origin_include?(object)
|
102
118
|
end
|
103
119
|
end
|
@@ -107,9 +123,11 @@ module Bullet
|
|
107
123
|
alias_method :origin_reader, :reader
|
108
124
|
def reader(force_reload = false)
|
109
125
|
result = origin_reader(force_reload)
|
110
|
-
if
|
111
|
-
|
112
|
-
|
126
|
+
if Bullet.start?
|
127
|
+
if @owner.class.name !~ /^HABTM_/ && !@inversed
|
128
|
+
Bullet::Detector::NPlusOneQuery.call_association(@owner, @reflection.name)
|
129
|
+
Bullet::Detector::NPlusOneQuery.add_possible_objects(result)
|
130
|
+
end
|
113
131
|
end
|
114
132
|
result
|
115
133
|
end
|
@@ -120,7 +138,9 @@ module Bullet
|
|
120
138
|
|
121
139
|
def has_cached_counter?(reflection = reflection())
|
122
140
|
result = origin_has_cached_counter?(reflection)
|
123
|
-
Bullet
|
141
|
+
if Bullet.start?
|
142
|
+
Bullet::Detector::CounterCache.add_counter_cache(owner, reflection.name) unless result
|
143
|
+
end
|
124
144
|
result
|
125
145
|
end
|
126
146
|
end
|
@@ -7,12 +7,14 @@ module Bullet
|
|
7
7
|
alias_method :origin_find, :find
|
8
8
|
def find(*args)
|
9
9
|
result = origin_find(*args)
|
10
|
-
if
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
10
|
+
if Bullet.start?
|
11
|
+
if result.is_a? Array
|
12
|
+
Bullet::Detector::NPlusOneQuery.add_possible_objects(result)
|
13
|
+
Bullet::Detector::CounterCache.add_possible_objects(result)
|
14
|
+
elsif result.is_a? ::ActiveRecord::Base
|
15
|
+
Bullet::Detector::NPlusOneQuery.add_impossible_object(result)
|
16
|
+
Bullet::Detector::CounterCache.add_impossible_object(result)
|
17
|
+
end
|
16
18
|
end
|
17
19
|
result
|
18
20
|
end
|
@@ -25,13 +27,15 @@ module Bullet
|
|
25
27
|
# if select only one object, then the only one object has impossible to cause N+1 query.
|
26
28
|
def to_a
|
27
29
|
records = origin_to_a
|
28
|
-
if
|
29
|
-
if records.
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
30
|
+
if Bullet.start?
|
31
|
+
if records.first.class.name !~ /^HABTM_/
|
32
|
+
if records.size > 1
|
33
|
+
Bullet::Detector::NPlusOneQuery.add_possible_objects(records)
|
34
|
+
Bullet::Detector::CounterCache.add_possible_objects(records)
|
35
|
+
elsif records.size == 1
|
36
|
+
Bullet::Detector::NPlusOneQuery.add_impossible_object(records.first)
|
37
|
+
Bullet::Detector::CounterCache.add_impossible_object(records.first)
|
38
|
+
end
|
35
39
|
end
|
36
40
|
end
|
37
41
|
records
|
@@ -42,12 +46,14 @@ module Bullet
|
|
42
46
|
alias_method :origin_preloaders_on, :preloaders_on
|
43
47
|
|
44
48
|
def preloaders_on(association, records, scope)
|
45
|
-
|
46
|
-
|
47
|
-
records.
|
48
|
-
|
49
|
+
if Bullet.start?
|
50
|
+
records.compact!
|
51
|
+
if records.first.class.name !~ /^HABTM_/
|
52
|
+
records.each do |record|
|
53
|
+
Bullet::Detector::Association.add_object_associations(record, association)
|
54
|
+
end
|
55
|
+
Bullet::Detector::UnusedEagerLoading.add_eager_loadings(records, association)
|
49
56
|
end
|
50
|
-
Bullet::Detector::UnusedEagerLoading.add_eager_loadings(records, association)
|
51
57
|
end
|
52
58
|
origin_preloaders_on(association, records, scope)
|
53
59
|
end
|
@@ -59,11 +65,13 @@ module Bullet
|
|
59
65
|
def find_with_associations
|
60
66
|
return origin_find_with_associations { |r| yield r } if block_given?
|
61
67
|
records = origin_find_with_associations
|
62
|
-
|
63
|
-
|
64
|
-
|
68
|
+
if Bullet.start?
|
69
|
+
associations = (eager_load_values + includes_values).uniq
|
70
|
+
records.each do |record|
|
71
|
+
Bullet::Detector::Association.add_object_associations(record, associations)
|
72
|
+
end
|
73
|
+
Bullet::Detector::UnusedEagerLoading.add_eager_loadings(records, associations)
|
65
74
|
end
|
66
|
-
Bullet::Detector::UnusedEagerLoading.add_eager_loadings(records, associations)
|
67
75
|
records
|
68
76
|
end
|
69
77
|
end
|
@@ -76,9 +84,11 @@ module Bullet
|
|
76
84
|
@bullet_eager_loadings = {}
|
77
85
|
records = origin_instantiate(result_set, aliases)
|
78
86
|
|
79
|
-
|
80
|
-
|
81
|
-
|
87
|
+
if Bullet.start?
|
88
|
+
@bullet_eager_loadings.each do |klazz, eager_loadings_hash|
|
89
|
+
objects = eager_loadings_hash.keys
|
90
|
+
Bullet::Detector::UnusedEagerLoading.add_eager_loadings(objects, eager_loadings_hash[objects.first].to_a)
|
91
|
+
end
|
82
92
|
end
|
83
93
|
records
|
84
94
|
end
|
@@ -87,12 +97,14 @@ module Bullet
|
|
87
97
|
def construct_model(record, node, row, model_cache, id, aliases)
|
88
98
|
result = origin_construct_model(record, node, row, model_cache, id, aliases)
|
89
99
|
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
100
|
+
if Bullet.start?
|
101
|
+
associations = node.reflection.name
|
102
|
+
Bullet::Detector::Association.add_object_associations(record, associations)
|
103
|
+
Bullet::Detector::NPlusOneQuery.call_association(record, associations)
|
104
|
+
@bullet_eager_loadings[record.class] ||= {}
|
105
|
+
@bullet_eager_loadings[record.class][record] ||= Set.new
|
106
|
+
@bullet_eager_loadings[record.class][record] << associations
|
107
|
+
end
|
96
108
|
|
97
109
|
result
|
98
110
|
end
|
@@ -102,16 +114,18 @@ module Bullet
|
|
102
114
|
# call one to many associations
|
103
115
|
alias_method :origin_load_target, :load_target
|
104
116
|
def load_target
|
105
|
-
Bullet::Detector::NPlusOneQuery.call_association(@owner, @reflection.name) unless @inversed
|
106
117
|
records = origin_load_target
|
107
118
|
|
108
|
-
if
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
119
|
+
if Bullet.start?
|
120
|
+
Bullet::Detector::NPlusOneQuery.call_association(@owner, @reflection.name) unless @inversed
|
121
|
+
if records.first.class.name !~ /^HABTM_/
|
122
|
+
if records.size > 1
|
123
|
+
Bullet::Detector::NPlusOneQuery.add_possible_objects(records)
|
124
|
+
Bullet::Detector::CounterCache.add_possible_objects(records)
|
125
|
+
elsif records.size == 1
|
126
|
+
Bullet::Detector::NPlusOneQuery.add_impossible_object(records.first)
|
127
|
+
Bullet::Detector::CounterCache.add_impossible_object(records.first)
|
128
|
+
end
|
115
129
|
end
|
116
130
|
end
|
117
131
|
records
|
@@ -119,13 +133,17 @@ module Bullet
|
|
119
133
|
|
120
134
|
alias_method :origin_empty?, :empty?
|
121
135
|
def empty?
|
122
|
-
Bullet
|
136
|
+
if Bullet.start?
|
137
|
+
Bullet::Detector::NPlusOneQuery.call_association(@owner, @reflection.name)
|
138
|
+
end
|
123
139
|
origin_empty?
|
124
140
|
end
|
125
141
|
|
126
142
|
alias_method :origin_include?, :include?
|
127
143
|
def include?(object)
|
128
|
-
Bullet
|
144
|
+
if Bullet.start?
|
145
|
+
Bullet::Detector::NPlusOneQuery.call_association(@owner, @reflection.name)
|
146
|
+
end
|
129
147
|
origin_include?(object)
|
130
148
|
end
|
131
149
|
end
|
@@ -135,12 +153,19 @@ module Bullet
|
|
135
153
|
alias_method :origin_reader, :reader
|
136
154
|
def reader(force_reload = false)
|
137
155
|
result = origin_reader(force_reload)
|
138
|
-
if
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
156
|
+
if Bullet.start?
|
157
|
+
if @owner.class.name !~ /^HABTM_/ && !@inversed
|
158
|
+
Bullet::Detector::NPlusOneQuery.call_association(@owner, @reflection.name)
|
159
|
+
if Bullet::Detector::NPlusOneQuery.impossible?(@owner)
|
160
|
+
Bullet::Detector::NPlusOneQuery.add_impossible_object(result)
|
161
|
+
else
|
162
|
+
Bullet::Detector::NPlusOneQuery.add_possible_objects(result)
|
163
|
+
end
|
164
|
+
end
|
165
|
+
if ::ActiveRecord::Reflection::HasOneReflection === @reflection && result
|
166
|
+
Bullet::Detector::NPlusOneQuery.call_association(@owner, @reflection.name)
|
167
|
+
Bullet::Detector::NPlusOneQuery.add_impossible_object(result)
|
168
|
+
end
|
144
169
|
end
|
145
170
|
result
|
146
171
|
end
|
@@ -152,15 +177,19 @@ module Bullet
|
|
152
177
|
Thread.current[:bullet_collection_empty] = true
|
153
178
|
result = origin_many_empty?
|
154
179
|
Thread.current[:bullet_collection_empty] = nil
|
155
|
-
Bullet
|
180
|
+
if Bullet.start?
|
181
|
+
Bullet::Detector::NPlusOneQuery.call_association(@owner, @reflection.name)
|
182
|
+
end
|
156
183
|
result
|
157
184
|
end
|
158
185
|
|
159
186
|
alias_method :origin_has_cached_counter?, :has_cached_counter?
|
160
187
|
def has_cached_counter?(reflection = reflection())
|
161
188
|
result = origin_has_cached_counter?(reflection)
|
162
|
-
if
|
163
|
-
|
189
|
+
if Bullet.start?
|
190
|
+
if !result && !Thread.current[:bullet_collection_empty]
|
191
|
+
Bullet::Detector::CounterCache.add_counter_cache(owner, reflection.name)
|
192
|
+
end
|
164
193
|
end
|
165
194
|
result
|
166
195
|
end
|
@@ -20,6 +20,22 @@ module Bullet
|
|
20
20
|
call_object_associations.add(object.bullet_key, associations)
|
21
21
|
end
|
22
22
|
|
23
|
+
# possible_objects keep the class to object relationships
|
24
|
+
# that the objects may cause N+1 query.
|
25
|
+
# e.g. { Post => ["Post:1", "Post:2"] }
|
26
|
+
def possible_objects
|
27
|
+
Thread.current[:bullet_possible_objects]
|
28
|
+
end
|
29
|
+
|
30
|
+
# impossible_objects keep the class to objects relationships
|
31
|
+
# that the objects may not cause N+1 query.
|
32
|
+
# e.g. { Post => ["Post:1", "Post:2"] }
|
33
|
+
# if find collection returns only one object, then the object is impossible object,
|
34
|
+
# impossible_objects are used to avoid treating 1+1 query to N+1 query.
|
35
|
+
def impossible_objects
|
36
|
+
Thread.current[:bullet_impossible_objects]
|
37
|
+
end
|
38
|
+
|
23
39
|
private
|
24
40
|
# object_associations keep the object relationships
|
25
41
|
# that the object has many associations.
|
@@ -38,22 +54,6 @@ module Bullet
|
|
38
54
|
Thread.current[:bullet_call_object_associations]
|
39
55
|
end
|
40
56
|
|
41
|
-
# possible_objects keep the class to object relationships
|
42
|
-
# that the objects may cause N+1 query.
|
43
|
-
# e.g. { Post => ["Post:1", "Post:2"] }
|
44
|
-
def possible_objects
|
45
|
-
Thread.current[:bullet_possible_objects]
|
46
|
-
end
|
47
|
-
|
48
|
-
# impossible_objects keep the class to objects relationships
|
49
|
-
# that the objects may not cause N+1 query.
|
50
|
-
# e.g. { Post => ["Post:1", "Post:2"] }
|
51
|
-
# if find collection returns only one object, then the object is impossible object,
|
52
|
-
# impossible_objects are used to avoid treating 1+1 query to N+1 query.
|
53
|
-
def impossible_objects
|
54
|
-
Thread.current[:bullet_impossible_objects]
|
55
|
-
end
|
56
|
-
|
57
57
|
# inversed_objects keeps object relationships
|
58
58
|
# that association is inversed.
|
59
59
|
# e.g. { "Comment:1" => ["post"] }
|
@@ -8,7 +8,7 @@ module Bullet
|
|
8
8
|
return unless object.primary_key_value
|
9
9
|
|
10
10
|
Bullet.debug("Detector::CounterCache#add_counter_cache", "object: #{object.bullet_key}, associations: #{associations}")
|
11
|
-
if conditions_met?(object
|
11
|
+
if conditions_met?(object, associations)
|
12
12
|
create_notification object.class.to_s, associations
|
13
13
|
end
|
14
14
|
end
|
@@ -32,6 +32,18 @@ module Bullet
|
|
32
32
|
impossible_objects.add object.bullet_key
|
33
33
|
end
|
34
34
|
|
35
|
+
def conditions_met?(object, associations)
|
36
|
+
possible_objects.include?(object.bullet_key) && !impossible_objects.include?(object.bullet_key)
|
37
|
+
end
|
38
|
+
|
39
|
+
def possible_objects
|
40
|
+
Thread.current[:bullet_counter_possible_objects]
|
41
|
+
end
|
42
|
+
|
43
|
+
def impossible_objects
|
44
|
+
Thread.current[:bullet_counter_impossible_objects]
|
45
|
+
end
|
46
|
+
|
35
47
|
private
|
36
48
|
def create_notification(klazz, associations)
|
37
49
|
notify_associations = Array(associations) - Bullet.get_whitelist_associations(:counter_cache, klazz)
|
@@ -41,18 +53,6 @@ module Bullet
|
|
41
53
|
Bullet.notification_collector.add notice
|
42
54
|
end
|
43
55
|
end
|
44
|
-
|
45
|
-
def possible_objects
|
46
|
-
Thread.current[:bullet_counter_possible_objects]
|
47
|
-
end
|
48
|
-
|
49
|
-
def impossible_objects
|
50
|
-
Thread.current[:bullet_counter_impossible_objects]
|
51
|
-
end
|
52
|
-
|
53
|
-
def conditions_met?(bullet_key, associations)
|
54
|
-
possible_objects.include?(bullet_key) && !impossible_objects.include?(bullet_key)
|
55
|
-
end
|
56
56
|
end
|
57
57
|
end
|
58
58
|
end
|