activerecord_cached 1.0.0 → 1.1.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 +4 -4
- data/CHANGELOG.md +11 -0
- data/lib/activerecord_cached/activerecord_extensions.rb +2 -7
- data/lib/activerecord_cached/cache.rb +38 -22
- data/lib/activerecord_cached/configuration.rb +4 -2
- data/lib/activerecord_cached/limit_checks.rb +2 -2
- data/lib/activerecord_cached/redis_mutex.rb +31 -0
- data/lib/activerecord_cached/version.rb +1 -1
- data/lib/activerecord_cached.rb +1 -0
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0e2aee53321cb1679b2d0a98406614eb9c718ae07fa80a6c156ccfcb2ecb4a5d
|
4
|
+
data.tar.gz: a36a4dc08fdb12b3020c44f4ab0210f0754c3504db1a22c4712d4c2b7a53adab
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ef43829aac59b7f8b9bc5a0aea7deaff9002ee532a94f91f9d5339e1160660242a3199a437c115233ba3cd6672c17f17c172f4d7cc09423000d991f118aa64c6
|
7
|
+
data.tar.gz: e428cf58d5b5f7af8b5034ec9e6b46ac6f5141a7bf8489d20604e3b47fe8287b1cd14b1109825cfa46e80179800ab459faf506f1d410916b308b0a8c8fc32529
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,16 @@
|
|
1
1
|
## [Unreleased]
|
2
2
|
|
3
|
+
## [1.1.0] - 2024-01-25
|
4
|
+
|
5
|
+
## Added
|
6
|
+
|
7
|
+
- cache expiry and race_condition_ttl
|
8
|
+
|
9
|
+
## Fixed
|
10
|
+
|
11
|
+
- some race conditions that would affect cache clearing by CRUD operations
|
12
|
+
- size mentioned in warning message when using a custom MemoryStore
|
13
|
+
|
3
14
|
## [1.0.0] - 2024-01-23
|
4
15
|
|
5
16
|
- Initial release
|
@@ -29,13 +29,8 @@ module ActiveRecordCached
|
|
29
29
|
ActiveRecord::Base.singleton_class.prepend BaseExtension
|
30
30
|
ActiveRecord::Relation.prepend RelationExtension
|
31
31
|
|
32
|
-
# bust cache on individual record changes
|
33
|
-
|
34
|
-
module CRUDCallbacks
|
35
|
-
def self.included(base)
|
36
|
-
base.after_commit { self.class.clear_cached_values }
|
37
|
-
end
|
38
|
-
end
|
32
|
+
# bust cache on individual record changes
|
33
|
+
ActiveRecord::Base.after_commit { self.class.clear_cached_values }
|
39
34
|
|
40
35
|
# bust cache on mass operations
|
41
36
|
module MassOperationWrapper
|
@@ -1,50 +1,66 @@
|
|
1
1
|
module ActiveRecordCached
|
2
2
|
def fetch(relation, method, args)
|
3
3
|
key = ['ActiveRecordCached', relation.to_sql, method, args.sort].join(':')
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
log_model_cache_key(
|
4
|
+
cache_store.fetch(key, expires_in: 1.day + rand(300).seconds, race_condition_ttl: 10.seconds) do
|
5
|
+
# The gem keeps track of all cache keys used for each model for easy clearing.
|
6
|
+
# Using #delete_matched would be too slow on large redis instances.
|
7
|
+
log_model_cache_key(relation.klass, key)
|
8
8
|
query_db(relation, method, args)
|
9
9
|
end
|
10
10
|
end
|
11
11
|
|
12
12
|
def clear_for_model(model)
|
13
|
-
|
14
|
-
|
13
|
+
synchronize do
|
14
|
+
hash = fetch_cache_keys_per_model
|
15
|
+
return unless model_keys = hash.delete(model)&.keys
|
15
16
|
|
16
|
-
|
17
|
-
|
17
|
+
# delete from the key list first so that if a fetch comes in right after,
|
18
|
+
# it can write the key to the list again
|
19
|
+
write_cache_keys_per_model(hash)
|
20
|
+
cache_store.delete_multi(model_keys)
|
21
|
+
end
|
18
22
|
end
|
19
23
|
|
20
24
|
def clear_all
|
21
|
-
|
22
|
-
|
23
|
-
|
25
|
+
synchronize do
|
26
|
+
all_keys = fetch_cache_keys_per_model.values.flat_map(&:keys)
|
27
|
+
write_cache_keys_per_model({})
|
28
|
+
cache_store.delete_multi(all_keys)
|
29
|
+
end
|
24
30
|
end
|
25
31
|
|
26
32
|
private
|
27
33
|
|
28
|
-
def
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
34
|
+
def log_model_cache_key(model, key)
|
35
|
+
synchronize do
|
36
|
+
hash = fetch_cache_keys_per_model
|
37
|
+
(hash[model] ||= {})[key] = true
|
38
|
+
write_cache_keys_per_model(hash)
|
33
39
|
end
|
34
40
|
end
|
35
41
|
|
36
|
-
|
42
|
+
def synchronize(&block)
|
43
|
+
cache_store_semaphore.synchronize(&block)
|
44
|
+
end
|
45
|
+
|
46
|
+
require 'monitor'
|
47
|
+
MONITOR = Monitor.new
|
37
48
|
|
38
|
-
def
|
39
|
-
|
40
|
-
|
41
|
-
cache_store.
|
49
|
+
def cache_store_semaphore
|
50
|
+
case cache_store
|
51
|
+
when ActiveSupport::Cache::MemoryStore then MONITOR
|
52
|
+
when ActiveSupport::Cache::RedisCacheStore then RedisMutex.new(cache_store.redis)
|
53
|
+
end
|
42
54
|
end
|
43
55
|
|
44
|
-
def
|
56
|
+
def fetch_cache_keys_per_model
|
45
57
|
cache_store.read(CACHE_KEYS_KEY) || {}
|
46
58
|
end
|
47
59
|
|
60
|
+
def write_cache_keys_per_model(val)
|
61
|
+
cache_store.write(CACHE_KEYS_KEY, val)
|
62
|
+
end
|
63
|
+
|
48
64
|
CACHE_KEYS_KEY = 'ActiveRecordCached:cache_keys_per_model'
|
49
65
|
|
50
66
|
def query_db(rel, method, args)
|
@@ -10,7 +10,9 @@ module ActiveRecordCached
|
|
10
10
|
end
|
11
11
|
|
12
12
|
def cache_store=(val)
|
13
|
-
val.is_a?(ActiveSupport::Cache::
|
13
|
+
val.is_a?(ActiveSupport::Cache::MemoryStore) ||
|
14
|
+
val.is_a?(ActiveSupport::Cache::RedisCacheStore) ||
|
15
|
+
raise(ArgumentError, 'pass a MemoryStore or RedisCacheStore')
|
14
16
|
@cache_store = store_with_limit_warning(val)
|
15
17
|
end
|
16
18
|
|
@@ -45,7 +47,7 @@ module ActiveRecordCached
|
|
45
47
|
|
46
48
|
store.singleton_class.prepend(Module.new do
|
47
49
|
def prune(...)
|
48
|
-
ActiveRecordCached.send(:warn_max_total_bytes_exceeded)
|
50
|
+
ActiveRecordCached.send(:warn_max_total_bytes_exceeded, @max_size)
|
49
51
|
super
|
50
52
|
end
|
51
53
|
end)
|
@@ -10,8 +10,8 @@ module ActiveRecordCached
|
|
10
10
|
end
|
11
11
|
end
|
12
12
|
|
13
|
-
def warn_max_total_bytes_exceeded
|
14
|
-
warn_limit_reached("Store size >= #{max_total_bytes} max_total_bytes")
|
13
|
+
def warn_max_total_bytes_exceeded(store_size = nil)
|
14
|
+
warn_limit_reached("Store size >= #{store_size || max_total_bytes} max_total_bytes")
|
15
15
|
end
|
16
16
|
|
17
17
|
def warn_limit_reached(info)
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module ActiveRecordCached
|
2
|
+
class RedisMutex
|
3
|
+
KEY = 'ActiveRecordCached:RedisMutex'
|
4
|
+
MAX_HOLD_TIME = 2
|
5
|
+
LOCK_ACQUIRER = "return redis.call('setnx', KEYS[1], 1) == 1 and redis.call('expire', KEYS[1], KEYS[2]) and 1 or 0"
|
6
|
+
|
7
|
+
def initialize(redis)
|
8
|
+
@redis = redis.then(&:itself)
|
9
|
+
end
|
10
|
+
|
11
|
+
def synchronize(&block)
|
12
|
+
acquire_lock
|
13
|
+
begin
|
14
|
+
block.call
|
15
|
+
ensure
|
16
|
+
release_lock
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def acquire_lock
|
23
|
+
sleep_t = 0.005
|
24
|
+
while @redis.eval(LOCK_ACQUIRER, [KEY, MAX_HOLD_TIME]) != 1 do sleep(sleep_t *= 1.6) end
|
25
|
+
end
|
26
|
+
|
27
|
+
def release_lock
|
28
|
+
@redis.del(KEY)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
data/lib/activerecord_cached.rb
CHANGED
@@ -8,6 +8,7 @@ require_relative "activerecord_cached/cache"
|
|
8
8
|
require_relative "activerecord_cached/configuration"
|
9
9
|
require_relative "activerecord_cached/limit_checks"
|
10
10
|
require_relative "activerecord_cached/railtie" if defined?(::Rails::Railtie)
|
11
|
+
require_relative "activerecord_cached/redis_mutex"
|
11
12
|
require_relative "activerecord_cached/version"
|
12
13
|
|
13
14
|
module ActiveRecordCached
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: activerecord_cached
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Janosch Müller
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-01-
|
11
|
+
date: 2024-01-25 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|
@@ -56,6 +56,7 @@ files:
|
|
56
56
|
- lib/activerecord_cached/configuration.rb
|
57
57
|
- lib/activerecord_cached/limit_checks.rb
|
58
58
|
- lib/activerecord_cached/railtie.rb
|
59
|
+
- lib/activerecord_cached/redis_mutex.rb
|
59
60
|
- lib/activerecord_cached/version.rb
|
60
61
|
homepage: https://github.com/jaynetics/activerecord_cached
|
61
62
|
licenses:
|