identity_cache 0.3.1 → 0.3.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +8 -2
- data/CHANGELOG.md +8 -0
- data/Gemfile.rails50 +6 -0
- data/README.md +10 -0
- data/dev.yml +54 -0
- data/identity_cache.gemspec +4 -4
- data/lib/identity_cache/belongs_to_caching.rb +5 -1
- data/lib/identity_cache/cache_key_generation.rb +1 -0
- data/lib/identity_cache/configuration_dsl.rb +13 -12
- data/lib/identity_cache/parent_model_expiration.rb +1 -1
- data/lib/identity_cache/query_api.rb +174 -82
- data/lib/identity_cache/version.rb +1 -1
- data/lib/identity_cache.rb +45 -1
- data/performance/cache_runner.rb +1 -1
- data/test/cache_hash_test.rb +0 -2
- data/test/cache_invalidation_test.rb +5 -4
- data/test/denormalized_has_many_test.rb +101 -7
- data/test/denormalized_has_one_test.rb +36 -0
- data/test/fetch_multi_test.rb +42 -0
- data/test/fetch_test.rb +32 -0
- data/test/helpers/database_connection.rb +4 -3
- data/test/index_cache_test.rb +2 -2
- data/test/memoized_attributes_test.rb +49 -0
- data/test/normalized_belongs_to_test.rb +24 -0
- data/test/normalized_has_many_test.rb +29 -5
- data/test/{prefetch_normalized_associations_test.rb → prefetch_associations_test.rb} +118 -9
- data/test/recursive_denormalized_has_many_test.rb +18 -2
- data/test/schema_change_test.rb +3 -2
- data/test/test_helper.rb +4 -2
- metadata +13 -12
- data/.ruby-version +0 -1
- data/Gemfile.rails40 +0 -6
- data/Gemfile.rails41 +0 -6
@@ -1,6 +1,6 @@
|
|
1
1
|
require "test_helper"
|
2
2
|
|
3
|
-
class
|
3
|
+
class PrefetchAssociationsTest < IdentityCache::TestCase
|
4
4
|
NAMESPACE = IdentityCache.cache_namespace
|
5
5
|
|
6
6
|
def setup
|
@@ -14,10 +14,120 @@ class PrefetchNormalizedAssociationsTest < IdentityCache::TestCase
|
|
14
14
|
@tenth_blob_key = "#{NAMESPACE}blob:Item:#{cache_hash("created_at:datetime,id:integer,item_id:integer,title:string,updated_at:datetime")}:10"
|
15
15
|
end
|
16
16
|
|
17
|
+
def test_prefetch_associations_on_fetched_records
|
18
|
+
Item.send(:cache_belongs_to, :item)
|
19
|
+
john = Item.create!(:title => 'john')
|
20
|
+
jim = Item.create!(:title => 'jim')
|
21
|
+
@bob.update_column(:item_id, john.id)
|
22
|
+
@joe.update_column(:item_id, jim.id)
|
23
|
+
items = Item.fetch_multi(@bob.id, @joe.id, @fred.id)
|
24
|
+
|
25
|
+
spy = Spy.on(Item, :fetch_multi).and_call_through
|
26
|
+
|
27
|
+
Item.prefetch_associations(:item, items)
|
28
|
+
|
29
|
+
assert spy.calls.one?{ |call| call.args == [[john.id, jim.id]] }
|
30
|
+
end
|
31
|
+
|
32
|
+
def test_prefetch_associations_on_db_records
|
33
|
+
Item.send(:cache_has_many, :associated_records, :embed => true)
|
34
|
+
AssociatedRecord.send(:cache_has_many, :deeply_associated_records, :embed => :ids)
|
35
|
+
|
36
|
+
setup_has_many_children_and_grandchildren(@bob)
|
37
|
+
|
38
|
+
item = Item.find(@bob.id)
|
39
|
+
|
40
|
+
assert_no_queries do
|
41
|
+
assert_memcache_operations(1) do
|
42
|
+
Item.prefetch_associations(:associated_records, [item])
|
43
|
+
end
|
44
|
+
assert_memcache_operations(0) do
|
45
|
+
item.fetch_associated_records.each(&:fetch_deeply_associated_record_ids)
|
46
|
+
end
|
47
|
+
assert_memcache_operations(1) do
|
48
|
+
Item.prefetch_associations({associated_records: :deeply_associated_records}, [item])
|
49
|
+
end
|
50
|
+
assert_memcache_operations(0) do
|
51
|
+
item.fetch_associated_records.each(&:fetch_deeply_associated_records)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def test_prefetch_associations_without_using_cache
|
57
|
+
Item.send(:cache_has_many, :associated_records, :embed => true)
|
58
|
+
|
59
|
+
associated1 = @bob.associated_records.create!(name: 'foo')
|
60
|
+
associated2 = @joe.associated_records.create!(name: 'bar')
|
61
|
+
items = [@bob, @joe].map(&:reload)
|
62
|
+
|
63
|
+
Item.transaction do
|
64
|
+
assert_memcache_operations(0) do
|
65
|
+
assert_queries(1) do
|
66
|
+
Item.prefetch_associations(:associated_records, items)
|
67
|
+
end
|
68
|
+
assert_no_queries do
|
69
|
+
assert_equal [[associated1], [associated2]], items.map(&:fetch_associated_records)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def test_prefetch_associations_cached_belongs_to
|
76
|
+
Item.send(:cache_belongs_to, :item)
|
77
|
+
@bob.update_attributes!(item_id: @joe.id)
|
78
|
+
@joe.update_attributes!(item_id: @fred.id)
|
79
|
+
@bob.fetch_item
|
80
|
+
@joe.fetch_item
|
81
|
+
items = [@bob, @joe].map(&:reload)
|
82
|
+
|
83
|
+
assert_no_queries do
|
84
|
+
assert_memcache_operations(1) do
|
85
|
+
Item.prefetch_associations(:item, items)
|
86
|
+
end
|
87
|
+
assert_memcache_operations(0) do
|
88
|
+
items.each { |item| item.fetch_item }
|
89
|
+
end
|
90
|
+
assert_memcache_operations(0) do
|
91
|
+
Item.prefetch_associations(:item, items)
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
def test_prefetch_associations_with_nil_cached_belongs_to
|
97
|
+
Item.send(:cache_belongs_to, :item)
|
98
|
+
@bob.update_attributes!(item_id: 1234)
|
99
|
+
assert_equal nil, @bob.fetch_item
|
100
|
+
|
101
|
+
assert_no_queries do
|
102
|
+
assert_memcache_operations(0) do
|
103
|
+
Item.prefetch_associations(:item, [@bob])
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
def test_prefetch_associations_on_association
|
109
|
+
AssociatedRecord.send(:cache_has_many, :deeply_associated_records, embed: true)
|
110
|
+
|
111
|
+
setup_has_many_children_and_grandchildren(@bob)
|
112
|
+
|
113
|
+
associated_records = @bob.associated_records
|
114
|
+
|
115
|
+
assert_queries(1) do
|
116
|
+
assert_memcache_operations(1) do
|
117
|
+
AssociatedRecord.prefetch_associations(:deeply_associated_records, associated_records)
|
118
|
+
end
|
119
|
+
end
|
120
|
+
assert_no_queries do
|
121
|
+
assert_memcache_operations(0) do
|
122
|
+
associated_records.each(&:fetch_deeply_associated_records)
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
17
127
|
def test_fetch_with_includes_option
|
18
128
|
Item.send(:cache_belongs_to, :item)
|
19
129
|
john = Item.create!(title: 'john')
|
20
|
-
@bob.update_column(:item_id, john)
|
130
|
+
@bob.update_column(:item_id, john.id)
|
21
131
|
|
22
132
|
spy = Spy.on(Item, :fetch_multi).and_call_through
|
23
133
|
|
@@ -30,8 +140,8 @@ class PrefetchNormalizedAssociationsTest < IdentityCache::TestCase
|
|
30
140
|
Item.send(:cache_belongs_to, :item)
|
31
141
|
john = Item.create!(:title => 'john')
|
32
142
|
jim = Item.create!(:title => 'jim')
|
33
|
-
@bob.update_column(:item_id, john)
|
34
|
-
@joe.update_column(:item_id, jim)
|
143
|
+
@bob.update_column(:item_id, john.id)
|
144
|
+
@joe.update_column(:item_id, jim.id)
|
35
145
|
|
36
146
|
spy = Spy.on(Item, :fetch_multi).and_call_through
|
37
147
|
|
@@ -78,7 +188,6 @@ class PrefetchNormalizedAssociationsTest < IdentityCache::TestCase
|
|
78
188
|
end
|
79
189
|
|
80
190
|
def test_fetch_multi_batch_fetches_first_level_associations_who_dont_include_identity_cache
|
81
|
-
Item.send(:has_many, :not_cached_records)
|
82
191
|
Item.send(:cache_has_many, :not_cached_records, :embed => true)
|
83
192
|
|
84
193
|
@bob_child = @bob.not_cached_records.create!(:name => "bob child")
|
@@ -96,7 +205,7 @@ class PrefetchNormalizedAssociationsTest < IdentityCache::TestCase
|
|
96
205
|
Item.send(:cache_has_many, :associated_records, :embed => :ids)
|
97
206
|
AssociatedRecord.send(:cache_has_many, :deeply_associated_records, :embed => :ids)
|
98
207
|
|
99
|
-
|
208
|
+
_child_records, grandchildren = setup_has_many_children_and_grandchildren(@bob, @joe)
|
100
209
|
|
101
210
|
assert_memcache_operations(3) do
|
102
211
|
@cached_bob, @cached_joe = Item.fetch_multi(@bob.id, @joe.id, :includes => {:associated_records => :deeply_associated_records})
|
@@ -151,7 +260,7 @@ class PrefetchNormalizedAssociationsTest < IdentityCache::TestCase
|
|
151
260
|
Item.send(:cache_has_many, :associated_records, :embed => true)
|
152
261
|
AssociatedRecord.send(:cache_has_many, :deeply_associated_records, :embed => :ids)
|
153
262
|
|
154
|
-
|
263
|
+
_child_records, grandchildren = setup_has_many_children_and_grandchildren(@bob, @joe)
|
155
264
|
|
156
265
|
assert_memcache_operations(2) do
|
157
266
|
@cached_bob, @cached_joe = Item.fetch_multi(@bob.id, @joe.id, :includes => {:associated_records => :deeply_associated_records})
|
@@ -200,7 +309,7 @@ class PrefetchNormalizedAssociationsTest < IdentityCache::TestCase
|
|
200
309
|
Item.send(:cache_belongs_to, :item)
|
201
310
|
Item.cache_index :title
|
202
311
|
john = Item.create!(title: 'john')
|
203
|
-
@bob.update_column(:item_id, john)
|
312
|
+
@bob.update_column(:item_id, john.id)
|
204
313
|
|
205
314
|
spy = Spy.on(Item, :fetch_multi).and_call_through
|
206
315
|
|
@@ -213,7 +322,7 @@ class PrefetchNormalizedAssociationsTest < IdentityCache::TestCase
|
|
213
322
|
Item.send(:cache_belongs_to, :item)
|
214
323
|
Item.cache_index :title, :unique => true
|
215
324
|
john = Item.create!(title: 'john')
|
216
|
-
@bob.update_column(:item_id, john)
|
325
|
+
@bob.update_column(:item_id, john.id)
|
217
326
|
|
218
327
|
spy = Spy.on(Item, :fetch_multi).and_call_through
|
219
328
|
|
@@ -61,7 +61,7 @@ class RecursiveDenormalizedHasManyTest < IdentityCache::TestCase
|
|
61
61
|
# one for the mid level has_one association
|
62
62
|
# one for the deep level level has_many on the mid level has_many association
|
63
63
|
# one for the deep level level has_many on the mid level has_one association
|
64
|
-
|
64
|
+
Item.fetch(@record.id)
|
65
65
|
end
|
66
66
|
end
|
67
67
|
|
@@ -88,6 +88,22 @@ class RecursiveDenormalizedHasManyTest < IdentityCache::TestCase
|
|
88
88
|
@deeply_associated_record.save!
|
89
89
|
end
|
90
90
|
|
91
|
+
def test_set_inverse_associations
|
92
|
+
DeeplyAssociatedRecord.cache_belongs_to :associated_record
|
93
|
+
AssociatedRecord.cache_belongs_to :item
|
94
|
+
Item.fetch(@record.id) # warm cache
|
95
|
+
|
96
|
+
item = Item.fetch(@record.id)
|
97
|
+
|
98
|
+
assert_queries(0) do
|
99
|
+
assert_memcache_operations(0) do
|
100
|
+
associated_record = item.fetch_associated_records.to_a.first
|
101
|
+
deeply_associated_record = associated_record.fetch_deeply_associated_records.first
|
102
|
+
assert_equal item.id, deeply_associated_record.fetch_associated_record.fetch_item.id
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
91
107
|
end
|
92
108
|
|
93
109
|
class RecursiveNormalizedHasManyTest < IdentityCache::TestCase
|
@@ -103,7 +119,7 @@ class RecursiveNormalizedHasManyTest < IdentityCache::TestCase
|
|
103
119
|
|
104
120
|
def test_cache_repopulation_should_not_fetch_non_embedded_associations
|
105
121
|
Item.any_instance.expects(:fetch_associated_records).never
|
106
|
-
|
122
|
+
Item.fetch(@record.id) # cache miss
|
107
123
|
end
|
108
124
|
end
|
109
125
|
|
data/test/schema_change_test.rb
CHANGED
@@ -89,10 +89,12 @@ class SchemaChangeTest < IdentityCache::TestCase
|
|
89
89
|
|
90
90
|
record = Item.fetch(@record.id)
|
91
91
|
|
92
|
+
teardown_models
|
93
|
+
setup_models
|
94
|
+
|
92
95
|
Item.cache_has_many :polymorphic_records, :inverse_name => :owner, :embed => true
|
93
96
|
read_new_schema
|
94
97
|
|
95
|
-
Item.expects(:resolve_cache_miss).returns(@record)
|
96
98
|
record = Item.fetch(@record.id)
|
97
99
|
end
|
98
100
|
|
@@ -103,7 +105,6 @@ class SchemaChangeTest < IdentityCache::TestCase
|
|
103
105
|
Item.cache_has_many :polymorphic_records, :inverse_name => :owner, :embed => :ids
|
104
106
|
read_new_schema
|
105
107
|
|
106
|
-
Item.expects(:resolve_cache_miss).returns(@record)
|
107
108
|
record = Item.fetch(@record.id)
|
108
109
|
end
|
109
110
|
end
|
data/test/test_helper.rb
CHANGED
@@ -11,7 +11,7 @@ require 'active_support/cache/memcached_store'
|
|
11
11
|
require File.dirname(__FILE__) + '/../lib/identity_cache'
|
12
12
|
|
13
13
|
DatabaseConnection.setup
|
14
|
-
ActiveSupport::Cache::Store.instrument = true
|
14
|
+
ActiveSupport::Cache::Store.instrument = true if ActiveSupport.version < Gem::Version.new("4.2.0")
|
15
15
|
|
16
16
|
# This patches AR::MemcacheStore to notify AS::Notifications upon read_multis like the rest of rails does
|
17
17
|
class ActiveSupport::Cache::MemcachedStore
|
@@ -38,7 +38,9 @@ class IdentityCache::TestCase < Minitest::Test
|
|
38
38
|
DatabaseConnection.create_tables
|
39
39
|
|
40
40
|
IdentityCache.logger = Logger.new(nil)
|
41
|
-
|
41
|
+
|
42
|
+
memcached_host = ENV['MEMCACHED_HOST'] || "127.0.0.1"
|
43
|
+
IdentityCache.cache_backend = @backend = ActiveSupport::Cache::MemcachedStore.new("#{memcached_host}:11211", :support_cas => true)
|
42
44
|
|
43
45
|
setup_models
|
44
46
|
end
|
metadata
CHANGED
@@ -1,20 +1,20 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: identity_cache
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.3.
|
4
|
+
version: 0.3.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Camilo Lopez
|
8
8
|
- Tom Burns
|
9
9
|
- Harry Brundage
|
10
|
-
- Dylan Smith
|
10
|
+
- Dylan Thacker-Smith
|
11
11
|
- Tobias Lutke
|
12
12
|
- Arthur Neves
|
13
13
|
- Francis Bogsanyi
|
14
14
|
autorequire:
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
|
-
date: 2016-
|
17
|
+
date: 2016-08-10 00:00:00.000000000 Z
|
18
18
|
dependencies:
|
19
19
|
- !ruby/object:Gem::Dependency
|
20
20
|
name: ar_transaction_changes
|
@@ -36,14 +36,14 @@ dependencies:
|
|
36
36
|
requirements:
|
37
37
|
- - ">="
|
38
38
|
- !ruby/object:Gem::Version
|
39
|
-
version: 4.0
|
39
|
+
version: 4.2.0
|
40
40
|
type: :runtime
|
41
41
|
prerelease: false
|
42
42
|
version_requirements: !ruby/object:Gem::Requirement
|
43
43
|
requirements:
|
44
44
|
- - ">="
|
45
45
|
- !ruby/object:Gem::Version
|
46
|
-
version: 4.0
|
46
|
+
version: 4.2.0
|
47
47
|
- !ruby/object:Gem::Dependency
|
48
48
|
name: memcached
|
49
49
|
requirement: !ruby/object:Gem::Requirement
|
@@ -186,23 +186,22 @@ dependencies:
|
|
186
186
|
version: '0'
|
187
187
|
description: Opt in read through ActiveRecord caching.
|
188
188
|
email:
|
189
|
-
-
|
189
|
+
- gems@shopify.com
|
190
190
|
executables: []
|
191
191
|
extensions: []
|
192
192
|
extra_rdoc_files: []
|
193
193
|
files:
|
194
194
|
- ".gitignore"
|
195
|
-
- ".ruby-version"
|
196
195
|
- ".travis.yml"
|
197
196
|
- CHANGELOG.md
|
198
197
|
- CONTRIBUTING.md
|
199
198
|
- Gemfile
|
200
|
-
- Gemfile.rails40
|
201
|
-
- Gemfile.rails41
|
202
199
|
- Gemfile.rails42
|
200
|
+
- Gemfile.rails50
|
203
201
|
- LICENSE
|
204
202
|
- README.md
|
205
203
|
- Rakefile
|
204
|
+
- dev.yml
|
206
205
|
- identity_cache.gemspec
|
207
206
|
- lib/identity_cache.rb
|
208
207
|
- lib/identity_cache/belongs_to_caching.rb
|
@@ -238,11 +237,12 @@ files:
|
|
238
237
|
- test/helpers/update_serialization_format.rb
|
239
238
|
- test/identity_cache_test.rb
|
240
239
|
- test/index_cache_test.rb
|
240
|
+
- test/memoized_attributes_test.rb
|
241
241
|
- test/memoized_cache_proxy_test.rb
|
242
242
|
- test/normalized_belongs_to_test.rb
|
243
243
|
- test/normalized_has_many_test.rb
|
244
244
|
- test/normalized_has_one_test.rb
|
245
|
-
- test/
|
245
|
+
- test/prefetch_associations_test.rb
|
246
246
|
- test/readonly_test.rb
|
247
247
|
- test/recursive_denormalized_has_many_test.rb
|
248
248
|
- test/save_test.rb
|
@@ -268,7 +268,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
268
268
|
version: '0'
|
269
269
|
requirements: []
|
270
270
|
rubyforge_project:
|
271
|
-
rubygems_version: 2.
|
271
|
+
rubygems_version: 2.5.1
|
272
272
|
signing_key:
|
273
273
|
specification_version: 4
|
274
274
|
summary: IdentityCache lets you specify how you want to cache your model objects,
|
@@ -293,11 +293,12 @@ test_files:
|
|
293
293
|
- test/helpers/update_serialization_format.rb
|
294
294
|
- test/identity_cache_test.rb
|
295
295
|
- test/index_cache_test.rb
|
296
|
+
- test/memoized_attributes_test.rb
|
296
297
|
- test/memoized_cache_proxy_test.rb
|
297
298
|
- test/normalized_belongs_to_test.rb
|
298
299
|
- test/normalized_has_many_test.rb
|
299
300
|
- test/normalized_has_one_test.rb
|
300
|
-
- test/
|
301
|
+
- test/prefetch_associations_test.rb
|
301
302
|
- test/readonly_test.rb
|
302
303
|
- test/recursive_denormalized_has_many_test.rb
|
303
304
|
- test/save_test.rb
|
data/.ruby-version
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
2.2.2
|
data/Gemfile.rails40
DELETED