identity_cache 0.1.0 → 0.2.0
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 +4 -4
- checksums.yaml.gz.sig +1 -0
- data/.travis.yml +0 -4
- data/README.md +5 -1
- data/identity_cache.gemspec +5 -5
- data/lib/identity_cache/cache_fetcher.rb +84 -0
- data/lib/identity_cache/memoized_cache_proxy.rb +53 -47
- data/lib/identity_cache/query_api.rb +1 -16
- data/lib/identity_cache/version.rb +2 -2
- data/lib/identity_cache.rb +17 -34
- data/performance/cache_runner.rb +61 -8
- data/shipit.rubygems.yml +1 -0
- data/test/attribute_cache_test.rb +13 -6
- data/test/denormalized_has_many_test.rb +1 -1
- data/test/denormalized_has_one_test.rb +7 -5
- data/test/fetch_multi_test.rb +39 -15
- data/test/fetch_test.rb +66 -26
- data/test/fixtures/serialized_record +0 -0
- data/test/helpers/update_serialization_format.rb +6 -8
- data/test/index_cache_test.rb +13 -13
- data/test/memoized_cache_proxy_test.rb +29 -33
- data/test/test_helper.rb +7 -10
- data.tar.gz.sig +1 -0
- metadata +59 -6
- metadata.gz.sig +0 -0
data/test/fetch_multi_test.rb
CHANGED
@@ -25,7 +25,7 @@ class FetchMultiTest < IdentityCache::TestCase
|
|
25
25
|
cache_response[bob_blob_key] = cache_response_for(@bob)
|
26
26
|
cache_response[joe_blob_key] = cache_response_for(@joe)
|
27
27
|
cache_response[fred_blob_key] = cache_response_for(@fred)
|
28
|
-
IdentityCache.cache.expects(:
|
28
|
+
IdentityCache.cache.expects(:fetch_multi).with(bob_blob_key, joe_blob_key, fred_blob_key).returns(cache_response)
|
29
29
|
assert_equal [@bob, @joe, @fred], Item.fetch_multi(@bob.id, @joe.id, @fred.id)
|
30
30
|
end
|
31
31
|
|
@@ -34,7 +34,7 @@ class FetchMultiTest < IdentityCache::TestCase
|
|
34
34
|
cache_response[@bob_blob_key] = cache_response_for(@bob)
|
35
35
|
cache_response[@joe_blob_key] = cache_response_for(@joe)
|
36
36
|
cache_response[@fred_blob_key] = cache_response_for(@fred)
|
37
|
-
IdentityCache.cache.expects(:
|
37
|
+
IdentityCache.cache.expects(:fetch_multi).with(@bob_blob_key, @joe_blob_key, @fred_blob_key).returns(cache_response)
|
38
38
|
assert_equal [@bob, @joe, @fred], Item.fetch_multi(@bob.id, @joe.id, @fred.id)
|
39
39
|
end
|
40
40
|
|
@@ -43,8 +43,9 @@ class FetchMultiTest < IdentityCache::TestCase
|
|
43
43
|
cache_response[@bob_blob_key] = nil
|
44
44
|
cache_response[@joe_blob_key] = nil
|
45
45
|
cache_response[@fred_blob_key] = nil
|
46
|
-
|
46
|
+
fetch_multi = fetch_multi_stub(cache_response)
|
47
47
|
assert_equal [@bob, @joe, @fred], Item.fetch_multi(@bob.id, @joe.id, @fred.id)
|
48
|
+
assert fetch_multi.has_been_called_with?(@bob_blob_key, @joe_blob_key, @fred_blob_key)
|
48
49
|
end
|
49
50
|
|
50
51
|
def test_fetch_multi_with_mixed_hits_and_misses
|
@@ -52,8 +53,9 @@ class FetchMultiTest < IdentityCache::TestCase
|
|
52
53
|
cache_response[@bob_blob_key] = cache_response_for(@bob)
|
53
54
|
cache_response[@joe_blob_key] = nil
|
54
55
|
cache_response[@fred_blob_key] = cache_response_for(@fred)
|
55
|
-
|
56
|
+
fetch_multi = fetch_multi_stub(cache_response)
|
56
57
|
assert_equal [@bob, @joe, @fred], Item.fetch_multi(@bob.id, @joe.id, @fred.id)
|
58
|
+
assert fetch_multi.has_been_called_with?(@bob_blob_key, @joe_blob_key, @fred_blob_key)
|
57
59
|
end
|
58
60
|
|
59
61
|
def test_fetch_multi_with_mixed_hits_and_misses_and_responses_in_the_wrong_order
|
@@ -61,22 +63,25 @@ class FetchMultiTest < IdentityCache::TestCase
|
|
61
63
|
cache_response[@bob_blob_key] = nil
|
62
64
|
cache_response[@joe_blob_key] = nil
|
63
65
|
cache_response[@fred_blob_key] = cache_response_for(@fred)
|
64
|
-
|
66
|
+
fetch_multi = fetch_multi_stub(cache_response)
|
65
67
|
assert_equal [@bob, @fred, @joe], Item.fetch_multi(@bob.id, @fred.id, @joe.id)
|
68
|
+
assert fetch_multi.has_been_called_with?(@bob_blob_key, @fred_blob_key, @joe_blob_key)
|
66
69
|
end
|
67
70
|
|
68
71
|
def test_fetch_multi_with_mixed_hits_and_misses_and_non_existant_keys_1
|
69
72
|
populate_only_fred
|
70
73
|
|
71
|
-
|
74
|
+
fetch_multi = fetch_multi_stub(@cache_response)
|
72
75
|
assert_equal [@bob, @joe, @fred], Item.fetch_multi(10, @bob.id, @joe.id, @fred.id)
|
76
|
+
assert fetch_multi.has_been_called_with?(@tenth_blob_key, @bob_blob_key, @joe_blob_key, @fred_blob_key)
|
73
77
|
end
|
74
78
|
|
75
79
|
def test_fetch_multi_with_mixed_hits_and_misses_and_non_existant_keys_2
|
76
80
|
populate_only_fred
|
77
81
|
|
78
|
-
|
82
|
+
fetch_multi = fetch_multi_stub(@cache_response)
|
79
83
|
assert_equal [@fred, @bob, @joe], Item.fetch_multi(@fred.id, @bob.id, 10, @joe.id)
|
84
|
+
assert fetch_multi.has_been_called_with?(@fred_blob_key, @bob_blob_key, @tenth_blob_key, @joe_blob_key)
|
80
85
|
end
|
81
86
|
|
82
87
|
|
@@ -84,9 +89,9 @@ class FetchMultiTest < IdentityCache::TestCase
|
|
84
89
|
cache_result = {1 => IdentityCache::CACHED_NIL, 2 => IdentityCache::CACHED_NIL}
|
85
90
|
fetch_result = {1 => nil, 2 => nil}
|
86
91
|
|
87
|
-
|
88
|
-
|
89
|
-
|
92
|
+
fetcher.expects(:cas_multi).with([1, 2]).twice.returns(nil, cache_result)
|
93
|
+
fetcher.expects(:add).with(1, IdentityCache::CACHED_NIL).once
|
94
|
+
fetcher.expects(:add).with(2, IdentityCache::CACHED_NIL).once
|
90
95
|
|
91
96
|
results = IdentityCache.fetch_multi(1,2) do |keys|
|
92
97
|
[nil, nil]
|
@@ -103,7 +108,7 @@ class FetchMultiTest < IdentityCache::TestCase
|
|
103
108
|
def test_fetch_multi_works_with_blanks
|
104
109
|
cache_result = {1 => false, 2 => ' '}
|
105
110
|
|
106
|
-
|
111
|
+
fetcher.expects(:fetch_multi).with([1,2]).returns(cache_result)
|
107
112
|
|
108
113
|
results = IdentityCache.fetch_multi(1,2) do |keys|
|
109
114
|
flunk "Contents should have been fetched from cache successfully"
|
@@ -118,7 +123,7 @@ class FetchMultiTest < IdentityCache::TestCase
|
|
118
123
|
|
119
124
|
def test_fetch_multi_with_open_transactions_hits_the_database
|
120
125
|
Item.connection.expects(:open_transactions).at_least_once.returns(1)
|
121
|
-
|
126
|
+
fetcher.expects(:fetch_multi).never
|
122
127
|
assert_equal [@bob, @joe, @fred], Item.fetch_multi(@bob.id, @joe.id, @fred.id)
|
123
128
|
end
|
124
129
|
|
@@ -165,15 +170,15 @@ class FetchMultiTest < IdentityCache::TestCase
|
|
165
170
|
cache_response[@joe_blob_key] = cache_response_for(@joe)
|
166
171
|
|
167
172
|
with_batch_size 1 do
|
168
|
-
|
169
|
-
|
173
|
+
fetcher.expects(:fetch_multi).with([@bob_blob_key]).returns(cache_response).once
|
174
|
+
fetcher.expects(:fetch_multi).with([@joe_blob_key]).returns(cache_response).once
|
170
175
|
assert_equal [@bob, @joe], Item.fetch_multi(@bob.id, @joe.id)
|
171
176
|
end
|
172
177
|
end
|
173
178
|
|
174
179
|
def test_fetch_multi_max_stack_level
|
175
180
|
cache_response = { @fred_blob_key => cache_response_for(@fred) }
|
176
|
-
|
181
|
+
fetcher.stubs(:fetch_multi).returns(cache_response)
|
177
182
|
assert_nothing_raised { Item.fetch_multi([@fred.id] * 200_000) }
|
178
183
|
end
|
179
184
|
|
@@ -182,6 +187,18 @@ class FetchMultiTest < IdentityCache::TestCase
|
|
182
187
|
assert_equal [fixture], KeyedRecord.fetch_multi(123, 456)
|
183
188
|
end
|
184
189
|
|
190
|
+
def test_fetch_multi_after_expiring_a_record
|
191
|
+
Item.fetch_multi(@joe.id, @fred.id)
|
192
|
+
@bob.send(:expire_cache)
|
193
|
+
assert_equal IdentityCache::DELETED, backend.read(@bob.primary_cache_index_key)
|
194
|
+
|
195
|
+
add = Spy.on(IdentityCache.cache.cache_fetcher, :add).and_call_through
|
196
|
+
|
197
|
+
assert_equal [@bob, @joe, @fred], Item.fetch_multi(@bob.id, @joe.id, @fred.id)
|
198
|
+
refute add.has_been_called?
|
199
|
+
assert_equal cache_response_for(Item.find(@bob.id)), backend.read(@bob.primary_cache_index_key)
|
200
|
+
end
|
201
|
+
|
185
202
|
private
|
186
203
|
|
187
204
|
def populate_only_fred
|
@@ -207,4 +224,11 @@ class FetchMultiTest < IdentityCache::TestCase
|
|
207
224
|
IdentityCache.send(:remove_const, :BATCH_SIZE)
|
208
225
|
IdentityCache.const_set(:BATCH_SIZE, previous_batch_size)
|
209
226
|
end
|
227
|
+
|
228
|
+
def fetch_multi_stub(cache_response)
|
229
|
+
Spy.on(IdentityCache.cache, :fetch_multi).and_return do |*args, &block|
|
230
|
+
nil_keys = cache_response.select {|_, v| v.nil? }.keys
|
231
|
+
cache_response.merge(Hash[nil_keys.zip(block.call(nil_keys))])
|
232
|
+
end
|
233
|
+
end
|
210
234
|
end
|
data/test/fetch_test.rb
CHANGED
@@ -26,7 +26,7 @@ class FetchTest < IdentityCache::TestCase
|
|
26
26
|
end
|
27
27
|
|
28
28
|
def test_fetch_cache_hit
|
29
|
-
IdentityCache.cache.expects(:
|
29
|
+
IdentityCache.cache.expects(:fetch).with(@blob_key).returns(@cached_value)
|
30
30
|
|
31
31
|
assert_equal @record, Item.fetch(1)
|
32
32
|
end
|
@@ -36,7 +36,7 @@ class FetchTest < IdentityCache::TestCase
|
|
36
36
|
IdentityCache.cache_namespace = proc { |model| "#{model.table_name}:#{old_ns}" }
|
37
37
|
|
38
38
|
new_blob_key = "items:#{@blob_key}"
|
39
|
-
IdentityCache.cache.expects(:
|
39
|
+
IdentityCache.cache.expects(:fetch).with(new_blob_key).returns(@cached_value)
|
40
40
|
|
41
41
|
assert_equal @record, Item.fetch(1)
|
42
42
|
ensure
|
@@ -44,32 +44,36 @@ class FetchTest < IdentityCache::TestCase
|
|
44
44
|
end
|
45
45
|
|
46
46
|
def test_exists_with_identity_cache_when_cache_hit
|
47
|
-
IdentityCache.cache.expects(:
|
47
|
+
IdentityCache.cache.expects(:fetch).with(@blob_key).returns(@cached_value)
|
48
48
|
|
49
49
|
assert Item.exists_with_identity_cache?(1)
|
50
50
|
end
|
51
51
|
|
52
52
|
def test_exists_with_identity_cache_when_cache_miss_and_in_db
|
53
|
-
IdentityCache.cache
|
53
|
+
fetch = Spy.on(IdentityCache.cache, :fetch).and_call_through
|
54
54
|
Item.expects(:resolve_cache_miss).with(1).once.returns(@record)
|
55
55
|
|
56
56
|
assert Item.exists_with_identity_cache?(1)
|
57
|
+
assert fetch.has_been_called_with?(@blob_key)
|
57
58
|
end
|
58
59
|
|
59
60
|
def test_exists_with_identity_cache_when_cache_miss_and_not_in_db
|
60
|
-
IdentityCache.cache
|
61
|
+
fetch = Spy.on(IdentityCache.cache, :fetch).and_call_through
|
61
62
|
Item.expects(:resolve_cache_miss).with(1).once.returns(nil)
|
62
63
|
|
63
64
|
assert !Item.exists_with_identity_cache?(1)
|
65
|
+
assert fetch.has_been_called_with?(@blob_key)
|
64
66
|
end
|
65
67
|
|
66
68
|
def test_fetch_miss
|
67
69
|
Item.expects(:resolve_cache_miss).with(1).once.returns(@record)
|
68
70
|
|
69
|
-
|
70
|
-
IdentityCache.cache
|
71
|
+
results = []
|
72
|
+
fetch = Spy.on(IdentityCache.cache, :fetch).and_return {|_, &block| block.call.tap {|result| results << result}}
|
71
73
|
|
72
74
|
assert_equal @record, Item.fetch(1)
|
75
|
+
assert fetch.has_been_called_with?(@blob_key)
|
76
|
+
assert_equal [@cached_value], results
|
73
77
|
end
|
74
78
|
|
75
79
|
def test_fetch_miss_with_non_id_primary_key
|
@@ -78,16 +82,45 @@ class FetchTest < IdentityCache::TestCase
|
|
78
82
|
assert_equal fixture, KeyedRecord.fetch(hashed_key)
|
79
83
|
end
|
80
84
|
|
85
|
+
def test_fetch_conflict
|
86
|
+
resolve_cache_miss = Spy.on(Item, :resolve_cache_miss).and_return do
|
87
|
+
@record.send(:expire_cache)
|
88
|
+
@record
|
89
|
+
end
|
90
|
+
add = Spy.on(fetcher, :add).and_call_through
|
91
|
+
|
92
|
+
assert_equal @record, Item.fetch(1)
|
93
|
+
assert resolve_cache_miss.has_been_called_with?(1)
|
94
|
+
assert add.has_been_called_with?(@blob_key, @cached_value)
|
95
|
+
assert_equal IdentityCache::DELETED, backend.read(@record.primary_cache_index_key)
|
96
|
+
end
|
97
|
+
|
98
|
+
def test_fetch_conflict_after_delete
|
99
|
+
@record.send(:expire_cache)
|
100
|
+
assert_equal IdentityCache::DELETED, backend.read(@record.primary_cache_index_key)
|
101
|
+
|
102
|
+
resolve_cache_miss = Spy.on(Item, :resolve_cache_miss).and_return do
|
103
|
+
@record.send(:expire_cache)
|
104
|
+
@record
|
105
|
+
end
|
106
|
+
add = Spy.on(IdentityCache.cache.cache_fetcher, :add).and_call_through
|
107
|
+
|
108
|
+
assert_equal @record, Item.fetch(1)
|
109
|
+
assert resolve_cache_miss.has_been_called_with?(1)
|
110
|
+
refute add.has_been_called?
|
111
|
+
assert_equal IdentityCache::DELETED, backend.read(@record.primary_cache_index_key)
|
112
|
+
end
|
113
|
+
|
81
114
|
def test_fetch_by_id_not_found_should_return_nil
|
82
115
|
nonexistent_record_id = 10
|
83
|
-
|
116
|
+
fetcher.expects(:add).with(@blob_key + '0', IdentityCache::CACHED_NIL)
|
84
117
|
|
85
118
|
assert_equal nil, Item.fetch_by_id(nonexistent_record_id)
|
86
119
|
end
|
87
120
|
|
88
121
|
def test_fetch_not_found_should_raise
|
89
122
|
nonexistent_record_id = 10
|
90
|
-
|
123
|
+
fetcher.expects(:add).with(@blob_key + '0', IdentityCache::CACHED_NIL)
|
91
124
|
|
92
125
|
assert_raises(ActiveRecord::RecordNotFound) { Item.fetch(nonexistent_record_id) }
|
93
126
|
end
|
@@ -96,44 +129,51 @@ class FetchTest < IdentityCache::TestCase
|
|
96
129
|
key = @record.primary_cache_index_key
|
97
130
|
|
98
131
|
assert_equal nil, Item.fetch_by_id(@record.id)
|
99
|
-
assert_equal IdentityCache::CACHED_NIL,
|
132
|
+
assert_equal IdentityCache::CACHED_NIL, backend.read(key)
|
100
133
|
|
101
134
|
@record.save!
|
102
|
-
|
135
|
+
assert_equal IdentityCache::DELETED, backend.read(key)
|
103
136
|
end
|
104
137
|
|
105
138
|
def test_fetch_by_title_hit
|
106
|
-
|
107
|
-
IdentityCache.cache
|
139
|
+
values = []
|
140
|
+
fetch = Spy.on(IdentityCache.cache, :fetch).and_return do |key, &block|
|
141
|
+
case key
|
142
|
+
# Read record with title bob
|
143
|
+
when @index_key then block.call.tap {|val| values << val}
|
144
|
+
# got id, do memcache lookup on that, hit -> done
|
145
|
+
when @blob_key then @cached_value
|
146
|
+
end
|
147
|
+
end
|
108
148
|
|
109
|
-
#
|
149
|
+
# Id not found, use sql, SELECT id FROM records WHERE title = '...' LIMIT 1"
|
110
150
|
Item.connection.expects(:exec_query).returns(ActiveRecord::Result.new(['id'], [[1]]))
|
111
151
|
|
112
|
-
# cache sql result
|
113
|
-
IdentityCache.cache.expects(:write).with(@index_key, 1)
|
114
|
-
|
115
|
-
# got id, do memcache lookup on that, hit -> done
|
116
|
-
IdentityCache.cache.expects(:read).with(@blob_key).returns(@cached_value)
|
117
|
-
|
118
152
|
assert_equal @record, Item.fetch_by_title('bob')
|
153
|
+
assert_equal [1], values
|
154
|
+
assert fetch.has_been_called_with?(@index_key)
|
155
|
+
assert fetch.has_been_called_with?(@blob_key)
|
119
156
|
end
|
120
157
|
|
121
158
|
def test_fetch_by_title_cache_namespace
|
122
159
|
Item.send(:include, SwitchNamespace)
|
123
|
-
IdentityCache.cache.expects(:
|
124
|
-
IdentityCache.cache.expects(:
|
160
|
+
IdentityCache.cache.expects(:fetch).with("ns:#{@index_key}").returns(1)
|
161
|
+
IdentityCache.cache.expects(:fetch).with("ns:#{@blob_key}").returns(@cached_value)
|
125
162
|
|
126
163
|
assert_equal @record, Item.fetch_by_title('bob')
|
127
164
|
end
|
128
165
|
|
129
166
|
def test_fetch_by_title_stores_idcnil
|
130
167
|
Item.connection.expects(:exec_query).once.returns(ActiveRecord::Result.new([], []))
|
131
|
-
|
132
|
-
|
168
|
+
add = Spy.on(fetcher, :add).and_call_through
|
169
|
+
fetch = Spy.on(fetcher, :fetch).and_call_through
|
133
170
|
assert_equal nil, Item.fetch_by_title('bob') # exec_query => nil
|
134
171
|
|
135
172
|
assert_equal nil, Item.fetch_by_title('bob') # returns cached nil
|
136
173
|
assert_equal nil, Item.fetch_by_title('bob') # returns cached nil
|
174
|
+
|
175
|
+
assert add.has_been_called_with?(@index_key, IdentityCache::CACHED_NIL)
|
176
|
+
assert_equal 3, fetch.calls.length
|
137
177
|
end
|
138
178
|
|
139
179
|
def test_fetch_by_bang_method
|
@@ -144,8 +184,8 @@ class FetchTest < IdentityCache::TestCase
|
|
144
184
|
end
|
145
185
|
|
146
186
|
def test_fetch_does_not_communicate_to_cache_with_nil_id
|
147
|
-
|
148
|
-
|
187
|
+
fetcher.expects(:fetch).never
|
188
|
+
fetcher.expects(:add).never
|
149
189
|
assert_raises(ActiveRecord::RecordNotFound) { Item.fetch(nil) }
|
150
190
|
end
|
151
191
|
end
|
Binary file
|
@@ -1,20 +1,16 @@
|
|
1
1
|
$LOAD_PATH.unshift File.expand_path("../../../lib", __FILE__)
|
2
2
|
|
3
|
+
require 'logger'
|
3
4
|
require 'active_record'
|
4
5
|
require 'memcached_store'
|
6
|
+
require 'active_support/cache/memcached_store'
|
5
7
|
require_relative 'serialization_format'
|
6
|
-
require_relative 'cache'
|
7
8
|
require_relative 'database_connection'
|
8
9
|
require_relative 'active_record_objects'
|
9
10
|
require 'identity_cache'
|
10
11
|
|
11
|
-
|
12
|
-
|
13
|
-
$mysql_port = 13306
|
14
|
-
else
|
15
|
-
$memcached_port = 11211
|
16
|
-
$mysql_port = 3306
|
17
|
-
end
|
12
|
+
$memcached_port = 11211
|
13
|
+
$mysql_port = 3306
|
18
14
|
|
19
15
|
include SerializationFormat
|
20
16
|
include ActiveRecordObjects
|
@@ -22,6 +18,8 @@ include ActiveRecordObjects
|
|
22
18
|
DatabaseConnection.setup
|
23
19
|
DatabaseConnection.drop_tables
|
24
20
|
DatabaseConnection.create_tables
|
21
|
+
IdentityCache.logger = Logger.new(nil)
|
22
|
+
IdentityCache.cache_backend = ActiveSupport::Cache::MemcachedStore.new("localhost:#{$memcached_port}", :support_cas => true)
|
25
23
|
setup_models
|
26
24
|
File.open(serialized_record_file, 'w') {|file| serialize(serialized_record, file) }
|
27
25
|
puts "Serialized record to #{serialized_record_file}"
|
data/test/index_cache_test.rb
CHANGED
@@ -34,21 +34,21 @@ class IndexCacheTest < IdentityCache::TestCase
|
|
34
34
|
def test_unique_index_caches_nil
|
35
35
|
Item.cache_index :title, :unique => true
|
36
36
|
assert_equal nil, Item.fetch_by_title('bob')
|
37
|
-
assert_equal IdentityCache::CACHED_NIL,
|
37
|
+
assert_equal IdentityCache::CACHED_NIL, backend.read(@cache_key)
|
38
38
|
end
|
39
39
|
|
40
40
|
def test_unique_index_expired_by_new_record
|
41
41
|
Item.cache_index :title, :unique => true
|
42
42
|
IdentityCache.cache.write(@cache_key, IdentityCache::CACHED_NIL)
|
43
43
|
@record.save!
|
44
|
-
assert_equal
|
44
|
+
assert_equal IdentityCache::DELETED, backend.read(@cache_key)
|
45
45
|
end
|
46
46
|
|
47
47
|
def test_unique_index_filled_on_fetch_by
|
48
48
|
Item.cache_index :title, :unique => true
|
49
49
|
@record.save!
|
50
50
|
assert_equal @record, Item.fetch_by_title('bob')
|
51
|
-
assert_equal @record.id,
|
51
|
+
assert_equal @record.id, backend.read(@cache_key)
|
52
52
|
end
|
53
53
|
|
54
54
|
def test_unique_index_expired_by_updated_record
|
@@ -60,28 +60,28 @@ class IndexCacheTest < IdentityCache::TestCase
|
|
60
60
|
new_cache_key = "#{NAMESPACE}index:Item:title:#{cache_hash(@record.title)}"
|
61
61
|
IdentityCache.cache.write(new_cache_key, IdentityCache::CACHED_NIL)
|
62
62
|
@record.save!
|
63
|
-
assert_equal
|
64
|
-
assert_equal
|
63
|
+
assert_equal IdentityCache::DELETED, backend.read(@cache_key)
|
64
|
+
assert_equal IdentityCache::DELETED, backend.read(new_cache_key)
|
65
65
|
end
|
66
66
|
|
67
67
|
def test_non_unique_index_caches_empty_result
|
68
68
|
Item.cache_index :title
|
69
69
|
assert_equal [], Item.fetch_by_title('bob')
|
70
|
-
assert_equal [],
|
70
|
+
assert_equal [], backend.read(@cache_key)
|
71
71
|
end
|
72
72
|
|
73
73
|
def test_non_unique_index_expired_by_new_record
|
74
74
|
Item.cache_index :title
|
75
75
|
IdentityCache.cache.write(@cache_key, [])
|
76
76
|
@record.save!
|
77
|
-
assert_equal
|
77
|
+
assert_equal IdentityCache::DELETED, backend.read(@cache_key)
|
78
78
|
end
|
79
79
|
|
80
80
|
def test_non_unique_index_filled_on_fetch_by
|
81
81
|
Item.cache_index :title
|
82
82
|
@record.save!
|
83
83
|
assert_equal [@record], Item.fetch_by_title('bob')
|
84
|
-
assert_equal [@record.id],
|
84
|
+
assert_equal [@record.id], backend.read(@cache_key)
|
85
85
|
end
|
86
86
|
|
87
87
|
def test_non_unique_index_fetches_multiple_records
|
@@ -90,7 +90,7 @@ class IndexCacheTest < IdentityCache::TestCase
|
|
90
90
|
record2 = Item.create(:id => 2, :title => 'bob')
|
91
91
|
|
92
92
|
assert_equal [@record, record2], Item.fetch_by_title('bob')
|
93
|
-
assert_equal [1, 2],
|
93
|
+
assert_equal [1, 2], backend.read(@cache_key)
|
94
94
|
end
|
95
95
|
|
96
96
|
def test_non_unique_index_expired_by_updating_record
|
@@ -102,8 +102,8 @@ class IndexCacheTest < IdentityCache::TestCase
|
|
102
102
|
new_cache_key = "#{NAMESPACE}index:Item:title:#{cache_hash(@record.title)}"
|
103
103
|
IdentityCache.cache.write(new_cache_key, [])
|
104
104
|
@record.save!
|
105
|
-
assert_equal
|
106
|
-
assert_equal
|
105
|
+
assert_equal IdentityCache::DELETED, backend.read(@cache_key)
|
106
|
+
assert_equal IdentityCache::DELETED, backend.read(new_cache_key)
|
107
107
|
end
|
108
108
|
|
109
109
|
def test_non_unique_index_expired_by_destroying_record
|
@@ -111,7 +111,7 @@ class IndexCacheTest < IdentityCache::TestCase
|
|
111
111
|
@record.save!
|
112
112
|
IdentityCache.cache.write(@cache_key, [@record.id])
|
113
113
|
@record.destroy
|
114
|
-
assert_equal
|
114
|
+
assert_equal IdentityCache::DELETED, backend.read(@cache_key)
|
115
115
|
end
|
116
116
|
|
117
117
|
def test_set_table_name_cache_fetch
|
@@ -119,6 +119,6 @@ class IndexCacheTest < IdentityCache::TestCase
|
|
119
119
|
Item.table_name = 'items2'
|
120
120
|
@record.save!
|
121
121
|
assert_equal [@record], Item.fetch_by_title('bob')
|
122
|
-
assert_equal [@record.id],
|
122
|
+
assert_equal [@record.id], backend.read(@cache_key)
|
123
123
|
end
|
124
124
|
end
|
@@ -1,95 +1,91 @@
|
|
1
1
|
require "test_helper"
|
2
2
|
|
3
3
|
class MemoizedCacheProxyTest < IdentityCache::TestCase
|
4
|
-
def setup
|
5
|
-
super
|
6
|
-
@backend = IdentityCache.cache.cache_backend
|
7
|
-
end
|
8
|
-
|
9
4
|
def test_changing_default_cache
|
10
5
|
IdentityCache.cache_backend = ActiveSupport::Cache::MemoryStore.new
|
11
6
|
IdentityCache.cache.write('foo', 'bar')
|
12
|
-
|
7
|
+
assert_raises(NoMethodError) { IdentityCache.cache.fetch('foo') }
|
13
8
|
end
|
14
9
|
|
15
|
-
def
|
16
|
-
|
10
|
+
def test_fetch_should_short_circuit_on_memoized_values
|
11
|
+
fetcher.expects(:fetch).never
|
17
12
|
|
18
13
|
IdentityCache.cache.with_memoization do
|
19
14
|
IdentityCache.cache.write('foo', 'bar')
|
20
|
-
assert_equal 'bar', IdentityCache.cache.
|
15
|
+
assert_equal 'bar', IdentityCache.cache.fetch('foo')
|
21
16
|
end
|
22
17
|
end
|
23
18
|
|
24
|
-
def
|
25
|
-
|
19
|
+
def test_fetch_should_short_circuit_on_falsy_memoized_values
|
20
|
+
fetcher.expects(:fetch).never
|
26
21
|
|
27
22
|
IdentityCache.cache.with_memoization do
|
28
23
|
IdentityCache.cache.write('foo', nil)
|
29
|
-
assert_equal nil, IdentityCache.cache.
|
24
|
+
assert_equal nil, IdentityCache.cache.fetch('foo')
|
30
25
|
IdentityCache.cache.write('bar', false)
|
31
|
-
assert_equal false, IdentityCache.cache.
|
26
|
+
assert_equal false, IdentityCache.cache.fetch('bar')
|
32
27
|
end
|
33
28
|
end
|
34
29
|
|
35
|
-
def
|
36
|
-
|
30
|
+
def test_fetch_should_try_memcached_on_not_memoized_values
|
31
|
+
fetcher.expects(:fetch).with('foo').returns('bar')
|
37
32
|
|
38
33
|
IdentityCache.cache.with_memoization do
|
39
|
-
assert_equal 'bar', IdentityCache.cache.
|
34
|
+
assert_equal 'bar', IdentityCache.cache.fetch('foo')
|
40
35
|
end
|
41
36
|
end
|
42
37
|
|
43
38
|
def test_write_should_memoize_values
|
44
|
-
|
45
|
-
|
39
|
+
fetcher.expects(:fetch).never
|
40
|
+
fetcher.expects(:write).with('foo', 'bar')
|
46
41
|
|
47
42
|
IdentityCache.cache.with_memoization do
|
48
43
|
IdentityCache.cache.write('foo', 'bar')
|
49
|
-
assert_equal 'bar', IdentityCache.cache.
|
44
|
+
assert_equal 'bar', IdentityCache.cache.fetch('foo')
|
50
45
|
end
|
51
46
|
end
|
52
47
|
|
53
|
-
def
|
54
|
-
IdentityCache.cache_backend = backend = ActiveSupport::Cache::MemoryStore.new
|
48
|
+
def test_fetch_should_memoize_values
|
55
49
|
backend.write('foo', 'bar')
|
56
50
|
|
57
51
|
IdentityCache.cache.with_memoization do
|
58
|
-
assert_equal 'bar', IdentityCache.cache.
|
52
|
+
assert_equal 'bar', IdentityCache.cache.fetch('foo')
|
59
53
|
backend.delete('foo')
|
60
|
-
assert_equal 'bar', IdentityCache.cache.
|
54
|
+
assert_equal 'bar', IdentityCache.cache.fetch('foo')
|
61
55
|
end
|
62
56
|
end
|
63
57
|
|
64
|
-
def
|
65
|
-
|
58
|
+
def test_fetch_multi_should_memoize_values
|
59
|
+
expected_hash = {'foo' => 'bar', 'fooz' => IdentityCache::CACHED_NIL}
|
60
|
+
|
66
61
|
backend.write('foo', 'bar')
|
67
62
|
|
68
63
|
IdentityCache.cache.with_memoization do
|
69
|
-
assert_equal(
|
64
|
+
assert_equal(expected_hash, IdentityCache.cache.fetch_multi('foo', 'fooz') {|_| [IdentityCache::CACHED_NIL] })
|
65
|
+
assert_equal(expected_hash, IdentityCache.cache.memoized_key_values)
|
70
66
|
backend.delete('foo')
|
71
67
|
backend.write('fooz', 'baz')
|
72
|
-
assert_equal(
|
68
|
+
assert_equal(expected_hash, IdentityCache.cache.fetch_multi('foo', 'fooz'))
|
73
69
|
end
|
74
70
|
end
|
75
71
|
|
76
|
-
def
|
72
|
+
def test_fetch_multi_with_partially_memoized_should_read_missing_keys_from_memcache
|
77
73
|
IdentityCache.cache.write('foo', 'bar')
|
78
74
|
@backend.write('fooz', 'baz')
|
79
75
|
|
80
76
|
IdentityCache.cache.with_memoization do
|
81
|
-
assert_equal({'foo' => 'bar', 'fooz' => 'baz'}, IdentityCache.cache.
|
77
|
+
assert_equal({'foo' => 'bar', 'fooz' => 'baz'}, IdentityCache.cache.fetch_multi('foo', 'fooz'))
|
82
78
|
end
|
83
79
|
end
|
84
80
|
|
85
|
-
def
|
86
|
-
@backend.expects(:
|
81
|
+
def test_fetch_multi_with_blank_values_should_not_hit_the_cache_engine
|
82
|
+
@backend.expects(:fetch_multi).never
|
87
83
|
|
88
84
|
IdentityCache.cache.with_memoization do
|
89
85
|
IdentityCache.cache.write('foo', [])
|
90
86
|
IdentityCache.cache.write('bar', false)
|
91
87
|
IdentityCache.cache.write('baz', {})
|
92
|
-
assert_equal({'foo' => [], 'bar' => false, 'baz' => {}}, IdentityCache.cache.
|
88
|
+
assert_equal({'foo' => [], 'bar' => false, 'baz' => {}}, IdentityCache.cache.fetch_multi('foo', 'bar', 'baz'))
|
93
89
|
end
|
94
90
|
end
|
95
91
|
|
@@ -97,6 +93,6 @@ class MemoizedCacheProxyTest < IdentityCache::TestCase
|
|
97
93
|
IdentityCache.cache.with_memoization do
|
98
94
|
IdentityCache.cache.write('foo', 'bar')
|
99
95
|
end
|
100
|
-
assert_equal 'bar', @backend.
|
96
|
+
assert_equal 'bar', @backend.fetch('foo')
|
101
97
|
end
|
102
98
|
end
|
data/test/test_helper.rb
CHANGED
@@ -4,19 +4,14 @@ require 'mocha/setup'
|
|
4
4
|
require 'active_record'
|
5
5
|
require 'helpers/database_connection'
|
6
6
|
require 'helpers/active_record_objects'
|
7
|
-
require 'spy'
|
7
|
+
require 'spy/integration'
|
8
8
|
require 'memcached_store'
|
9
9
|
require 'active_support/cache/memcached_store'
|
10
10
|
|
11
11
|
require File.dirname(__FILE__) + '/../lib/identity_cache'
|
12
12
|
|
13
|
-
|
14
|
-
|
15
|
-
$mysql_port = 13306
|
16
|
-
else
|
17
|
-
$memcached_port = 11211
|
18
|
-
$mysql_port = 3306
|
19
|
-
end
|
13
|
+
$memcached_port = 11211
|
14
|
+
$mysql_port = 3306
|
20
15
|
|
21
16
|
DatabaseConnection.setup
|
22
17
|
ActiveSupport::Cache::Store.instrument = true
|
@@ -34,13 +29,15 @@ end
|
|
34
29
|
|
35
30
|
class IdentityCache::TestCase < MiniTest::Unit::TestCase
|
36
31
|
include ActiveRecordObjects
|
32
|
+
attr_reader :backend, :fetcher
|
37
33
|
|
38
34
|
def setup
|
39
35
|
DatabaseConnection.drop_tables
|
40
36
|
DatabaseConnection.create_tables
|
41
37
|
|
42
38
|
IdentityCache.logger = Logger.new(nil)
|
43
|
-
IdentityCache.cache_backend = ActiveSupport::Cache::MemcachedStore.new("localhost:#{$memcached_port}")
|
39
|
+
IdentityCache.cache_backend = @backend = ActiveSupport::Cache::MemcachedStore.new("localhost:#{$memcached_port}", :support_cas => true)
|
40
|
+
@fetcher = IdentityCache.cache.cache_fetcher
|
44
41
|
|
45
42
|
setup_models
|
46
43
|
end
|
@@ -129,6 +126,6 @@ class CacheCounter
|
|
129
126
|
end
|
130
127
|
|
131
128
|
def call(name, start, finish, message_id, values)
|
132
|
-
self.log << (values[:keys].try(:join, ', ') || values[:key])
|
129
|
+
self.log << "#{name} #{(values[:keys].try(:join, ', ') || values[:key])}"
|
133
130
|
end
|
134
131
|
end
|
data.tar.gz.sig
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
�W�~k�#��V�����ѻ?en`��g�t\˦������̃b�!�;��m�+���:F���".A�v��ex�R��S�$�=�����u�!�-�)Ie�^������[h0˨}.Q���>���]#
|