record-cache 0.1.3 → 0.1.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +8 -8
- data/lib/record_cache/base.rb +4 -4
- data/lib/record_cache/datastore/active_record_30.rb +1 -1
- data/lib/record_cache/datastore/active_record_31.rb +1 -1
- data/lib/record_cache/datastore/active_record_32.rb +2 -2
- data/lib/record_cache/datastore/active_record_40.rb +445 -0
- data/lib/record_cache/datastore/active_record_41.rb +446 -0
- data/lib/record_cache/strategy/full_table_cache.rb +1 -1
- data/lib/record_cache/strategy/util.rb +20 -3
- data/lib/record_cache/version.rb +1 -1
- data/spec/db/create-record-cache-db_and_user.sql +5 -0
- data/spec/db/database.yml +7 -0
- data/spec/db/schema.rb +9 -15
- data/spec/initializers/backward_compatibility.rb +32 -0
- data/spec/lib/active_record/visitor_spec.rb +1 -1
- data/spec/lib/base_spec.rb +2 -2
- data/spec/lib/dispatcher_spec.rb +1 -1
- data/spec/lib/multi_read_spec.rb +1 -1
- data/spec/lib/query_spec.rb +1 -1
- data/spec/lib/statistics_spec.rb +1 -1
- data/spec/lib/strategy/base_spec.rb +39 -39
- data/spec/lib/strategy/full_table_cache_spec.rb +18 -18
- data/spec/lib/strategy/index_cache_spec.rb +58 -52
- data/spec/lib/strategy/query_cache_spec.rb +1 -1
- data/spec/lib/strategy/unique_index_on_id_cache_spec.rb +57 -45
- data/spec/lib/strategy/unique_index_on_string_cache_spec.rb +47 -45
- data/spec/lib/strategy/util_spec.rb +49 -43
- data/spec/lib/version_store_spec.rb +1 -1
- data/spec/models/apple.rb +1 -2
- data/spec/spec_helper.rb +16 -7
- data/spec/support/matchers/hit_cache_matcher.rb +1 -1
- data/spec/support/matchers/miss_cache_matcher.rb +1 -1
- data/spec/support/matchers/use_cache_matcher.rb +1 -1
- metadata +63 -17
- data/spec/support/after_commit.rb +0 -73
@@ -1,26 +1,26 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
|
-
describe RecordCache::Strategy::FullTableCache do
|
3
|
+
RSpec.describe RecordCache::Strategy::FullTableCache do
|
4
4
|
|
5
5
|
it "should retrieve a Language from the cache" do
|
6
|
-
expect{ Language.where(:locale => 'en-US').
|
7
|
-
expect{ Language.where(:locale => 'en-US').
|
6
|
+
expect { Language.where(:locale => 'en-US').load }.to miss_cache(Language).on(:full_table).times(1)
|
7
|
+
expect { Language.where(:locale => 'en-US').load }.to hit_cache(Language).on(:full_table).times(1)
|
8
8
|
end
|
9
9
|
|
10
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)
|
11
|
+
expect { Language.all.load }.to miss_cache(Language).on(:full_table).times(1)
|
12
|
+
expect { Language.all.load }.to hit_cache(Language).on(:full_table).times(1)
|
13
13
|
expect(Language.all.map(&:locale).sort).to eq(%w(du-NL en-GB en-US hu))
|
14
14
|
end
|
15
15
|
|
16
16
|
context "logging" do
|
17
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")
|
18
|
+
Language.all.load
|
19
|
+
expect { Language.all.load }.to log(:debug, "FullTableCache hit for model Language")
|
20
20
|
end
|
21
21
|
|
22
22
|
it "should write miss to the debug log" do
|
23
|
-
expect{ Language.all }.to log(:debug, "FullTableCache miss for model Language")
|
23
|
+
expect{ Language.all.load }.to log(:debug, "FullTableCache miss for model Language")
|
24
24
|
end
|
25
25
|
end
|
26
26
|
|
@@ -29,38 +29,38 @@ describe RecordCache::Strategy::FullTableCache do
|
|
29
29
|
expect(Language.record_cache[:full_table].cacheable?("any query")).to be_truthy
|
30
30
|
end
|
31
31
|
end
|
32
|
-
|
32
|
+
|
33
33
|
context "record_change" do
|
34
34
|
before(:each) do
|
35
|
-
@Languages = Language.all
|
35
|
+
@Languages = Language.all.load
|
36
36
|
end
|
37
37
|
|
38
38
|
it "should invalidate the cache when a record is added" do
|
39
|
-
expect{ Language.where(:locale => 'en-US').
|
39
|
+
expect{ Language.where(:locale => 'en-US').load }.to hit_cache(Language).on(:full_table).times(1)
|
40
40
|
Language.create!(:name => 'Deutsch', :locale => 'de')
|
41
|
-
expect{ Language.where(:locale => 'en-US').
|
41
|
+
expect{ Language.where(:locale => 'en-US').load }.to miss_cache(Language).on(:full_table).times(1)
|
42
42
|
end
|
43
43
|
|
44
44
|
it "should invalidate the cache when any record is deleted" do
|
45
|
-
expect{ Language.where(:locale => 'en-US').
|
45
|
+
expect{ Language.where(:locale => 'en-US').load }.to hit_cache(Language).on(:full_table).times(1)
|
46
46
|
Language.where(:locale => 'hu').first.destroy
|
47
|
-
expect{ Language.where(:locale => 'en-US').
|
47
|
+
expect{ Language.where(:locale => 'en-US').load }.to miss_cache(Language).on(:full_table).times(1)
|
48
48
|
end
|
49
49
|
|
50
50
|
it "should invalidate the cache when any record is modified" do
|
51
|
-
expect{ Language.where(:locale => 'en-US').
|
51
|
+
expect{ Language.where(:locale => 'en-US').load }.to hit_cache(Language).on(:full_table).times(1)
|
52
52
|
hungarian = Language.where(:locale => 'hu').first
|
53
53
|
hungarian.name = 'Magyar (Magyarorszag)'
|
54
54
|
hungarian.save!
|
55
|
-
expect{ Language.where(:locale => 'en-US').
|
55
|
+
expect{ Language.where(:locale => 'en-US').load }.to miss_cache(Language).on(:full_table).times(1)
|
56
56
|
end
|
57
57
|
end
|
58
|
-
|
58
|
+
|
59
59
|
context "invalidate" do
|
60
60
|
|
61
61
|
it "should invalidate the full cache" do
|
62
62
|
Language.record_cache[:full_table].invalidate(-10) # any id
|
63
|
-
expect{ Language.where(:locale => 'en-US').
|
63
|
+
expect{ Language.where(:locale => 'en-US').load }.to miss_cache(Language).on(:full_table).times(1)
|
64
64
|
end
|
65
65
|
|
66
66
|
end
|
@@ -1,6 +1,6 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
|
-
describe RecordCache::Strategy::IndexCache do
|
3
|
+
RSpec.describe RecordCache::Strategy::IndexCache do
|
4
4
|
|
5
5
|
context "initialize" do
|
6
6
|
it "should only accept index cache on DB columns" do
|
@@ -13,58 +13,64 @@ describe RecordCache::Strategy::IndexCache do
|
|
13
13
|
end
|
14
14
|
|
15
15
|
it "should use the id cache to retrieve the actual records" do
|
16
|
-
expect{ @apples = Apple.where(:store_id => 1).
|
17
|
-
expect{ Apple.where(:store_id => 1).
|
18
|
-
expect{ Apple.where(:store_id => 1).
|
16
|
+
expect{ @apples = Apple.where(:store_id => 1).load }.to miss_cache(Apple).on(:store_id).times(1)
|
17
|
+
expect{ Apple.where(:store_id => 1).load }.to hit_cache(Apple).on(:store_id).times(1)
|
18
|
+
expect{ Apple.where(:store_id => 1).load }.to hit_cache(Apple).on(:id).times(@apples.size)
|
19
|
+
end
|
20
|
+
|
21
|
+
it "should find cached records through relationship" do
|
22
|
+
store = Store.first
|
23
|
+
expect{ @apple = store.apples.find(3) }.to use_cache(Apple).on(:id).times(1)
|
24
|
+
expect(@apple.name).to eq("Adams Apple 3")
|
19
25
|
end
|
20
26
|
|
21
27
|
context "logging" do
|
22
28
|
before(:each) do
|
23
|
-
Apple.where(:store_id => 1).
|
29
|
+
Apple.where(:store_id => 1).load
|
24
30
|
end
|
25
31
|
|
26
32
|
it "should write hit to the debug log" do
|
27
|
-
expect{ Apple.where(:store_id => 1).
|
33
|
+
expect{ Apple.where(:store_id => 1).load }.to log(:debug, /IndexCache hit for rc\/apl\/store_id=1v\d+: found 5 ids/)
|
28
34
|
end
|
29
35
|
|
30
36
|
it "should write miss to the debug log" do
|
31
|
-
expect{ Apple.where(:store_id => 2).
|
37
|
+
expect{ Apple.where(:store_id => 2).load }.to log(:debug, /IndexCache miss for rc\/apl\/store_id=2v\d+: found no ids/)
|
32
38
|
end
|
33
39
|
end
|
34
40
|
|
35
41
|
context "cacheable?" do
|
36
42
|
before(:each) do
|
37
|
-
@store1_apples = Apple.where(:store_id => 1).
|
38
|
-
@store2_apples = Apple.where(:store_id => 2).
|
43
|
+
@store1_apples = Apple.where(:store_id => 1).load
|
44
|
+
@store2_apples = Apple.where(:store_id => 2).load
|
39
45
|
end
|
40
46
|
|
41
47
|
it "should hit the cache for a single index id" do
|
42
|
-
expect{ Apple.where(:store_id => 1).
|
48
|
+
expect{ Apple.where(:store_id => 1).load }.to hit_cache(Apple).on(:store_id).times(1)
|
43
49
|
end
|
44
50
|
|
45
51
|
it "should hit the cache for a single index id with other where clauses" do
|
46
|
-
expect{ Apple.where(:store_id => 1).where(:name => "applegate").
|
52
|
+
expect{ Apple.where(:store_id => 1).where(:name => "applegate").load }.to hit_cache(Apple).on(:store_id).times(1)
|
47
53
|
end
|
48
54
|
|
49
55
|
it "should hit the cache for a single index id with (simple) sort clauses" do
|
50
|
-
expect{ Apple.where(:store_id => 1).order("name ASC").
|
56
|
+
expect{ Apple.where(:store_id => 1).order("name ASC").load }.to hit_cache(Apple).on(:store_id).times(1)
|
51
57
|
end
|
52
58
|
|
53
59
|
#Allow limit == 1 by filtering records after cache hit. Needed for has_one
|
54
60
|
it "should not hit the cache for a single index id with limit > 0" do
|
55
|
-
expect{ Apple.where(:store_id => 1).limit(2).
|
61
|
+
expect{ Apple.where(:store_id => 1).limit(2).load }.to_not hit_cache(Apple).on(:store_id)
|
56
62
|
end
|
57
63
|
|
58
64
|
it "should not hit the cache when an :id where clause is defined" do
|
59
65
|
# this query should make use of the :id cache, which is faster
|
60
|
-
expect{ Apple.where(:store_id => 1).where(:id => 1).
|
66
|
+
expect{ Apple.where(:store_id => 1).where(:id => 1).load }.to_not hit_cache(Apple).on(:store_id)
|
61
67
|
end
|
62
68
|
end
|
63
|
-
|
69
|
+
|
64
70
|
context "record_change" do
|
65
71
|
before(:each) do
|
66
|
-
@store1_apples = Apple.where(:store_id => 1).order('id ASC').
|
67
|
-
@store2_apples = Apple.where(:store_id => 2).order('id ASC').
|
72
|
+
@store1_apples = Apple.where(:store_id => 1).order('id ASC').to_a
|
73
|
+
@store2_apples = Apple.where(:store_id => 2).order('id ASC').to_a
|
68
74
|
end
|
69
75
|
|
70
76
|
[false, true].each do |fresh|
|
@@ -76,14 +82,14 @@ describe RecordCache::Strategy::IndexCache do
|
|
76
82
|
@destroyed.destroy
|
77
83
|
# check the cache hit/miss on the index that contained that apple
|
78
84
|
if fresh
|
79
|
-
expect{ @apples = Apple.where(:store_id => 1).order('id ASC').
|
85
|
+
expect{ @apples = Apple.where(:store_id => 1).order('id ASC').load }.to hit_cache(Apple).on(:store_id).times(1)
|
80
86
|
else
|
81
|
-
expect{ @apples = Apple.where(:store_id => 1).order('id ASC').
|
87
|
+
expect{ @apples = Apple.where(:store_id => 1).order('id ASC').load }.to miss_cache(Apple).on(:store_id).times(1)
|
82
88
|
end
|
83
89
|
expect(@apples.size).to eq(@store1_apples.size - 1)
|
84
90
|
expect(@apples.map(&:id)).to eq(@store1_apples.map(&:id) - [@destroyed.id])
|
85
91
|
# and the index should be cached again
|
86
|
-
expect{ Apple.where(:store_id => 1).
|
92
|
+
expect{ Apple.where(:store_id => 1).load }.to hit_cache(Apple).on(:store_id).times(1)
|
87
93
|
end
|
88
94
|
|
89
95
|
it "should #{fresh ? 'update' : 'invalidate'} the index when a record in the index is created and the current index is #{fresh ? '' : 'not '}fresh" do
|
@@ -93,14 +99,14 @@ describe RecordCache::Strategy::IndexCache do
|
|
93
99
|
@new_apple_in_store1 = Apple.create!(:name => "Fresh Apple", :store_id => 1)
|
94
100
|
# check the cache hit/miss on the index that contains that apple
|
95
101
|
if fresh
|
96
|
-
expect{ @apples = Apple.where(:store_id => 1).order('id ASC').
|
102
|
+
expect{ @apples = Apple.where(:store_id => 1).order('id ASC').load }.to hit_cache(Apple).on(:store_id).times(1)
|
97
103
|
else
|
98
|
-
expect{ @apples = Apple.where(:store_id => 1).order('id ASC').
|
104
|
+
expect{ @apples = Apple.where(:store_id => 1).order('id ASC').load }.to miss_cache(Apple).on(:store_id).times(1)
|
99
105
|
end
|
100
106
|
expect(@apples.size).to eq(@store1_apples.size + 1)
|
101
107
|
expect(@apples.map(&:id)).to eq(@store1_apples.map(&:id) + [@new_apple_in_store1.id])
|
102
108
|
# and the index should be cached again
|
103
|
-
expect{ Apple.where(:store_id => 1).
|
109
|
+
expect{ Apple.where(:store_id => 1).load }.to hit_cache(Apple).on(:store_id).times(1)
|
104
110
|
end
|
105
111
|
|
106
112
|
it "should #{fresh ? 'update' : 'invalidate'} two indexes when the indexed value is updated and the current index is #{fresh ? '' : 'not '}fresh" do
|
@@ -113,25 +119,25 @@ describe RecordCache::Strategy::IndexCache do
|
|
113
119
|
@apple_moved_from_store1_to_store2.save!
|
114
120
|
# check the cache hit/miss on the indexes that contained/contains that apple
|
115
121
|
if fresh
|
116
|
-
expect{ @apples1 = Apple.where(:store_id => 1).order('id ASC').
|
117
|
-
expect{ @apples2 = Apple.where(:store_id => 2).order('id ASC').
|
122
|
+
expect{ @apples1 = Apple.where(:store_id => 1).order('id ASC').load }.to hit_cache(Apple).on(:store_id).times(1)
|
123
|
+
expect{ @apples2 = Apple.where(:store_id => 2).order('id ASC').load }.to hit_cache(Apple).on(:store_id).times(1)
|
118
124
|
else
|
119
|
-
expect{ @apples1 = Apple.where(:store_id => 1).order('id ASC').
|
120
|
-
expect{ @apples2 = Apple.where(:store_id => 2).order('id ASC').
|
125
|
+
expect{ @apples1 = Apple.where(:store_id => 1).order('id ASC').load }.to miss_cache(Apple).on(:store_id).times(1)
|
126
|
+
expect{ @apples2 = Apple.where(:store_id => 2).order('id ASC').load }.to miss_cache(Apple).on(:store_id).times(1)
|
121
127
|
end
|
122
128
|
expect(@apples1.size).to eq(@store1_apples.size - 1)
|
123
129
|
expect(@apples2.size).to eq(@store2_apples.size + 1)
|
124
130
|
expect(@apples1.map(&:id)).to eq(@store1_apples.map(&:id) - [@apple_moved_from_store1_to_store2.id])
|
125
131
|
expect(@apples2.map(&:id)).to eq([@apple_moved_from_store1_to_store2.id] + @store2_apples.map(&:id))
|
126
132
|
# and the index should be cached again
|
127
|
-
expect{ Apple.where(:store_id => 1).
|
128
|
-
expect{ Apple.where(:store_id => 2).
|
133
|
+
expect{ Apple.where(:store_id => 1).load }.to hit_cache(Apple).on(:store_id).times(1)
|
134
|
+
expect{ Apple.where(:store_id => 2).load }.to hit_cache(Apple).on(:store_id).times(1)
|
129
135
|
end
|
130
136
|
|
131
137
|
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
|
132
138
|
# find the apples for person 1 and 5 (Chase)
|
133
|
-
@person4_apples = Apple.where(:person_id => 4).
|
134
|
-
@person5_apples = Apple.where(:person_id => 5).
|
139
|
+
@person4_apples = Apple.where(:person_id => 4).to_a # Fry's Apples
|
140
|
+
@person5_apples = Apple.where(:person_id => 5).to_a # Chases' Apples
|
135
141
|
# make sure person indexes are no longer fresh
|
136
142
|
Apple.record_cache.invalidate(:person_id, 4) unless fresh
|
137
143
|
Apple.record_cache.invalidate(:person_id, 5) unless fresh
|
@@ -141,14 +147,14 @@ describe RecordCache::Strategy::IndexCache do
|
|
141
147
|
@apple_moved_from_s1to2_p5to4.person_id = 4
|
142
148
|
@apple_moved_from_s1to2_p5to4.save!
|
143
149
|
# check the cache hit/miss on the indexes that contained/contains that apple
|
144
|
-
expect{ @apples_s1 = Apple.where(:store_id => 1).order('id ASC').
|
145
|
-
expect{ @apples_s2 = Apple.where(:store_id => 2).order('id ASC').
|
150
|
+
expect{ @apples_s1 = Apple.where(:store_id => 1).order('id ASC').load }.to hit_cache(Apple).on(:store_id).times(1)
|
151
|
+
expect{ @apples_s2 = Apple.where(:store_id => 2).order('id ASC').load }.to hit_cache(Apple).on(:store_id).times(1)
|
146
152
|
if fresh
|
147
|
-
expect{ @apples_p1 = Apple.where(:person_id => 4).order('id ASC').
|
148
|
-
expect{ @apples_p2 = Apple.where(:person_id => 5).order('id ASC').
|
153
|
+
expect{ @apples_p1 = Apple.where(:person_id => 4).order('id ASC').load }.to hit_cache(Apple).on(:person_id).times(1)
|
154
|
+
expect{ @apples_p2 = Apple.where(:person_id => 5).order('id ASC').load }.to hit_cache(Apple).on(:person_id).times(1)
|
149
155
|
else
|
150
|
-
expect{ @apples_p1 = Apple.where(:person_id => 4).order('id ASC').
|
151
|
-
expect{ @apples_p2 = Apple.where(:person_id => 5).order('id ASC').
|
156
|
+
expect{ @apples_p1 = Apple.where(:person_id => 4).order('id ASC').load }.to miss_cache(Apple).on(:person_id).times(1)
|
157
|
+
expect{ @apples_p2 = Apple.where(:person_id => 5).order('id ASC').load }.to miss_cache(Apple).on(:person_id).times(1)
|
152
158
|
end
|
153
159
|
expect(@apples_s1.size).to eq(@store1_apples.size - 1)
|
154
160
|
expect(@apples_s2.size).to eq(@store2_apples.size + 1)
|
@@ -159,10 +165,10 @@ describe RecordCache::Strategy::IndexCache do
|
|
159
165
|
expect(@apples_p1.map(&:id)).to eq(([@apple_moved_from_s1to2_p5to4.id] + @person4_apples.map(&:id)).sort)
|
160
166
|
expect(@apples_p2.map(&:id)).to eq( (@person5_apples.map(&:id) - [@apple_moved_from_s1to2_p5to4.id]).sort)
|
161
167
|
# and the index should be cached again
|
162
|
-
expect{ Apple.where(:store_id => 1).
|
163
|
-
expect{ Apple.where(:store_id => 2).
|
164
|
-
expect{ Apple.where(:person_id => 4).
|
165
|
-
expect{ Apple.where(:person_id => 5).
|
168
|
+
expect{ Apple.where(:store_id => 1).load }.to hit_cache(Apple).on(:store_id).times(1)
|
169
|
+
expect{ Apple.where(:store_id => 2).load }.to hit_cache(Apple).on(:store_id).times(1)
|
170
|
+
expect{ Apple.where(:person_id => 4).load }.to hit_cache(Apple).on(:person_id).times(1)
|
171
|
+
expect{ Apple.where(:person_id => 5).load }.to hit_cache(Apple).on(:person_id).times(1)
|
166
172
|
end
|
167
173
|
end
|
168
174
|
|
@@ -170,36 +176,36 @@ describe RecordCache::Strategy::IndexCache do
|
|
170
176
|
# destroy an apple of store 2
|
171
177
|
@store2_apples.first.destroy
|
172
178
|
# index of apples of store 1 are not affected
|
173
|
-
expect{ @apples = Apple.where(:store_id => 1).order('id ASC').
|
179
|
+
expect{ @apples = Apple.where(:store_id => 1).order('id ASC').load }.to hit_cache(Apple).on(:store_id).times(1)
|
174
180
|
end
|
175
181
|
|
176
182
|
it "should leave the index alone when a record outside the index is created" do
|
177
183
|
# create an apple for store 2
|
178
184
|
Apple.create!(:name => "Fresh Apple", :store_id => 2)
|
179
185
|
# index of apples of store 1 are not affected
|
180
|
-
expect{ @apples = Apple.where(:store_id => 1).order('id ASC').
|
186
|
+
expect{ @apples = Apple.where(:store_id => 1).order('id ASC').load }.to hit_cache(Apple).on(:store_id).times(1)
|
181
187
|
end
|
182
188
|
end
|
183
189
|
|
184
190
|
context "invalidate" do
|
185
191
|
before(:each) do
|
186
|
-
@store1_apples = Apple.where(:store_id => 1).
|
187
|
-
@store2_apples = Apple.where(:store_id => 2).
|
188
|
-
@address_1 = Address.where(:store_id => 1).
|
189
|
-
@address_2 = Address.where(:store_id => 2).
|
192
|
+
@store1_apples = Apple.where(:store_id => 1).to_a
|
193
|
+
@store2_apples = Apple.where(:store_id => 2).to_a
|
194
|
+
@address_1 = Address.where(:store_id => 1).to_a
|
195
|
+
@address_2 = Address.where(:store_id => 2).to_a
|
190
196
|
end
|
191
197
|
|
192
198
|
it "should invalidate single index" do
|
193
199
|
Apple.record_cache[:store_id].invalidate(1)
|
194
|
-
expect{ Apple.where(:store_id => 1).
|
200
|
+
expect{ Apple.where(:store_id => 1).load }.to miss_cache(Apple).on(:store_id).times(1)
|
195
201
|
end
|
196
202
|
|
197
203
|
it "should invalidate indexes when using update_all" do
|
198
204
|
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
205
|
# update 2 apples for index values store 1 and store 2
|
200
206
|
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).
|
202
|
-
expect{ @apples_2 = Apple.where(:store_id => 2).
|
207
|
+
expect{ @apples_1 = Apple.where(:store_id => 1).load }.to miss_cache(Apple).on(:store_id).times(1)
|
208
|
+
expect{ @apples_2 = Apple.where(:store_id => 2).load }.to miss_cache(Apple).on(:store_id).times(1)
|
203
209
|
expect(@apples_1.map(&:id).sort).to eq(@store1_apples[1..-1].sort)
|
204
210
|
expect(@apples_2.map(&:id).sort).to eq(@store2_apples[1..-1].sort)
|
205
211
|
end
|
@@ -212,10 +218,10 @@ describe RecordCache::Strategy::IndexCache do
|
|
212
218
|
store1.apple_ids = store2_apple_ids
|
213
219
|
store1.save!
|
214
220
|
# apples in Store 1 should be all (only) the apples that were in Store 2 (cache invalidated)
|
215
|
-
expect{ @apples_1 = Apple.where(:store_id => 1).
|
221
|
+
expect{ @apples_1 = Apple.where(:store_id => 1).load }.to miss_cache(Apple).on(:store_id).times(1)
|
216
222
|
expect(@apples_1.map(&:id).sort).to eq(store2_apple_ids)
|
217
223
|
# there are no apples in Store 2 anymore (incremental cache update, as each apples in store 2 was saved separately)
|
218
|
-
expect{ @apples_2 = Apple.where(:store_id => 2).
|
224
|
+
expect{ @apples_2 = Apple.where(:store_id => 2).load }.to hit_cache(Apple).on(:store_id).times(1)
|
219
225
|
expect(@apples_2).to eq([])
|
220
226
|
end
|
221
227
|
|
@@ -15,7 +15,7 @@ require 'spec_helper'
|
|
15
15
|
# happen to records by another process, while you have already got
|
16
16
|
# references to that records in QueryCache, that you won't see the changes
|
17
17
|
# made by the other process.
|
18
|
-
describe "QueryCache" do
|
18
|
+
RSpec.describe "QueryCache" do
|
19
19
|
|
20
20
|
it "should retrieve a record from the QueryCache" do
|
21
21
|
ActiveRecord::Base.cache do
|
@@ -1,6 +1,6 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
|
-
describe RecordCache::Strategy::UniqueIndexCache do
|
3
|
+
RSpec.describe RecordCache::Strategy::UniqueIndexCache do
|
4
4
|
|
5
5
|
it "should retrieve an Apple from the cache" do
|
6
6
|
expect{ Apple.find(1) }.to miss_cache(Apple).on(:id).times(1)
|
@@ -40,9 +40,9 @@ describe RecordCache::Strategy::UniqueIndexCache do
|
|
40
40
|
it "should write full miss to the debug log" do
|
41
41
|
expect{ Apple.find(2) }.to log(:debug, %(UniqueIndexCache on 'Apple.id' miss for ids 2))
|
42
42
|
end
|
43
|
-
|
43
|
+
|
44
44
|
it "should write partial hits to the debug log" do
|
45
|
-
expect{ Apple.where(:id => [1,2]).
|
45
|
+
expect{ Apple.where(:id => [1,2]).load }.to log(:debug, %(UniqueIndexCache on 'Apple.id' partial hit for ids [1, 2]: missing [2]))
|
46
46
|
end
|
47
47
|
end
|
48
48
|
|
@@ -55,50 +55,60 @@ describe RecordCache::Strategy::UniqueIndexCache do
|
|
55
55
|
|
56
56
|
# @see https://github.com/orslumen/record-cache/issues/2
|
57
57
|
it "should not use the cache when a lock is used" do
|
58
|
-
|
58
|
+
pending("Any_lock is sqlite specific and I'm not aware of a mysql alternative") unless ActiveRecord::Base.connection.adapter_name == "SQLite"
|
59
|
+
|
60
|
+
expect{ Apple.lock("any_lock").where(:id => 1).load }.to_not hit_cache(Apple)
|
59
61
|
end
|
60
62
|
|
61
63
|
it "should use the cache when a single id is requested" do
|
62
|
-
expect{ Apple.where(:id => 1).
|
64
|
+
expect{ Apple.where(:id => 1).load }.to hit_cache(Apple).on(:id).times(1)
|
63
65
|
end
|
64
66
|
|
65
67
|
it "should use the cache when a multiple ids are requested" do
|
66
|
-
expect{ Apple.where(:id => [1, 2]).
|
68
|
+
expect{ Apple.where(:id => [1, 2]).load }.to hit_cache(Apple).on(:id).times(2)
|
67
69
|
end
|
68
70
|
|
69
71
|
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).
|
72
|
+
expect{ Apple.where(:id => 1).limit(1).load }.to hit_cache(Apple).on(:id).times(1)
|
71
73
|
end
|
72
74
|
|
73
75
|
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).
|
76
|
+
expect{ Apple.where(:id => 1).limit(2).load }.to_not use_cache(Apple).on(:id)
|
75
77
|
end
|
76
78
|
|
77
79
|
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).
|
80
|
+
expect{ Apple.where(:id => [1, 2]).limit(1).load }.to_not use_cache(Apple).on(:id)
|
79
81
|
end
|
80
82
|
|
81
83
|
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").
|
84
|
+
expect{ Apple.where(:id => 1).where(:name => "Adams Apple x").load }.to hit_cache(Apple).on(:id).times(1)
|
83
85
|
end
|
84
86
|
|
85
87
|
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").
|
88
|
+
expect{ Apple.where(:id => [1,2]).where(:name => "Adams Apple x").load }.to hit_cache(Apple).on(:id).times(2)
|
87
89
|
end
|
88
90
|
|
89
91
|
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").
|
92
|
+
expect{ Apple.where(:id => 1).order("name ASC").load }.to hit_cache(Apple).on(:id).times(1)
|
91
93
|
end
|
92
94
|
|
93
95
|
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").
|
96
|
+
expect{ Apple.where(:id => [1,2]).order("name ASC").load }.to hit_cache(Apple).on(:id).times(2)
|
95
97
|
end
|
96
98
|
|
97
99
|
it "should not use the cache when a join clause is used" do
|
98
|
-
expect{ Apple.where(:id => [1,2]).joins(:store).
|
100
|
+
expect{ Apple.where(:id => [1,2]).joins(:store).load }.to_not use_cache(Apple).on(:id)
|
101
|
+
end
|
102
|
+
|
103
|
+
it "should not use the cache when distinct is used in a select" do
|
104
|
+
expect{ Apple.select('distinct person_id').where(:id => [1, 2]).load }.not_to hit_cache(Apple).on(:id)
|
105
|
+
end
|
106
|
+
|
107
|
+
it "should not use the cache when distinct is used in a select" do
|
108
|
+
expect{ Apple.select('distinct person_id').where(:id => [1, 2]).load }.not_to hit_cache(Apple).on(:id)
|
99
109
|
end
|
100
110
|
end
|
101
|
-
|
111
|
+
|
102
112
|
context "record_change" do
|
103
113
|
before(:each) do
|
104
114
|
# fill cache
|
@@ -107,12 +117,12 @@ describe RecordCache::Strategy::UniqueIndexCache do
|
|
107
117
|
end
|
108
118
|
|
109
119
|
it "should invalidate destroyed records" do
|
110
|
-
expect{ Apple.where(:id => 1).
|
120
|
+
expect{ Apple.where(:id => 1).load }.to hit_cache(Apple).on(:id).times(1)
|
111
121
|
@apple1.destroy
|
112
|
-
expect{ @apples = Apple.where(:id => 1).
|
122
|
+
expect{ @apples = Apple.where(:id => 1).load }.to miss_cache(Apple).on(:id).times(1)
|
113
123
|
expect(@apples).to be_empty
|
114
124
|
# try again, to make sure the "missing record" is not cached
|
115
|
-
expect{ Apple.where(:id => 1).
|
125
|
+
expect{ Apple.where(:id => 1).load }.to miss_cache(Apple).on(:id).times(1)
|
116
126
|
end
|
117
127
|
|
118
128
|
it "should add updated records directly to the cache" do
|
@@ -131,12 +141,12 @@ describe RecordCache::Strategy::UniqueIndexCache do
|
|
131
141
|
it "should add updated records to the cache, also when multiple ids are queried" do
|
132
142
|
@apple1.name = "Applejuice"
|
133
143
|
@apple1.save!
|
134
|
-
expect{ @apples = Apple.where(:id => [1, 2]).order('id ASC').
|
144
|
+
expect{ @apples = Apple.where(:id => [1, 2]).order('id ASC').load }.to hit_cache(Apple).on(:id).times(2)
|
135
145
|
expect(@apples.map(&:name)).to eq(["Applejuice", "Adams Apple 2"])
|
136
146
|
end
|
137
147
|
|
138
148
|
end
|
139
|
-
|
149
|
+
|
140
150
|
context "invalidate" do
|
141
151
|
before(:each) do
|
142
152
|
@apple1 = Apple.find(1)
|
@@ -151,27 +161,27 @@ describe RecordCache::Strategy::UniqueIndexCache do
|
|
151
161
|
it "should only miss the cache for the invalidated record when multiple ids are queried" do
|
152
162
|
# miss on 1
|
153
163
|
Apple.record_cache[:id].invalidate(1)
|
154
|
-
expect{ Apple.where(:id => [1, 2]).
|
164
|
+
expect{ Apple.where(:id => [1, 2]).load }.to miss_cache(Apple).on(:id).times(1)
|
155
165
|
# hit on 2
|
156
166
|
Apple.record_cache[:id].invalidate(1)
|
157
|
-
expect{ Apple.where(:id => [1, 2]).
|
167
|
+
expect{ Apple.where(:id => [1, 2]).load }.to hit_cache(Apple).on(:id).times(1)
|
158
168
|
# nothing invalidated, both hit
|
159
|
-
expect{ Apple.where(:id => [1, 2]).
|
169
|
+
expect{ Apple.where(:id => [1, 2]).load }.to hit_cache(Apple).on(:id).times(2)
|
160
170
|
end
|
161
171
|
|
162
172
|
it "should invalidate records when using update_all" do
|
163
|
-
Apple.where(:id => [3,4,5]).
|
164
|
-
expect{ @apples = Apple.where(:id => [1, 2, 3, 4, 5]).order('id ASC').
|
173
|
+
Apple.where(:id => [3,4,5]).load # fill id cache on all Adam Store apples
|
174
|
+
expect{ @apples = Apple.where(:id => [1, 2, 3, 4, 5]).order('id ASC').load }.to hit_cache(Apple).on(:id).times(5)
|
165
175
|
expect(@apples.map(&:name)).to eq(["Adams Apple 1", "Adams Apple 2", "Adams Apple 3", "Adams Apple 4", "Adams Apple 5"])
|
166
176
|
# update 3 of the 5 apples in the Adam Store
|
167
177
|
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').
|
178
|
+
expect{ @apples = Apple.where(:id => [1, 2, 3, 4, 5]).order('id ASC').load }.to hit_cache(Apple).on(:id).times(2)
|
169
179
|
expect(@apples.map(&:name)).to eq(["Uniform Apple", "Uniform Apple", "Uniform Apple", "Adams Apple 4", "Adams Apple 5"])
|
170
180
|
end
|
171
181
|
|
172
182
|
it "should invalidate reflection indexes when a has_many relation is updated" do
|
173
183
|
# assign different apples to store 2
|
174
|
-
expect{ Apple.where(:store_id => 1).
|
184
|
+
expect{ Apple.where(:store_id => 1).load }.to hit_cache(Apple).on(:id).times(2)
|
175
185
|
store2_apple_ids = Apple.where(:store_id => 2).map(&:id)
|
176
186
|
store1 = Store.find(1)
|
177
187
|
store1.apple_ids = store2_apple_ids
|
@@ -288,30 +298,32 @@ describe RecordCache::Strategy::UniqueIndexCache do
|
|
288
298
|
end
|
289
299
|
end
|
290
300
|
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
301
|
+
# does not work for Rails 3.0
|
302
|
+
unless ActiveRecord::VERSION::MAJOR == 3 && ActiveRecord::VERSION::MINOR == 0
|
303
|
+
it "should not update the cache for the rolled back inner transaction" do
|
304
|
+
apple1, apple2 = nil
|
295
305
|
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
306
|
+
ActiveRecord::Base.transaction do
|
307
|
+
apple1 = Apple.find(1)
|
308
|
+
apple1.name = "Committed Apple 1"
|
309
|
+
apple1.save!
|
300
310
|
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
311
|
+
ActiveRecord::Base.transaction(requires_new: true) do
|
312
|
+
apple2 = Apple.find(2)
|
313
|
+
apple2.name = "Rollback Apple 2"
|
314
|
+
apple2.save!
|
305
315
|
|
306
|
-
|
316
|
+
raise ActiveRecord::Rollback, "oops"
|
317
|
+
end
|
307
318
|
end
|
308
|
-
end
|
309
319
|
|
310
|
-
|
311
|
-
|
320
|
+
expect{ apple1 = Apple.find(1) }.to use_cache(Apple).on(:id)
|
321
|
+
expect(apple1.name).to eq("Committed Apple 1")
|
312
322
|
|
313
|
-
|
314
|
-
|
323
|
+
expect{ apple2 = Apple.find(2) }.to use_cache(Apple).on(:id)
|
324
|
+
expect(apple2.name).to eq("Adams Apple 2")
|
325
|
+
end
|
315
326
|
end
|
327
|
+
|
316
328
|
end
|
317
329
|
end
|