record-cache 0.1.2 → 0.1.3
Sign up to get free protection for your applications and to get access to all the features.
- 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,68 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe RecordCache::Strategy::FullTableCache do
|
4
|
+
|
5
|
+
it "should retrieve a Language from the cache" do
|
6
|
+
expect{ Language.where(:locale => 'en-US').all }.to miss_cache(Language).on(:full_table).times(1)
|
7
|
+
expect{ Language.where(:locale => 'en-US').all }.to hit_cache(Language).on(:full_table).times(1)
|
8
|
+
end
|
9
|
+
|
10
|
+
it "should retrieve all Languages from cache" do
|
11
|
+
expect{ Language.all }.to miss_cache(Language).on(:full_table).times(1)
|
12
|
+
expect{ Language.all }.to hit_cache(Language).on(:full_table).times(1)
|
13
|
+
expect(Language.all.map(&:locale).sort).to eq(%w(du-NL en-GB en-US hu))
|
14
|
+
end
|
15
|
+
|
16
|
+
context "logging" do
|
17
|
+
it "should write hit to the debug log" do
|
18
|
+
Language.all
|
19
|
+
expect{ Language.all }.to log(:debug, "FullTableCache hit for model Language")
|
20
|
+
end
|
21
|
+
|
22
|
+
it "should write miss to the debug log" do
|
23
|
+
expect{ Language.all }.to log(:debug, "FullTableCache miss for model Language")
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
context "cacheable?" do
|
28
|
+
it "should always return true" do
|
29
|
+
expect(Language.record_cache[:full_table].cacheable?("any query")).to be_truthy
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
context "record_change" do
|
34
|
+
before(:each) do
|
35
|
+
@Languages = Language.all
|
36
|
+
end
|
37
|
+
|
38
|
+
it "should invalidate the cache when a record is added" do
|
39
|
+
expect{ Language.where(:locale => 'en-US').all }.to hit_cache(Language).on(:full_table).times(1)
|
40
|
+
Language.create!(:name => 'Deutsch', :locale => 'de')
|
41
|
+
expect{ Language.where(:locale => 'en-US').all }.to miss_cache(Language).on(:full_table).times(1)
|
42
|
+
end
|
43
|
+
|
44
|
+
it "should invalidate the cache when any record is deleted" do
|
45
|
+
expect{ Language.where(:locale => 'en-US').all }.to hit_cache(Language).on(:full_table).times(1)
|
46
|
+
Language.where(:locale => 'hu').first.destroy
|
47
|
+
expect{ Language.where(:locale => 'en-US').all }.to miss_cache(Language).on(:full_table).times(1)
|
48
|
+
end
|
49
|
+
|
50
|
+
it "should invalidate the cache when any record is modified" do
|
51
|
+
expect{ Language.where(:locale => 'en-US').all }.to hit_cache(Language).on(:full_table).times(1)
|
52
|
+
hungarian = Language.where(:locale => 'hu').first
|
53
|
+
hungarian.name = 'Magyar (Magyarorszag)'
|
54
|
+
hungarian.save!
|
55
|
+
expect{ Language.where(:locale => 'en-US').all }.to miss_cache(Language).on(:full_table).times(1)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
context "invalidate" do
|
60
|
+
|
61
|
+
it "should invalidate the full cache" do
|
62
|
+
Language.record_cache[:full_table].invalidate(-10) # any id
|
63
|
+
expect{ Language.where(:locale => 'en-US').all }.to miss_cache(Language).on(:full_table).times(1)
|
64
|
+
end
|
65
|
+
|
66
|
+
end
|
67
|
+
|
68
|
+
end
|
@@ -4,18 +4,18 @@ describe RecordCache::Strategy::IndexCache do
|
|
4
4
|
|
5
5
|
context "initialize" do
|
6
6
|
it "should only accept index cache on DB columns" do
|
7
|
-
|
7
|
+
expect{ Apple.send(:cache_records, :index => :unknown_column) }.to raise_error("No column found for index 'unknown_column' on Apple.")
|
8
8
|
end
|
9
9
|
|
10
10
|
it "should only accept index cache on integer columns" do
|
11
|
-
|
11
|
+
expect{ Apple.send(:cache_records, :index => :name) }.to raise_error("Incorrect type (expected integer, found string) for index 'name' on Apple.")
|
12
12
|
end
|
13
13
|
end
|
14
14
|
|
15
15
|
it "should use the id cache to retrieve the actual records" do
|
16
|
-
|
17
|
-
|
18
|
-
|
16
|
+
expect{ @apples = Apple.where(:store_id => 1).all }.to miss_cache(Apple).on(:store_id).times(1)
|
17
|
+
expect{ Apple.where(:store_id => 1).all }.to hit_cache(Apple).on(:store_id).times(1)
|
18
|
+
expect{ Apple.where(:store_id => 1).all }.to hit_cache(Apple).on(:id).times(@apples.size)
|
19
19
|
end
|
20
20
|
|
21
21
|
context "logging" do
|
@@ -24,13 +24,11 @@ describe RecordCache::Strategy::IndexCache do
|
|
24
24
|
end
|
25
25
|
|
26
26
|
it "should write hit to the debug log" do
|
27
|
-
|
28
|
-
Apple.where(:store_id => 1).all
|
27
|
+
expect{ Apple.where(:store_id => 1).all }.to log(:debug, /IndexCache hit for rc\/apl\/store_id=1v\d+: found 5 ids/)
|
29
28
|
end
|
30
29
|
|
31
30
|
it "should write miss to the debug log" do
|
32
|
-
|
33
|
-
Apple.where(:store_id => 2).all
|
31
|
+
expect{ Apple.where(:store_id => 2).all }.to log(:debug, /IndexCache miss for rc\/apl\/store_id=2v\d+: found no ids/)
|
34
32
|
end
|
35
33
|
end
|
36
34
|
|
@@ -41,24 +39,25 @@ describe RecordCache::Strategy::IndexCache do
|
|
41
39
|
end
|
42
40
|
|
43
41
|
it "should hit the cache for a single index id" do
|
44
|
-
|
42
|
+
expect{ Apple.where(:store_id => 1).all }.to hit_cache(Apple).on(:store_id).times(1)
|
45
43
|
end
|
46
44
|
|
47
45
|
it "should hit the cache for a single index id with other where clauses" do
|
48
|
-
|
46
|
+
expect{ Apple.where(:store_id => 1).where(:name => "applegate").all }.to hit_cache(Apple).on(:store_id).times(1)
|
49
47
|
end
|
50
48
|
|
51
49
|
it "should hit the cache for a single index id with (simple) sort clauses" do
|
52
|
-
|
50
|
+
expect{ Apple.where(:store_id => 1).order("name ASC").all }.to hit_cache(Apple).on(:store_id).times(1)
|
53
51
|
end
|
54
52
|
|
55
|
-
|
56
|
-
|
53
|
+
#Allow limit == 1 by filtering records after cache hit. Needed for has_one
|
54
|
+
it "should not hit the cache for a single index id with limit > 0" do
|
55
|
+
expect{ Apple.where(:store_id => 1).limit(2).all }.to_not hit_cache(Apple).on(:store_id)
|
57
56
|
end
|
58
57
|
|
59
58
|
it "should not hit the cache when an :id where clause is defined" do
|
60
59
|
# this query should make use of the :id cache, which is faster
|
61
|
-
|
60
|
+
expect{ Apple.where(:store_id => 1).where(:id => 1).all }.to_not hit_cache(Apple).on(:store_id)
|
62
61
|
end
|
63
62
|
end
|
64
63
|
|
@@ -77,14 +76,14 @@ describe RecordCache::Strategy::IndexCache do
|
|
77
76
|
@destroyed.destroy
|
78
77
|
# check the cache hit/miss on the index that contained that apple
|
79
78
|
if fresh
|
80
|
-
|
79
|
+
expect{ @apples = Apple.where(:store_id => 1).order('id ASC').all }.to hit_cache(Apple).on(:store_id).times(1)
|
81
80
|
else
|
82
|
-
|
81
|
+
expect{ @apples = Apple.where(:store_id => 1).order('id ASC').all }.to miss_cache(Apple).on(:store_id).times(1)
|
83
82
|
end
|
84
|
-
@apples.size.
|
85
|
-
@apples.map(&:id).
|
83
|
+
expect(@apples.size).to eq(@store1_apples.size - 1)
|
84
|
+
expect(@apples.map(&:id)).to eq(@store1_apples.map(&:id) - [@destroyed.id])
|
86
85
|
# and the index should be cached again
|
87
|
-
|
86
|
+
expect{ Apple.where(:store_id => 1).all }.to hit_cache(Apple).on(:store_id).times(1)
|
88
87
|
end
|
89
88
|
|
90
89
|
it "should #{fresh ? 'update' : 'invalidate'} the index when a record in the index is created and the current index is #{fresh ? '' : 'not '}fresh" do
|
@@ -94,14 +93,14 @@ describe RecordCache::Strategy::IndexCache do
|
|
94
93
|
@new_apple_in_store1 = Apple.create!(:name => "Fresh Apple", :store_id => 1)
|
95
94
|
# check the cache hit/miss on the index that contains that apple
|
96
95
|
if fresh
|
97
|
-
|
96
|
+
expect{ @apples = Apple.where(:store_id => 1).order('id ASC').all }.to hit_cache(Apple).on(:store_id).times(1)
|
98
97
|
else
|
99
|
-
|
98
|
+
expect{ @apples = Apple.where(:store_id => 1).order('id ASC').all }.to miss_cache(Apple).on(:store_id).times(1)
|
100
99
|
end
|
101
|
-
@apples.size.
|
102
|
-
@apples.map(&:id).
|
100
|
+
expect(@apples.size).to eq(@store1_apples.size + 1)
|
101
|
+
expect(@apples.map(&:id)).to eq(@store1_apples.map(&:id) + [@new_apple_in_store1.id])
|
103
102
|
# and the index should be cached again
|
104
|
-
|
103
|
+
expect{ Apple.where(:store_id => 1).all }.to hit_cache(Apple).on(:store_id).times(1)
|
105
104
|
end
|
106
105
|
|
107
106
|
it "should #{fresh ? 'update' : 'invalidate'} two indexes when the indexed value is updated and the current index is #{fresh ? '' : 'not '}fresh" do
|
@@ -114,19 +113,19 @@ describe RecordCache::Strategy::IndexCache do
|
|
114
113
|
@apple_moved_from_store1_to_store2.save!
|
115
114
|
# check the cache hit/miss on the indexes that contained/contains that apple
|
116
115
|
if fresh
|
117
|
-
|
118
|
-
|
116
|
+
expect{ @apples1 = Apple.where(:store_id => 1).order('id ASC').all }.to hit_cache(Apple).on(:store_id).times(1)
|
117
|
+
expect{ @apples2 = Apple.where(:store_id => 2).order('id ASC').all }.to hit_cache(Apple).on(:store_id).times(1)
|
119
118
|
else
|
120
|
-
|
121
|
-
|
119
|
+
expect{ @apples1 = Apple.where(:store_id => 1).order('id ASC').all }.to miss_cache(Apple).on(:store_id).times(1)
|
120
|
+
expect{ @apples2 = Apple.where(:store_id => 2).order('id ASC').all }.to miss_cache(Apple).on(:store_id).times(1)
|
122
121
|
end
|
123
|
-
@apples1.size.
|
124
|
-
@apples2.size.
|
125
|
-
@apples1.map(&:id).
|
126
|
-
@apples2.map(&:id).
|
122
|
+
expect(@apples1.size).to eq(@store1_apples.size - 1)
|
123
|
+
expect(@apples2.size).to eq(@store2_apples.size + 1)
|
124
|
+
expect(@apples1.map(&:id)).to eq(@store1_apples.map(&:id) - [@apple_moved_from_store1_to_store2.id])
|
125
|
+
expect(@apples2.map(&:id)).to eq([@apple_moved_from_store1_to_store2.id] + @store2_apples.map(&:id))
|
127
126
|
# and the index should be cached again
|
128
|
-
|
129
|
-
|
127
|
+
expect{ Apple.where(:store_id => 1).all }.to hit_cache(Apple).on(:store_id).times(1)
|
128
|
+
expect{ Apple.where(:store_id => 2).all }.to hit_cache(Apple).on(:store_id).times(1)
|
130
129
|
end
|
131
130
|
|
132
131
|
it "should #{fresh ? 'update' : 'invalidate'} multiple indexes when several values on different indexed attributes are updated at once and one of the indexes is #{fresh ? '' : 'not '}fresh" do
|
@@ -142,28 +141,28 @@ describe RecordCache::Strategy::IndexCache do
|
|
142
141
|
@apple_moved_from_s1to2_p5to4.person_id = 4
|
143
142
|
@apple_moved_from_s1to2_p5to4.save!
|
144
143
|
# check the cache hit/miss on the indexes that contained/contains that apple
|
145
|
-
|
146
|
-
|
144
|
+
expect{ @apples_s1 = Apple.where(:store_id => 1).order('id ASC').all }.to hit_cache(Apple).on(:store_id).times(1)
|
145
|
+
expect{ @apples_s2 = Apple.where(:store_id => 2).order('id ASC').all }.to hit_cache(Apple).on(:store_id).times(1)
|
147
146
|
if fresh
|
148
|
-
|
149
|
-
|
147
|
+
expect{ @apples_p1 = Apple.where(:person_id => 4).order('id ASC').all }.to hit_cache(Apple).on(:person_id).times(1)
|
148
|
+
expect{ @apples_p2 = Apple.where(:person_id => 5).order('id ASC').all }.to hit_cache(Apple).on(:person_id).times(1)
|
150
149
|
else
|
151
|
-
|
152
|
-
|
150
|
+
expect{ @apples_p1 = Apple.where(:person_id => 4).order('id ASC').all }.to miss_cache(Apple).on(:person_id).times(1)
|
151
|
+
expect{ @apples_p2 = Apple.where(:person_id => 5).order('id ASC').all }.to miss_cache(Apple).on(:person_id).times(1)
|
153
152
|
end
|
154
|
-
@apples_s1.size.
|
155
|
-
@apples_s2.size.
|
156
|
-
@apples_p1.size.
|
157
|
-
@apples_p2.size.
|
158
|
-
@apples_s1.map(&:id).
|
159
|
-
@apples_s2.map(&:id).
|
160
|
-
@apples_p1.map(&:id).
|
161
|
-
@apples_p2.map(&:id).
|
153
|
+
expect(@apples_s1.size).to eq(@store1_apples.size - 1)
|
154
|
+
expect(@apples_s2.size).to eq(@store2_apples.size + 1)
|
155
|
+
expect(@apples_p1.size).to eq(@person4_apples.size + 1)
|
156
|
+
expect(@apples_p2.size).to eq(@person5_apples.size - 1)
|
157
|
+
expect(@apples_s1.map(&:id)).to eq(@store1_apples.map(&:id) - [@apple_moved_from_s1to2_p5to4.id])
|
158
|
+
expect(@apples_s2.map(&:id)).to eq([@apple_moved_from_s1to2_p5to4.id] + @store2_apples.map(&:id))
|
159
|
+
expect(@apples_p1.map(&:id)).to eq(([@apple_moved_from_s1to2_p5to4.id] + @person4_apples.map(&:id)).sort)
|
160
|
+
expect(@apples_p2.map(&:id)).to eq( (@person5_apples.map(&:id) - [@apple_moved_from_s1to2_p5to4.id]).sort)
|
162
161
|
# and the index should be cached again
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
162
|
+
expect{ Apple.where(:store_id => 1).all }.to hit_cache(Apple).on(:store_id).times(1)
|
163
|
+
expect{ Apple.where(:store_id => 2).all }.to hit_cache(Apple).on(:store_id).times(1)
|
164
|
+
expect{ Apple.where(:person_id => 4).all }.to hit_cache(Apple).on(:person_id).times(1)
|
165
|
+
expect{ Apple.where(:person_id => 5).all }.to hit_cache(Apple).on(:person_id).times(1)
|
167
166
|
end
|
168
167
|
end
|
169
168
|
|
@@ -171,14 +170,14 @@ describe RecordCache::Strategy::IndexCache do
|
|
171
170
|
# destroy an apple of store 2
|
172
171
|
@store2_apples.first.destroy
|
173
172
|
# index of apples of store 1 are not affected
|
174
|
-
|
173
|
+
expect{ @apples = Apple.where(:store_id => 1).order('id ASC').all }.to hit_cache(Apple).on(:store_id).times(1)
|
175
174
|
end
|
176
175
|
|
177
176
|
it "should leave the index alone when a record outside the index is created" do
|
178
177
|
# create an apple for store 2
|
179
178
|
Apple.create!(:name => "Fresh Apple", :store_id => 2)
|
180
179
|
# index of apples of store 1 are not affected
|
181
|
-
|
180
|
+
expect{ @apples = Apple.where(:store_id => 1).order('id ASC').all }.to hit_cache(Apple).on(:store_id).times(1)
|
182
181
|
end
|
183
182
|
end
|
184
183
|
|
@@ -186,37 +185,81 @@ describe RecordCache::Strategy::IndexCache do
|
|
186
185
|
before(:each) do
|
187
186
|
@store1_apples = Apple.where(:store_id => 1).all
|
188
187
|
@store2_apples = Apple.where(:store_id => 2).all
|
188
|
+
@address_1 = Address.where(:store_id => 1).all
|
189
|
+
@address_2 = Address.where(:store_id => 2).all
|
189
190
|
end
|
190
191
|
|
191
192
|
it "should invalidate single index" do
|
192
193
|
Apple.record_cache[:store_id].invalidate(1)
|
193
|
-
|
194
|
+
expect{ Apple.where(:store_id => 1).all }.to miss_cache(Apple).on(:store_id).times(1)
|
194
195
|
end
|
195
196
|
|
196
197
|
it "should invalidate indexes when using update_all" do
|
197
|
-
pending "is there a performant way to invalidate index caches within update_all? only the new value is available, so we should query the old values..."
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
end
|
198
|
+
pending "is there a performant way to invalidate index caches within update_all? only the new value is available, so we should query the old values..."
|
199
|
+
# update 2 apples for index values store 1 and store 2
|
200
|
+
Apple.where(:id => [@store1_apples.first.id, @store2_apples.first.id]).update_all(:store_id => 3)
|
201
|
+
expect{ @apples_1 = Apple.where(:store_id => 1).all }.to miss_cache(Apple).on(:store_id).times(1)
|
202
|
+
expect{ @apples_2 = Apple.where(:store_id => 2).all }.to miss_cache(Apple).on(:store_id).times(1)
|
203
|
+
expect(@apples_1.map(&:id).sort).to eq(@store1_apples[1..-1].sort)
|
204
|
+
expect(@apples_2.map(&:id).sort).to eq(@store2_apples[1..-1].sort)
|
205
205
|
end
|
206
206
|
|
207
207
|
it "should invalidate reflection indexes when a has_many relation is updated" do
|
208
208
|
# assign different apples to store 2
|
209
|
-
|
209
|
+
expect{ Apple.where(:store_id => 1).first }.to hit_cache(Apple).on(:store_id).times(1)
|
210
210
|
store2_apple_ids = @store2_apples.map(&:id).sort
|
211
211
|
store1 = Store.find(1)
|
212
212
|
store1.apple_ids = store2_apple_ids
|
213
213
|
store1.save!
|
214
214
|
# apples in Store 1 should be all (only) the apples that were in Store 2 (cache invalidated)
|
215
|
-
|
216
|
-
@apples_1.map(&:id).sort.
|
215
|
+
expect{ @apples_1 = Apple.where(:store_id => 1).all }.to miss_cache(Apple).on(:store_id).times(1)
|
216
|
+
expect(@apples_1.map(&:id).sort).to eq(store2_apple_ids)
|
217
217
|
# there are no apples in Store 2 anymore (incremental cache update, as each apples in store 2 was saved separately)
|
218
|
-
|
219
|
-
@apples_2.
|
218
|
+
expect{ @apples_2 = Apple.where(:store_id => 2).all }.to hit_cache(Apple).on(:store_id).times(1)
|
219
|
+
expect(@apples_2).to eq([])
|
220
|
+
end
|
221
|
+
|
222
|
+
it "should invalidate reflection indexes when a has_one relation is updated" do
|
223
|
+
# assign different address to store 2
|
224
|
+
expect{ Address.where(:store_id => 1).limit(1).first }.to hit_cache(Address).on(:store_id).times(1)
|
225
|
+
store2 = Store.find(2)
|
226
|
+
store2_address = store2.address
|
227
|
+
Address.where(:store_id => 1).first.id == 1
|
228
|
+
store1 = Store.find(1)
|
229
|
+
store1.address = store2_address
|
230
|
+
store1.save!
|
231
|
+
Address.where(:store_id => 1).first.id == 2
|
232
|
+
# address for Store 1 should be the address that was for Store 2 (cache invalidated)
|
233
|
+
expect{ @address_1 = Address.where(:store_id => 1).first }.to hit_cache(Address).on(:store_id).times(1)
|
234
|
+
expect(@address_1.id).to eq(store2_address.id)
|
235
|
+
# there are no address in Store 2 anymore (incremental cache update, as address for store 2 was saved separately)
|
236
|
+
expect{ @address_2 = Address.where(:store_id => 2).first }.to hit_cache(Address).on(:store_id).times(1)
|
237
|
+
expect(@address_2).to be_nil
|
238
|
+
end
|
239
|
+
|
240
|
+
# see https://github.com/orslumen/record-cache/issues/19
|
241
|
+
it "should work with serialized object" do
|
242
|
+
address = Address.find(3) # not from cache
|
243
|
+
address = Address.find(3) # from cache
|
244
|
+
expect(address.location[:latitue]).to eq(27.175015)
|
245
|
+
expect(address.location[:dms_lat]).to eq(%(27\u00B0 10' 30.0540" N))
|
246
|
+
address.name = 'updated name'
|
247
|
+
address.save!
|
248
|
+
end
|
249
|
+
end
|
250
|
+
|
251
|
+
context 'subclassing' do
|
252
|
+
it "should delegate cache updates to the base class" do
|
253
|
+
class RedDelicious < Apple; end
|
254
|
+
apple = Apple.find(1)
|
255
|
+
delicious = RedDelicious.find(1)
|
256
|
+
store_id = apple.store_id
|
257
|
+
delicious.store_id = 100
|
258
|
+
delicious.save
|
259
|
+
apple = Apple.find(1)
|
260
|
+
expect(apple.store_id).to_not eq(store_id)
|
261
|
+
apple.store_id = store_id
|
262
|
+
apple.save
|
220
263
|
end
|
221
264
|
end
|
222
265
|
|
@@ -0,0 +1,83 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
# This describes the behaviour as expected when ActiveRecord::QueryCache is
|
4
|
+
# enabled. ActiveRecord::QueryCache is enabled by default in rails via a
|
5
|
+
# middleware. During the scope of a request that cache is used.
|
6
|
+
#
|
7
|
+
# In console mode (or within e.g. a cron job) QueryCache isn't enabled.
|
8
|
+
# You can still take advantage of this cache by executing
|
9
|
+
#
|
10
|
+
# ActiveRecord::Base.cache do
|
11
|
+
# # your queries
|
12
|
+
# end
|
13
|
+
#
|
14
|
+
# Be aware that though that during the execution of the block if updates
|
15
|
+
# happen to records by another process, while you have already got
|
16
|
+
# references to that records in QueryCache, that you won't see the changes
|
17
|
+
# made by the other process.
|
18
|
+
describe "QueryCache" do
|
19
|
+
|
20
|
+
it "should retrieve a record from the QueryCache" do
|
21
|
+
ActiveRecord::Base.cache do
|
22
|
+
expect{ Store.find(1) }.to miss_cache(Store).on(:id).times(1)
|
23
|
+
second_lookup = expect{ Store.find(1) }
|
24
|
+
second_lookup.to miss_cache(Store).times(0)
|
25
|
+
second_lookup.to hit_cache(Store).on(:id).times(0)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
it "should maintain object identity when the same query is used" do
|
30
|
+
ActiveRecord::Base.cache do
|
31
|
+
@store_1 = Store.find(1)
|
32
|
+
@store_2 = Store.find(1)
|
33
|
+
expect(@store_1).to eq(@store_2)
|
34
|
+
expect(@store_1.object_id).to eq(@store_2.object_id)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
context "record_change" do
|
39
|
+
it "should clear the query cache completely when a record is created" do
|
40
|
+
ActiveRecord::Base.cache do
|
41
|
+
init_query_cache
|
42
|
+
expect{ Store.find(2) }.to hit_cache(Store).times(0)
|
43
|
+
expect{ Apple.find(1) }.to hit_cache(Apple).times(0)
|
44
|
+
Store.create!(:name => "New Apple Store")
|
45
|
+
expect{ Store.find(2) }.to hit_cache(Store).times(1)
|
46
|
+
expect{ Apple.find(1) }.to hit_cache(Apple).times(1)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
it "should clear the query cache completely when a record is updated" do
|
51
|
+
ActiveRecord::Base.cache do
|
52
|
+
init_query_cache
|
53
|
+
expect{ Store.find(2) }.to hit_cache(Store).times(0)
|
54
|
+
expect{ Apple.find(1) }.to hit_cache(Apple).times(0)
|
55
|
+
@store1.name = "Store E"
|
56
|
+
@store1.save!
|
57
|
+
expect{ Store.find(2) }.to hit_cache(Store).times(1)
|
58
|
+
expect{ Apple.find(1) }.to hit_cache(Apple).times(1)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
it "should clear the query cache completely when a record is destroyed" do
|
63
|
+
ActiveRecord::Base.cache do
|
64
|
+
init_query_cache
|
65
|
+
expect{ Store.find(2) }.to hit_cache(Store).times(0)
|
66
|
+
expect{ Apple.find(1) }.to hit_cache(Apple).times(0)
|
67
|
+
@store1.destroy
|
68
|
+
expect{ Store.find(2) }.to hit_cache(Store).times(1)
|
69
|
+
expect{ Apple.find(1) }.to hit_cache(Apple).times(1)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
private
|
75
|
+
|
76
|
+
# Cache a few objects in QueryCache to test with
|
77
|
+
def init_query_cache
|
78
|
+
@store1 = Store.find(1)
|
79
|
+
@store2 = Store.find(2)
|
80
|
+
@apple1 = Apple.find(1)
|
81
|
+
end
|
82
|
+
|
83
|
+
end
|