second_level_cache 2.6.4 → 2.7.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +3 -0
- data/Gemfile +1 -2
- data/LICENSE +21 -0
- data/README.md +19 -14
- data/lib/second_level_cache/active_record/fetch_by_uniq_key.rb +16 -15
- data/lib/second_level_cache/active_record/has_one_association.rb +1 -2
- data/lib/second_level_cache/active_record/preloader/association.rb +61 -0
- data/lib/second_level_cache/active_record/preloader/legacy.rb +57 -0
- data/lib/second_level_cache/active_record.rb +13 -4
- data/lib/second_level_cache/adapter/paranoia.rb +2 -1
- data/lib/second_level_cache/mixin.rb +2 -1
- data/lib/second_level_cache/version.rb +1 -1
- data/second_level_cache.gemspec +13 -12
- data/test/belongs_to_association_test.rb +1 -1
- data/test/test_helper.rb +1 -1
- metadata +32 -42
- data/lib/second_level_cache/active_record/preloader.rb +0 -51
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9161291526eed87047708e5128c64e2907ddf61f017dbf073831c1281b6b504d
|
4
|
+
data.tar.gz: 192fffc74fe056dd8bc2d8e6486f4f5de380698bfc728d65bf02853c110a9d66
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 84e57628cd9eb58d52d7a4d5c23844d8e470ee599d865845287ac9fee9b0659fe7a0b563ab5f2e6d7fd3c59ffce6de543ec0ae91a36e77fd8d2c628f05cd53a1
|
7
|
+
data.tar.gz: 60aa90c2dfa0be0920ee121772a426c3ce209423a4c5a2c28bf61efad3ad5da14738317f40df8704e6628db9fc7a2e439619ca2bdb1d7cb2d22073d1ccded656
|
data/CHANGELOG.md
CHANGED
data/Gemfile
CHANGED
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2020 Hackershare
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
# SecondLevelCache
|
2
2
|
|
3
3
|
[![Gem Version](https://badge.fury.io/rb/second_level_cache.svg)](http://badge.fury.io/rb/second_level_cache)
|
4
|
-
[![
|
4
|
+
[![build](https://github.com/hooopo/second_level_cache/actions/workflows/build.yml/badge.svg)](https://github.com/hooopo/second_level_cache/actions/workflows/build.yml)
|
5
5
|
[![Code Climate](https://codeclimate.com/github/hooopo/second_level_cache.svg)](https://codeclimate.com/github/hooopo/second_level_cache)
|
6
6
|
|
7
7
|
SecondLevelCache is a write-through and read-through caching library inspired by Cache Money and cache_fu, support ActiveRecord 4, ActiveRecord 5 and ActiveRecord 6.
|
@@ -10,11 +10,16 @@ Read-Through: Queries by ID, like `current_user.articles.find(params[:id])`, wil
|
|
10
10
|
|
11
11
|
Write-Through: As objects are created, updated, and deleted, all of the caches are automatically kept up-to-date and coherent.
|
12
12
|
|
13
|
-
|
14
13
|
## Install
|
15
14
|
|
16
15
|
In your gem file:
|
17
16
|
|
17
|
+
ActiveRecord 7
|
18
|
+
|
19
|
+
```ruby
|
20
|
+
gem 'second_level_cache', '~> 2.7'
|
21
|
+
```
|
22
|
+
|
18
23
|
ActiveRecord 5.2 and 6.0:
|
19
24
|
|
20
25
|
```ruby
|
@@ -97,9 +102,9 @@ User.select("id, name").find(1)
|
|
97
102
|
|
98
103
|
## Notice
|
99
104
|
|
100
|
-
|
101
|
-
|
102
|
-
|
105
|
+
- SecondLevelCache cache by model name and id, so only find_one query will work.
|
106
|
+
- Only equal conditions query WILL get cache; and SQL string query like `User.where("name = 'Hooopo'").find(1)` WILL NOT work.
|
107
|
+
- SecondLevelCache sync cache after transaction commit:
|
103
108
|
|
104
109
|
```ruby
|
105
110
|
# user and account's write_second_level_cache operation will invoke after the logger.
|
@@ -117,7 +122,7 @@ end # <- Cache write
|
|
117
122
|
Rails.logger.info "info"
|
118
123
|
```
|
119
124
|
|
120
|
-
|
125
|
+
- If you are using SecondLevelCache with database_cleaner, you should set cleaning strategy to `:truncation`:
|
121
126
|
|
122
127
|
```ruby
|
123
128
|
DatabaseCleaner.strategy = :truncation
|
@@ -133,15 +138,15 @@ config.cache_store = [:dalli_store, APP_CONFIG["memcached_host"], { namespace: "
|
|
133
138
|
|
134
139
|
## Tips:
|
135
140
|
|
136
|
-
|
137
|
-
you can only change the `cache_key_prefix` (default: `slc`):
|
141
|
+
- When you want to clear only second level cache apart from other cache for example fragment cache in cache store,
|
142
|
+
you can only change the `cache_key_prefix` (default: `slc`):
|
138
143
|
|
139
144
|
```ruby
|
140
145
|
SecondLevelCache.configure.cache_key_prefix = "slc1"
|
141
146
|
```
|
142
147
|
|
143
|
-
|
144
|
-
|
148
|
+
- SecondLevelCache was added model schema digest as cache version, this means when you add/remove/change columns, the caches of this Model will expires.
|
149
|
+
- When your want change the model cache version by manualy, just add the `version` option like this:
|
145
150
|
|
146
151
|
```ruby
|
147
152
|
class User < ActiveRecord::Base
|
@@ -149,7 +154,7 @@ class User < ActiveRecord::Base
|
|
149
154
|
end
|
150
155
|
```
|
151
156
|
|
152
|
-
|
157
|
+
- It provides a great feature, not hits db when fetching record via unique key (not primary key).
|
153
158
|
|
154
159
|
```ruby
|
155
160
|
# this will fetch from cache
|
@@ -160,7 +165,7 @@ post = Post.fetch_by_uniq_keys(user_id: 2, slug: "foo")
|
|
160
165
|
user = User.fetch_by_uniq_keys!(nick_name: "hooopo") # this will raise `ActiveRecord::RecordNotFound` Exception when nick name not exists.
|
161
166
|
```
|
162
167
|
|
163
|
-
|
168
|
+
- You can use Rails's [Eager Loading](http://guides.rubyonrails.org/active_record_querying.html#eager-loading-associations) feature as normal. Even better, second_level_cache will transform the `IN` query into a Rails.cache.multi_read operation. For example:
|
164
169
|
|
165
170
|
```ruby
|
166
171
|
Answer.includes(:question).limit(10).order("id DESC").each{|answer| answer.question.title}
|
@@ -171,8 +176,8 @@ Answer Load (0.2ms) SELECT `answers`.* FROM `answers` ORDER BY id DESC LIMIT 10
|
|
171
176
|
|
172
177
|
## Original design by:
|
173
178
|
|
174
|
-
|
175
|
-
|
179
|
+
- [chloerei](https://github.com/chloerei)
|
180
|
+
- [hooopo](https://github.com/hooopo)
|
176
181
|
|
177
182
|
## Contributors
|
178
183
|
|
@@ -9,10 +9,10 @@ module SecondLevelCache
|
|
9
9
|
|
10
10
|
if obj_id
|
11
11
|
record = begin
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
12
|
+
find(obj_id)
|
13
|
+
rescue
|
14
|
+
nil
|
15
|
+
end
|
16
16
|
end
|
17
17
|
return record if record_attributes_equal_where_values?(record, where_values)
|
18
18
|
record = where(where_values).first
|
@@ -42,20 +42,21 @@ module SecondLevelCache
|
|
42
42
|
end
|
43
43
|
|
44
44
|
private
|
45
|
-
def cache_uniq_key(where_values)
|
46
|
-
keys = where_values.collect do |k, v|
|
47
|
-
v = Digest::MD5.hexdigest(v) if v.respond_to?(:size) && v.size >= 32
|
48
|
-
[k, v].join("_")
|
49
|
-
end
|
50
45
|
|
51
|
-
|
52
|
-
|
46
|
+
def cache_uniq_key(where_values)
|
47
|
+
keys = where_values.collect do |k, v|
|
48
|
+
v = Digest::MD5.hexdigest(v) if v.respond_to?(:size) && v.size >= 32
|
49
|
+
[k, v].join("_")
|
53
50
|
end
|
54
51
|
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
52
|
+
ext_key = keys.join(",")
|
53
|
+
"#{SecondLevelCache.configure.cache_key_prefix}/uniq_key_#{name}_#{ext_key}"
|
54
|
+
end
|
55
|
+
|
56
|
+
def record_attributes_equal_where_values?(record, where_values)
|
57
|
+
# https://api.rubyonrails.org/classes/ActiveRecord/ModelSchema/ClassMethods.html#method-i-type_for_attribute
|
58
|
+
where_values.all? { |k, v| record&.read_attribute(k) == type_for_attribute(k).cast(v) }
|
59
|
+
end
|
59
60
|
end
|
60
61
|
end
|
61
62
|
end
|
@@ -11,8 +11,7 @@ module SecondLevelCache
|
|
11
11
|
|
12
12
|
through = reflection.options[:through]
|
13
13
|
record = if through
|
14
|
-
return super
|
15
|
-
return super unless klass.reflections[through.to_s].klass.second_level_cache_enabled?
|
14
|
+
return super unless owner.class.reflections[through.to_s].klass.second_level_cache_enabled?
|
16
15
|
begin
|
17
16
|
reflection.klass.find(owner.send(through).read_attribute(reflection.foreign_key))
|
18
17
|
rescue StandardError
|
@@ -0,0 +1,61 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module SecondLevelCache
|
4
|
+
module ActiveRecord
|
5
|
+
module Associations
|
6
|
+
module Preloader
|
7
|
+
module Association
|
8
|
+
# Override load_query method for add Association instance in arguments to LoaderQuery
|
9
|
+
# https://github.com/rails/rails/blob/7-0-stable/activerecord/lib/active_record/associations/preloader/association.rb#L148
|
10
|
+
def loader_query
|
11
|
+
::ActiveRecord::Associations::Preloader::Association::LoaderQuery.new(self, scope, association_key_name)
|
12
|
+
end
|
13
|
+
|
14
|
+
# Override load_records_for_keys for use SecondLevelCache before preload association
|
15
|
+
# https://github.com/rails/rails/blob/8f5b35b6107c28125b571b9842e248b13f804e5c/activerecord/lib/active_record/associations/preloader/association.rb#L7
|
16
|
+
module LoaderQuery
|
17
|
+
attr_reader :association
|
18
|
+
|
19
|
+
delegate :klass, to: :association
|
20
|
+
|
21
|
+
def initialize(association, scope, association_key_name)
|
22
|
+
@association = association
|
23
|
+
@scope = scope
|
24
|
+
@association_key_name = association_key_name
|
25
|
+
end
|
26
|
+
|
27
|
+
def reflection
|
28
|
+
association.send(:reflection)
|
29
|
+
end
|
30
|
+
|
31
|
+
def load_records_for_keys(keys, &block)
|
32
|
+
ids = keys.to_a
|
33
|
+
|
34
|
+
return super unless klass.second_level_cache_enabled?
|
35
|
+
return super unless reflection.is_a?(::ActiveRecord::Reflection::BelongsToReflection)
|
36
|
+
return super if klass.default_scopes.present? || reflection.scope
|
37
|
+
return super if association_key_name.to_s != klass.primary_key
|
38
|
+
|
39
|
+
map_cache_keys = ids.map { |id| klass.second_level_cache_key(id) }
|
40
|
+
records_from_cache = ::SecondLevelCache.cache_store.read_multi(*map_cache_keys)
|
41
|
+
record_marshals = RecordMarshal.load_multi(records_from_cache.values, &block)
|
42
|
+
|
43
|
+
# NOTICE
|
44
|
+
# Rails.cache.read_multi return hash that has keys only hitted.
|
45
|
+
# eg. Rails.cache.read_multi(1,2,3) => {2 => hit_value, 3 => hit_value}
|
46
|
+
hitted_ids = record_marshals.map { |record| record.read_attribute(association_key_name).to_s }
|
47
|
+
missed_ids = ids.map(&:to_s) - hitted_ids
|
48
|
+
ActiveSupport::Notifications.instrument("preload.second_level_cache", key: association_key_name, hit: hitted_ids, miss: missed_ids)
|
49
|
+
return SecondLevelCache::RecordRelation.new(record_marshals) if missed_ids.empty?
|
50
|
+
|
51
|
+
records_from_db = super(missed_ids.to_set, &block)
|
52
|
+
records_from_db.map { |r| r.write_second_level_cache }
|
53
|
+
|
54
|
+
SecondLevelCache::RecordRelation.new(records_from_db + record_marshals)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module SecondLevelCache
|
4
|
+
module ActiveRecord
|
5
|
+
module Associations
|
6
|
+
module Preloader
|
7
|
+
module Association
|
8
|
+
# For < Rails 7
|
9
|
+
module Legacy
|
10
|
+
RAILS6 = ::ActiveRecord.version >= ::Gem::Version.new("6")
|
11
|
+
|
12
|
+
def records_for(ids, &block)
|
13
|
+
return super unless klass.second_level_cache_enabled?
|
14
|
+
return super unless reflection.is_a?(::ActiveRecord::Reflection::BelongsToReflection)
|
15
|
+
return super if klass.default_scopes.present? || reflection.scope
|
16
|
+
return super if association_key_name.to_s != klass.primary_key
|
17
|
+
|
18
|
+
map_cache_keys = ids.map { |id| klass.second_level_cache_key(id) }
|
19
|
+
records_from_cache = ::SecondLevelCache.cache_store.read_multi(*map_cache_keys)
|
20
|
+
|
21
|
+
record_marshals = if RAILS6
|
22
|
+
RecordMarshal.load_multi(records_from_cache.values) do |record|
|
23
|
+
# This block is copy from:
|
24
|
+
# https://github.com/rails/rails/blob/6-0-stable/activerecord/lib/active_record/associations/preloader/association.rb#L101
|
25
|
+
owner = owners_by_key[convert_key(record[association_key_name])].first
|
26
|
+
association = owner.association(reflection.name)
|
27
|
+
association.set_inverse_instance(record)
|
28
|
+
end
|
29
|
+
else
|
30
|
+
RecordMarshal.load_multi(records_from_cache.values, &block)
|
31
|
+
end
|
32
|
+
|
33
|
+
# NOTICE
|
34
|
+
# Rails.cache.read_multi return hash that has keys only hitted.
|
35
|
+
# eg. Rails.cache.read_multi(1,2,3) => {2 => hit_value, 3 => hit_value}
|
36
|
+
hitted_ids = record_marshals.map { |record| record.read_attribute(association_key_name).to_s }
|
37
|
+
missed_ids = ids.map(&:to_s) - hitted_ids
|
38
|
+
ActiveSupport::Notifications.instrument("preload.second_level_cache", key: association_key_name, hit: hitted_ids, miss: missed_ids)
|
39
|
+
return SecondLevelCache::RecordRelation.new(record_marshals) if missed_ids.empty?
|
40
|
+
|
41
|
+
records_from_db = super(missed_ids, &block)
|
42
|
+
records_from_db.map { |r| write_cache(r) }
|
43
|
+
|
44
|
+
SecondLevelCache::RecordRelation.new(records_from_db + record_marshals)
|
45
|
+
end
|
46
|
+
|
47
|
+
private
|
48
|
+
|
49
|
+
def write_cache(record)
|
50
|
+
record.write_second_level_cache
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -8,7 +8,8 @@ require "second_level_cache/active_record/finder_methods"
|
|
8
8
|
require "second_level_cache/active_record/persistence"
|
9
9
|
require "second_level_cache/active_record/belongs_to_association"
|
10
10
|
require "second_level_cache/active_record/has_one_association"
|
11
|
-
require "second_level_cache/active_record/preloader"
|
11
|
+
require "second_level_cache/active_record/preloader/association"
|
12
|
+
require "second_level_cache/active_record/preloader/legacy"
|
12
13
|
|
13
14
|
# http://api.rubyonrails.org/classes/ActiveSupport/LazyLoadHooks.html
|
14
15
|
# ActiveSupport.run_load_hooks(:active_record, ActiveRecord::Base)
|
@@ -27,7 +28,15 @@ ActiveSupport.on_load(:active_record, run_once: true) do
|
|
27
28
|
ActiveRecord::Associations::BelongsToAssociation.send(:prepend, SecondLevelCache::ActiveRecord::Associations::BelongsToAssociation)
|
28
29
|
ActiveRecord::Associations::HasOneAssociation.send(:prepend, SecondLevelCache::ActiveRecord::Associations::HasOneAssociation)
|
29
30
|
ActiveRecord::Relation.send(:prepend, SecondLevelCache::ActiveRecord::FinderMethods)
|
30
|
-
|
31
|
-
# https://github.com/rails/rails/
|
32
|
-
ActiveRecord::
|
31
|
+
|
32
|
+
# https://github.com/rails/rails/blob/6-0-stable/activerecord/lib/active_record/associations/preloader/association.rb#L117
|
33
|
+
if ::ActiveRecord.version < ::Gem::Version.new("7")
|
34
|
+
ActiveRecord::Associations::Preloader::Association.send(:prepend, SecondLevelCache::ActiveRecord::Associations::Preloader::Association::Legacy)
|
35
|
+
end
|
36
|
+
|
37
|
+
if ::ActiveRecord.version >= ::Gem::Version.new("7")
|
38
|
+
# https://github.com/rails/rails/blob/7-0-stable/activerecord/lib/active_record/associations/preloader/association.rb#L25
|
39
|
+
ActiveRecord::Associations::Preloader::Association.send(:prepend, SecondLevelCache::ActiveRecord::Associations::Preloader::Association)
|
40
|
+
ActiveRecord::Associations::Preloader::Association::LoaderQuery.send(:prepend, SecondLevelCache::ActiveRecord::Associations::Preloader::Association::LoaderQuery)
|
41
|
+
end
|
33
42
|
end
|
@@ -76,6 +76,7 @@ module SecondLevelCache
|
|
76
76
|
expires_in = klass.second_level_cache_options[:expires_in]
|
77
77
|
SecondLevelCache.cache_store.write(second_level_cache_key, marshal, expires_in: expires_in)
|
78
78
|
end
|
79
|
-
|
79
|
+
|
80
|
+
alias_method :update_second_level_cache, :write_second_level_cache
|
80
81
|
end
|
81
82
|
end
|
data/second_level_cache.gemspec
CHANGED
@@ -5,10 +5,10 @@ lib = File.expand_path("../lib", __FILE__)
|
|
5
5
|
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
6
6
|
|
7
7
|
Gem::Specification.new do |gem|
|
8
|
-
gem.authors
|
9
|
-
gem.email
|
10
|
-
gem.description
|
11
|
-
gem.summary
|
8
|
+
gem.authors = ["Hooopo"]
|
9
|
+
gem.email = ["hoooopo@gmail.com"]
|
10
|
+
gem.description = "Write Through and Read Through caching library inspired by CacheMoney and cache_fu, support ActiveRecord 4."
|
11
|
+
gem.summary = <<-SUMMARY
|
12
12
|
SecondLevelCache is a write-through and read-through caching library inspired by Cache Money and cache_fu, support only Rails3 and ActiveRecord.
|
13
13
|
|
14
14
|
Read-Through: Queries by ID, like current_user.articles.find(params[:id]), will first look in cache store and then look in the database for the results of that query. If there is a cache miss, it will populate the cache.
|
@@ -16,23 +16,24 @@ Gem::Specification.new do |gem|
|
|
16
16
|
Write-Through: As objects are created, updated, and deleted, all of the caches are automatically kept up-to-date and coherent.
|
17
17
|
SUMMARY
|
18
18
|
|
19
|
-
gem.homepage
|
19
|
+
gem.homepage = "https://github.com/hooopo/second_level_cache"
|
20
20
|
|
21
|
-
gem.files
|
21
|
+
gem.files = Dir.glob("lib/**/*.rb") + [
|
22
22
|
"README.md",
|
23
|
+
"LICENSE",
|
23
24
|
"Rakefile",
|
24
25
|
"Gemfile",
|
25
26
|
"CHANGELOG.md",
|
26
27
|
"second_level_cache.gemspec"
|
27
28
|
]
|
28
|
-
gem.test_files
|
29
|
-
gem.executables
|
30
|
-
gem.name
|
29
|
+
gem.test_files = Dir.glob("test/**/*.rb")
|
30
|
+
gem.executables = gem.files.grep(%r{^bin/})
|
31
|
+
gem.name = "second_level_cache"
|
31
32
|
gem.require_paths = ["lib"]
|
32
|
-
gem.version
|
33
|
+
gem.version = SecondLevelCache::VERSION
|
33
34
|
|
34
|
-
gem.add_runtime_dependency "activerecord",
|
35
|
-
gem.add_runtime_dependency "activesupport",
|
35
|
+
gem.add_runtime_dependency "activerecord", ">= 6.0"
|
36
|
+
gem.add_runtime_dependency "activesupport", ">= 6.0"
|
36
37
|
|
37
38
|
gem.add_development_dependency "database_cleaner"
|
38
39
|
gem.add_development_dependency "rake"
|
data/test/test_helper.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: second_level_cache
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.
|
4
|
+
version: 2.7.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Hooopo
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2021-12-17 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|
@@ -16,40 +16,28 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - ">="
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: '
|
20
|
-
- - "<"
|
21
|
-
- !ruby/object:Gem::Version
|
22
|
-
version: '7'
|
19
|
+
version: '6.0'
|
23
20
|
type: :runtime
|
24
21
|
prerelease: false
|
25
22
|
version_requirements: !ruby/object:Gem::Requirement
|
26
23
|
requirements:
|
27
24
|
- - ">="
|
28
25
|
- !ruby/object:Gem::Version
|
29
|
-
version: '
|
30
|
-
- - "<"
|
31
|
-
- !ruby/object:Gem::Version
|
32
|
-
version: '7'
|
26
|
+
version: '6.0'
|
33
27
|
- !ruby/object:Gem::Dependency
|
34
28
|
name: activesupport
|
35
29
|
requirement: !ruby/object:Gem::Requirement
|
36
30
|
requirements:
|
37
31
|
- - ">="
|
38
32
|
- !ruby/object:Gem::Version
|
39
|
-
version: '
|
40
|
-
- - "<"
|
41
|
-
- !ruby/object:Gem::Version
|
42
|
-
version: '7'
|
33
|
+
version: '6.0'
|
43
34
|
type: :runtime
|
44
35
|
prerelease: false
|
45
36
|
version_requirements: !ruby/object:Gem::Requirement
|
46
37
|
requirements:
|
47
38
|
- - ">="
|
48
39
|
- !ruby/object:Gem::Version
|
49
|
-
version: '
|
50
|
-
- - "<"
|
51
|
-
- !ruby/object:Gem::Version
|
52
|
-
version: '7'
|
40
|
+
version: '6.0'
|
53
41
|
- !ruby/object:Gem::Dependency
|
54
42
|
name: database_cleaner
|
55
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -116,6 +104,7 @@ extra_rdoc_files: []
|
|
116
104
|
files:
|
117
105
|
- CHANGELOG.md
|
118
106
|
- Gemfile
|
107
|
+
- LICENSE
|
119
108
|
- README.md
|
120
109
|
- Rakefile
|
121
110
|
- lib/second_level_cache.rb
|
@@ -127,7 +116,8 @@ files:
|
|
127
116
|
- lib/second_level_cache/active_record/finder_methods.rb
|
128
117
|
- lib/second_level_cache/active_record/has_one_association.rb
|
129
118
|
- lib/second_level_cache/active_record/persistence.rb
|
130
|
-
- lib/second_level_cache/active_record/preloader.rb
|
119
|
+
- lib/second_level_cache/active_record/preloader/association.rb
|
120
|
+
- lib/second_level_cache/active_record/preloader/legacy.rb
|
131
121
|
- lib/second_level_cache/adapter/paranoia.rb
|
132
122
|
- lib/second_level_cache/config.rb
|
133
123
|
- lib/second_level_cache/log_subscriber.rb
|
@@ -185,7 +175,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
185
175
|
- !ruby/object:Gem::Version
|
186
176
|
version: '0'
|
187
177
|
requirements: []
|
188
|
-
rubygems_version: 3.
|
178
|
+
rubygems_version: 3.2.3
|
189
179
|
signing_key:
|
190
180
|
specification_version: 4
|
191
181
|
summary: 'SecondLevelCache is a write-through and read-through caching library inspired
|
@@ -195,34 +185,34 @@ summary: 'SecondLevelCache is a write-through and read-through caching library i
|
|
195
185
|
is a cache miss, it will populate the cache. Write-Through: As objects are created,
|
196
186
|
updated, and deleted, all of the caches are automatically kept up-to-date and coherent.'
|
197
187
|
test_files:
|
198
|
-
- test/
|
199
|
-
- test/
|
200
|
-
- test/require_test.rb
|
201
|
-
- test/paranoid_test.rb
|
202
|
-
- test/finder_methods_test.rb
|
203
|
-
- test/preloader_has_one_test.rb
|
204
|
-
- test/preloader_non_integer_test.rb
|
188
|
+
- test/active_record_test_case_helper.rb
|
189
|
+
- test/base_test.rb
|
205
190
|
- test/belongs_to_association_test.rb
|
206
|
-
- test/second_level_cache_test.rb
|
207
191
|
- test/enum_attr_test.rb
|
208
|
-
- test/
|
209
|
-
- test/
|
210
|
-
- test/
|
211
|
-
- test/single_table_inheritance_test.rb
|
212
|
-
- test/model/image.rb
|
192
|
+
- test/fetch_by_uniq_key_test.rb
|
193
|
+
- test/finder_methods_test.rb
|
194
|
+
- test/has_one_association_test.rb
|
213
195
|
- test/model/account.rb
|
214
|
-
- test/model/order_item.rb
|
215
|
-
- test/model/contribution.rb
|
216
|
-
- test/model/book.rb
|
217
|
-
- test/model/topic.rb
|
218
196
|
- test/model/animal.rb
|
197
|
+
- test/model/application_record.rb
|
198
|
+
- test/model/book.rb
|
199
|
+
- test/model/contribution.rb
|
200
|
+
- test/model/image.rb
|
219
201
|
- test/model/order.rb
|
202
|
+
- test/model/order_item.rb
|
220
203
|
- test/model/paranoid.rb
|
221
|
-
- test/model/application_record.rb
|
222
204
|
- test/model/post.rb
|
205
|
+
- test/model/topic.rb
|
223
206
|
- test/model/user.rb
|
224
|
-
- test/
|
207
|
+
- test/paranoid_test.rb
|
208
|
+
- test/persistence_test.rb
|
209
|
+
- test/polymorphic_association_test.rb
|
210
|
+
- test/preloader_belongs_to_test.rb
|
211
|
+
- test/preloader_has_many_test.rb
|
212
|
+
- test/preloader_has_one_test.rb
|
213
|
+
- test/preloader_non_integer_test.rb
|
214
|
+
- test/record_marshal_test.rb
|
215
|
+
- test/require_test.rb
|
216
|
+
- test/second_level_cache_test.rb
|
217
|
+
- test/single_table_inheritance_test.rb
|
225
218
|
- test/test_helper.rb
|
226
|
-
- test/has_one_association_test.rb
|
227
|
-
- test/base_test.rb
|
228
|
-
- test/active_record_test_case_helper.rb
|
@@ -1,51 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module SecondLevelCache
|
4
|
-
module ActiveRecord
|
5
|
-
module Associations
|
6
|
-
module Preloader
|
7
|
-
RAILS6 = ::ActiveRecord.version >= ::Gem::Version.new("6")
|
8
|
-
|
9
|
-
def records_for(ids, &block)
|
10
|
-
return super unless klass.second_level_cache_enabled?
|
11
|
-
return super unless reflection.is_a?(::ActiveRecord::Reflection::BelongsToReflection)
|
12
|
-
return super if klass.default_scopes.present? || reflection.scope
|
13
|
-
return super if association_key_name.to_s != klass.primary_key
|
14
|
-
|
15
|
-
map_cache_keys = ids.map { |id| klass.second_level_cache_key(id) }
|
16
|
-
records_from_cache = ::SecondLevelCache.cache_store.read_multi(*map_cache_keys)
|
17
|
-
|
18
|
-
record_marshals = if RAILS6
|
19
|
-
RecordMarshal.load_multi(records_from_cache.values) do |record|
|
20
|
-
# This block is copy from:
|
21
|
-
# https://github.com/rails/rails/blob/6-0-stable/activerecord/lib/active_record/associations/preloader/association.rb#L101
|
22
|
-
owner = owners_by_key[convert_key(record[association_key_name])].first
|
23
|
-
association = owner.association(reflection.name)
|
24
|
-
association.set_inverse_instance(record)
|
25
|
-
end
|
26
|
-
else
|
27
|
-
RecordMarshal.load_multi(records_from_cache.values, &block)
|
28
|
-
end
|
29
|
-
|
30
|
-
# NOTICE
|
31
|
-
# Rails.cache.read_multi return hash that has keys only hitted.
|
32
|
-
# eg. Rails.cache.read_multi(1,2,3) => {2 => hit_value, 3 => hit_value}
|
33
|
-
hitted_ids = record_marshals.map { |record| record.read_attribute(association_key_name).to_s }
|
34
|
-
missed_ids = ids.map(&:to_s) - hitted_ids
|
35
|
-
ActiveSupport::Notifications.instrument("preload.second_level_cache", key: association_key_name, hit: hitted_ids, miss: missed_ids)
|
36
|
-
return SecondLevelCache::RecordRelation.new(record_marshals) if missed_ids.empty?
|
37
|
-
|
38
|
-
records_from_db = super(missed_ids, &block)
|
39
|
-
records_from_db.map { |r| write_cache(r) }
|
40
|
-
|
41
|
-
SecondLevelCache::RecordRelation.new(records_from_db + record_marshals)
|
42
|
-
end
|
43
|
-
|
44
|
-
private
|
45
|
-
def write_cache(record)
|
46
|
-
record.write_second_level_cache
|
47
|
-
end
|
48
|
-
end
|
49
|
-
end
|
50
|
-
end
|
51
|
-
end
|