identity_cache 0.5.1 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/.github/probots.yml +2 -0
- data/.github/workflows/ci.yml +26 -0
- data/.gitignore +1 -0
- data/.rubocop.yml +5 -0
- data/.travis.yml +24 -9
- data/CHANGELOG.md +21 -0
- data/Gemfile +5 -1
- data/README.md +28 -26
- data/Rakefile +14 -5
- data/dev.yml +9 -16
- data/gemfiles/Gemfile.latest-release +6 -0
- data/gemfiles/Gemfile.rails-edge +6 -0
- data/gemfiles/Gemfile.rails52 +6 -0
- data/identity_cache.gemspec +26 -10
- data/lib/identity_cache.rb +49 -46
- data/lib/identity_cache/belongs_to_caching.rb +12 -40
- data/lib/identity_cache/cache_fetcher.rb +6 -5
- data/lib/identity_cache/cache_hash.rb +2 -2
- data/lib/identity_cache/cache_invalidation.rb +4 -11
- data/lib/identity_cache/cache_key_generation.rb +17 -65
- data/lib/identity_cache/cache_key_loader.rb +128 -0
- data/lib/identity_cache/cached.rb +7 -0
- data/lib/identity_cache/cached/association.rb +87 -0
- data/lib/identity_cache/cached/attribute.rb +123 -0
- data/lib/identity_cache/cached/attribute_by_multi.rb +37 -0
- data/lib/identity_cache/cached/attribute_by_one.rb +88 -0
- data/lib/identity_cache/cached/belongs_to.rb +93 -0
- data/lib/identity_cache/cached/embedded_fetching.rb +41 -0
- data/lib/identity_cache/cached/prefetcher.rb +51 -0
- data/lib/identity_cache/cached/primary_index.rb +97 -0
- data/lib/identity_cache/cached/recursive/association.rb +68 -0
- data/lib/identity_cache/cached/recursive/has_many.rb +9 -0
- data/lib/identity_cache/cached/recursive/has_one.rb +9 -0
- data/lib/identity_cache/cached/reference/association.rb +16 -0
- data/lib/identity_cache/cached/reference/has_many.rb +105 -0
- data/lib/identity_cache/cached/reference/has_one.rb +100 -0
- data/lib/identity_cache/configuration_dsl.rb +53 -215
- data/lib/identity_cache/encoder.rb +95 -0
- data/lib/identity_cache/expiry_hook.rb +36 -0
- data/lib/identity_cache/fallback_fetcher.rb +2 -1
- data/lib/identity_cache/load_strategy/eager.rb +28 -0
- data/lib/identity_cache/load_strategy/lazy.rb +71 -0
- data/lib/identity_cache/load_strategy/load_request.rb +20 -0
- data/lib/identity_cache/load_strategy/multi_load_request.rb +27 -0
- data/lib/identity_cache/memoized_cache_proxy.rb +127 -58
- data/lib/identity_cache/parent_model_expiration.rb +45 -11
- data/lib/identity_cache/query_api.rb +128 -394
- data/lib/identity_cache/railtie.rb +8 -0
- data/lib/identity_cache/record_not_found.rb +6 -0
- data/lib/identity_cache/should_use_cache.rb +1 -0
- data/lib/identity_cache/version.rb +3 -2
- data/lib/identity_cache/with_primary_index.rb +136 -0
- data/lib/identity_cache/without_primary_index.rb +24 -3
- data/performance/cache_runner.rb +28 -34
- data/performance/cpu.rb +3 -2
- data/performance/externals.rb +4 -3
- data/performance/profile.rb +6 -5
- data/railgun.yml +16 -0
- metadata +44 -73
- data/Gemfile.rails42 +0 -6
- data/Gemfile.rails50 +0 -6
- data/test/attribute_cache_test.rb +0 -110
- data/test/cache_fetch_includes_test.rb +0 -46
- data/test/cache_hash_test.rb +0 -14
- data/test/cache_invalidation_test.rb +0 -139
- data/test/deeply_nested_associated_record_test.rb +0 -19
- data/test/denormalized_has_many_test.rb +0 -214
- data/test/denormalized_has_one_test.rb +0 -160
- data/test/fetch_multi_test.rb +0 -308
- data/test/fetch_test.rb +0 -258
- data/test/fixtures/serialized_record.mysql2 +0 -0
- data/test/fixtures/serialized_record.postgresql +0 -0
- data/test/helpers/active_record_objects.rb +0 -106
- data/test/helpers/database_connection.rb +0 -72
- data/test/helpers/serialization_format.rb +0 -51
- data/test/helpers/update_serialization_format.rb +0 -27
- data/test/identity_cache_test.rb +0 -29
- data/test/index_cache_test.rb +0 -161
- data/test/memoized_attributes_test.rb +0 -59
- data/test/memoized_cache_proxy_test.rb +0 -107
- data/test/normalized_belongs_to_test.rb +0 -107
- data/test/normalized_has_many_test.rb +0 -231
- data/test/normalized_has_one_test.rb +0 -9
- data/test/prefetch_associations_test.rb +0 -379
- data/test/readonly_test.rb +0 -109
- data/test/recursive_denormalized_has_many_test.rb +0 -131
- data/test/save_test.rb +0 -82
- data/test/schema_change_test.rb +0 -112
- data/test/serialization_format_change_test.rb +0 -16
- data/test/test_helper.rb +0 -140
@@ -1,3 +1,4 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
module IdentityCache
|
2
3
|
class FallbackFetcher
|
3
4
|
attr_accessor :cache_backend
|
@@ -18,7 +19,7 @@ module IdentityCache
|
|
18
19
|
@cache_backend.clear
|
19
20
|
end
|
20
21
|
|
21
|
-
def fetch_multi(keys
|
22
|
+
def fetch_multi(keys)
|
22
23
|
results = @cache_backend.read_multi(*keys)
|
23
24
|
missed_keys = keys - results.keys
|
24
25
|
unless missed_keys.empty?
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module IdentityCache
|
4
|
+
module LoadStrategy
|
5
|
+
module Eager
|
6
|
+
extend self
|
7
|
+
|
8
|
+
def load(cache_fetcher, db_key)
|
9
|
+
yield CacheKeyLoader.load(cache_fetcher, db_key)
|
10
|
+
end
|
11
|
+
|
12
|
+
def load_multi(cache_fetcher, db_keys)
|
13
|
+
yield CacheKeyLoader.load_multi(cache_fetcher, db_keys)
|
14
|
+
end
|
15
|
+
|
16
|
+
def load_batch(db_keys_by_cache_fetcher)
|
17
|
+
yield CacheKeyLoader.load_batch(db_keys_by_cache_fetcher)
|
18
|
+
end
|
19
|
+
|
20
|
+
def lazy_load
|
21
|
+
lazy_loader = Lazy.new
|
22
|
+
yield lazy_loader
|
23
|
+
lazy_loader.load_now
|
24
|
+
nil
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module IdentityCache
|
4
|
+
module LoadStrategy
|
5
|
+
class Lazy
|
6
|
+
def initialize
|
7
|
+
@pending_loads = {}
|
8
|
+
end
|
9
|
+
|
10
|
+
def load(cache_fetcher, db_key)
|
11
|
+
load_multi(cache_fetcher, [db_key]) do |results|
|
12
|
+
yield results.fetch(db_key)
|
13
|
+
end
|
14
|
+
nil
|
15
|
+
end
|
16
|
+
|
17
|
+
def load_multi(cache_fetcher, db_keys, &callback)
|
18
|
+
load_request = LoadRequest.new(db_keys, callback)
|
19
|
+
|
20
|
+
if (prev_load_request = @pending_loads[cache_fetcher])
|
21
|
+
if prev_load_request.instance_of?(MultiLoadRequest)
|
22
|
+
prev_load_request.load_requests << load_request
|
23
|
+
else
|
24
|
+
@pending_loads[cache_fetcher] = MultiLoadRequest.new([prev_load_request, load_request])
|
25
|
+
end
|
26
|
+
else
|
27
|
+
@pending_loads[cache_fetcher] = LoadRequest.new(db_keys, callback)
|
28
|
+
end
|
29
|
+
nil
|
30
|
+
end
|
31
|
+
|
32
|
+
def load_batch(db_keys_by_cache_fetcher)
|
33
|
+
batch_result = {}
|
34
|
+
db_keys_by_cache_fetcher.each do |cache_fetcher, db_keys|
|
35
|
+
load_multi(cache_fetcher, db_keys) do |load_result|
|
36
|
+
batch_result[cache_fetcher] = load_result
|
37
|
+
if batch_result.size == db_keys_by_cache_fetcher.size
|
38
|
+
yield batch_result
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
nil
|
43
|
+
end
|
44
|
+
|
45
|
+
def lazy_load
|
46
|
+
yield self
|
47
|
+
nil
|
48
|
+
end
|
49
|
+
|
50
|
+
def load_now
|
51
|
+
until @pending_loads.empty?
|
52
|
+
pending_loads = @pending_loads
|
53
|
+
@pending_loads = {}
|
54
|
+
load_pending(pending_loads)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
private
|
59
|
+
|
60
|
+
def load_pending(pending_loads)
|
61
|
+
result = CacheKeyLoader.load_batch(pending_loads.transform_values(&:db_keys))
|
62
|
+
result.each do |cache_fetcher, load_result|
|
63
|
+
load_request = pending_loads.fetch(cache_fetcher)
|
64
|
+
load_request.after_load(load_result)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
private_constant :Lazy
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module IdentityCache
|
4
|
+
module LoadStrategy
|
5
|
+
class LoadRequest
|
6
|
+
attr_reader :db_keys
|
7
|
+
|
8
|
+
def initialize(db_keys, callback)
|
9
|
+
@db_keys = db_keys
|
10
|
+
@callback = callback
|
11
|
+
end
|
12
|
+
|
13
|
+
def after_load(results)
|
14
|
+
@callback.call(results)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
private_constant :LoadRequest
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module IdentityCache
|
4
|
+
module LoadStrategy
|
5
|
+
class MultiLoadRequest
|
6
|
+
def initialize(load_requests)
|
7
|
+
@load_requests = load_requests
|
8
|
+
end
|
9
|
+
|
10
|
+
def db_keys
|
11
|
+
@load_requests.flat_map(&:db_keys).tap(&:uniq!)
|
12
|
+
end
|
13
|
+
|
14
|
+
def after_load(all_results)
|
15
|
+
@load_requests.each do |load_request|
|
16
|
+
load_result = {}
|
17
|
+
load_request.db_keys.each do |key|
|
18
|
+
load_result[key] = all_results[key]
|
19
|
+
end
|
20
|
+
load_request.after_load(load_result)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
private_constant :MultiLoadRequest
|
26
|
+
end
|
27
|
+
end
|
@@ -1,4 +1,6 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
require 'monitor'
|
3
|
+
require 'benchmark'
|
2
4
|
|
3
5
|
module IdentityCache
|
4
6
|
class MemoizedCacheProxy
|
@@ -6,13 +8,20 @@ module IdentityCache
|
|
6
8
|
|
7
9
|
def initialize(cache_adaptor = nil)
|
8
10
|
self.cache_backend = cache_adaptor || Rails.cache
|
9
|
-
@key_value_maps = Hash.new {|h, k| h[k] = {} }
|
11
|
+
@key_value_maps = Hash.new { |h, k| h[k] = {} }
|
10
12
|
end
|
11
13
|
|
12
14
|
def cache_backend=(cache_adaptor)
|
13
15
|
if cache_adaptor.respond_to?(:cas) && cache_adaptor.respond_to?(:cas_multi)
|
14
16
|
@cache_fetcher = CacheFetcher.new(cache_adaptor)
|
15
17
|
else
|
18
|
+
case cache_adaptor
|
19
|
+
when ActiveSupport::Cache::MemoryStore, ActiveSupport::Cache::NullStore
|
20
|
+
# no need for CAS support
|
21
|
+
else
|
22
|
+
warn("[IdentityCache] Missing CAS support in cache backend #{cache_adaptor.class} "\
|
23
|
+
"which is needed for cache consistency")
|
24
|
+
end
|
16
25
|
@cache_fetcher = FallbackFetcher.new(cache_adaptor)
|
17
26
|
end
|
18
27
|
end
|
@@ -21,7 +30,7 @@ module IdentityCache
|
|
21
30
|
@key_value_maps[Thread.current]
|
22
31
|
end
|
23
32
|
|
24
|
-
def with_memoization
|
33
|
+
def with_memoization
|
25
34
|
Thread.current[:memoizing_idc] = true
|
26
35
|
yield
|
27
36
|
ensure
|
@@ -30,101 +39,161 @@ module IdentityCache
|
|
30
39
|
end
|
31
40
|
|
32
41
|
def write(key, value)
|
33
|
-
|
34
|
-
|
42
|
+
memoizing = memoizing?
|
43
|
+
ActiveSupport::Notifications.instrument('cache_write.identity_cache', memoizing: memoizing) do
|
44
|
+
memoized_key_values[key] = value if memoizing
|
45
|
+
@cache_fetcher.write(key, value)
|
46
|
+
end
|
35
47
|
end
|
36
48
|
|
37
49
|
def delete(key)
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
50
|
+
memoizing = memoizing?
|
51
|
+
ActiveSupport::Notifications.instrument('cache_delete.identity_cache', memoizing: memoizing) do
|
52
|
+
memoized_key_values.delete(key) if memoizing
|
53
|
+
if (result = @cache_fetcher.delete(key))
|
54
|
+
IdentityCache.logger.debug { "[IdentityCache] delete recorded for #{key}" }
|
55
|
+
else
|
56
|
+
IdentityCache.logger.error { "[IdentityCache] delete failed for #{key}" }
|
57
|
+
end
|
58
|
+
result
|
59
|
+
end
|
42
60
|
end
|
43
61
|
|
44
62
|
def fetch(key)
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
63
|
+
memo_misses = 0
|
64
|
+
cache_misses = 0
|
65
|
+
|
66
|
+
value = ActiveSupport::Notifications.instrument('cache_fetch.identity_cache') do |payload|
|
67
|
+
payload[:resolve_miss_time] = 0.0
|
68
|
+
|
69
|
+
value = fetch_memoized(key) do
|
70
|
+
memo_misses = 1
|
71
|
+
@cache_fetcher.fetch(key) do
|
72
|
+
cache_misses = 1
|
73
|
+
instrument_duration(payload, :resolve_miss_time) do
|
74
|
+
yield
|
75
|
+
end
|
54
76
|
end
|
55
77
|
end
|
56
|
-
|
57
|
-
|
58
|
-
missed = true
|
59
|
-
yield
|
60
|
-
end
|
78
|
+
set_instrumentation_payload(payload, num_keys: 1, memo_misses: memo_misses, cache_misses: cache_misses)
|
79
|
+
value
|
61
80
|
end
|
62
81
|
|
63
|
-
if
|
82
|
+
if cache_misses > 0
|
64
83
|
IdentityCache.logger.debug { "[IdentityCache] cache miss for #{key}" }
|
65
84
|
else
|
66
|
-
IdentityCache.logger.debug
|
85
|
+
IdentityCache.logger.debug do
|
86
|
+
"[IdentityCache] #{memo_misses > 0 ? '(cache_backend)' : '(memoized)'} cache hit for #{key}"
|
87
|
+
end
|
67
88
|
end
|
68
89
|
|
69
90
|
value
|
70
91
|
end
|
71
92
|
|
72
93
|
def fetch_multi(*keys)
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
94
|
+
memo_miss_keys = EMPTY_ARRAY
|
95
|
+
cache_miss_keys = EMPTY_ARRAY
|
96
|
+
|
97
|
+
result = ActiveSupport::Notifications.instrument('cache_fetch_multi.identity_cache') do |payload|
|
98
|
+
payload[:resolve_miss_time] = 0.0
|
99
|
+
|
100
|
+
result = fetch_multi_memoized(keys) do |non_memoized_keys|
|
101
|
+
memo_miss_keys = non_memoized_keys
|
102
|
+
@cache_fetcher.fetch_multi(non_memoized_keys) do |missing_keys|
|
103
|
+
cache_miss_keys = missing_keys
|
104
|
+
instrument_duration(payload, :resolve_miss_time) do
|
105
|
+
yield missing_keys
|
106
|
+
end
|
85
107
|
end
|
86
108
|
end
|
87
109
|
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
end
|
96
|
-
hash
|
97
|
-
else
|
98
|
-
@cache_fetcher.fetch_multi(keys) do |missing_keys|
|
99
|
-
missed_keys.concat(missing_keys) if IdentityCache.logger.debug?
|
100
|
-
yield missing_keys
|
101
|
-
end
|
110
|
+
set_instrumentation_payload(
|
111
|
+
payload,
|
112
|
+
num_keys: keys.length,
|
113
|
+
memo_misses: memo_miss_keys.length,
|
114
|
+
cache_misses: cache_miss_keys.length
|
115
|
+
)
|
116
|
+
result
|
102
117
|
end
|
103
118
|
|
104
|
-
log_multi_result(
|
119
|
+
log_multi_result(keys, memo_miss_keys, cache_miss_keys)
|
105
120
|
|
106
121
|
result
|
107
122
|
end
|
108
123
|
|
109
124
|
def clear
|
110
|
-
|
111
|
-
|
125
|
+
ActiveSupport::Notifications.instrument('cache_clear.identity_cache') do
|
126
|
+
clear_memoization
|
127
|
+
@cache_fetcher.clear
|
128
|
+
end
|
112
129
|
end
|
113
130
|
|
114
131
|
private
|
115
132
|
|
133
|
+
EMPTY_ARRAY = [].freeze
|
134
|
+
private_constant :EMPTY_ARRAY
|
135
|
+
|
136
|
+
def set_instrumentation_payload(payload, num_keys:, memo_misses:, cache_misses:)
|
137
|
+
payload[:memoizing] = memoizing?
|
138
|
+
payload[:memo_hits] = num_keys - memo_misses
|
139
|
+
payload[:cache_hits] = memo_misses - cache_misses
|
140
|
+
payload[:cache_misses] = cache_misses
|
141
|
+
end
|
142
|
+
|
143
|
+
def fetch_memoized(key)
|
144
|
+
return yield unless memoizing?
|
145
|
+
if memoized_key_values.key?(key)
|
146
|
+
return memoized_key_values[key]
|
147
|
+
end
|
148
|
+
memoized_key_values[key] = yield
|
149
|
+
end
|
150
|
+
|
151
|
+
def fetch_multi_memoized(keys)
|
152
|
+
return yield keys unless memoizing?
|
153
|
+
|
154
|
+
result = {}
|
155
|
+
missing_keys = keys.reject do |key|
|
156
|
+
if memoized_key_values.key?(key)
|
157
|
+
result[key] = memoized_key_values[key]
|
158
|
+
true
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
unless missing_keys.empty?
|
163
|
+
block_result = yield missing_keys
|
164
|
+
memoized_key_values.merge!(block_result)
|
165
|
+
result.merge!(block_result)
|
166
|
+
end
|
167
|
+
|
168
|
+
result
|
169
|
+
end
|
170
|
+
|
171
|
+
def instrument_duration(payload, key)
|
172
|
+
value = nil
|
173
|
+
payload[key] += Benchmark.realtime do
|
174
|
+
value = yield
|
175
|
+
end
|
176
|
+
value
|
177
|
+
end
|
178
|
+
|
116
179
|
def clear_memoization
|
117
180
|
@key_value_maps.delete(Thread.current)
|
118
181
|
end
|
119
182
|
|
120
183
|
def memoizing?
|
121
|
-
Thread.current[:memoizing_idc]
|
184
|
+
!!Thread.current[:memoizing_idc]
|
122
185
|
end
|
123
186
|
|
124
|
-
def log_multi_result(
|
125
|
-
|
126
|
-
|
127
|
-
|
187
|
+
def log_multi_result(keys, memo_miss_keys, cache_miss_keys)
|
188
|
+
IdentityCache.logger.debug do
|
189
|
+
memoized_keys = keys - memo_miss_keys
|
190
|
+
cache_hit_keys = memo_miss_keys - cache_miss_keys
|
191
|
+
missed_keys = cache_miss_keys
|
192
|
+
|
193
|
+
memoized_keys.each { |k| IdentityCache.logger.debug("[IdentityCache] (memoized) cache hit for #{k} (multi)") }
|
194
|
+
cache_hit_keys.each { |k| IdentityCache.logger.debug("[IdentityCache] (backend) cache hit for #{k} (multi)") }
|
195
|
+
missed_keys.each { |k| IdentityCache.logger.debug("[IdentityCache] cache miss for #{k} (multi)") }
|
196
|
+
end
|
128
197
|
end
|
129
198
|
end
|
130
199
|
end
|
@@ -1,17 +1,50 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
module IdentityCache
|
2
3
|
module ParentModelExpiration # :nodoc:
|
3
4
|
extend ActiveSupport::Concern
|
4
5
|
|
5
|
-
|
6
|
-
|
7
|
-
|
6
|
+
class << self
|
7
|
+
def add_parent_expiry_hook(cached_association)
|
8
|
+
name = cached_association.reflection.class_name.demodulize
|
9
|
+
lazy_hooks[name] << ExpiryHook.new(cached_association)
|
10
|
+
end
|
11
|
+
|
12
|
+
def install_all_pending_parent_expiry_hooks
|
13
|
+
until lazy_hooks.empty?
|
14
|
+
lazy_hooks.keys.each do |name|
|
15
|
+
if (hooks = lazy_hooks.delete(name))
|
16
|
+
hooks.each(&:install)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def install_pending_parent_expiry_hooks(model)
|
23
|
+
name = model.name.demodulize
|
24
|
+
if (hooks = lazy_hooks.delete(name))
|
25
|
+
hooks.each(&:install)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
def lazy_hooks
|
32
|
+
@lazy_hooks ||= Hash.new { |hash, key| hash[key] = [] }
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
included do
|
37
|
+
class_attribute(:parent_expiration_entries)
|
38
|
+
self.parent_expiration_entries = Hash.new { |hash, key| hash[key] = [] }
|
39
|
+
after_commit(:expire_parent_caches)
|
8
40
|
end
|
9
41
|
|
10
42
|
def expire_parent_caches
|
11
|
-
|
43
|
+
ParentModelExpiration.install_pending_parent_expiry_hooks(cached_model)
|
44
|
+
parents_to_expire = Set.new
|
12
45
|
add_parents_to_cache_expiry_set(parents_to_expire)
|
13
|
-
parents_to_expire.
|
14
|
-
parent.
|
46
|
+
parents_to_expire.each do |parent|
|
47
|
+
parent.expire_primary_index if parent.class.primary_cache_index_enabled
|
15
48
|
end
|
16
49
|
end
|
17
50
|
|
@@ -22,9 +55,7 @@ module IdentityCache
|
|
22
55
|
end
|
23
56
|
|
24
57
|
def add_record_to_cache_expiry_set(parents_to_expire, record)
|
25
|
-
|
26
|
-
unless parents_to_expire[key]
|
27
|
-
parents_to_expire[key] = record
|
58
|
+
if parents_to_expire.add?(record)
|
28
59
|
record.add_parents_to_cache_expiry_set(parents_to_expire)
|
29
60
|
end
|
30
61
|
end
|
@@ -52,11 +83,12 @@ module IdentityCache
|
|
52
83
|
end
|
53
84
|
|
54
85
|
cached_associations.each do |parent_class, only_on_foreign_key_change|
|
55
|
-
if new_parent
|
86
|
+
if new_parent&.is_a?(parent_class) &&
|
87
|
+
should_expire_identity_cache_parent?(foreign_key, only_on_foreign_key_change)
|
56
88
|
add_record_to_cache_expiry_set(parents_to_expire, new_parent)
|
57
89
|
end
|
58
90
|
|
59
|
-
if old_parent
|
91
|
+
if old_parent&.is_a?(parent_class)
|
60
92
|
add_record_to_cache_expiry_set(parents_to_expire, old_parent)
|
61
93
|
end
|
62
94
|
end
|
@@ -70,4 +102,6 @@ module IdentityCache
|
|
70
102
|
end
|
71
103
|
end
|
72
104
|
end
|
105
|
+
|
106
|
+
private_constant :ParentModelExpiration
|
73
107
|
end
|