identity_cache 1.6.1 → 1.6.2
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.md +4 -0
- data/Gemfile.lock +3 -3
- data/Rakefile +24 -0
- data/dev.yml +10 -6
- data/lib/identity_cache/cache_fetcher.rb +5 -0
- data/lib/identity_cache/cached/attribute.rb +15 -3
- data/lib/identity_cache/cached/primary_index.rb +5 -1
- data/lib/identity_cache/memoized_cache_proxy.rb +10 -0
- data/lib/identity_cache/parent_model_expiration.rb +0 -4
- data/lib/identity_cache/version.rb +1 -1
- data/lib/identity_cache.rb +27 -18
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 463112ddb217364a26a8232ef1958c45bdb24fc7dcd891be3e7d4db4ca4d29a4
|
4
|
+
data.tar.gz: 13ea742aca4c33f710a6f8a229f6cf0531dba50291c1ec7c67bce735028ce527
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d4c3cae9c3c4a6444f62d84242326da8901e0aa66895c8b6d2916930a5824fe402299d5e7c6270982e37fa9cd97991310756320aea0fb93b64a1f515a27f1c1f
|
7
|
+
data.tar.gz: 5512f9b845a4bc9ea8821d6e8563bc4307ba46a32fe32f1f5d2a708b624be3a5ed72c1b64fe2a9ade21f52ffa8d6c3220e23bc3bb433249bceb4d6eacdf7ba67
|
data/CHANGELOG.md
CHANGED
data/Gemfile.lock
CHANGED
@@ -8,7 +8,7 @@ GIT
|
|
8
8
|
PATH
|
9
9
|
remote: .
|
10
10
|
specs:
|
11
|
-
identity_cache (1.6.
|
11
|
+
identity_cache (1.6.2)
|
12
12
|
activerecord (>= 7.0)
|
13
13
|
ar_transaction_changes (~> 1.1)
|
14
14
|
|
@@ -64,8 +64,8 @@ GEM
|
|
64
64
|
rainbow (3.1.1)
|
65
65
|
rake (13.1.0)
|
66
66
|
regexp_parser (2.9.0)
|
67
|
-
rexml (3.
|
68
|
-
strscan
|
67
|
+
rexml (3.3.6)
|
68
|
+
strscan
|
69
69
|
rubocop (1.61.0)
|
70
70
|
json (~> 2.3)
|
71
71
|
language_server-protocol (>= 3.17.0)
|
data/Rakefile
CHANGED
@@ -47,3 +47,27 @@ namespace :profile do
|
|
47
47
|
ruby "./performance/profile.rb"
|
48
48
|
end
|
49
49
|
end
|
50
|
+
|
51
|
+
namespace :db do
|
52
|
+
desc "Create the identity_cache_test database"
|
53
|
+
task :create do
|
54
|
+
require "mysql2"
|
55
|
+
|
56
|
+
config = {
|
57
|
+
host: ENV.fetch("MYSQL_HOST") || "localhost",
|
58
|
+
port: ENV.fetch("MYSQL_PORT") || 1037,
|
59
|
+
username: ENV.fetch("MYSQL_USER") || "root",
|
60
|
+
password: ENV.fetch("MYSQL_PASSWORD") || "",
|
61
|
+
}
|
62
|
+
|
63
|
+
begin
|
64
|
+
client = Mysql2::Client.new(config)
|
65
|
+
client.query("CREATE DATABASE IF NOT EXISTS identity_cache_test")
|
66
|
+
puts "Database 'identity_cache_test' created successfully. host: #{config[:host]}, port: #{config[:port]}"
|
67
|
+
rescue Mysql2::Error => e
|
68
|
+
puts "Error creating database: #{e.message}"
|
69
|
+
ensure
|
70
|
+
client&.close
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
data/dev.yml
CHANGED
@@ -5,6 +5,10 @@ up:
|
|
5
5
|
- bundler
|
6
6
|
- memcached
|
7
7
|
- mysql
|
8
|
+
- custom:
|
9
|
+
name: create database identity_cache_test
|
10
|
+
met?: mysql -u root -h 127.0.0.1 -P 1037 -e "SHOW DATABASES;" | grep identity_cache_test
|
11
|
+
meet: bundle exec rake db:create
|
8
12
|
|
9
13
|
commands:
|
10
14
|
test:
|
@@ -12,7 +16,7 @@ commands:
|
|
12
16
|
optional:
|
13
17
|
argument: file
|
14
18
|
optional: args...
|
15
|
-
desc:
|
19
|
+
desc: "Run tests"
|
16
20
|
run: |
|
17
21
|
if [[ $# -eq 0 ]]; then
|
18
22
|
bundle exec rake test
|
@@ -21,21 +25,21 @@ commands:
|
|
21
25
|
fi
|
22
26
|
|
23
27
|
style:
|
24
|
-
desc:
|
28
|
+
desc: "Run rubocop checks"
|
25
29
|
run: bundle exec rubocop "$@"
|
26
30
|
|
27
31
|
check:
|
28
|
-
desc:
|
32
|
+
desc: "Run tests and style checks"
|
29
33
|
run: bundle exec rake test && bundle exec rubocop
|
30
34
|
|
31
35
|
benchmark-cpu:
|
32
|
-
desc:
|
36
|
+
desc: "Run the identity cache CPU benchmark"
|
33
37
|
run: bundle exec rake benchmark:cpu
|
34
38
|
|
35
39
|
profile:
|
36
|
-
desc:
|
40
|
+
desc: "Profile IDC code"
|
37
41
|
run: bundle exec rake profile:run
|
38
42
|
|
39
43
|
update-serialization-format:
|
40
|
-
desc:
|
44
|
+
desc: "Update serialization format test fixture"
|
41
45
|
run: bundle exec rake update_serialization_format
|
@@ -60,6 +60,11 @@ module IdentityCache
|
|
60
60
|
@cache_backend.write(key, IdentityCache::DELETED, expires_in: IdentityCache::DELETED_TTL.seconds)
|
61
61
|
end
|
62
62
|
|
63
|
+
def delete_multi(keys)
|
64
|
+
key_values = keys.map { |key| [key, IdentityCache::DELETED] }.to_h
|
65
|
+
@cache_backend.write_multi(key_values, expires_in: IdentityCache::DELETED_TTL.seconds)
|
66
|
+
end
|
67
|
+
|
63
68
|
def clear
|
64
69
|
@cache_backend.clear
|
65
70
|
end
|
@@ -39,12 +39,24 @@ module IdentityCache
|
|
39
39
|
|
40
40
|
unless record.send(:was_new_record?)
|
41
41
|
old_key = old_cache_key(record)
|
42
|
-
|
42
|
+
|
43
|
+
if Thread.current[:idc_deferred_expiration]
|
44
|
+
Thread.current[:idc_attributes_to_expire] << old_key
|
45
|
+
# defer the deletion, and don't block the following deletion
|
46
|
+
all_deleted = true
|
47
|
+
else
|
48
|
+
all_deleted = IdentityCache.cache.delete(old_key)
|
49
|
+
end
|
43
50
|
end
|
44
51
|
unless record.destroyed?
|
45
52
|
new_key = new_cache_key(record)
|
46
53
|
if new_key != old_key
|
47
|
-
|
54
|
+
if Thread.current[:idc_deferred_expiration]
|
55
|
+
Thread.current[:idc_attributes_to_expire] << new_key
|
56
|
+
all_deleted = true
|
57
|
+
else
|
58
|
+
all_deleted = IdentityCache.cache.delete(new_key) && all_deleted
|
59
|
+
end
|
48
60
|
end
|
49
61
|
end
|
50
62
|
|
@@ -152,9 +164,9 @@ module IdentityCache
|
|
152
164
|
end
|
153
165
|
|
154
166
|
def old_cache_key(record)
|
167
|
+
changes = record.transaction_changed_attributes
|
155
168
|
old_key_values = key_fields.map do |field|
|
156
169
|
field_string = field.to_s
|
157
|
-
changes = record.transaction_changed_attributes
|
158
170
|
if record.destroyed? && changes.key?(field_string)
|
159
171
|
changes[field_string]
|
160
172
|
elsif record.persisted? && changes.key?(field_string)
|
@@ -45,7 +45,11 @@ module IdentityCache
|
|
45
45
|
|
46
46
|
def expire(id)
|
47
47
|
id = cast_id(id)
|
48
|
-
|
48
|
+
if Thread.current[:idc_deferred_expiration]
|
49
|
+
Thread.current[:idc_records_to_expire] << cache_key(id)
|
50
|
+
else
|
51
|
+
IdentityCache.cache.delete(cache_key(id))
|
52
|
+
end
|
49
53
|
end
|
50
54
|
|
51
55
|
def cache_key(id)
|
@@ -70,6 +70,16 @@ module IdentityCache
|
|
70
70
|
end
|
71
71
|
end
|
72
72
|
|
73
|
+
def delete_multi(keys)
|
74
|
+
memoizing = memoizing?
|
75
|
+
ActiveSupport::Notifications.instrument("cache_delete_multi.identity_cache", memoizing: memoizing) do
|
76
|
+
if memoizing
|
77
|
+
keys.each { |key| memoized_key_values.delete(key) }
|
78
|
+
end
|
79
|
+
@cache_fetcher.delete_multi(keys)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
73
83
|
def fetch(key, cache_fetcher_options = {}, &block)
|
74
84
|
memo_misses = 0
|
75
85
|
cache_misses = 0
|
@@ -47,10 +47,6 @@ module IdentityCache
|
|
47
47
|
add_parents_to_cache_expiry_set(parents_to_expire)
|
48
48
|
parents_to_expire.select! { |parent| parent.class.primary_cache_index_enabled }
|
49
49
|
parents_to_expire.reduce(true) do |all_expired, parent|
|
50
|
-
if Thread.current[:idc_deferred_parent_expiration]
|
51
|
-
Thread.current[:idc_parent_records_for_cache_expiry] << parent
|
52
|
-
next parent
|
53
|
-
end
|
54
50
|
parent.expire_primary_index && all_expired
|
55
51
|
end
|
56
52
|
end
|
data/lib/identity_cache.rb
CHANGED
@@ -60,7 +60,7 @@ module IdentityCache
|
|
60
60
|
|
61
61
|
class InverseAssociationError < StandardError; end
|
62
62
|
|
63
|
-
class
|
63
|
+
class NestedDeferredCacheExpirationBlockError < StandardError; end
|
64
64
|
|
65
65
|
class UnsupportedScopeError < StandardError; end
|
66
66
|
|
@@ -202,42 +202,51 @@ module IdentityCache
|
|
202
202
|
result
|
203
203
|
end
|
204
204
|
|
205
|
-
# Executes a block with deferred
|
206
|
-
#
|
207
|
-
# completes, it
|
208
|
-
#
|
209
|
-
# is already active on the current thread.
|
205
|
+
# Executes a block with deferred cache expiration, ensuring that the records' (parent,
|
206
|
+
# children and attributes) cache expiration is deferred until the block completes. When
|
207
|
+
# the block completes, it issues delete_multi calls for all the records and attributes
|
208
|
+
# that were marked for expiration.
|
210
209
|
#
|
211
210
|
# == Parameters:
|
212
211
|
# No parameters.
|
213
212
|
#
|
214
213
|
# == Raises:
|
215
|
-
#
|
214
|
+
# NestedDeferredCacheExpirationBlockError if a deferred cache expiration block is already active.
|
216
215
|
#
|
217
216
|
# == Yield:
|
218
|
-
# Runs the provided block with deferred
|
217
|
+
# Runs the provided block with deferred cache expiration.
|
219
218
|
#
|
220
219
|
# == Returns:
|
221
220
|
# The result of executing the provided block.
|
222
221
|
#
|
223
222
|
# == Ensures:
|
224
|
-
# Cleans up thread-local variables related to deferred
|
223
|
+
# Cleans up thread-local variables related to deferred cache expiration regardless
|
225
224
|
# of whether the block raises an exception.
|
226
|
-
def
|
227
|
-
raise
|
225
|
+
def with_deferred_expiration
|
226
|
+
raise NestedDeferredCacheExpirationBlockError if Thread.current[:idc_deferred_expiration]
|
228
227
|
|
229
|
-
Thread.current[:
|
230
|
-
Thread.current[:
|
228
|
+
Thread.current[:idc_deferred_expiration] = true
|
229
|
+
Thread.current[:idc_records_to_expire] = Set.new
|
230
|
+
Thread.current[:idc_attributes_to_expire] = Set.new
|
231
231
|
|
232
232
|
result = yield
|
233
233
|
|
234
|
-
Thread.current[:
|
235
|
-
Thread.current[:
|
236
|
-
|
234
|
+
Thread.current[:idc_deferred_expiration] = nil
|
235
|
+
if Thread.current[:idc_records_to_expire].any?
|
236
|
+
IdentityCache.cache.delete_multi(
|
237
|
+
Thread.current[:idc_records_to_expire]
|
238
|
+
)
|
239
|
+
end
|
240
|
+
if Thread.current[:idc_attributes_to_expire].any?
|
241
|
+
IdentityCache.cache.delete_multi(
|
242
|
+
Thread.current[:idc_attributes_to_expire]
|
243
|
+
)
|
244
|
+
end
|
237
245
|
result
|
238
246
|
ensure
|
239
|
-
Thread.current[:
|
240
|
-
Thread.current[:
|
247
|
+
Thread.current[:idc_deferred_expiration] = nil
|
248
|
+
Thread.current[:idc_records_to_expire].clear
|
249
|
+
Thread.current[:idc_attributes_to_expire].clear
|
241
250
|
end
|
242
251
|
|
243
252
|
def with_fetch_read_only_records(value = true)
|
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: 1.6.
|
4
|
+
version: 1.6.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Camilo Lopez
|
@@ -14,7 +14,7 @@ authors:
|
|
14
14
|
autorequire:
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
|
-
date: 2024-
|
17
|
+
date: 2024-10-10 00:00:00.000000000 Z
|
18
18
|
dependencies:
|
19
19
|
- !ruby/object:Gem::Dependency
|
20
20
|
name: activerecord
|
@@ -191,7 +191,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
191
191
|
- !ruby/object:Gem::Version
|
192
192
|
version: '0'
|
193
193
|
requirements: []
|
194
|
-
rubygems_version: 3.5.
|
194
|
+
rubygems_version: 3.5.21
|
195
195
|
signing_key:
|
196
196
|
specification_version: 4
|
197
197
|
summary: IdentityCache lets you specify how you want to cache your model objects,
|