bulk_cache_fetcher 0.0.3 → 1.0.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/README.md +17 -0
- data/lib/bulk_cache_fetcher.rb +21 -23
- data/test/bulk_cache_fetcher_test.rb +11 -2
- data/test/test_helper.rb +0 -1
- metadata +13 -13
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 164f9631e7c60c573ea490d677851e3ae2c1994a
|
4
|
+
data.tar.gz: a27506b07291839747eff96c8b6098c12eac5d8c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e24adcc2d3df76a46634720f77cbcf9d22fa5e66120a996bd42999ec7dfd3375649d7d17be8d28b1b4b17dbae300a44890cfb50e19a8d3b6fa144b56695b97b2
|
7
|
+
data.tar.gz: 84a0977c2db53ad7dec3d13fc446094f775faff28e6c8191d51cbdd3cc63d2d023d87118dd34dd2e7a5653f48abb9f6b57baddb1ca347926c8188444d85a034a
|
data/README.md
CHANGED
@@ -14,6 +14,23 @@ Bulk Cache Fetcher allows you to query the cache for a list of
|
|
14
14
|
objects, and gives you the opportunity to fetch all of the cache
|
15
15
|
misses at once, using whatever `:include`s you want.
|
16
16
|
|
17
|
+
For example, if you have a list of objects by id to fetch, and a list
|
18
|
+
of keys you want them to be cached under, you'd use Bulk Cache Fetcher
|
19
|
+
like this:
|
20
|
+
|
21
|
+
```ruby
|
22
|
+
identifiers = {:cache_key_1 => 1, :cache_key_2 => 2, :cache_key_3 => 3}
|
23
|
+
BulkCacheFetcher.new(Rails.cache).fetch(identifiers) do |uncached_keys_and_ids|
|
24
|
+
ids = uncached_keys_and_ids.values
|
25
|
+
BlogPost.where(:id => ids).includes([:author, :comments])
|
26
|
+
end
|
27
|
+
```
|
28
|
+
|
29
|
+
This will include and cache each `BlogPost`, with comments and
|
30
|
+
authors, as if you did the `.where.includes` without caching. If a
|
31
|
+
`BlogPost` is cached already, it won't fetch it (or its includes) from
|
32
|
+
the database.
|
33
|
+
|
17
34
|
## Installation
|
18
35
|
|
19
36
|
Add this line to your application's Gemfile:
|
data/lib/bulk_cache_fetcher.rb
CHANGED
@@ -5,7 +5,7 @@
|
|
5
5
|
# Rails' nested caching while avoiding the n+1 queries problem in the
|
6
6
|
# uncached case.
|
7
7
|
class BulkCacheFetcher
|
8
|
-
VERSION = '0.0
|
8
|
+
VERSION = '1.0.0'
|
9
9
|
|
10
10
|
# Creates a new bulk cache fetcher, backed by +cache+. Cache must
|
11
11
|
# respond to the standard Rails cache API, described on
|
@@ -28,41 +28,38 @@ class BulkCacheFetcher
|
|
28
28
|
# objects, so you can use it for things like setting cache
|
29
29
|
# expiration.
|
30
30
|
def fetch(object_identifiers, options = {}, &finder_block)
|
31
|
-
|
32
|
-
|
31
|
+
object_identifiers = normalize(object_identifiers)
|
32
|
+
cached_keys_with_objects, uncached_identifiers = partition(object_identifiers)
|
33
|
+
found_objects = find(uncached_identifiers, options, &finder_block)
|
34
|
+
coalesce(cache_keys(object_identifiers), cached_keys_with_objects, found_objects)
|
33
35
|
end
|
34
36
|
|
35
37
|
private
|
36
38
|
|
37
|
-
#
|
38
|
-
#
|
39
|
-
#
|
40
|
-
# that weren't cached.
|
39
|
+
# Splits a list of identifiers into two objects. The first is a hash
|
40
|
+
# of <tt>{cache_key: object}</tt> for all the objects we were able to serve
|
41
|
+
# from the cache. The second is a list of all of the identifiers for
|
42
|
+
# objects that weren't cached.
|
41
43
|
def partition(object_identifiers)
|
42
|
-
object_identifiers = normalize(object_identifiers)
|
43
|
-
cached_objects = []
|
44
44
|
uncached_identifiers = object_identifiers.dup
|
45
45
|
|
46
46
|
cache_keys = cache_keys(object_identifiers)
|
47
|
-
|
47
|
+
cached_keys_with_objects = @cache.read_multi(*cache_keys)
|
48
48
|
|
49
49
|
cache_keys.each do |cache_key|
|
50
|
-
|
51
|
-
uncached_identifiers.delete(cache_key) if cached_object
|
52
|
-
cached_objects << cached_object
|
50
|
+
uncached_identifiers.delete(cache_key) if cached_keys_with_objects.has_key?(cache_key)
|
53
51
|
end
|
54
52
|
|
55
|
-
[
|
53
|
+
[cached_keys_with_objects, uncached_identifiers]
|
56
54
|
end
|
57
55
|
|
58
56
|
# Finds all of the objects identified by +identifiers+, using the
|
59
57
|
# +finder_block+. Will pass +options+ on to the cache.
|
60
58
|
def find(identifiers, options = {}, &finder_block)
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
end
|
59
|
+
return [] if identifiers.empty?
|
60
|
+
Array(finder_block.call(identifiers)).tap do |objects|
|
61
|
+
verify_equal_key_and_value_counts!(identifiers, objects)
|
62
|
+
cache_all(identifiers, objects, options)
|
66
63
|
end
|
67
64
|
end
|
68
65
|
|
@@ -78,11 +75,12 @@ class BulkCacheFetcher
|
|
78
75
|
keys.zip(values) { |k, v| @cache.write(cache_key(k), v, options) }
|
79
76
|
end
|
80
77
|
|
81
|
-
#
|
82
|
-
#
|
83
|
-
|
78
|
+
# Given a list of +cache_keys+, either find associated objects from
|
79
|
+
# +cached_keys_with_objects, or grab them from +found_objects+, in
|
80
|
+
# order.
|
81
|
+
def coalesce(cache_keys, cached_keys_with_objects, found_objects)
|
84
82
|
found_objects = Array(found_objects)
|
85
|
-
|
83
|
+
cache_keys.map { |key| cached_keys_with_objects.fetch(key) { found_objects.shift } }
|
86
84
|
end
|
87
85
|
|
88
86
|
# Returns the part of the identifier that we can use as the cache
|
@@ -13,14 +13,14 @@ class InMemoryCache
|
|
13
13
|
def read_multi(*keys)
|
14
14
|
results = {}
|
15
15
|
keys.each do |key|
|
16
|
-
results[key] = read(key) if
|
16
|
+
results[key] = read(key) if cache.has_key?(key)
|
17
17
|
end
|
18
18
|
results
|
19
19
|
end
|
20
20
|
|
21
21
|
end
|
22
22
|
|
23
|
-
class BulkCacheFetcherTest < Minitest::
|
23
|
+
class BulkCacheFetcherTest < Minitest::Test
|
24
24
|
|
25
25
|
def setup
|
26
26
|
@cache = InMemoryCache.new
|
@@ -126,4 +126,13 @@ class BulkCacheFetcherTest < Minitest::Unit::TestCase
|
|
126
126
|
end
|
127
127
|
assert_equal(2, @cache.read(:one))
|
128
128
|
end
|
129
|
+
|
130
|
+
def test_doesnt_break_if_nils_are_cached
|
131
|
+
@cache.write(:two, nil)
|
132
|
+
results = @cache_fetcher.fetch([:one, :two, :three]) do |keys|
|
133
|
+
[1, 3]
|
134
|
+
end
|
135
|
+
assert_equal(nil, results[1])
|
136
|
+
assert_equal(3, results[2])
|
137
|
+
end
|
129
138
|
end
|
data/test/test_helper.rb
CHANGED
metadata
CHANGED
@@ -1,55 +1,55 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: bulk_cache_fetcher
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 1.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Justin Weiss
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2015-03-03 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
|
-
- - ~>
|
17
|
+
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
19
|
version: '1.3'
|
20
20
|
type: :development
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
|
-
- - ~>
|
24
|
+
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '1.3'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: rake
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
|
-
- -
|
31
|
+
- - ">="
|
32
32
|
- !ruby/object:Gem::Version
|
33
33
|
version: '0'
|
34
34
|
type: :development
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
|
-
- -
|
38
|
+
- - ">="
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: '0'
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
42
|
name: minitest
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
44
44
|
requirements:
|
45
|
-
- -
|
45
|
+
- - ">="
|
46
46
|
- !ruby/object:Gem::Version
|
47
47
|
version: '0'
|
48
48
|
type: :development
|
49
49
|
prerelease: false
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
51
51
|
requirements:
|
52
|
-
- -
|
52
|
+
- - ">="
|
53
53
|
- !ruby/object:Gem::Version
|
54
54
|
version: '0'
|
55
55
|
description: Fetches cache misses in bulk
|
@@ -59,8 +59,8 @@ executables: []
|
|
59
59
|
extensions: []
|
60
60
|
extra_rdoc_files: []
|
61
61
|
files:
|
62
|
-
- .gitignore
|
63
|
-
- .travis.yml
|
62
|
+
- ".gitignore"
|
63
|
+
- ".travis.yml"
|
64
64
|
- Gemfile
|
65
65
|
- LICENSE.txt
|
66
66
|
- README.md
|
@@ -79,17 +79,17 @@ require_paths:
|
|
79
79
|
- lib
|
80
80
|
required_ruby_version: !ruby/object:Gem::Requirement
|
81
81
|
requirements:
|
82
|
-
- -
|
82
|
+
- - ">="
|
83
83
|
- !ruby/object:Gem::Version
|
84
84
|
version: '0'
|
85
85
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
86
86
|
requirements:
|
87
|
-
- -
|
87
|
+
- - ">="
|
88
88
|
- !ruby/object:Gem::Version
|
89
89
|
version: '0'
|
90
90
|
requirements: []
|
91
91
|
rubyforge_project:
|
92
|
-
rubygems_version: 2.
|
92
|
+
rubygems_version: 2.4.5
|
93
93
|
signing_key:
|
94
94
|
specification_version: 4
|
95
95
|
summary: Bulk Cache Fetcher allows you to query the cache for a list of objects, and
|