identity_cache 0.0.6 → 0.0.7
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
- data/CHANGELOG +6 -0
- data/Gemfile.rails4 +1 -2
- data/identity_cache.gemspec +2 -1
- data/lib/identity_cache/cache_key_generation.rb +8 -13
- data/lib/identity_cache/configuration_dsl.rb +5 -7
- data/lib/identity_cache/parent_model_expiration.rb +4 -4
- data/lib/identity_cache/query_api.rb +15 -21
- data/lib/identity_cache/version.rb +1 -1
- data/performance/cache_runner.rb +58 -46
- data/performance/cpu.rb +24 -12
- data/performance/profile.rb +12 -17
- data/test/cache_fetch_includes_test.rb +12 -12
- data/test/denormalized_has_one_test.rb +3 -3
- data/test/fetch_multi_test.rb +1 -1
- data/test/fetch_multi_with_batched_associations_test.rb +1 -1
- data/test/helpers/cache.rb +1 -1
- data/test/normalized_has_one_test.rb +9 -0
- data/test/recursive_denormalized_has_many_test.rb +1 -1
- data/test/schema_change_test.rb +10 -0
- data/test/test_helper.rb +3 -1
- metadata +24 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4486319de757bbba037be1e7687cb18a2fd72dc8
|
4
|
+
data.tar.gz: 8869e35fceeca476227e9b8181a573cfe38f1d96
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1a7927f13588a480924f558473f536a94790a165820c4ff606e69456a5374394ae66c1b6eb6e40d39bd76490b0537a1b3bc164855f0174b122cbd1109376580e
|
7
|
+
data.tar.gz: 90f5bec88b0edd0e6c915804533eebb2d5f020dcf99ecc03b262eeaec9313d64f6c30aa9420384d8652014642198083d0bf17a8bd507872705c7838e2df827e8
|
data/CHANGELOG
CHANGED
@@ -1,3 +1,9 @@
|
|
1
|
+
0.0.7
|
2
|
+
* Fix: Not implemented error for cache_has_one with embed: false
|
3
|
+
* Fix: cache key to change when adding a cache_has_many association with :embed => false
|
4
|
+
* Add support for non-integer primary keys
|
5
|
+
* Fix: Compatibility rails 4.1 for quote_value, which needs default column.
|
6
|
+
|
1
7
|
0.0.6
|
2
8
|
* Fix: bug where previously nil-cached attribute caches weren't expired on record creation
|
3
9
|
* Fix: cache key to not change when adding a non-embedded association.
|
data/Gemfile.rails4
CHANGED
data/identity_cache.gemspec
CHANGED
@@ -18,7 +18,7 @@ Gem::Specification.new do |gem|
|
|
18
18
|
gem.add_dependency('ar_transaction_changes', '= 0.0.3')
|
19
19
|
gem.add_dependency('activerecord', '>= 3.2', '< 4.1')
|
20
20
|
|
21
|
-
gem.add_development_dependency('
|
21
|
+
gem.add_development_dependency('memcached_store', '~> 0.11.2')
|
22
22
|
gem.add_development_dependency('rake')
|
23
23
|
gem.add_development_dependency('mocha', '0.14.0')
|
24
24
|
|
@@ -29,5 +29,6 @@ Gem::Specification.new do |gem|
|
|
29
29
|
else
|
30
30
|
gem.add_development_dependency('cityhash', '0.6.0')
|
31
31
|
gem.add_development_dependency('mysql2')
|
32
|
+
gem.add_development_dependency('stackprof') if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new("2.1.0")
|
32
33
|
end
|
33
34
|
end
|
@@ -9,23 +9,18 @@ module IdentityCache
|
|
9
9
|
|
10
10
|
def self.denormalized_schema_hash(klass)
|
11
11
|
schema_string = schema_to_string(klass.columns)
|
12
|
-
if
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
12
|
+
if klass.include?(IdentityCache)
|
13
|
+
klass.send(:all_cached_associations).sort.each do |name, options|
|
14
|
+
if options[:embed]
|
15
|
+
schema_string << ",#{name}:(#{denormalized_schema_hash(options[:association_class])})"
|
16
|
+
elsif options[:cached_ids_name]
|
17
|
+
schema_string << ",#{name}:ids"
|
18
|
+
end
|
19
|
+
end
|
17
20
|
end
|
18
21
|
IdentityCache.memcache_hash(schema_string)
|
19
22
|
end
|
20
23
|
|
21
|
-
def self.embedded_associations(klass)
|
22
|
-
if klass.respond_to?(:all_embedded_associations)
|
23
|
-
klass.all_embedded_associations
|
24
|
-
else
|
25
|
-
{}
|
26
|
-
end
|
27
|
-
end
|
28
|
-
|
29
24
|
module ClassMethods
|
30
25
|
def rails_cache_key(id)
|
31
26
|
"#{rails_cache_key_prefix}#{id}"
|
@@ -14,10 +14,6 @@ module IdentityCache
|
|
14
14
|
base.cache_attributes = []
|
15
15
|
base.cache_indexes = []
|
16
16
|
base.primary_cache_index_enabled = true
|
17
|
-
|
18
|
-
base.private_class_method :build_normalized_has_many_cache, :build_denormalized_association_cache,
|
19
|
-
:add_parent_expiry_hook, :identity_cache_multiple_value_dynamic_fetcher,
|
20
|
-
:identity_cache_single_value_dynamic_fetcher, :identity_cache_sql_conditions
|
21
17
|
end
|
22
18
|
|
23
19
|
module ClassMethods
|
@@ -134,7 +130,7 @@ module IdentityCache
|
|
134
130
|
# necessary if the name is not the lowercase pluralization of the
|
135
131
|
# parent object's class)
|
136
132
|
def cache_has_one(association, options = {})
|
137
|
-
options[:embed]
|
133
|
+
options[:embed] = true unless options.has_key?(:embed)
|
138
134
|
options[:inverse_name] ||= self.name.underscore.to_sym
|
139
135
|
raise InverseAssociationError unless self.reflect_on_association(association)
|
140
136
|
self.cached_has_ones[association] = options
|
@@ -182,12 +178,14 @@ module IdentityCache
|
|
182
178
|
self.primary_cache_index_enabled = false
|
183
179
|
end
|
184
180
|
|
181
|
+
private
|
182
|
+
|
185
183
|
def identity_cache_single_value_dynamic_fetcher(fields, values) # :nodoc:
|
186
184
|
sql_on_miss = "SELECT #{quoted_primary_key} FROM #{quoted_table_name} WHERE #{identity_cache_sql_conditions(fields, values)} LIMIT 1"
|
187
185
|
cache_key = rails_cache_index_key_for_fields_and_values(fields, values)
|
188
186
|
id = IdentityCache.fetch(cache_key) { connection.select_value(sql_on_miss) }
|
189
187
|
unless id.nil?
|
190
|
-
record = fetch_by_id(id
|
188
|
+
record = fetch_by_id(id)
|
191
189
|
IdentityCache.cache.delete(cache_key) unless record
|
192
190
|
end
|
193
191
|
|
@@ -292,7 +290,7 @@ module IdentityCache
|
|
292
290
|
end
|
293
291
|
|
294
292
|
def identity_cache_sql_conditions(fields, values)
|
295
|
-
fields.each_with_index.collect { |f, i| "#{connection.quote_column_name(f)} = #{quote_value(values[i])}" }.join(" AND ")
|
293
|
+
fields.each_with_index.collect { |f, i| "#{connection.quote_column_name(f)} = #{quote_value(values[i],nil)}" }.join(" AND ")
|
296
294
|
end
|
297
295
|
end
|
298
296
|
end
|
@@ -5,16 +5,16 @@ module IdentityCache
|
|
5
5
|
|
6
6
|
if new_parent && new_parent.respond_to?(:expire_primary_index, true)
|
7
7
|
if should_expire_identity_cache_parent?(foreign_key, only_on_foreign_key_change)
|
8
|
-
new_parent.expire_primary_index
|
9
|
-
new_parent.expire_parent_cache if new_parent.respond_to?(:expire_parent_cache)
|
8
|
+
new_parent.send(:expire_primary_index)
|
9
|
+
new_parent.send(:expire_parent_cache) if new_parent.respond_to?(:expire_parent_cache, true)
|
10
10
|
end
|
11
11
|
end
|
12
12
|
|
13
13
|
if transaction_changed_attributes[foreign_key].present?
|
14
14
|
begin
|
15
15
|
old_parent = parent_class.find(transaction_changed_attributes[foreign_key])
|
16
|
-
old_parent.expire_primary_index if old_parent.respond_to?(:expire_primary_index)
|
17
|
-
old_parent.expire_parent_cache if old_parent.respond_to?(:expire_parent_cache)
|
16
|
+
old_parent.send(:expire_primary_index) if old_parent.respond_to?(:expire_primary_index, true)
|
17
|
+
old_parent.send(:expire_parent_cache) if old_parent.respond_to?(:expire_parent_cache, true)
|
18
18
|
rescue ActiveRecord::RecordNotFound => e
|
19
19
|
# suppress errors finding the old parent if its been destroyed since it will have expired itself in that case
|
20
20
|
end
|
@@ -3,16 +3,6 @@ module IdentityCache
|
|
3
3
|
extend ActiveSupport::Concern
|
4
4
|
|
5
5
|
included do |base|
|
6
|
-
base.private_class_method :require_if_necessary
|
7
|
-
base.private_class_method :coder_from_record
|
8
|
-
base.private_class_method :record_from_coder
|
9
|
-
base.private_class_method :set_embedded_association
|
10
|
-
base.private_class_method :get_embedded_association
|
11
|
-
base.private_class_method :add_cached_associations_to_coder
|
12
|
-
base.instance_eval(<<-CODE, __FILE__, __LINE__ + 1)
|
13
|
-
private :expire_cache, :was_new_record?, :fetch_denormalized_cached_association,
|
14
|
-
:populate_denormalized_cached_association
|
15
|
-
CODE
|
16
6
|
base.after_commit :expire_cache
|
17
7
|
base.after_touch :expire_cache
|
18
8
|
end
|
@@ -66,7 +56,7 @@ module IdentityCache
|
|
66
56
|
coders_by_key = IdentityCache.fetch_multi(*cache_keys) do |unresolved_keys|
|
67
57
|
ids = unresolved_keys.map {|key| key_to_id_map[key] }
|
68
58
|
records = find_batch(ids, options)
|
69
|
-
records.compact.each(
|
59
|
+
records.compact.each{ |record| record.send(:populate_association_caches) }
|
70
60
|
records.map {|record| coder_from_record(record) }
|
71
61
|
end
|
72
62
|
|
@@ -81,6 +71,8 @@ module IdentityCache
|
|
81
71
|
end
|
82
72
|
end
|
83
73
|
|
74
|
+
private
|
75
|
+
|
84
76
|
def record_from_coder(coder) #:nodoc:
|
85
77
|
if coder.present? && coder.has_key?(:class)
|
86
78
|
record = coder[:class].allocate
|
@@ -122,7 +114,7 @@ module IdentityCache
|
|
122
114
|
else
|
123
115
|
record_from_coder(coder_or_array)
|
124
116
|
end
|
125
|
-
variable_name = record.class.all_embedded_associations[association_name][:records_variable_name]
|
117
|
+
variable_name = record.class.send(:all_embedded_associations)[association_name][:records_variable_name]
|
126
118
|
record.instance_variable_set(:"@#{variable_name}", IdentityCache.map_cached_nil_for(value))
|
127
119
|
end
|
128
120
|
|
@@ -147,8 +139,8 @@ module IdentityCache
|
|
147
139
|
end
|
148
140
|
|
149
141
|
def add_cached_associations_to_coder(record, coder)
|
150
|
-
if record.class.respond_to?(:all_embedded_associations) && record.class.all_embedded_associations.present?
|
151
|
-
coder[:associations] = record.class.all_embedded_associations.each_with_object({}) do |(name, options), hash|
|
142
|
+
if record.class.respond_to?(:all_embedded_associations, true) && record.class.send(:all_embedded_associations).present?
|
143
|
+
coder[:associations] = record.class.send(:all_embedded_associations).each_with_object({}) do |(name, options), hash|
|
152
144
|
hash[name] = IdentityCache.map_cached_nil_for(get_embedded_association(record, name, options))
|
153
145
|
end
|
154
146
|
end
|
@@ -179,7 +171,7 @@ module IdentityCache
|
|
179
171
|
|
180
172
|
def resolve_cache_miss(id)
|
181
173
|
object = self.includes(cache_fetch_includes).where(id: id).try(:first)
|
182
|
-
object.
|
174
|
+
object.send(:populate_association_caches) if object
|
183
175
|
object
|
184
176
|
end
|
185
177
|
|
@@ -208,8 +200,8 @@ module IdentityCache
|
|
208
200
|
|
209
201
|
child_includes = additions.delete(child_association)
|
210
202
|
|
211
|
-
if child_class.respond_to?(:cache_fetch_includes)
|
212
|
-
child_includes = child_class.cache_fetch_includes
|
203
|
+
if child_class.respond_to?(:cache_fetch_includes, true)
|
204
|
+
child_includes = child_class.send(:cache_fetch_includes, child_includes)
|
213
205
|
end
|
214
206
|
|
215
207
|
if child_includes.blank?
|
@@ -295,8 +287,8 @@ module IdentityCache
|
|
295
287
|
raise ArgumentError.new("Unknown cached association #{association} listed for prefetching")
|
296
288
|
end
|
297
289
|
|
298
|
-
if details && details[:association_class].respond_to?(:prefetch_associations)
|
299
|
-
details[:association_class].prefetch_associations
|
290
|
+
if details && details[:association_class].respond_to?(:prefetch_associations, true)
|
291
|
+
details[:association_class].send(:prefetch_associations, sub_associations, next_level_records)
|
300
292
|
end
|
301
293
|
end
|
302
294
|
end
|
@@ -322,13 +314,15 @@ module IdentityCache
|
|
322
314
|
end
|
323
315
|
end
|
324
316
|
|
317
|
+
private
|
318
|
+
|
325
319
|
def populate_association_caches # :nodoc:
|
326
|
-
self.class.all_cached_associations_needing_population.each do |cached_association, options|
|
320
|
+
self.class.send(:all_cached_associations_needing_population).each do |cached_association, options|
|
327
321
|
send(options[:population_method_name])
|
328
322
|
reflection = options[:embed] && self.class.reflect_on_association(cached_association)
|
329
323
|
if reflection && reflection.klass.respond_to?(:cached_has_manys)
|
330
324
|
child_objects = Array.wrap(send(options[:cached_accessor_name]))
|
331
|
-
child_objects.each(
|
325
|
+
child_objects.each{ |child| child.send(:populate_association_caches) }
|
332
326
|
end
|
333
327
|
end
|
334
328
|
end
|
data/performance/cache_runner.rb
CHANGED
@@ -3,7 +3,8 @@ require 'active_record'
|
|
3
3
|
require 'active_support/core_ext'
|
4
4
|
require 'active_support/cache'
|
5
5
|
require 'identity_cache'
|
6
|
-
require '
|
6
|
+
require 'memcached_store'
|
7
|
+
require 'active_support/cache/memcached_store'
|
7
8
|
|
8
9
|
if ENV['BOXEN_HOME'].present?
|
9
10
|
$memcached_port = 21211
|
@@ -29,17 +30,9 @@ end
|
|
29
30
|
|
30
31
|
def create_database(count)
|
31
32
|
DatabaseConnection.setup
|
32
|
-
a = CacheRunner.new(count)
|
33
33
|
|
34
|
-
|
35
|
-
|
36
|
-
DatabaseConnection.setup
|
37
|
-
# set up associations
|
38
|
-
Item.cache_has_one :associated
|
39
|
-
Item.cache_has_many :associated_records, :embed => true
|
40
|
-
Item.cache_has_many :normalized_associated_records, :embed => false
|
41
|
-
Item.cache_index :title, :unique => :true
|
42
|
-
AssociatedRecord.cache_has_many :deeply_associated_records, :embed => true
|
34
|
+
helper = Object.new.extend(ActiveRecordObjects)
|
35
|
+
helper.setup_models
|
43
36
|
|
44
37
|
return if database_ready(count)
|
45
38
|
puts "Database not ready for performance testing, generating records"
|
@@ -60,6 +53,14 @@ def create_database(count)
|
|
60
53
|
a.save
|
61
54
|
end
|
62
55
|
end
|
56
|
+
ensure
|
57
|
+
helper.teardown_models
|
58
|
+
end
|
59
|
+
|
60
|
+
def setup_embedded_associations
|
61
|
+
Item.cache_has_one :associated
|
62
|
+
Item.cache_has_many :associated_records, :embed => true
|
63
|
+
AssociatedRecord.cache_has_many :deeply_associated_records, :embed => true
|
63
64
|
end
|
64
65
|
|
65
66
|
class CacheRunner
|
@@ -71,81 +72,92 @@ class CacheRunner
|
|
71
72
|
end
|
72
73
|
|
73
74
|
def prepare
|
75
|
+
setup_models
|
76
|
+
end
|
77
|
+
|
78
|
+
def cleanup
|
79
|
+
teardown_models
|
74
80
|
end
|
75
81
|
end
|
76
82
|
|
83
|
+
CACHE_RUNNERS = []
|
84
|
+
|
77
85
|
class FindRunner < CacheRunner
|
78
86
|
def run
|
79
87
|
(1..@count).each do |i|
|
80
|
-
::Item.find(i)
|
88
|
+
::Item.find(i, :include => [:associated, {:associated_records => :deeply_associated_records}])
|
81
89
|
end
|
82
90
|
end
|
83
91
|
end
|
92
|
+
CACHE_RUNNERS << FindRunner
|
84
93
|
|
85
94
|
module MissRunner
|
86
95
|
def prepare
|
96
|
+
super
|
87
97
|
IdentityCache.cache.clear
|
88
98
|
end
|
89
99
|
end
|
90
100
|
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
(1..@count).each do |i|
|
96
|
-
rec = ::Item.fetch(i)
|
97
|
-
rec.fetch_associated
|
98
|
-
rec.fetch_associated_records
|
99
|
-
end
|
101
|
+
module HitRunner
|
102
|
+
def prepare
|
103
|
+
super
|
104
|
+
run
|
100
105
|
end
|
101
106
|
end
|
102
107
|
|
103
|
-
class
|
104
|
-
|
108
|
+
class EmbedRunner < CacheRunner
|
109
|
+
def setup_models
|
110
|
+
super
|
111
|
+
Item.cache_has_one :associated
|
112
|
+
Item.cache_has_many :associated_records, :embed => true
|
113
|
+
AssociatedRecord.cache_has_many :deeply_associated_records, :embed => true
|
114
|
+
end
|
105
115
|
|
106
116
|
def run
|
107
117
|
(1..@count).each do |i|
|
108
118
|
rec = ::Item.fetch(i)
|
109
119
|
rec.fetch_associated
|
110
120
|
rec.fetch_associated_records
|
111
|
-
rec.fetch_normalized_associated_records
|
112
121
|
end
|
113
122
|
end
|
114
123
|
end
|
115
124
|
|
116
|
-
|
117
|
-
|
118
|
-
IdentityCache.cache.clear
|
119
|
-
(1..@count).each do |i|
|
120
|
-
rec = ::Item.fetch(i)
|
121
|
-
rec.fetch_normalized_associated_records
|
122
|
-
end
|
123
|
-
end
|
125
|
+
class FetchEmbedMissRunner < EmbedRunner
|
126
|
+
include MissRunner
|
124
127
|
end
|
128
|
+
CACHE_RUNNERS << FetchEmbedMissRunner
|
125
129
|
|
126
|
-
class
|
130
|
+
class FetchEmbedHitRunner < EmbedRunner
|
127
131
|
include HitRunner
|
132
|
+
end
|
133
|
+
CACHE_RUNNERS << FetchEmbedHitRunner
|
134
|
+
|
135
|
+
|
136
|
+
class NormalizedRunner < CacheRunner
|
137
|
+
def setup_models
|
138
|
+
super
|
139
|
+
Item.cache_has_one :associated # :embed => false isn't supported
|
140
|
+
Item.cache_has_many :associated_records, :embed => false
|
141
|
+
AssociatedRecord.cache_has_many :deeply_associated_records, :embed => false
|
142
|
+
end
|
128
143
|
|
129
144
|
def run
|
130
145
|
(1..@count).each do |i|
|
131
146
|
rec = ::Item.fetch(i)
|
132
|
-
# these should all be no cost
|
133
147
|
rec.fetch_associated
|
134
|
-
rec.fetch_associated_records
|
148
|
+
associated_records = rec.fetch_associated_records
|
149
|
+
# FIXME: Only fetch_multi has :includes support, so use what it uses internally
|
150
|
+
AssociatedRecord.send(:prefetch_associations, :deeply_associated_records, associated_records)
|
135
151
|
end
|
136
152
|
end
|
137
153
|
end
|
138
154
|
|
139
|
-
class
|
140
|
-
include
|
155
|
+
class FetchNormalizedMissRunner < NormalizedRunner
|
156
|
+
include MissRunner
|
157
|
+
end
|
158
|
+
CACHE_RUNNERS << FetchNormalizedMissRunner
|
141
159
|
|
142
|
-
|
143
|
-
|
144
|
-
rec = ::Item.fetch(i)
|
145
|
-
# these should all be no cost
|
146
|
-
rec.fetch_associated
|
147
|
-
rec.fetch_associated_records
|
148
|
-
rec.fetch_normalized_associated_records
|
149
|
-
end
|
150
|
-
end
|
160
|
+
class FetchNormalizedHitRunner < NormalizedRunner
|
161
|
+
include HitRunner
|
151
162
|
end
|
163
|
+
CACHE_RUNNERS << FetchNormalizedHitRunner
|
data/performance/cpu.rb
CHANGED
@@ -3,29 +3,41 @@ require 'benchmark'
|
|
3
3
|
|
4
4
|
require_relative 'cache_runner'
|
5
5
|
|
6
|
-
RUNS =
|
6
|
+
RUNS = 400
|
7
7
|
|
8
8
|
class ARCreator
|
9
9
|
include ActiveRecordObjects
|
10
10
|
end
|
11
11
|
|
12
|
-
def run(obj
|
13
|
-
|
14
|
-
|
12
|
+
def run(obj)
|
13
|
+
obj.prepare
|
14
|
+
GC.start
|
15
|
+
Benchmark.measure do
|
15
16
|
obj.run
|
16
17
|
end
|
18
|
+
ensure
|
19
|
+
obj.cleanup
|
17
20
|
end
|
18
21
|
|
19
|
-
|
22
|
+
def benchmark(runners, label_width=0)
|
23
|
+
IdentityCache.cache.clear
|
24
|
+
runners.each do |runner|
|
25
|
+
print "#{runner.name}: ".ljust(label_width)
|
26
|
+
puts run(runner.new(RUNS))
|
27
|
+
end
|
28
|
+
end
|
20
29
|
|
21
|
-
|
22
|
-
|
30
|
+
def bmbm(runners)
|
31
|
+
label_width = runners.map{ |r| r.name.size }.max + 2
|
32
|
+
width = label_width + Benchmark::CAPTION.size
|
23
33
|
|
24
|
-
|
34
|
+
puts 'Rehearsal: '.ljust(width, '-')
|
35
|
+
benchmark(runners, label_width)
|
36
|
+
puts '-' * width
|
25
37
|
|
26
|
-
|
38
|
+
benchmark(runners, label_width)
|
39
|
+
end
|
27
40
|
|
28
|
-
|
41
|
+
create_database(RUNS)
|
29
42
|
|
30
|
-
|
31
|
-
end
|
43
|
+
bmbm(CACHE_RUNNERS)
|
data/performance/profile.rb
CHANGED
@@ -1,30 +1,25 @@
|
|
1
1
|
require 'rubygems'
|
2
2
|
require 'benchmark'
|
3
|
-
require '
|
3
|
+
require 'stackprof'
|
4
4
|
|
5
5
|
require_relative 'cache_runner'
|
6
6
|
|
7
7
|
RUNS = 1000
|
8
|
-
RubyProf.measure_mode = RubyProf::CPU_TIME
|
9
8
|
|
10
9
|
def run(obj)
|
10
|
+
puts "#{obj.class.name}:"
|
11
11
|
obj.prepare
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
12
|
+
data = StackProf.run(mode: :cpu) do
|
13
|
+
obj.run
|
14
|
+
end
|
15
|
+
StackProf::Report.new(data).print_text(false, 20)
|
16
|
+
puts
|
17
|
+
ensure
|
18
|
+
obj.cleanup
|
18
19
|
end
|
19
20
|
|
20
21
|
create_database(RUNS)
|
21
22
|
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
run(FetchHitRunner.new(RUNS))
|
27
|
-
|
28
|
-
run(DoubleFetchHitRunner.new(RUNS))
|
29
|
-
|
30
|
-
run(DoubleFetchMissRunner.new(RUNS))
|
23
|
+
CACHE_RUNNERS.each do |runner|
|
24
|
+
run(runner.new(RUNS))
|
25
|
+
end
|
@@ -7,28 +7,28 @@ class CacheFetchIncludesTest < IdentityCache::TestCase
|
|
7
7
|
|
8
8
|
def test_cached_embedded_has_manys_are_included_in_includes
|
9
9
|
Item.send(:cache_has_many, :associated_records, :embed => true)
|
10
|
-
assert_equal [:associated_records], Item.cache_fetch_includes
|
10
|
+
assert_equal [:associated_records], Item.send(:cache_fetch_includes)
|
11
11
|
end
|
12
12
|
|
13
13
|
def test_cached_nonembedded_has_manys_are_included_in_includes
|
14
14
|
Item.send(:cache_has_many, :associated_records, :embed => false)
|
15
|
-
assert_equal [], Item.cache_fetch_includes
|
15
|
+
assert_equal [], Item.send(:cache_fetch_includes)
|
16
16
|
end
|
17
17
|
|
18
18
|
def test_cached_has_ones_are_included_in_includes
|
19
19
|
Item.send(:cache_has_one, :associated)
|
20
|
-
assert_equal [:associated], Item.cache_fetch_includes
|
20
|
+
assert_equal [:associated], Item.send(:cache_fetch_includes)
|
21
21
|
end
|
22
22
|
|
23
23
|
def test_cached_nonembedded_belongs_tos_are_not_included_in_includes
|
24
24
|
Item.send(:cache_belongs_to, :item)
|
25
|
-
assert_equal [], Item.cache_fetch_includes
|
25
|
+
assert_equal [], Item.send(:cache_fetch_includes)
|
26
26
|
end
|
27
27
|
|
28
28
|
def test_cached_child_associations_are_included_in_includes
|
29
29
|
Item.send(:cache_has_many, :associated_records, :embed => true)
|
30
30
|
AssociatedRecord.send(:cache_has_many, :deeply_associated_records, :embed => true)
|
31
|
-
assert_equal [{:associated_records => [:deeply_associated_records]}], Item.cache_fetch_includes
|
31
|
+
assert_equal [{:associated_records => [:deeply_associated_records]}], Item.send(:cache_fetch_includes)
|
32
32
|
end
|
33
33
|
|
34
34
|
def test_multiple_cached_associations_and_child_associations_are_included_in_includes
|
@@ -40,16 +40,16 @@ class CacheFetchIncludesTest < IdentityCache::TestCase
|
|
40
40
|
{:associated_records => [:deeply_associated_records]},
|
41
41
|
:polymorphic_records,
|
42
42
|
{:associated => [:deeply_associated_records]}
|
43
|
-
], Item.cache_fetch_includes
|
43
|
+
], Item.send(:cache_fetch_includes)
|
44
44
|
end
|
45
45
|
|
46
46
|
def test_empty_additions_for_top_level_associations_makes_no_difference
|
47
47
|
Item.send(:cache_has_many, :associated_records, :embed => true)
|
48
|
-
assert_equal [:associated_records], Item.cache_fetch_includes
|
48
|
+
assert_equal [:associated_records], Item.send(:cache_fetch_includes, {})
|
49
49
|
end
|
50
50
|
|
51
51
|
def test_top_level_additions_are_included_in_includes
|
52
|
-
assert_equal [{:associated_records => []}], Item.cache_fetch_includes
|
52
|
+
assert_equal [{:associated_records => []}], Item.send(:cache_fetch_includes, {:associated_records => []})
|
53
53
|
end
|
54
54
|
|
55
55
|
def test_top_level_additions_alongside_top_level_cached_associations_are_included_in_includes
|
@@ -57,21 +57,21 @@ class CacheFetchIncludesTest < IdentityCache::TestCase
|
|
57
57
|
assert_equal [
|
58
58
|
:associated_records,
|
59
59
|
{:polymorphic_records => []}
|
60
|
-
], Item.cache_fetch_includes
|
60
|
+
], Item.send(:cache_fetch_includes, {:polymorphic_records => []})
|
61
61
|
end
|
62
62
|
|
63
63
|
def test_child_level_additions_for_top_level_cached_associations_are_included_in_includes
|
64
64
|
Item.send(:cache_has_many, :associated_records, :embed => true)
|
65
65
|
assert_equal [
|
66
66
|
{:associated_records => [{:deeply_associated_records => []}]}
|
67
|
-
], Item.cache_fetch_includes
|
67
|
+
], Item.send(:cache_fetch_includes, {:associated_records => :deeply_associated_records})
|
68
68
|
end
|
69
69
|
|
70
70
|
def test_array_child_level_additions_for_top_level_cached_associations_are_included_in_includes
|
71
71
|
Item.send(:cache_has_many, :associated_records, :embed => true)
|
72
72
|
assert_equal [
|
73
73
|
{:associated_records => [{:deeply_associated_records => []}]}
|
74
|
-
], Item.cache_fetch_includes
|
74
|
+
], Item.send(:cache_fetch_includes, {:associated_records => [:deeply_associated_records]})
|
75
75
|
end
|
76
76
|
|
77
77
|
def test_array_child_level_additions_for_child_level_cached_associations_are_included_in_includes
|
@@ -82,7 +82,7 @@ class CacheFetchIncludesTest < IdentityCache::TestCase
|
|
82
82
|
:deeply_associated_records,
|
83
83
|
{:record => []}
|
84
84
|
]}
|
85
|
-
], Item.cache_fetch_includes
|
85
|
+
], Item.send(:cache_fetch_includes, {:associated_records => [:record]})
|
86
86
|
end
|
87
87
|
|
88
88
|
end
|
@@ -28,7 +28,7 @@ class DenormalizedHasOneTest < IdentityCache::TestCase
|
|
28
28
|
@record.associated = nil
|
29
29
|
@record.save!
|
30
30
|
@record.reload
|
31
|
-
@record.populate_association_caches
|
31
|
+
@record.send(:populate_association_caches)
|
32
32
|
Item.expects(:resolve_cache_miss).with(@record.id).once.returns(@record)
|
33
33
|
|
34
34
|
IdentityCache.cache.expects(:read).with(@record.secondary_cache_index_key_for_current_values([:title]))
|
@@ -51,7 +51,7 @@ class DenormalizedHasOneTest < IdentityCache::TestCase
|
|
51
51
|
end
|
52
52
|
|
53
53
|
def test_on_cache_hit_record_should_come_back_with_cached_association
|
54
|
-
@record.populate_association_caches
|
54
|
+
@record.send(:populate_association_caches)
|
55
55
|
Item.expects(:resolve_cache_miss).with(1).once.returns(@record)
|
56
56
|
Item.fetch_by_title('foo')
|
57
57
|
|
@@ -67,7 +67,7 @@ class DenormalizedHasOneTest < IdentityCache::TestCase
|
|
67
67
|
@record.save!
|
68
68
|
@record.reload
|
69
69
|
|
70
|
-
@record.populate_association_caches
|
70
|
+
@record.send(:populate_association_caches)
|
71
71
|
Item.expects(:resolve_cache_miss).with(1).once.returns(@record)
|
72
72
|
Item.fetch_by_title('foo')
|
73
73
|
|
data/test/fetch_multi_test.rb
CHANGED
@@ -125,7 +125,7 @@ class FetchMultiTest < IdentityCache::TestCase
|
|
125
125
|
Item.expects(:where).returns(mock_relation)
|
126
126
|
mock_relation.expects(:includes).returns(stub(:to_a => [@bob, @joe, @fred]))
|
127
127
|
|
128
|
-
Item.find_batch
|
128
|
+
Item.send(:find_batch, [@bob, @joe, @fred].map(&:id).map(&:to_s))
|
129
129
|
end
|
130
130
|
|
131
131
|
def test_fetch_multi_doesnt_freeze_keys
|
@@ -203,7 +203,7 @@ class FetchMultiWithBatchedAssociationsTest < IdentityCache::TestCase
|
|
203
203
|
Item.expects(:where).returns(mock_relation)
|
204
204
|
mock_relation.expects(:includes).returns(stub(:to_a => [@bob, @joe, @fred]))
|
205
205
|
|
206
|
-
Item.find_batch
|
206
|
+
Item.send(:find_batch, [@bob, @joe, @fred].map(&:id).map(&:to_s))
|
207
207
|
end
|
208
208
|
|
209
209
|
private
|
data/test/helpers/cache.rb
CHANGED
@@ -23,7 +23,7 @@ class RecursiveDenormalizedHasManyTest < IdentityCache::TestCase
|
|
23
23
|
end
|
24
24
|
|
25
25
|
def test_cache_fetch_includes
|
26
|
-
assert_equal [{:associated_records => [:deeply_associated_records]}, :associated => [:deeply_associated_records]], Item.cache_fetch_includes
|
26
|
+
assert_equal [{:associated_records => [:deeply_associated_records]}, :associated => [:deeply_associated_records]], Item.send(:cache_fetch_includes)
|
27
27
|
end
|
28
28
|
|
29
29
|
def test_uncached_record_from_the_db_will_use_normal_association_for_deeply_associated_records
|
data/test/schema_change_test.rb
CHANGED
@@ -94,4 +94,14 @@ class SchemaChangeTest < IdentityCache::TestCase
|
|
94
94
|
Item.expects(:resolve_cache_miss).returns(@record)
|
95
95
|
record = Item.fetch(@record.id)
|
96
96
|
end
|
97
|
+
|
98
|
+
def test_add_non_embedded_cache_has_many
|
99
|
+
record = Item.fetch(@record.id)
|
100
|
+
|
101
|
+
Item.cache_has_many :polymorphic_records, :inverse_name => :owner, :embed => false
|
102
|
+
read_new_schema
|
103
|
+
|
104
|
+
Item.expects(:resolve_cache_miss).returns(@record)
|
105
|
+
record = Item.fetch(@record.id)
|
106
|
+
end
|
97
107
|
end
|
data/test/test_helper.rb
CHANGED
@@ -1,6 +1,8 @@
|
|
1
1
|
require 'minitest/autorun'
|
2
2
|
require 'mocha/setup'
|
3
3
|
require 'active_record'
|
4
|
+
require 'memcached_store'
|
5
|
+
require 'active_support/cache/memcached_store'
|
4
6
|
require 'helpers/cache'
|
5
7
|
require 'helpers/database_connection'
|
6
8
|
require 'helpers/active_record_objects'
|
@@ -19,7 +21,7 @@ DatabaseConnection.setup
|
|
19
21
|
ActiveSupport::Cache::Store.instrument = true
|
20
22
|
|
21
23
|
# This patches AR::MemcacheStore to notify AS::Notifications upon read_multis like the rest of rails does
|
22
|
-
class ActiveSupport::Cache::
|
24
|
+
class ActiveSupport::Cache::MemcachedStore
|
23
25
|
def read_multi_with_instrumentation(*args, &block)
|
24
26
|
instrument("read_multi", "MULTI", {:keys => args}) do
|
25
27
|
read_multi_without_instrumentation(*args, &block)
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: identity_cache
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.7
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Camilo Lopez
|
@@ -13,7 +13,7 @@ authors:
|
|
13
13
|
autorequire:
|
14
14
|
bindir: bin
|
15
15
|
cert_chain: []
|
16
|
-
date: 2014-
|
16
|
+
date: 2014-03-05 00:00:00.000000000 Z
|
17
17
|
dependencies:
|
18
18
|
- !ruby/object:Gem::Dependency
|
19
19
|
name: ar_transaction_changes
|
@@ -50,19 +50,19 @@ dependencies:
|
|
50
50
|
- !ruby/object:Gem::Version
|
51
51
|
version: '4.1'
|
52
52
|
- !ruby/object:Gem::Dependency
|
53
|
-
name:
|
53
|
+
name: memcached_store
|
54
54
|
requirement: !ruby/object:Gem::Requirement
|
55
55
|
requirements:
|
56
|
-
- - "
|
56
|
+
- - "~>"
|
57
57
|
- !ruby/object:Gem::Version
|
58
|
-
version:
|
58
|
+
version: 0.11.2
|
59
59
|
type: :development
|
60
60
|
prerelease: false
|
61
61
|
version_requirements: !ruby/object:Gem::Requirement
|
62
62
|
requirements:
|
63
|
-
- - "
|
63
|
+
- - "~>"
|
64
64
|
- !ruby/object:Gem::Version
|
65
|
-
version:
|
65
|
+
version: 0.11.2
|
66
66
|
- !ruby/object:Gem::Dependency
|
67
67
|
name: rake
|
68
68
|
requirement: !ruby/object:Gem::Requirement
|
@@ -119,6 +119,20 @@ dependencies:
|
|
119
119
|
- - ">="
|
120
120
|
- !ruby/object:Gem::Version
|
121
121
|
version: '0'
|
122
|
+
- !ruby/object:Gem::Dependency
|
123
|
+
name: stackprof
|
124
|
+
requirement: !ruby/object:Gem::Requirement
|
125
|
+
requirements:
|
126
|
+
- - ">="
|
127
|
+
- !ruby/object:Gem::Version
|
128
|
+
version: '0'
|
129
|
+
type: :development
|
130
|
+
prerelease: false
|
131
|
+
version_requirements: !ruby/object:Gem::Requirement
|
132
|
+
requirements:
|
133
|
+
- - ">="
|
134
|
+
- !ruby/object:Gem::Version
|
135
|
+
version: '0'
|
122
136
|
description: Opt in read through ActiveRecord caching.
|
123
137
|
email:
|
124
138
|
- harry.brundage@shopify.com
|
@@ -168,6 +182,7 @@ files:
|
|
168
182
|
- test/memoized_cache_proxy_test.rb
|
169
183
|
- test/normalized_belongs_to_test.rb
|
170
184
|
- test/normalized_has_many_test.rb
|
185
|
+
- test/normalized_has_one_test.rb
|
171
186
|
- test/recursive_denormalized_has_many_test.rb
|
172
187
|
- test/save_test.rb
|
173
188
|
- test/schema_change_test.rb
|
@@ -219,8 +234,10 @@ test_files:
|
|
219
234
|
- test/memoized_cache_proxy_test.rb
|
220
235
|
- test/normalized_belongs_to_test.rb
|
221
236
|
- test/normalized_has_many_test.rb
|
237
|
+
- test/normalized_has_one_test.rb
|
222
238
|
- test/recursive_denormalized_has_many_test.rb
|
223
239
|
- test/save_test.rb
|
224
240
|
- test/schema_change_test.rb
|
225
241
|
- test/serialization_format_change_test.rb
|
226
242
|
- test/test_helper.rb
|
243
|
+
has_rdoc:
|