second_level_cache 2.6.4 → 2.7.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 +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
|
[](http://badge.fury.io/rb/second_level_cache)
|
4
|
-
[](https://github.com/hooopo/second_level_cache/actions/workflows/build.yml)
|
5
5
|
[](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
|