identity_cache 0.5.1 → 1.0.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 +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
|