record-cache 0.1.2 → 0.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +15 -0
- data/lib/record_cache.rb +2 -1
- data/lib/record_cache/base.rb +63 -22
- data/lib/record_cache/datastore/active_record.rb +5 -3
- data/lib/record_cache/datastore/active_record_30.rb +95 -38
- data/lib/record_cache/datastore/active_record_31.rb +157 -54
- data/lib/record_cache/datastore/active_record_32.rb +444 -0
- data/lib/record_cache/dispatcher.rb +47 -47
- data/lib/record_cache/multi_read.rb +14 -1
- data/lib/record_cache/query.rb +36 -25
- data/lib/record_cache/statistics.rb +5 -5
- data/lib/record_cache/strategy/base.rb +49 -19
- data/lib/record_cache/strategy/full_table_cache.rb +81 -0
- data/lib/record_cache/strategy/index_cache.rb +38 -36
- data/lib/record_cache/strategy/unique_index_cache.rb +130 -0
- data/lib/record_cache/strategy/util.rb +12 -12
- data/lib/record_cache/test/resettable_version_store.rb +2 -9
- data/lib/record_cache/version.rb +1 -1
- data/lib/record_cache/version_store.rb +23 -16
- data/spec/db/schema.rb +12 -0
- data/spec/db/seeds.rb +10 -0
- data/spec/lib/active_record/visitor_spec.rb +22 -0
- data/spec/lib/base_spec.rb +21 -0
- data/spec/lib/dispatcher_spec.rb +24 -46
- data/spec/lib/multi_read_spec.rb +6 -6
- data/spec/lib/query_spec.rb +43 -43
- data/spec/lib/statistics_spec.rb +28 -28
- data/spec/lib/strategy/base_spec.rb +98 -87
- data/spec/lib/strategy/full_table_cache_spec.rb +68 -0
- data/spec/lib/strategy/index_cache_spec.rb +112 -69
- data/spec/lib/strategy/query_cache_spec.rb +83 -0
- data/spec/lib/strategy/unique_index_on_id_cache_spec.rb +317 -0
- data/spec/lib/strategy/unique_index_on_string_cache_spec.rb +168 -0
- data/spec/lib/strategy/util_spec.rb +67 -49
- data/spec/lib/version_store_spec.rb +22 -41
- data/spec/models/address.rb +9 -0
- data/spec/models/apple.rb +1 -1
- data/spec/models/banana.rb +21 -2
- data/spec/models/language.rb +5 -0
- data/spec/models/person.rb +1 -1
- data/spec/models/store.rb +2 -1
- data/spec/spec_helper.rb +7 -4
- data/spec/support/after_commit.rb +2 -0
- data/spec/support/matchers/hit_cache_matcher.rb +10 -6
- data/spec/support/matchers/log.rb +45 -0
- data/spec/support/matchers/miss_cache_matcher.rb +10 -6
- data/spec/support/matchers/use_cache_matcher.rb +10 -6
- metadata +156 -161
- data/lib/record_cache/strategy/id_cache.rb +0 -93
- data/lib/record_cache/strategy/request_cache.rb +0 -49
- data/spec/lib/strategy/id_cache_spec.rb +0 -168
- data/spec/lib/strategy/request_cache_spec.rb +0 -85
@@ -0,0 +1,317 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe RecordCache::Strategy::UniqueIndexCache do
|
4
|
+
|
5
|
+
it "should retrieve an Apple from the cache" do
|
6
|
+
expect{ Apple.find(1) }.to miss_cache(Apple).on(:id).times(1)
|
7
|
+
expect{ Apple.find(1) }.to hit_cache(Apple).on(:id).times(1)
|
8
|
+
end
|
9
|
+
|
10
|
+
it "should accept find_by_sql queries (can not use the cache though)" do
|
11
|
+
apple2 = Apple.find(2) # prefill cache
|
12
|
+
apples = []
|
13
|
+
expect{ apples = Apple.find_by_sql("select * from apples where id = 2") }.to_not use_cache(Apple).on(:id)
|
14
|
+
expect(apples).to eq([apple2])
|
15
|
+
end
|
16
|
+
|
17
|
+
it "should accept parameterized find_by_sql queries (can not use the cache though)" do
|
18
|
+
apple1 = Apple.find(1) # prefill cache
|
19
|
+
apples = []
|
20
|
+
expect{ apples = Apple.find_by_sql(["select * from apples where id = ?", 1]) }.to_not use_cache(Apple).on(:id)
|
21
|
+
expect(apples).to eq([apple1])
|
22
|
+
end
|
23
|
+
|
24
|
+
it "should retrieve cloned records" do
|
25
|
+
@apple_1a = Apple.find(1)
|
26
|
+
@apple_1b = Apple.find(1)
|
27
|
+
expect(@apple_1a).to eq(@apple_1b)
|
28
|
+
expect(@apple_1a.object_id).to_not eq(@apple_1b.object_id)
|
29
|
+
end
|
30
|
+
|
31
|
+
context "logging" do
|
32
|
+
before(:each) do
|
33
|
+
Apple.find(1)
|
34
|
+
end
|
35
|
+
|
36
|
+
it "should write full hits to the debug log" do
|
37
|
+
expect{ Apple.find(1) }.to log(:debug, %(UniqueIndexCache on 'Apple.id' hit for ids 1))
|
38
|
+
end
|
39
|
+
|
40
|
+
it "should write full miss to the debug log" do
|
41
|
+
expect{ Apple.find(2) }.to log(:debug, %(UniqueIndexCache on 'Apple.id' miss for ids 2))
|
42
|
+
end
|
43
|
+
|
44
|
+
it "should write partial hits to the debug log" do
|
45
|
+
expect{ Apple.where(:id => [1,2]).all }.to log(:debug, %(UniqueIndexCache on 'Apple.id' partial hit for ids [1, 2]: missing [2]))
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
context "cacheable?" do
|
50
|
+
before(:each) do
|
51
|
+
# fill cache
|
52
|
+
@apple1 = Apple.find(1)
|
53
|
+
@apple2 = Apple.find(2)
|
54
|
+
end
|
55
|
+
|
56
|
+
# @see https://github.com/orslumen/record-cache/issues/2
|
57
|
+
it "should not use the cache when a lock is used" do
|
58
|
+
expect{ Apple.lock("any_lock").where(:id => 1).all }.to_not hit_cache(Apple)
|
59
|
+
end
|
60
|
+
|
61
|
+
it "should use the cache when a single id is requested" do
|
62
|
+
expect{ Apple.where(:id => 1).all }.to hit_cache(Apple).on(:id).times(1)
|
63
|
+
end
|
64
|
+
|
65
|
+
it "should use the cache when a multiple ids are requested" do
|
66
|
+
expect{ Apple.where(:id => [1, 2]).all }.to hit_cache(Apple).on(:id).times(2)
|
67
|
+
end
|
68
|
+
|
69
|
+
it "should use the cache when a single id is requested and the limit is 1" do
|
70
|
+
expect{ Apple.where(:id => 1).limit(1).all }.to hit_cache(Apple).on(:id).times(1)
|
71
|
+
end
|
72
|
+
|
73
|
+
it "should not use the cache when a single id is requested and the limit is > 1" do
|
74
|
+
expect{ Apple.where(:id => 1).limit(2).all }.to_not use_cache(Apple).on(:id)
|
75
|
+
end
|
76
|
+
|
77
|
+
it "should not use the cache when multiple ids are requested and the limit is 1" do
|
78
|
+
expect{ Apple.where(:id => [1, 2]).limit(1).all }.to_not use_cache(Apple).on(:id)
|
79
|
+
end
|
80
|
+
|
81
|
+
it "should use the cache when a single id is requested together with other where clauses" do
|
82
|
+
expect{ Apple.where(:id => 1).where(:name => "Adams Apple x").all }.to hit_cache(Apple).on(:id).times(1)
|
83
|
+
end
|
84
|
+
|
85
|
+
it "should use the cache when a multiple ids are requested together with other where clauses" do
|
86
|
+
expect{ Apple.where(:id => [1,2]).where(:name => "Adams Apple x").all }.to hit_cache(Apple).on(:id).times(2)
|
87
|
+
end
|
88
|
+
|
89
|
+
it "should use the cache when a single id is requested together with (simple) sort clauses" do
|
90
|
+
expect{ Apple.where(:id => 1).order("name ASC").all }.to hit_cache(Apple).on(:id).times(1)
|
91
|
+
end
|
92
|
+
|
93
|
+
it "should use the cache when a multiple ids are requested together with (simple) sort clauses" do
|
94
|
+
expect{ Apple.where(:id => [1,2]).order("name ASC").all }.to hit_cache(Apple).on(:id).times(2)
|
95
|
+
end
|
96
|
+
|
97
|
+
it "should not use the cache when a join clause is used" do
|
98
|
+
expect{ Apple.where(:id => [1,2]).joins(:store).all }.to_not use_cache(Apple).on(:id)
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
context "record_change" do
|
103
|
+
before(:each) do
|
104
|
+
# fill cache
|
105
|
+
@apple1 = Apple.find(1)
|
106
|
+
@apple2 = Apple.find(2)
|
107
|
+
end
|
108
|
+
|
109
|
+
it "should invalidate destroyed records" do
|
110
|
+
expect{ Apple.where(:id => 1).all }.to hit_cache(Apple).on(:id).times(1)
|
111
|
+
@apple1.destroy
|
112
|
+
expect{ @apples = Apple.where(:id => 1).all }.to miss_cache(Apple).on(:id).times(1)
|
113
|
+
expect(@apples).to be_empty
|
114
|
+
# try again, to make sure the "missing record" is not cached
|
115
|
+
expect{ Apple.where(:id => 1).all }.to miss_cache(Apple).on(:id).times(1)
|
116
|
+
end
|
117
|
+
|
118
|
+
it "should add updated records directly to the cache" do
|
119
|
+
@apple1.name = "Applejuice"
|
120
|
+
@apple1.save!
|
121
|
+
expect{ @apple = Apple.find(1) }.to hit_cache(Apple).on(:id).times(1)
|
122
|
+
expect(@apple.name).to eq("Applejuice")
|
123
|
+
end
|
124
|
+
|
125
|
+
it "should add created records directly to the cache" do
|
126
|
+
@new_apple = Apple.create!(:name => "Fresh Apple", :store_id => 1)
|
127
|
+
expect{ @apple = Apple.find(@new_apple.id) }.to hit_cache(Apple).on(:id).times(1)
|
128
|
+
expect(@apple.name).to eq("Fresh Apple")
|
129
|
+
end
|
130
|
+
|
131
|
+
it "should add updated records to the cache, also when multiple ids are queried" do
|
132
|
+
@apple1.name = "Applejuice"
|
133
|
+
@apple1.save!
|
134
|
+
expect{ @apples = Apple.where(:id => [1, 2]).order('id ASC').all }.to hit_cache(Apple).on(:id).times(2)
|
135
|
+
expect(@apples.map(&:name)).to eq(["Applejuice", "Adams Apple 2"])
|
136
|
+
end
|
137
|
+
|
138
|
+
end
|
139
|
+
|
140
|
+
context "invalidate" do
|
141
|
+
before(:each) do
|
142
|
+
@apple1 = Apple.find(1)
|
143
|
+
@apple2 = Apple.find(2)
|
144
|
+
end
|
145
|
+
|
146
|
+
it "should invalidate single records" do
|
147
|
+
Apple.record_cache[:id].invalidate(1)
|
148
|
+
expect{ Apple.find(1) }.to miss_cache(Apple).on(:id).times(1)
|
149
|
+
end
|
150
|
+
|
151
|
+
it "should only miss the cache for the invalidated record when multiple ids are queried" do
|
152
|
+
# miss on 1
|
153
|
+
Apple.record_cache[:id].invalidate(1)
|
154
|
+
expect{ Apple.where(:id => [1, 2]).all }.to miss_cache(Apple).on(:id).times(1)
|
155
|
+
# hit on 2
|
156
|
+
Apple.record_cache[:id].invalidate(1)
|
157
|
+
expect{ Apple.where(:id => [1, 2]).all }.to hit_cache(Apple).on(:id).times(1)
|
158
|
+
# nothing invalidated, both hit
|
159
|
+
expect{ Apple.where(:id => [1, 2]).all }.to hit_cache(Apple).on(:id).times(2)
|
160
|
+
end
|
161
|
+
|
162
|
+
it "should invalidate records when using update_all" do
|
163
|
+
Apple.where(:id => [3,4,5]).all # fill id cache on all Adam Store apples
|
164
|
+
expect{ @apples = Apple.where(:id => [1, 2, 3, 4, 5]).order('id ASC').all }.to hit_cache(Apple).on(:id).times(5)
|
165
|
+
expect(@apples.map(&:name)).to eq(["Adams Apple 1", "Adams Apple 2", "Adams Apple 3", "Adams Apple 4", "Adams Apple 5"])
|
166
|
+
# update 3 of the 5 apples in the Adam Store
|
167
|
+
Apple.where(:id => [1,2,3]).update_all(:name => "Uniform Apple")
|
168
|
+
expect{ @apples = Apple.where(:id => [1, 2, 3, 4, 5]).order('id ASC').all }.to hit_cache(Apple).on(:id).times(2)
|
169
|
+
expect(@apples.map(&:name)).to eq(["Uniform Apple", "Uniform Apple", "Uniform Apple", "Adams Apple 4", "Adams Apple 5"])
|
170
|
+
end
|
171
|
+
|
172
|
+
it "should invalidate reflection indexes when a has_many relation is updated" do
|
173
|
+
# assign different apples to store 2
|
174
|
+
expect{ Apple.where(:store_id => 1).all }.to hit_cache(Apple).on(:id).times(2)
|
175
|
+
store2_apple_ids = Apple.where(:store_id => 2).map(&:id)
|
176
|
+
store1 = Store.find(1)
|
177
|
+
store1.apple_ids = store2_apple_ids
|
178
|
+
store1.save!
|
179
|
+
# the apples that used to belong to store 2 are now in store 1 (incremental update)
|
180
|
+
expect{ @apple1 = Apple.find(store2_apple_ids.first) }.to hit_cache(Apple).on(:id).times(1)
|
181
|
+
expect(@apple1.store_id).to eq(1)
|
182
|
+
# the apples that used to belong to store 1 are now homeless (cache invalidated)
|
183
|
+
expect{ @homeless_apple = Apple.find(1) }.to miss_cache(Apple).on(:id).times(1)
|
184
|
+
expect(@homeless_apple.store_id).to be_nil
|
185
|
+
end
|
186
|
+
|
187
|
+
it "should reload from the DB after invalidation" do
|
188
|
+
@apple = Apple.last
|
189
|
+
Apple.record_cache.invalidate(@apple.id)
|
190
|
+
expect{ Apple.find(@apple.id) }.to miss_cache(Apple).on(:id).times(1)
|
191
|
+
end
|
192
|
+
|
193
|
+
end
|
194
|
+
|
195
|
+
context "transactions" do
|
196
|
+
|
197
|
+
it "should update the cache once the transaction is committed" do
|
198
|
+
apple1 = Apple.find(1)
|
199
|
+
ActiveRecord::Base.transaction do
|
200
|
+
apple1.name = "Committed Apple"
|
201
|
+
apple1.save!
|
202
|
+
|
203
|
+
# do not use the cache within a transaction
|
204
|
+
expect{ apple1 = Apple.find(1) }.to_not use_cache(Apple).on(:id)
|
205
|
+
expect(apple1.name).to eq("Committed Apple")
|
206
|
+
end
|
207
|
+
|
208
|
+
# use the cache again once the transaction is over
|
209
|
+
expect{ apple1 = Apple.find(1) }.to use_cache(Apple).on(:id)
|
210
|
+
expect(apple1.name).to eq("Committed Apple")
|
211
|
+
end
|
212
|
+
|
213
|
+
it "should not update the cache when the transaction is rolled back" do
|
214
|
+
apple1 = Apple.find(1)
|
215
|
+
ActiveRecord::Base.transaction do
|
216
|
+
apple1.name = "Rollback Apple"
|
217
|
+
apple1.save!
|
218
|
+
|
219
|
+
# test to make sure appl1 is not retrieved 1:1 from the cache
|
220
|
+
apple1.name = "Not saved apple"
|
221
|
+
|
222
|
+
# do not use the cache within a transaction
|
223
|
+
expect{ apple1 = Apple.find(1) }.to_not use_cache(Apple).on(:id)
|
224
|
+
expect(apple1.name).to eq("Rollback Apple")
|
225
|
+
|
226
|
+
raise ActiveRecord::Rollback, "oops"
|
227
|
+
end
|
228
|
+
|
229
|
+
# use the cache again once the transaction is over
|
230
|
+
expect{ apple1 = Apple.find(1) }.to use_cache(Apple).on(:id)
|
231
|
+
expect(apple1.name).to eq("Adams Apple 1")
|
232
|
+
end
|
233
|
+
|
234
|
+
end
|
235
|
+
|
236
|
+
context "nested transactions" do
|
237
|
+
|
238
|
+
it "should update the cache in case both transactions are committed" do
|
239
|
+
apple1, apple2 = nil
|
240
|
+
|
241
|
+
ActiveRecord::Base.transaction do
|
242
|
+
apple1 = Apple.find(1)
|
243
|
+
apple1.name = "Committed Apple 1"
|
244
|
+
apple1.save!
|
245
|
+
|
246
|
+
ActiveRecord::Base.transaction(requires_new: true) do
|
247
|
+
apple2 = Apple.find(2)
|
248
|
+
apple2.name = "Committed Apple 2"
|
249
|
+
apple2.save!
|
250
|
+
end
|
251
|
+
end
|
252
|
+
|
253
|
+
expect{ apple1 = Apple.find(1) }.to use_cache(Apple).on(:id)
|
254
|
+
expect(apple1.name).to eq("Committed Apple 1")
|
255
|
+
|
256
|
+
expect{ apple2 = Apple.find(2) }.to use_cache(Apple).on(:id)
|
257
|
+
expect(apple2.name).to eq("Committed Apple 2")
|
258
|
+
end
|
259
|
+
|
260
|
+
[:implicitly, :explicitly].each do |inner_rollback_explicit_or_implicit|
|
261
|
+
it "should not update the cache in case both transactions are #{inner_rollback_explicit_or_implicit} rolled back" do
|
262
|
+
pending "nested transaction support" if ActiveRecord::VERSION::MAJOR == 3 && ActiveRecord::VERSION::MINOR == 0
|
263
|
+
apple1, apple2 = nil
|
264
|
+
|
265
|
+
ActiveRecord::Base.transaction do
|
266
|
+
apple1 = Apple.find(1)
|
267
|
+
apple1.name = "Rollback Apple 1"
|
268
|
+
apple1.save!
|
269
|
+
apple1.name = "Saved Apple 1"
|
270
|
+
|
271
|
+
ActiveRecord::Base.transaction(requires_new: true) do
|
272
|
+
apple2 = Apple.find(2)
|
273
|
+
apple2.name = "Rollback Apple 2"
|
274
|
+
apple2.save!
|
275
|
+
apple1.name = "Saved Apple 2"
|
276
|
+
|
277
|
+
raise ActiveRecord::Rollback, "oops" if inner_rollback_explicit_or_implicit == :explicitly
|
278
|
+
end
|
279
|
+
|
280
|
+
raise ActiveRecord::Rollback, "oops"
|
281
|
+
end
|
282
|
+
|
283
|
+
expect{ apple1 = Apple.find(1) }.to use_cache(Apple).on(:id)
|
284
|
+
expect(apple1.name).to eq("Adams Apple 1")
|
285
|
+
|
286
|
+
expect{ apple2 = Apple.find(2) }.to use_cache(Apple).on(:id)
|
287
|
+
expect(apple2.name).to eq("Adams Apple 2")
|
288
|
+
end
|
289
|
+
end
|
290
|
+
|
291
|
+
it "should not update the cache for the rolled back inner transaction" do
|
292
|
+
pending "rails calls after_commit on records that are in a transaction that is rolled back"
|
293
|
+
|
294
|
+
apple1, apple2 = nil
|
295
|
+
|
296
|
+
ActiveRecord::Base.transaction do
|
297
|
+
apple1 = Apple.find(1)
|
298
|
+
apple1.name = "Committed Apple 1"
|
299
|
+
apple1.save!
|
300
|
+
|
301
|
+
ActiveRecord::Base.transaction(requires_new: true) do
|
302
|
+
apple2 = Apple.find(2)
|
303
|
+
apple2.name = "Rollback Apple 2"
|
304
|
+
apple2.save!
|
305
|
+
|
306
|
+
raise ActiveRecord::Rollback, "oops"
|
307
|
+
end
|
308
|
+
end
|
309
|
+
|
310
|
+
expect{ apple1 = Apple.find(1) }.to use_cache(Apple).on(:id)
|
311
|
+
expect(apple1.name).to eq("Committed Apple 1")
|
312
|
+
|
313
|
+
expect{ apple2 = Apple.find(2) }.to use_cache(Apple).on(:id)
|
314
|
+
expect(apple2.name).to eq("Adams Apple 2")
|
315
|
+
end
|
316
|
+
end
|
317
|
+
end
|
@@ -0,0 +1,168 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe RecordCache::Strategy::UniqueIndexCache do
|
4
|
+
|
5
|
+
it "should retrieve an Person from the cache" do
|
6
|
+
expect{ Person.find_by_name("Fry") }.to miss_cache(Person).on(:name).times(1)
|
7
|
+
expect{ Person.find_by_name("Fry") }.to hit_cache(Person).on(:name).times(1)
|
8
|
+
end
|
9
|
+
|
10
|
+
it "should retrieve cloned records" do
|
11
|
+
@fry_a = Person.find_by_name("Fry")
|
12
|
+
@fry_b = Person.find_by_name("Fry")
|
13
|
+
expect(@fry_a).to eq(@fry_b)
|
14
|
+
expect(@fry_a.object_id).to_not eq(@fry_b.object_id)
|
15
|
+
end
|
16
|
+
|
17
|
+
context "logging" do
|
18
|
+
before(:each) do
|
19
|
+
Person.find_by_name("Fry")
|
20
|
+
end
|
21
|
+
|
22
|
+
it "should write full hits to the debug log" do
|
23
|
+
expect{ Person.find_by_name("Fry") }.to log(:debug, %(UniqueIndexCache on 'Person.name' hit for ids "Fry"))
|
24
|
+
end
|
25
|
+
|
26
|
+
it "should write full miss to the debug log" do
|
27
|
+
expect{ Person.find_by_name("Chase") }.to log(:debug, %(UniqueIndexCache on 'Person.name' miss for ids "Chase"))
|
28
|
+
end
|
29
|
+
|
30
|
+
it "should write partial hits to the debug log" do
|
31
|
+
expect{ Person.where(:name => ["Fry", "Chase"]).all }.to log(:debug, %(UniqueIndexCache on 'Person.name' partial hit for ids ["Fry", "Chase"]: missing ["Chase"]))
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
context "cacheable?" do
|
36
|
+
before(:each) do
|
37
|
+
# fill cache
|
38
|
+
@fry = Person.find_by_name("Fry")
|
39
|
+
@chase = Person.find_by_name("Chase")
|
40
|
+
end
|
41
|
+
|
42
|
+
# @see https://github.com/orslumen/record-cache/issues/2
|
43
|
+
it "should not use the cache when a lock is used" do
|
44
|
+
expect{ Person.lock("any_lock").where(:name => "Fry").all }.to_not hit_cache(Person)
|
45
|
+
end
|
46
|
+
|
47
|
+
it "should use the cache when a single id is requested" do
|
48
|
+
expect{ Person.where(:name => "Fry").all }.to hit_cache(Person).on(:name).times(1)
|
49
|
+
end
|
50
|
+
|
51
|
+
it "should use the cache when a multiple ids are requested" do
|
52
|
+
expect{ Person.where(:name => ["Fry", "Chase"]).all }.to hit_cache(Person).on(:name).times(2)
|
53
|
+
end
|
54
|
+
|
55
|
+
it "should use the cache when a single id is requested and the limit is 1" do
|
56
|
+
expect{ Person.where(:name => "Fry").limit(1).all }.to hit_cache(Person).on(:name).times(1)
|
57
|
+
end
|
58
|
+
|
59
|
+
it "should not use the cache when a single id is requested and the limit is > 1" do
|
60
|
+
expect{ Person.where(:name => "Fry").limit(2).all }.to_not use_cache(Person).on(:name)
|
61
|
+
end
|
62
|
+
|
63
|
+
it "should not use the cache when multiple ids are requested and the limit is 1" do
|
64
|
+
expect{ Person.where(:name => ["Fry", "Chase"]).limit(1).all }.to_not use_cache(Person).on(:name)
|
65
|
+
end
|
66
|
+
|
67
|
+
it "should use the cache when a single id is requested together with other where clauses" do
|
68
|
+
expect{ Person.where(:name => "Fry").where(:height => 1.67).all }.to hit_cache(Person).on(:name).times(1)
|
69
|
+
end
|
70
|
+
|
71
|
+
it "should use the cache when a multiple ids are requested together with other where clauses" do
|
72
|
+
expect{ Person.where(:name => ["Fry", "Chase"]).where(:height => 1.67).all }.to hit_cache(Person).on(:name).times(2)
|
73
|
+
end
|
74
|
+
|
75
|
+
it "should use the cache when a single id is requested together with (simple) sort clauses" do
|
76
|
+
expect{ Person.where(:name => "Fry").order("name ASC").all }.to hit_cache(Person).on(:name).times(1)
|
77
|
+
end
|
78
|
+
|
79
|
+
it "should use the cache when a single id is requested together with (simple) case insensitive sort clauses" do
|
80
|
+
expect{ Person.where(:name => "Fry").order("name desc").all }.to hit_cache(Person).on(:name).times(1)
|
81
|
+
end
|
82
|
+
|
83
|
+
it "should use the cache when a single id is requested together with (simple) sort clauses with table prefix" do
|
84
|
+
expect{ Person.where(:name => "Fry").order("people.name desc").all }.to hit_cache(Person).on(:name).times(1)
|
85
|
+
end
|
86
|
+
|
87
|
+
it "should not use the cache when a single id is requested together with an unknown sort clause" do
|
88
|
+
expect{ Person.where(:name => "Fry").order("lower(people.name) desc").all }.to_not hit_cache(Person).on(:name).times(1)
|
89
|
+
end
|
90
|
+
|
91
|
+
it "should use the cache when a multiple ids are requested together with (simple) sort clauses" do
|
92
|
+
expect{ Person.where(:name => ["Fry", "Chase"]).order("name ASC").all }.to hit_cache(Person).on(:name).times(2)
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
context "record_change" do
|
97
|
+
before(:each) do
|
98
|
+
# fill cache
|
99
|
+
@fry = Person.find_by_name("Fry")
|
100
|
+
@chase = Person.find_by_name("Chase")
|
101
|
+
end
|
102
|
+
|
103
|
+
it "should invalidate destroyed records" do
|
104
|
+
expect{ Person.where(:name => "Fry").all }.to hit_cache(Person).on(:name).times(1)
|
105
|
+
@fry.destroy
|
106
|
+
expect{ @people = Person.where(:name => "Fry").all }.to miss_cache(Person).on(:name).times(1)
|
107
|
+
expect(@people).to be_empty
|
108
|
+
# try again, to make sure the "missing record" is not cached
|
109
|
+
expect{ Person.where(:name => "Fry").all }.to miss_cache(Person).on(:name).times(1)
|
110
|
+
end
|
111
|
+
|
112
|
+
it "should add updated records directly to the cache" do
|
113
|
+
@fry.height = 1.71
|
114
|
+
@fry.save!
|
115
|
+
expect{ @person = Person.find_by_name("Fry") }.to hit_cache(Person).on(:name).times(1)
|
116
|
+
expect(@person.height).to eq(1.71)
|
117
|
+
end
|
118
|
+
|
119
|
+
it "should add created records directly to the cache" do
|
120
|
+
Person.create!(:name => "Flower", :birthday => Date.civil(1990,07,29), :height => 1.80)
|
121
|
+
expect{ @person = Person.find_by_name("Flower") }.to hit_cache(Person).on(:name).times(1)
|
122
|
+
expect(@person.height).to eq(1.80)
|
123
|
+
end
|
124
|
+
|
125
|
+
it "should add updated records to the cache, also when multiple ids are queried" do
|
126
|
+
@fry.height = 1.71
|
127
|
+
@fry.save!
|
128
|
+
expect{ @people = Person.where(:name => ["Fry", "Chase"]).order("id ASC").all }.to hit_cache(Person).on(:name).times(2)
|
129
|
+
expect(@people.map(&:height)).to eq([1.71, 1.91])
|
130
|
+
end
|
131
|
+
|
132
|
+
end
|
133
|
+
|
134
|
+
context "invalidate" do
|
135
|
+
before(:each) do
|
136
|
+
@fry = Person.find_by_name("Fry")
|
137
|
+
@chase = Person.find_by_name("Chase")
|
138
|
+
end
|
139
|
+
|
140
|
+
it "should invalidate single records" do
|
141
|
+
Person.record_cache[:name].invalidate("Fry")
|
142
|
+
expect{ Person.find_by_name("Fry") }.to miss_cache(Person).on(:name).times(1)
|
143
|
+
end
|
144
|
+
|
145
|
+
it "should only miss the cache for the invalidated record when multiple ids are queried" do
|
146
|
+
# miss on 1
|
147
|
+
Person.record_cache[:name].invalidate("Fry")
|
148
|
+
expect{ Person.where(:name => ["Fry", "Chase"]).all }.to miss_cache(Person).on(:name).times(1)
|
149
|
+
# hit on 2
|
150
|
+
Person.record_cache[:name].invalidate("Fry")
|
151
|
+
expect{ Person.where(:name => ["Fry", "Chase"]).all }.to hit_cache(Person).on(:name).times(1)
|
152
|
+
# nothing invalidated, both hit
|
153
|
+
expect{ Person.where(:name => ["Fry", "Chase"]).all }.to hit_cache(Person).on(:name).times(2)
|
154
|
+
end
|
155
|
+
|
156
|
+
it "should invalidate records when using update_all" do
|
157
|
+
Person.where(:id => ["Fry", "Chase", "Penny"]).all # fill id cache on all Adam Store apples
|
158
|
+
expect{ @people = Person.where(:name => ["Fry", "Chase", "Penny"]).order("name ASC").all }.to hit_cache(Person).on(:name).times(2)
|
159
|
+
expect(@people.map(&:name)).to eq(["Chase", "Fry", "Penny"])
|
160
|
+
# update 2 of the 3 People
|
161
|
+
Person.where(:name => ["Fry", "Penny"]).update_all(:height => 1.21)
|
162
|
+
expect{ @people = Person.where(:name => ["Fry", "Chase", "Penny"]).order("height ASC").all }.to hit_cache(Person).on(:name).times(1)
|
163
|
+
expect(@people.map(&:height)).to eq([1.21, 1.21, 1.91])
|
164
|
+
end
|
165
|
+
|
166
|
+
end
|
167
|
+
|
168
|
+
end
|