second_level_cache 2.1.9 → 2.1.16
Sign up to get free protection for your applications and to get access to all the features.
- 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
|