async_cache 1.1.0 → 1.2.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/.gitignore +2 -0
- data/.travis.yml +8 -4
- data/Appraisals +10 -0
- data/Gemfile.lock +120 -123
- data/LICENSE +1 -0
- data/README.md +2 -2
- data/async_cache.gemspec +7 -5
- data/gemfiles/rails_4.gemfile +10 -0
- data/gemfiles/rails_5.gemfile +5 -0
- data/lib/async_cache.rb +24 -13
- data/lib/async_cache/store.rb +36 -24
- data/lib/async_cache/version.rb +1 -1
- data/lib/async_cache/workers/base.rb +2 -2
- data/lib/async_cache/workers/sidekiq.rb +13 -2
- data/spec/integration/support/run_all.rb +0 -0
- data/spec/spec_helper.rb +4 -0
- data/spec/store_spec.rb +19 -11
- data/spec/workers/sidekiq_spec.rb +37 -0
- metadata +44 -14
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA1:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: f5e390b11f0a6f259d8b5d8fed3ba2ce042045c4
|
|
4
|
+
data.tar.gz: bce164634a83146a3e385d33cb79b74e69c4222d
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: bd4e66433f1c2e5a2c36a33573fcee5e2f228743056d12a885d65efe0d31bbf811471fed0c593ee06866a2014f0000ad4c7c8584e7ca2bbf330a8ff51d6df494
|
|
7
|
+
data.tar.gz: 63bc29c179cca10113d65bef8649cf3e1137af95b0448d767200edd2c37cdd4cd9932bde75f95938d29ab5e2eb8abdc9265ce421cdd6b7e2518547810520e1a9
|
data/.gitignore
CHANGED
data/.travis.yml
CHANGED
|
@@ -1,14 +1,18 @@
|
|
|
1
1
|
sudo: false
|
|
2
2
|
language: ruby
|
|
3
3
|
cache: bundler
|
|
4
|
-
rvm:
|
|
5
|
-
- '2.2.1'
|
|
6
|
-
- '2.2.3'
|
|
7
|
-
- '2.3.0'
|
|
8
4
|
|
|
9
5
|
services:
|
|
10
6
|
- redis-server
|
|
11
7
|
|
|
8
|
+
gemfile:
|
|
9
|
+
- gemfiles/rails_4.gemfile
|
|
10
|
+
- gemfiles/rails_5.gemfile
|
|
11
|
+
|
|
12
|
+
rvm:
|
|
13
|
+
- '2.5.1'
|
|
14
|
+
- '2.4.4'
|
|
15
|
+
|
|
12
16
|
before_script:
|
|
13
17
|
- bundle exec ruby spec/integration/support/run_all.rb &
|
|
14
18
|
- sleep 2
|
data/Appraisals
ADDED
data/Gemfile.lock
CHANGED
|
@@ -1,136 +1,129 @@
|
|
|
1
1
|
PATH
|
|
2
2
|
remote: .
|
|
3
3
|
specs:
|
|
4
|
-
async_cache (1.
|
|
4
|
+
async_cache (1.2.0)
|
|
5
5
|
sourcify (~> 0.5.0)
|
|
6
6
|
|
|
7
7
|
GEM
|
|
8
8
|
remote: https://rubygems.org/
|
|
9
9
|
specs:
|
|
10
|
-
|
|
11
|
-
actionpack (=
|
|
12
|
-
|
|
13
|
-
|
|
10
|
+
actioncable (5.0.7)
|
|
11
|
+
actionpack (= 5.0.7)
|
|
12
|
+
nio4r (>= 1.2, < 3.0)
|
|
13
|
+
websocket-driver (~> 0.6.1)
|
|
14
|
+
actionmailer (5.0.7)
|
|
15
|
+
actionpack (= 5.0.7)
|
|
16
|
+
actionview (= 5.0.7)
|
|
17
|
+
activejob (= 5.0.7)
|
|
14
18
|
mail (~> 2.5, >= 2.5.4)
|
|
15
|
-
rails-dom-testing (~>
|
|
16
|
-
actionpack (
|
|
17
|
-
actionview (=
|
|
18
|
-
activesupport (=
|
|
19
|
-
rack (~>
|
|
20
|
-
rack-test (~> 0.6.
|
|
21
|
-
rails-dom-testing (~>
|
|
19
|
+
rails-dom-testing (~> 2.0)
|
|
20
|
+
actionpack (5.0.7)
|
|
21
|
+
actionview (= 5.0.7)
|
|
22
|
+
activesupport (= 5.0.7)
|
|
23
|
+
rack (~> 2.0)
|
|
24
|
+
rack-test (~> 0.6.3)
|
|
25
|
+
rails-dom-testing (~> 2.0)
|
|
22
26
|
rails-html-sanitizer (~> 1.0, >= 1.0.2)
|
|
23
|
-
actionview (
|
|
24
|
-
activesupport (=
|
|
27
|
+
actionview (5.0.7)
|
|
28
|
+
activesupport (= 5.0.7)
|
|
25
29
|
builder (~> 3.1)
|
|
26
30
|
erubis (~> 2.7.0)
|
|
27
|
-
rails-dom-testing (~>
|
|
28
|
-
rails-html-sanitizer (~> 1.0, >= 1.0.
|
|
29
|
-
activejob (
|
|
30
|
-
activesupport (=
|
|
31
|
-
globalid (>= 0.3.
|
|
32
|
-
activemodel (
|
|
33
|
-
activesupport (=
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
i18n (
|
|
41
|
-
json (~> 1.7, >= 1.7.7)
|
|
31
|
+
rails-dom-testing (~> 2.0)
|
|
32
|
+
rails-html-sanitizer (~> 1.0, >= 1.0.3)
|
|
33
|
+
activejob (5.0.7)
|
|
34
|
+
activesupport (= 5.0.7)
|
|
35
|
+
globalid (>= 0.3.6)
|
|
36
|
+
activemodel (5.0.7)
|
|
37
|
+
activesupport (= 5.0.7)
|
|
38
|
+
activerecord (5.0.7)
|
|
39
|
+
activemodel (= 5.0.7)
|
|
40
|
+
activesupport (= 5.0.7)
|
|
41
|
+
arel (~> 7.0)
|
|
42
|
+
activesupport (5.0.7)
|
|
43
|
+
concurrent-ruby (~> 1.0, >= 1.0.2)
|
|
44
|
+
i18n (>= 0.7, < 2)
|
|
42
45
|
minitest (~> 5.1)
|
|
43
|
-
thread_safe (~> 0.3, >= 0.3.4)
|
|
44
46
|
tzinfo (~> 1.1)
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
celluloid-pool
|
|
52
|
-
celluloid-supervision
|
|
53
|
-
timers (>= 4.1.1)
|
|
54
|
-
celluloid-essentials (0.20.5)
|
|
55
|
-
timers (>= 4.1.1)
|
|
56
|
-
celluloid-extras (0.20.5)
|
|
57
|
-
timers (>= 4.1.1)
|
|
58
|
-
celluloid-fsm (0.20.5)
|
|
59
|
-
timers (>= 4.1.1)
|
|
60
|
-
celluloid-pool (0.20.5)
|
|
61
|
-
timers (>= 4.1.1)
|
|
62
|
-
celluloid-supervision (0.20.5)
|
|
63
|
-
timers (>= 4.1.1)
|
|
47
|
+
appraisal (2.2.0)
|
|
48
|
+
bundler
|
|
49
|
+
rake
|
|
50
|
+
thor (>= 0.14.0)
|
|
51
|
+
arel (7.1.4)
|
|
52
|
+
builder (3.2.3)
|
|
64
53
|
coderay (1.1.0)
|
|
65
|
-
|
|
54
|
+
concurrent-ruby (1.0.5)
|
|
55
|
+
connection_pool (2.2.2)
|
|
56
|
+
crass (1.0.4)
|
|
66
57
|
diff-lcs (1.2.5)
|
|
67
58
|
docile (1.1.5)
|
|
68
59
|
erubis (2.7.0)
|
|
69
60
|
file-tail (1.1.0)
|
|
70
61
|
tins (~> 1.0)
|
|
71
|
-
globalid (0.
|
|
72
|
-
activesupport (>= 4.
|
|
73
|
-
hitimes (1.2.3)
|
|
62
|
+
globalid (0.4.1)
|
|
63
|
+
activesupport (>= 4.2.0)
|
|
74
64
|
httparty (0.13.7)
|
|
75
65
|
json (~> 1.8)
|
|
76
66
|
multi_xml (>= 0.5.2)
|
|
77
|
-
i18n (
|
|
78
|
-
|
|
79
|
-
|
|
67
|
+
i18n (1.1.0)
|
|
68
|
+
concurrent-ruby (~> 1.0)
|
|
69
|
+
json (1.8.6)
|
|
70
|
+
loofah (2.2.2)
|
|
71
|
+
crass (~> 1.0.2)
|
|
80
72
|
nokogiri (>= 1.5.9)
|
|
81
|
-
mail (2.
|
|
82
|
-
|
|
73
|
+
mail (2.7.0)
|
|
74
|
+
mini_mime (>= 0.1.1)
|
|
83
75
|
method_source (0.8.2)
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
minitest (5.
|
|
76
|
+
mini_mime (1.0.1)
|
|
77
|
+
mini_portile2 (2.3.0)
|
|
78
|
+
minitest (5.11.3)
|
|
87
79
|
multi_json (1.11.2)
|
|
88
80
|
multi_xml (0.5.5)
|
|
89
|
-
|
|
90
|
-
|
|
81
|
+
mustermann (1.0.3)
|
|
82
|
+
nio4r (2.3.1)
|
|
83
|
+
nokogiri (1.8.5)
|
|
84
|
+
mini_portile2 (~> 2.3.0)
|
|
91
85
|
pry (0.10.1)
|
|
92
86
|
coderay (~> 1.1.0)
|
|
93
87
|
method_source (~> 0.8.1)
|
|
94
88
|
slop (~> 3.4)
|
|
95
|
-
rack (
|
|
96
|
-
rack-protection (
|
|
89
|
+
rack (2.0.5)
|
|
90
|
+
rack-protection (2.0.4)
|
|
97
91
|
rack
|
|
98
92
|
rack-test (0.6.3)
|
|
99
93
|
rack (>= 1.0)
|
|
100
|
-
rails (
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
activesupport (= 4.2.4)
|
|
94
|
+
rails (5.0.7)
|
|
95
|
+
actioncable (= 5.0.7)
|
|
96
|
+
actionmailer (= 5.0.7)
|
|
97
|
+
actionpack (= 5.0.7)
|
|
98
|
+
actionview (= 5.0.7)
|
|
99
|
+
activejob (= 5.0.7)
|
|
100
|
+
activemodel (= 5.0.7)
|
|
101
|
+
activerecord (= 5.0.7)
|
|
102
|
+
activesupport (= 5.0.7)
|
|
103
|
+
bundler (>= 1.3.0)
|
|
104
|
+
railties (= 5.0.7)
|
|
105
|
+
sprockets-rails (>= 2.0.0)
|
|
106
|
+
rails-dom-testing (2.0.3)
|
|
107
|
+
activesupport (>= 4.2.0)
|
|
108
|
+
nokogiri (>= 1.6)
|
|
109
|
+
rails-html-sanitizer (1.0.4)
|
|
110
|
+
loofah (~> 2.2, >= 2.2.2)
|
|
111
|
+
railties (5.0.7)
|
|
112
|
+
actionpack (= 5.0.7)
|
|
113
|
+
activesupport (= 5.0.7)
|
|
114
|
+
method_source
|
|
122
115
|
rake (>= 0.8.7)
|
|
123
116
|
thor (>= 0.18.1, < 2.0)
|
|
124
|
-
rake (
|
|
117
|
+
rake (12.3.1)
|
|
125
118
|
redcarpet (3.3.4)
|
|
126
|
-
redis (
|
|
127
|
-
redis-activesupport (
|
|
128
|
-
activesupport (>= 3, <
|
|
129
|
-
redis-store (
|
|
130
|
-
redis-namespace (1.
|
|
131
|
-
redis (
|
|
132
|
-
redis-store (1.
|
|
133
|
-
redis (>= 2.2)
|
|
119
|
+
redis (4.0.2)
|
|
120
|
+
redis-activesupport (5.0.7)
|
|
121
|
+
activesupport (>= 3, < 6)
|
|
122
|
+
redis-store (>= 1.3, < 2)
|
|
123
|
+
redis-namespace (1.6.0)
|
|
124
|
+
redis (>= 3.0.4)
|
|
125
|
+
redis-store (1.6.0)
|
|
126
|
+
redis (>= 2.2, < 5)
|
|
134
127
|
rspec (3.4.0)
|
|
135
128
|
rspec-core (~> 3.4.0)
|
|
136
129
|
rspec-expectations (~> 3.4.0)
|
|
@@ -150,58 +143,62 @@ GEM
|
|
|
150
143
|
ruby_parser (3.7.2)
|
|
151
144
|
sexp_processor (~> 4.1)
|
|
152
145
|
sexp_processor (4.6.0)
|
|
153
|
-
sidekiq (
|
|
154
|
-
|
|
146
|
+
sidekiq (5.0.5)
|
|
147
|
+
concurrent-ruby (~> 1.0)
|
|
155
148
|
connection_pool (~> 2.2, >= 2.2.0)
|
|
156
|
-
|
|
157
|
-
redis (
|
|
158
|
-
redis-namespace (~> 1.5, >= 1.5.2)
|
|
149
|
+
rack-protection (>= 1.5.0)
|
|
150
|
+
redis (>= 3.3.4, < 5)
|
|
159
151
|
simplecov (0.9.2)
|
|
160
152
|
docile (~> 1.1.0)
|
|
161
153
|
multi_json (~> 1.0)
|
|
162
154
|
simplecov-html (~> 0.9.0)
|
|
163
155
|
simplecov-html (0.9.0)
|
|
164
|
-
sinatra (
|
|
165
|
-
|
|
166
|
-
rack
|
|
167
|
-
|
|
156
|
+
sinatra (2.0.4)
|
|
157
|
+
mustermann (~> 1.0)
|
|
158
|
+
rack (~> 2.0)
|
|
159
|
+
rack-protection (= 2.0.4)
|
|
160
|
+
tilt (~> 2.0)
|
|
168
161
|
slop (3.6.0)
|
|
169
162
|
sourcify (0.5.0)
|
|
170
163
|
file-tail (>= 1.0.5)
|
|
171
164
|
ruby2ruby (>= 1.2.5)
|
|
172
165
|
ruby_parser (>= 2.0.5)
|
|
173
166
|
sexp_processor (>= 3.0.5)
|
|
174
|
-
sprockets (3.
|
|
167
|
+
sprockets (3.7.2)
|
|
168
|
+
concurrent-ruby (~> 1.0)
|
|
175
169
|
rack (> 1, < 3)
|
|
176
|
-
sprockets-rails (2.
|
|
177
|
-
actionpack (>=
|
|
178
|
-
activesupport (>=
|
|
179
|
-
sprockets (>=
|
|
180
|
-
thor (0.
|
|
181
|
-
thread_safe (0.3.
|
|
182
|
-
tilt (
|
|
183
|
-
timers (4.1.1)
|
|
184
|
-
hitimes
|
|
170
|
+
sprockets-rails (3.2.1)
|
|
171
|
+
actionpack (>= 4.0)
|
|
172
|
+
activesupport (>= 4.0)
|
|
173
|
+
sprockets (>= 3.0.0)
|
|
174
|
+
thor (0.20.0)
|
|
175
|
+
thread_safe (0.3.6)
|
|
176
|
+
tilt (2.0.8)
|
|
185
177
|
tins (1.8.1)
|
|
186
|
-
tzinfo (1.2.
|
|
178
|
+
tzinfo (1.2.5)
|
|
187
179
|
thread_safe (~> 0.1)
|
|
188
|
-
|
|
180
|
+
websocket-driver (0.6.5)
|
|
181
|
+
websocket-extensions (>= 0.1.0)
|
|
182
|
+
websocket-extensions (0.1.3)
|
|
183
|
+
yard (0.9.16)
|
|
189
184
|
|
|
190
185
|
PLATFORMS
|
|
191
186
|
ruby
|
|
192
187
|
|
|
193
188
|
DEPENDENCIES
|
|
189
|
+
appraisal (~> 2.2.0)
|
|
194
190
|
async_cache!
|
|
195
191
|
httparty (~> 0.13.7)
|
|
196
192
|
pry (~> 0.10.1)
|
|
197
|
-
rails (~>
|
|
193
|
+
rails (~> 5.0.1)
|
|
198
194
|
redcarpet (~> 3.3.4)
|
|
199
|
-
redis-activesupport (~>
|
|
195
|
+
redis-activesupport (~> 5.0.7)
|
|
196
|
+
redis-namespace (~> 1.6.0)
|
|
200
197
|
rspec (~> 3.4.0)
|
|
201
|
-
sidekiq (~>
|
|
198
|
+
sidekiq (~> 5.0.5)
|
|
202
199
|
simplecov (~> 0.9.2)
|
|
203
|
-
sinatra (~>
|
|
204
|
-
yard (~> 0.
|
|
200
|
+
sinatra (~> 2.0.4)
|
|
201
|
+
yard (~> 0.9.11)
|
|
205
202
|
|
|
206
203
|
BUNDLED WITH
|
|
207
|
-
1.
|
|
204
|
+
1.16.1
|
data/LICENSE
CHANGED
data/README.md
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# async_cache
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Async caching lets you serve slightly-stale data immediately while generating an up-to-date version in the background.
|
|
4
4
|
|
|
5
|
-
This
|
|
5
|
+
This provides a Ruby implementation of the async caching strategy (detailed below). It works with both Sidekiq (recommended) and ActiveJob systems. Usage examples are provided below and in the [`examples`](examples/) directory.
|
|
6
6
|
|
|
7
7
|
## Usage
|
|
8
8
|
|
data/async_cache.gemspec
CHANGED
|
@@ -15,15 +15,17 @@ Gem::Specification.new do |s|
|
|
|
15
15
|
|
|
16
16
|
s.add_dependency 'sourcify', '~> 0.5.0'
|
|
17
17
|
|
|
18
|
-
s.add_development_dependency '
|
|
19
|
-
s.add_development_dependency '
|
|
20
|
-
s.add_development_dependency '
|
|
18
|
+
s.add_development_dependency 'appraisal', '~> 2.2.0'
|
|
19
|
+
s.add_development_dependency 'rails', '~> 5.0.1'
|
|
20
|
+
s.add_development_dependency 'sidekiq', '~> 5.0.5'
|
|
21
|
+
s.add_development_dependency 'redis-namespace', '~> 1.6.0'
|
|
22
|
+
s.add_development_dependency 'sinatra', '~> 2.0.4'
|
|
21
23
|
s.add_development_dependency 'rspec', '~> 3.4.0'
|
|
22
24
|
s.add_development_dependency 'pry', '~> 0.10.1'
|
|
23
25
|
s.add_development_dependency 'simplecov', '~> 0.9.2'
|
|
24
|
-
s.add_development_dependency 'redis-activesupport', '~>
|
|
26
|
+
s.add_development_dependency 'redis-activesupport', '~> 5.0.7'
|
|
25
27
|
s.add_development_dependency 'httparty', '~> 0.13.7'
|
|
26
|
-
s.add_development_dependency 'yard', '~> 0.
|
|
28
|
+
s.add_development_dependency 'yard', '~> 0.9.11'
|
|
27
29
|
s.add_development_dependency 'redcarpet', '~> 3.3.4'
|
|
28
30
|
|
|
29
31
|
s.files = `git ls-files`.split("\n")
|
data/lib/async_cache.rb
CHANGED
|
@@ -1,20 +1,31 @@
|
|
|
1
1
|
require 'sourcify'
|
|
2
2
|
|
|
3
3
|
module AsyncCache
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
@backend = backend
|
|
10
|
-
end
|
|
4
|
+
DEFAULT_OPTIONS = {
|
|
5
|
+
# How long Sidekiq Enterprise should hold a uniqueness lock. The default
|
|
6
|
+
# is 10 minutes.
|
|
7
|
+
uniqueness_timeout: 600,
|
|
8
|
+
}
|
|
11
9
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
10
|
+
def self.options
|
|
11
|
+
@options ||= DEFAULT_OPTIONS.dup
|
|
12
|
+
end
|
|
13
|
+
def self.options=(options)
|
|
14
|
+
@options = options
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def self.backend
|
|
18
|
+
@backend ||= Rails.cache
|
|
19
|
+
end
|
|
20
|
+
def self.backend=(backend)
|
|
21
|
+
@backend = backend
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def self.logger
|
|
25
|
+
@logger ||= Rails.logger
|
|
26
|
+
end
|
|
27
|
+
def self.logger=(logger)
|
|
28
|
+
@logger = logger
|
|
18
29
|
end
|
|
19
30
|
end
|
|
20
31
|
|
data/lib/async_cache/store.rb
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
require 'digest/md5'
|
|
2
|
+
|
|
1
3
|
module AsyncCache
|
|
2
4
|
class Store
|
|
3
5
|
attr_accessor :backend, :worker_klass
|
|
@@ -34,43 +36,45 @@ module AsyncCache
|
|
|
34
36
|
# @param [Hash] options
|
|
35
37
|
# @yield [*arguments in options[:arguments]] Called if entry out-of-date
|
|
36
38
|
def fetch(locator, version, options = {}, &block)
|
|
37
|
-
options = options.dup
|
|
39
|
+
options = options.dup # Duplicate to avoid side effects
|
|
38
40
|
version = version.to_i # Versions must *always* be convertible to integers
|
|
39
41
|
|
|
40
42
|
# Expires-in must be an integer if present, nil if not
|
|
41
43
|
expires_in = options[:expires_in] ? options[:expires_in].to_i : nil
|
|
42
44
|
|
|
45
|
+
block_source = block.to_source
|
|
43
46
|
block_arguments = check_arguments(options.delete(:arguments) || [])
|
|
44
47
|
|
|
45
48
|
# Serialize arguments into the full cache key
|
|
46
|
-
key = ActiveSupport::Cache.expand_cache_key
|
|
49
|
+
key = ActiveSupport::Cache.expand_cache_key([
|
|
50
|
+
Store.base_cache_key(locator, block_source),
|
|
51
|
+
block_arguments
|
|
52
|
+
].flatten)
|
|
47
53
|
|
|
48
54
|
cached_data, cached_version = @backend.read key
|
|
49
55
|
|
|
50
56
|
strategy = determine_strategy(
|
|
51
|
-
:
|
|
52
|
-
:
|
|
53
|
-
:
|
|
57
|
+
has_cached_data: !!cached_data,
|
|
58
|
+
needs_regen: version > (cached_version || 0),
|
|
59
|
+
synchronous_regen: options[:synchronous_regen]
|
|
54
60
|
)
|
|
55
61
|
|
|
62
|
+
return cached_data if strategy == :current
|
|
63
|
+
|
|
56
64
|
context = {
|
|
57
|
-
:
|
|
58
|
-
:
|
|
59
|
-
:
|
|
60
|
-
:
|
|
61
|
-
:
|
|
65
|
+
key: key,
|
|
66
|
+
version: version,
|
|
67
|
+
expires_in: expires_in,
|
|
68
|
+
block_source: block_source,
|
|
69
|
+
arguments: block_arguments
|
|
62
70
|
}
|
|
63
71
|
|
|
64
72
|
case strategy
|
|
65
73
|
when :generate
|
|
66
74
|
return generate_and_cache context
|
|
67
|
-
|
|
68
75
|
when :enqueue
|
|
69
76
|
enqueue_generation context
|
|
70
77
|
return cached_data
|
|
71
|
-
|
|
72
|
-
when :current
|
|
73
|
-
return cached_data
|
|
74
78
|
end
|
|
75
79
|
end
|
|
76
80
|
|
|
@@ -98,9 +102,7 @@ module AsyncCache
|
|
|
98
102
|
end
|
|
99
103
|
end
|
|
100
104
|
|
|
101
|
-
def generate_and_cache(key:, version:, expires_in:,
|
|
102
|
-
block_source = block.to_source
|
|
103
|
-
|
|
105
|
+
def generate_and_cache(key:, version:, expires_in:, block_source:, arguments:)
|
|
104
106
|
# Mimic the destruction-of-scope behavior of the worker in development
|
|
105
107
|
# so it will *fail* for developers if they try to depend upon scope
|
|
106
108
|
block = eval(block_source)
|
|
@@ -113,19 +115,19 @@ module AsyncCache
|
|
|
113
115
|
return data
|
|
114
116
|
end
|
|
115
117
|
|
|
116
|
-
def enqueue_generation(key:, version:, expires_in:,
|
|
118
|
+
def enqueue_generation(key:, version:, expires_in:, block_source:, arguments:)
|
|
117
119
|
worker_klass.enqueue_async_job(
|
|
118
|
-
key:
|
|
119
|
-
version:
|
|
120
|
+
key: key,
|
|
121
|
+
version: version,
|
|
120
122
|
expires_in: expires_in,
|
|
121
|
-
block:
|
|
122
|
-
arguments:
|
|
123
|
+
block: block_source,
|
|
124
|
+
arguments: arguments
|
|
123
125
|
)
|
|
124
126
|
end
|
|
125
127
|
|
|
126
128
|
def inspect
|
|
127
|
-
pointer_format
|
|
128
|
-
pointer
|
|
129
|
+
pointer_format = '0x%014x'
|
|
130
|
+
pointer = Kernel.sprintf pointer_format, self.object_id * 2
|
|
129
131
|
backend_pointer = Kernel.sprintf pointer_format, @backend.object_id * 2
|
|
130
132
|
|
|
131
133
|
'#<' + [
|
|
@@ -135,6 +137,16 @@ module AsyncCache
|
|
|
135
137
|
].join('') + '>'
|
|
136
138
|
end
|
|
137
139
|
|
|
140
|
+
# Build the base part of the cache key with the locator and the digest
|
|
141
|
+
# of the block source. This ensures that if the implementation (block)
|
|
142
|
+
# changes then the cache key will also change.
|
|
143
|
+
def self.base_cache_key(locator, block_source)
|
|
144
|
+
ActiveSupport::Cache.expand_cache_key([
|
|
145
|
+
locator,
|
|
146
|
+
Digest::MD5.hexdigest(block_source)
|
|
147
|
+
])
|
|
148
|
+
end
|
|
149
|
+
|
|
138
150
|
private
|
|
139
151
|
|
|
140
152
|
# Ensures the arguments are primitives.
|
data/lib/async_cache/version.rb
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
require 'active_support/core_ext/array/wrap'
|
|
2
|
+
|
|
1
3
|
module AsyncCache
|
|
2
4
|
module Workers
|
|
3
5
|
def self.worker_for_name(name)
|
|
@@ -41,8 +43,6 @@ module AsyncCache
|
|
|
41
43
|
# @param [Array] block_arguments Arguments with which to call the block
|
|
42
44
|
# @param [String] block_source Ruby source to evaluate to produce the value
|
|
43
45
|
def perform key, version, expires_in, block_arguments, block_source
|
|
44
|
-
t0 = Time.now
|
|
45
|
-
|
|
46
46
|
_cached_data, cached_version = backend.read key
|
|
47
47
|
return unless version > (cached_version || 0)
|
|
48
48
|
|
|
@@ -7,8 +7,19 @@ module AsyncCache
|
|
|
7
7
|
include Base
|
|
8
8
|
include Sidekiq::Worker
|
|
9
9
|
|
|
10
|
-
#
|
|
11
|
-
|
|
10
|
+
# Pulled out into a module so it can be tested.
|
|
11
|
+
module Options
|
|
12
|
+
def self.included(mod)
|
|
13
|
+
if defined?(Sidekiq::Enterprise)
|
|
14
|
+
mod.sidekiq_options unique_for: AsyncCache.options[:uniqueness_timeout]
|
|
15
|
+
elsif defined?(SidekiqUniqueJobs)
|
|
16
|
+
# Only allow one job per set of arguments to ever be in the queue
|
|
17
|
+
mod.sidekiq_options unique: :until_executed
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
include Options
|
|
12
23
|
|
|
13
24
|
# Use the Sidekiq API to see if there are worker processes available to
|
|
14
25
|
# handle the async cache jobs queue.
|
|
File without changes
|
data/spec/spec_helper.rb
CHANGED
|
@@ -11,4 +11,8 @@ SimpleCov.start
|
|
|
11
11
|
Rails.cache = ActiveSupport::Cache::MemoryStore.new
|
|
12
12
|
Rails.logger = Logger.new($stdout).tap { |log| log.level = Logger::ERROR }
|
|
13
13
|
|
|
14
|
+
# Fake the `sidekiq-unique-jobs` gem being loaded.
|
|
15
|
+
module SidekiqUniqueJobs
|
|
16
|
+
end
|
|
17
|
+
|
|
14
18
|
require 'async_cache'
|
data/spec/store_spec.rb
CHANGED
|
@@ -2,9 +2,10 @@ require 'spec_helper'
|
|
|
2
2
|
require 'async_cache/workers/sidekiq'
|
|
3
3
|
|
|
4
4
|
describe AsyncCache::Store do
|
|
5
|
+
Store = AsyncCache::Store
|
|
5
6
|
|
|
6
7
|
subject do
|
|
7
|
-
|
|
8
|
+
Store.new(
|
|
8
9
|
backend: Rails.cache,
|
|
9
10
|
worker: :sidekiq
|
|
10
11
|
)
|
|
@@ -12,7 +13,7 @@ describe AsyncCache::Store do
|
|
|
12
13
|
|
|
13
14
|
it "raises if it doesn't receive a worker class" do
|
|
14
15
|
expect {
|
|
15
|
-
|
|
16
|
+
Store.new backend: Rails.cache
|
|
16
17
|
}.to raise_error(ArgumentError)
|
|
17
18
|
end
|
|
18
19
|
|
|
@@ -32,20 +33,26 @@ describe AsyncCache::Store do
|
|
|
32
33
|
end
|
|
33
34
|
|
|
34
35
|
it "synchronously calls #fetch if entry isn't present" do
|
|
35
|
-
|
|
36
|
+
block = proc { 'something' }
|
|
37
|
+
cache_key = Store.base_cache_key @key, block.to_source
|
|
38
|
+
|
|
39
|
+
stub_not_present cache_key
|
|
36
40
|
|
|
37
41
|
version = Time.now.to_i
|
|
38
42
|
expires_in = 1.minute
|
|
39
43
|
|
|
40
44
|
# Expect another synchronous call with a block to compute the value
|
|
41
|
-
expect(Rails.cache).to receive(:write).with(
|
|
45
|
+
expect(Rails.cache).to receive(:write).with(cache_key, ['something', version], {:expires_in => expires_in}).and_call_original
|
|
42
46
|
|
|
43
|
-
fetched_value = subject.fetch(@key, version, :expires_in => expires_in)
|
|
47
|
+
fetched_value = subject.fetch(@key, version, :expires_in => expires_in, &block)
|
|
44
48
|
|
|
45
49
|
expect(fetched_value).to eql 'something'
|
|
46
50
|
end
|
|
47
51
|
|
|
48
52
|
it 'returns the stale value and enqueues the worker if entry is present and timestamp is changed' do
|
|
53
|
+
block = proc { |private_argument| private_argument * 2 }
|
|
54
|
+
base_key = Store.base_cache_key @key, block.to_source
|
|
55
|
+
|
|
49
56
|
# It will try to check that workers are present, so we need to make that
|
|
50
57
|
# check be a no-op
|
|
51
58
|
allow(subject.worker_klass).to receive(:has_workers?).and_return(true)
|
|
@@ -57,7 +64,7 @@ describe AsyncCache::Store do
|
|
|
57
64
|
|
|
58
65
|
# Cache key is composed of *both* the key and the arguments given to the
|
|
59
66
|
# block since those arguments determine the output of the block
|
|
60
|
-
cache_key = ActiveSupport::Cache.expand_cache_key([
|
|
67
|
+
cache_key = ActiveSupport::Cache.expand_cache_key([base_key] + arguments)
|
|
61
68
|
|
|
62
69
|
stub_present cache_key, 'old!'
|
|
63
70
|
|
|
@@ -76,20 +83,21 @@ describe AsyncCache::Store do
|
|
|
76
83
|
expect(eval(block_source).call(*block_arguments)).to eql 2
|
|
77
84
|
end
|
|
78
85
|
|
|
79
|
-
fetched_value = subject.fetch(@key, timestamp, :expires_in => expires_in, :arguments => arguments)
|
|
80
|
-
private_argument * 2
|
|
81
|
-
end
|
|
86
|
+
fetched_value = subject.fetch(@key, timestamp, :expires_in => expires_in, :arguments => arguments, &block)
|
|
82
87
|
|
|
83
88
|
# Check that it immediately returns the stale value
|
|
84
89
|
expect(fetched_value).to eql old_value
|
|
85
90
|
end
|
|
86
91
|
|
|
87
92
|
it "returns the current value if timestamp isn't changed" do
|
|
88
|
-
|
|
93
|
+
block = proc { 'bad!' }
|
|
94
|
+
cache_key = Store.base_cache_key @key, block.to_source
|
|
95
|
+
|
|
96
|
+
stub_present cache_key, 'value'
|
|
89
97
|
|
|
90
98
|
timestamp = 0 # `stub_present` returns a timestamp of 0
|
|
91
99
|
|
|
92
|
-
expect(subject.fetch(@key, timestamp, :expires_in => 1.minute
|
|
100
|
+
expect(subject.fetch(@key, timestamp, :expires_in => 1.minute, &block)).to eql 'value'
|
|
93
101
|
end
|
|
94
102
|
|
|
95
103
|
end # context caching
|
|
@@ -6,6 +6,13 @@ describe AsyncCache::Workers::SidekiqWorker do
|
|
|
6
6
|
AsyncCache::Workers::SidekiqWorker
|
|
7
7
|
end
|
|
8
8
|
|
|
9
|
+
describe '.sidekiq_options' do
|
|
10
|
+
# See `spec_helper.rb` which makes it think the gem is loaded.
|
|
11
|
+
it 'has the uniqueness option for `sidekiq-unique-jobs`' do
|
|
12
|
+
expect(subject.sidekiq_options_hash).to include 'unique'
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
|
|
9
16
|
describe '::has_workers?' do
|
|
10
17
|
it 'returns false if no Sidekiq queues are available' do
|
|
11
18
|
allow(subject).to receive(:sidekiq_options).and_return({'queue' => 'good_queue'})
|
|
@@ -37,4 +44,34 @@ describe AsyncCache::Workers::SidekiqWorker do
|
|
|
37
44
|
)
|
|
38
45
|
end
|
|
39
46
|
end
|
|
47
|
+
|
|
48
|
+
describe AsyncCache::Workers::SidekiqWorker::Options do
|
|
49
|
+
subject do
|
|
50
|
+
AsyncCache::Workers::SidekiqWorker::Options
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
before do
|
|
54
|
+
# Set by `spec_helper.rb`.
|
|
55
|
+
hide_const 'SidekiqUniqueJobs'
|
|
56
|
+
|
|
57
|
+
class Worker
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
it 'sets correct option for `sidekiq-unique-jobs`' do
|
|
62
|
+
stub_const 'SidekiqUniqueJobs', Module.new
|
|
63
|
+
|
|
64
|
+
expect(Worker).to receive(:sidekiq_options).with(unique: :until_executed)
|
|
65
|
+
|
|
66
|
+
Worker.include subject
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
it 'sets correct options for Sidekiq Enterprise' do
|
|
70
|
+
stub_const 'Sidekiq::Enterprise', Module.new
|
|
71
|
+
|
|
72
|
+
expect(Worker).to receive(:sidekiq_options).with(unique_for: AsyncCache.options[:uniqueness_timeout])
|
|
73
|
+
|
|
74
|
+
Worker.include subject
|
|
75
|
+
end
|
|
76
|
+
end
|
|
40
77
|
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: async_cache
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.
|
|
4
|
+
version: 1.2.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Adam Derewecki
|
|
@@ -9,7 +9,7 @@ authors:
|
|
|
9
9
|
autorequire:
|
|
10
10
|
bindir: bin
|
|
11
11
|
cert_chain: []
|
|
12
|
-
date:
|
|
12
|
+
date: 2018-10-10 00:00:00.000000000 Z
|
|
13
13
|
dependencies:
|
|
14
14
|
- !ruby/object:Gem::Dependency
|
|
15
15
|
name: sourcify
|
|
@@ -25,48 +25,76 @@ dependencies:
|
|
|
25
25
|
- - "~>"
|
|
26
26
|
- !ruby/object:Gem::Version
|
|
27
27
|
version: 0.5.0
|
|
28
|
+
- !ruby/object:Gem::Dependency
|
|
29
|
+
name: appraisal
|
|
30
|
+
requirement: !ruby/object:Gem::Requirement
|
|
31
|
+
requirements:
|
|
32
|
+
- - "~>"
|
|
33
|
+
- !ruby/object:Gem::Version
|
|
34
|
+
version: 2.2.0
|
|
35
|
+
type: :development
|
|
36
|
+
prerelease: false
|
|
37
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
38
|
+
requirements:
|
|
39
|
+
- - "~>"
|
|
40
|
+
- !ruby/object:Gem::Version
|
|
41
|
+
version: 2.2.0
|
|
28
42
|
- !ruby/object:Gem::Dependency
|
|
29
43
|
name: rails
|
|
30
44
|
requirement: !ruby/object:Gem::Requirement
|
|
31
45
|
requirements:
|
|
32
46
|
- - "~>"
|
|
33
47
|
- !ruby/object:Gem::Version
|
|
34
|
-
version:
|
|
48
|
+
version: 5.0.1
|
|
35
49
|
type: :development
|
|
36
50
|
prerelease: false
|
|
37
51
|
version_requirements: !ruby/object:Gem::Requirement
|
|
38
52
|
requirements:
|
|
39
53
|
- - "~>"
|
|
40
54
|
- !ruby/object:Gem::Version
|
|
41
|
-
version:
|
|
55
|
+
version: 5.0.1
|
|
42
56
|
- !ruby/object:Gem::Dependency
|
|
43
57
|
name: sidekiq
|
|
44
58
|
requirement: !ruby/object:Gem::Requirement
|
|
45
59
|
requirements:
|
|
46
60
|
- - "~>"
|
|
47
61
|
- !ruby/object:Gem::Version
|
|
48
|
-
version:
|
|
62
|
+
version: 5.0.5
|
|
63
|
+
type: :development
|
|
64
|
+
prerelease: false
|
|
65
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
66
|
+
requirements:
|
|
67
|
+
- - "~>"
|
|
68
|
+
- !ruby/object:Gem::Version
|
|
69
|
+
version: 5.0.5
|
|
70
|
+
- !ruby/object:Gem::Dependency
|
|
71
|
+
name: redis-namespace
|
|
72
|
+
requirement: !ruby/object:Gem::Requirement
|
|
73
|
+
requirements:
|
|
74
|
+
- - "~>"
|
|
75
|
+
- !ruby/object:Gem::Version
|
|
76
|
+
version: 1.6.0
|
|
49
77
|
type: :development
|
|
50
78
|
prerelease: false
|
|
51
79
|
version_requirements: !ruby/object:Gem::Requirement
|
|
52
80
|
requirements:
|
|
53
81
|
- - "~>"
|
|
54
82
|
- !ruby/object:Gem::Version
|
|
55
|
-
version:
|
|
83
|
+
version: 1.6.0
|
|
56
84
|
- !ruby/object:Gem::Dependency
|
|
57
85
|
name: sinatra
|
|
58
86
|
requirement: !ruby/object:Gem::Requirement
|
|
59
87
|
requirements:
|
|
60
88
|
- - "~>"
|
|
61
89
|
- !ruby/object:Gem::Version
|
|
62
|
-
version:
|
|
90
|
+
version: 2.0.4
|
|
63
91
|
type: :development
|
|
64
92
|
prerelease: false
|
|
65
93
|
version_requirements: !ruby/object:Gem::Requirement
|
|
66
94
|
requirements:
|
|
67
95
|
- - "~>"
|
|
68
96
|
- !ruby/object:Gem::Version
|
|
69
|
-
version:
|
|
97
|
+
version: 2.0.4
|
|
70
98
|
- !ruby/object:Gem::Dependency
|
|
71
99
|
name: rspec
|
|
72
100
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -115,14 +143,14 @@ dependencies:
|
|
|
115
143
|
requirements:
|
|
116
144
|
- - "~>"
|
|
117
145
|
- !ruby/object:Gem::Version
|
|
118
|
-
version:
|
|
146
|
+
version: 5.0.7
|
|
119
147
|
type: :development
|
|
120
148
|
prerelease: false
|
|
121
149
|
version_requirements: !ruby/object:Gem::Requirement
|
|
122
150
|
requirements:
|
|
123
151
|
- - "~>"
|
|
124
152
|
- !ruby/object:Gem::Version
|
|
125
|
-
version:
|
|
153
|
+
version: 5.0.7
|
|
126
154
|
- !ruby/object:Gem::Dependency
|
|
127
155
|
name: httparty
|
|
128
156
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -143,14 +171,14 @@ dependencies:
|
|
|
143
171
|
requirements:
|
|
144
172
|
- - "~>"
|
|
145
173
|
- !ruby/object:Gem::Version
|
|
146
|
-
version:
|
|
174
|
+
version: 0.9.11
|
|
147
175
|
type: :development
|
|
148
176
|
prerelease: false
|
|
149
177
|
version_requirements: !ruby/object:Gem::Requirement
|
|
150
178
|
requirements:
|
|
151
179
|
- - "~>"
|
|
152
180
|
- !ruby/object:Gem::Version
|
|
153
|
-
version:
|
|
181
|
+
version: 0.9.11
|
|
154
182
|
- !ruby/object:Gem::Dependency
|
|
155
183
|
name: redcarpet
|
|
156
184
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -177,6 +205,7 @@ files:
|
|
|
177
205
|
- ".rspec"
|
|
178
206
|
- ".travis.yml"
|
|
179
207
|
- ".yardopts"
|
|
208
|
+
- Appraisals
|
|
180
209
|
- Gemfile
|
|
181
210
|
- Gemfile.lock
|
|
182
211
|
- LICENSE
|
|
@@ -184,6 +213,8 @@ files:
|
|
|
184
213
|
- Rakefile
|
|
185
214
|
- async_cache.gemspec
|
|
186
215
|
- examples/examples_controller.rb
|
|
216
|
+
- gemfiles/rails_4.gemfile
|
|
217
|
+
- gemfiles/rails_5.gemfile
|
|
187
218
|
- lib/async_cache.rb
|
|
188
219
|
- lib/async_cache/railtie.rb
|
|
189
220
|
- lib/async_cache/store.rb
|
|
@@ -224,9 +255,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
224
255
|
version: '0'
|
|
225
256
|
requirements: []
|
|
226
257
|
rubyforge_project:
|
|
227
|
-
rubygems_version: 2.
|
|
258
|
+
rubygems_version: 2.6.8
|
|
228
259
|
signing_key:
|
|
229
260
|
specification_version: 4
|
|
230
261
|
summary: Pattern and library for implementing asynchronous caching
|
|
231
262
|
test_files: []
|
|
232
|
-
has_rdoc:
|