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