second_level_cache 2.1.9 → 2.1.16
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/CHANGELOG.md +10 -0
- data/README.md +7 -0
- data/lib/second_level_cache/active_record.rb +2 -0
- data/lib/second_level_cache/active_record/core.rb +1 -1
- data/lib/second_level_cache/active_record/finder_methods.rb +30 -2
- data/lib/second_level_cache/active_record/has_one_association.rb +5 -2
- data/lib/second_level_cache/active_record/multi_read_from_cache.rb +21 -0
- data/lib/second_level_cache/active_record/railtie.rb +1 -0
- data/lib/second_level_cache/version.rb +2 -2
- data/second_level_cache.gemspec +1 -1
- data/test/finder_methods_test.rb +38 -0
- data/test/has_one_association_test.rb +5 -1
- data/test/model/multi_read_from_cache_test.rb +19 -0
- metadata +25 -23
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: f6149c9f3aaaa26ad9fc6a0fa5633a5602a08c68451eb04c010973e1305649f0
|
4
|
+
data.tar.gz: 1945c39303777ea938b7187a3cad868db17348153fe91bf3844e6d4868201171
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: '09e8ac1d40c06ce9642af57ff1d98a3759490196380caf5093fd203484a2b5f8eda94157d59424227824755778ae4cbeeff75399591576b79411abeee48ba99e'
|
7
|
+
data.tar.gz: 549350c57237148d9b8bb305c8ad6aa7985459111c133bffa4025de02ff61b6380f762b74d619802ad00d09863af0ef596cfdeb2c67b6466c7c50712d8354774
|
data/CHANGELOG.md
CHANGED
data/README.md
CHANGED
@@ -141,6 +141,13 @@ post = Post.fetch_by_uniq_keys(user_id: 2, slug: "foo")
|
|
141
141
|
user = User.fetch_by_uniq_keys!(nick_name: "hooopo") # this will raise `ActiveRecord::RecordNotFound` Exception when nick name not exists.
|
142
142
|
```
|
143
143
|
|
144
|
+
* multi_read_from_cache
|
145
|
+
|
146
|
+
```ruby
|
147
|
+
# this will use Rails.cache.multi_read method to fetch record, if miss, then use SQL in query.
|
148
|
+
blogs = Blog.multi_read_from_cache([1,2,3])
|
149
|
+
```
|
150
|
+
|
144
151
|
* 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:
|
145
152
|
|
146
153
|
```ruby
|
@@ -7,6 +7,7 @@ require 'second_level_cache/active_record/persistence'
|
|
7
7
|
require 'second_level_cache/active_record/belongs_to_association'
|
8
8
|
require 'second_level_cache/active_record/has_one_association'
|
9
9
|
require 'second_level_cache/active_record/preloader'
|
10
|
+
require 'second_level_cache/active_record/multi_read_from_cache'
|
10
11
|
|
11
12
|
if defined? Rails
|
12
13
|
require 'second_level_cache/active_record/railtie'
|
@@ -14,6 +15,7 @@ else
|
|
14
15
|
ActiveRecord::Base.send(:include, SecondLevelCache::Mixin)
|
15
16
|
ActiveRecord::Base.send(:include, SecondLevelCache::ActiveRecord::Base)
|
16
17
|
ActiveRecord::Base.send(:extend, SecondLevelCache::ActiveRecord::FetchByUniqKey)
|
18
|
+
ActiveRecord::Base.send(:extend, SecondLevelCache::ActiveRecord::MultiReadFromCache)
|
17
19
|
|
18
20
|
ActiveRecord::Base.send(:include, SecondLevelCache::ActiveRecord::Persistence)
|
19
21
|
ActiveRecord::Associations::BelongsToAssociation.send(:include, SecondLevelCache::ActiveRecord::Associations::BelongsToAssociation)
|
@@ -13,7 +13,7 @@ module SecondLevelCache
|
|
13
13
|
|
14
14
|
module ClassMethods
|
15
15
|
def find_with_cache(*ids)
|
16
|
-
return all.find(ids.first) if ids.size == 1 && ids.first.is_a?(
|
16
|
+
return all.find(ids.first) if ids.size == 1 && ids.first.is_a?(Integer)
|
17
17
|
find_without_cache(*ids)
|
18
18
|
end
|
19
19
|
end
|
@@ -6,10 +6,9 @@ module SecondLevelCache
|
|
6
6
|
|
7
7
|
included do
|
8
8
|
alias_method_chain :find_one, :second_level_cache
|
9
|
+
alias_method_chain :find_some, :second_level_cache
|
9
10
|
end
|
10
11
|
|
11
|
-
# TODO find_some
|
12
|
-
# https://github.com/rails/rails/blob/master/activerecord/lib/active_record/relation/finder_methods.rb#L289-L309
|
13
12
|
#
|
14
13
|
# Cacheable:
|
15
14
|
#
|
@@ -39,6 +38,35 @@ module SecondLevelCache
|
|
39
38
|
record
|
40
39
|
end
|
41
40
|
|
41
|
+
def find_some_with_second_level_cache(ids)
|
42
|
+
return find_some_without_second_level_cache(ids) unless second_level_cache_enabled?
|
43
|
+
return find_some_without_second_level_cache(ids) unless select_all_column?
|
44
|
+
|
45
|
+
if cachable?
|
46
|
+
result = multi_read_from_cache(ids)
|
47
|
+
else
|
48
|
+
result = where(:id => ids).all
|
49
|
+
end
|
50
|
+
|
51
|
+
expected_size =
|
52
|
+
if limit_value && ids.size > limit_value
|
53
|
+
limit_value
|
54
|
+
else
|
55
|
+
ids.size
|
56
|
+
end
|
57
|
+
|
58
|
+
# 11 ids with limit 3, offset 9 should give 2 results.
|
59
|
+
if offset_value && (ids.size - offset_value < expected_size)
|
60
|
+
expected_size = ids.size - offset_value
|
61
|
+
end
|
62
|
+
|
63
|
+
if result.size == expected_size
|
64
|
+
result
|
65
|
+
else
|
66
|
+
raise_record_not_found_exception!(ids, result.size, expected_size)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
42
70
|
private
|
43
71
|
|
44
72
|
def cachable?
|
@@ -12,10 +12,13 @@ module SecondLevelCache
|
|
12
12
|
|
13
13
|
def find_target_with_second_level_cache
|
14
14
|
return find_target_without_second_level_cache unless klass.second_level_cache_enabled?
|
15
|
-
|
16
|
-
|
15
|
+
|
16
|
+
return find_target_without_second_level_cache if reflection.options[:through]
|
17
|
+
# TODO: implement cache with has_one through
|
17
18
|
if reflection.options[:as]
|
18
19
|
cache_record = klass.fetch_by_uniq_keys({reflection.foreign_key => owner[reflection.active_record_primary_key], reflection.type => owner.class.base_class.name})
|
20
|
+
elsif reflection.scope
|
21
|
+
cache_record = klass.fetch_by_uniq_keys(create_scope)
|
19
22
|
else
|
20
23
|
cache_record = klass.fetch_by_uniq_key(owner[reflection.active_record_primary_key], reflection.foreign_key)
|
21
24
|
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module SecondLevelCache
|
2
|
+
module ActiveRecord
|
3
|
+
module MultiReadFromCache
|
4
|
+
def multi_read_from_cache(ids)
|
5
|
+
map_cache_keys = ids.map{|id| second_level_cache_key(id)}
|
6
|
+
records_from_cache = ::SecondLevelCache.cache_store.read_multi(*map_cache_keys)
|
7
|
+
hitted_ids = records_from_cache.map{|key, _| key.split("/")[2].to_i}
|
8
|
+
missed_ids = ids.map{|x| x.to_i} - hitted_ids
|
9
|
+
|
10
|
+
::SecondLevelCache::Config.logger.info "missed ids -> #{missed_ids.inspect} | hitted ids -> #{hitted_ids.inspect}"
|
11
|
+
|
12
|
+
if missed_ids.empty?
|
13
|
+
RecordMarshal.load_multi(records_from_cache.values)
|
14
|
+
else
|
15
|
+
records_from_db = where(:id => missed_ids)
|
16
|
+
records_from_db.map{|record| record.write_second_level_cache ; record} + RecordMarshal.load_multi(records_from_cache.values)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -3,6 +3,7 @@ class SecondLevelCache::ActiveRecord::Railtie < Rails::Railtie
|
|
3
3
|
ActiveRecord::Base.send(:include, SecondLevelCache::Mixin)
|
4
4
|
ActiveRecord::Base.send(:include, SecondLevelCache::ActiveRecord::Base)
|
5
5
|
ActiveRecord::Base.send(:extend, SecondLevelCache::ActiveRecord::FetchByUniqKey)
|
6
|
+
ActiveRecord::Base.send(:extend, SecondLevelCache::ActiveRecord::MultiReadFromCache)
|
6
7
|
|
7
8
|
ActiveRecord::Base.send(:include, SecondLevelCache::ActiveRecord::Persistence)
|
8
9
|
ActiveRecord::Associations::BelongsToAssociation.send(:include, SecondLevelCache::ActiveRecord::Associations::BelongsToAssociation)
|
data/second_level_cache.gemspec
CHANGED
@@ -33,7 +33,7 @@ Gem::Specification.new do |gem|
|
|
33
33
|
gem.add_runtime_dependency "activesupport", ["> 4.0.0", "< 5.0"]
|
34
34
|
|
35
35
|
gem.add_runtime_dependency "activerecord", ["> 4.0.0", "< 5.0"]
|
36
|
-
gem.add_development_dependency "sqlite3"
|
36
|
+
gem.add_development_dependency "sqlite3", "~> 1.3.6"
|
37
37
|
gem.add_development_dependency "rake"
|
38
38
|
gem.add_development_dependency 'pry'
|
39
39
|
gem.add_development_dependency "database_cleaner", "~> 1.3.0"
|
data/test/finder_methods_test.rb
CHANGED
@@ -4,6 +4,7 @@ require 'test_helper'
|
|
4
4
|
class FinderMethodsTest < ActiveSupport::TestCase
|
5
5
|
def setup
|
6
6
|
@user = User.create :name => 'csdn', :email => 'test@csdn.com'
|
7
|
+
@other_user = User.create :name => 'shopper+', :email => 'test@shopperplus.com'
|
7
8
|
end
|
8
9
|
|
9
10
|
def test_should_find_without_cache
|
@@ -41,4 +42,41 @@ class FinderMethodsTest < ActiveSupport::TestCase
|
|
41
42
|
end
|
42
43
|
refute_equal @user.name, @from_db.name
|
43
44
|
end
|
45
|
+
|
46
|
+
def test_find_some_record
|
47
|
+
@users = User.find(@user.id, @other_user.id)
|
48
|
+
assert_equal 2, @users.size
|
49
|
+
end
|
50
|
+
|
51
|
+
def test_find_some_record_without_second_level_cache
|
52
|
+
User.without_second_level_cache do
|
53
|
+
@users = User.find(@user.id, @other_user.id)
|
54
|
+
end
|
55
|
+
assert_equal 2, @users.size
|
56
|
+
end
|
57
|
+
|
58
|
+
def test_missing_id_will_raise_for_find_some
|
59
|
+
assert_raises(ActiveRecord::RecordNotFound) do
|
60
|
+
@users = User.find(@user.id, User.last.id + 10000)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def test_filter_works_fine_for_find_some
|
65
|
+
assert_raises(ActiveRecord::RecordNotFound) do
|
66
|
+
@users = User.where("name is null").find(@user.id, @other_user.id)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def test_half_in_cache_for_find_some
|
71
|
+
@user.expire_second_level_cache
|
72
|
+
@users = User.find(@user.id, @other_user.id)
|
73
|
+
assert_equal 2, @users.size
|
74
|
+
end
|
75
|
+
|
76
|
+
def test_no_record_in_cache_for_find_some
|
77
|
+
@user.expire_second_level_cache
|
78
|
+
@other_user.expire_second_level_cache
|
79
|
+
@users = User.find(@user.id, @other_user.id)
|
80
|
+
assert_equal 2, @users.size
|
81
|
+
end
|
44
82
|
end
|
@@ -31,7 +31,11 @@ class HasOneAssociationTest < ActiveSupport::TestCase
|
|
31
31
|
user.create_namespace(name: 'hooopo')
|
32
32
|
group_namespace2 = Namespace.create(user_id: user.id, name: 'rails', kind: 'group')
|
33
33
|
assert_not_equal user.namespace, nil
|
34
|
-
|
34
|
+
assert_equal user.reload.namespace.name, 'hooopo'
|
35
|
+
clear_user = user.reload
|
36
|
+
assert_no_queries do
|
37
|
+
clear_user.namespace
|
38
|
+
end
|
35
39
|
assert_equal clear_user.namespace.name, 'hooopo'
|
36
40
|
end
|
37
41
|
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
2
|
+
require 'test_helper'
|
3
|
+
|
4
|
+
class MultiReadFromCacheTest < ActiveSupport::TestCase
|
5
|
+
def setup
|
6
|
+
@user = User.create :name => 'hooopo', :email => 'hoooopo@gmail.com'
|
7
|
+
@other_user = User.create :name => 'hoooopo', :email => "hooooopo@gmail.com"
|
8
|
+
end
|
9
|
+
|
10
|
+
def test_multi_read_from_cache
|
11
|
+
result = User.multi_read_from_cache([@user.id, @other_user.id])
|
12
|
+
assert_equal 2, result.size
|
13
|
+
end
|
14
|
+
|
15
|
+
def test_multi_read_not_exist_id_from_cache
|
16
|
+
result = User.multi_read_from_cache([@user.id, @other_user.id + 100])
|
17
|
+
assert_equal 1, result.size
|
18
|
+
end
|
19
|
+
end
|
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.1.
|
4
|
+
version: 2.1.16
|
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: 2020-11-18 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -54,16 +54,16 @@ dependencies:
|
|
54
54
|
name: sqlite3
|
55
55
|
requirement: !ruby/object:Gem::Requirement
|
56
56
|
requirements:
|
57
|
-
- - "
|
57
|
+
- - "~>"
|
58
58
|
- !ruby/object:Gem::Version
|
59
|
-
version:
|
59
|
+
version: 1.3.6
|
60
60
|
type: :development
|
61
61
|
prerelease: false
|
62
62
|
version_requirements: !ruby/object:Gem::Requirement
|
63
63
|
requirements:
|
64
|
-
- - "
|
64
|
+
- - "~>"
|
65
65
|
- !ruby/object:Gem::Version
|
66
|
-
version:
|
66
|
+
version: 1.3.6
|
67
67
|
- !ruby/object:Gem::Dependency
|
68
68
|
name: rake
|
69
69
|
requirement: !ruby/object:Gem::Requirement
|
@@ -126,6 +126,7 @@ files:
|
|
126
126
|
- lib/second_level_cache/active_record/fetch_by_uniq_key.rb
|
127
127
|
- lib/second_level_cache/active_record/finder_methods.rb
|
128
128
|
- lib/second_level_cache/active_record/has_one_association.rb
|
129
|
+
- lib/second_level_cache/active_record/multi_read_from_cache.rb
|
129
130
|
- lib/second_level_cache/active_record/persistence.rb
|
130
131
|
- lib/second_level_cache/active_record/preloader.rb
|
131
132
|
- lib/second_level_cache/active_record/railtie.rb
|
@@ -143,6 +144,7 @@ files:
|
|
143
144
|
- test/model/animal.rb
|
144
145
|
- test/model/book.rb
|
145
146
|
- test/model/image.rb
|
147
|
+
- test/model/multi_read_from_cache_test.rb
|
146
148
|
- test/model/post.rb
|
147
149
|
- test/model/topic.rb
|
148
150
|
- test/model/user.rb
|
@@ -172,8 +174,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
172
174
|
- !ruby/object:Gem::Version
|
173
175
|
version: '0'
|
174
176
|
requirements: []
|
175
|
-
|
176
|
-
rubygems_version: 2.2.2
|
177
|
+
rubygems_version: 3.0.3
|
177
178
|
signing_key:
|
178
179
|
specification_version: 4
|
179
180
|
summary: 'SecondLevelCache is a write-through and read-through caching library inspired
|
@@ -183,24 +184,25 @@ summary: 'SecondLevelCache is a write-through and read-through caching library i
|
|
183
184
|
is a cache miss, it will populate the cache. Write-Through: As objects are created,
|
184
185
|
updated, and deleted, all of the caches are automatically kept up-to-date and coherent.'
|
185
186
|
test_files:
|
186
|
-
- test/
|
187
|
-
- test/
|
188
|
-
- test/
|
189
|
-
- test/fetch_by_uniq_key_test.rb
|
187
|
+
- test/polymorphic_association_test.rb
|
188
|
+
- test/require_test.rb
|
189
|
+
- test/preloader_test.rb
|
190
190
|
- test/finder_methods_test.rb
|
191
|
-
- test/
|
191
|
+
- test/belongs_to_association_test.rb
|
192
|
+
- test/second_level_cache_test.rb
|
193
|
+
- test/record_marshal_test.rb
|
194
|
+
- test/persistence_test.rb
|
195
|
+
- test/single_table_inheritance_test.rb
|
196
|
+
- test/model/image.rb
|
192
197
|
- test/model/account.rb
|
193
|
-
- test/model/animal.rb
|
194
198
|
- test/model/book.rb
|
195
|
-
- test/model/image.rb
|
196
|
-
- test/model/post.rb
|
197
199
|
- test/model/topic.rb
|
200
|
+
- test/model/animal.rb
|
201
|
+
- test/model/multi_read_from_cache_test.rb
|
202
|
+
- test/model/post.rb
|
198
203
|
- test/model/user.rb
|
199
|
-
- test/
|
200
|
-
- test/polymorphic_association_test.rb
|
201
|
-
- test/preloader_test.rb
|
202
|
-
- test/record_marshal_test.rb
|
203
|
-
- test/require_test.rb
|
204
|
-
- test/second_level_cache_test.rb
|
205
|
-
- test/single_table_inheritance_test.rb
|
204
|
+
- test/fetch_by_uniq_key_test.rb
|
206
205
|
- test/test_helper.rb
|
206
|
+
- test/has_one_association_test.rb
|
207
|
+
- test/base_test.rb
|
208
|
+
- test/active_record_test_case_helper.rb
|