incrdecr_cached_counts 0.0.6 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/cached_counts/version.rb +1 -1
- data/lib/cached_counts.rb +36 -55
- metadata +4 -13
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 933d0e94e08544064f20c36691f4c82fc581a843
|
4
|
+
data.tar.gz: d60e51a4e1f2f3be7329f41999199ee04c471675
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0e83facc3fb470a922d4dbbca8fbefdfe525a8a03016a49a9e3f244b97f8ca46e4c77a1d46143fd9ad84046c98d5bc1d9c46ab50fbd861f58c15d618947e9c7b
|
7
|
+
data.tar.gz: c7fb4d8e58784e72ac21884abb76412050a18c6e55520d470b65fc336c49336728ff2677bf2f6b5760c2c6a5bba2ed8683ff44280ebd1967e4982b1125b80352
|
data/lib/cached_counts.rb
CHANGED
@@ -39,13 +39,11 @@ module CachedCounts
|
|
39
39
|
# @option options [Integer, #to_s] :version
|
40
40
|
# Cache version - bump if you change the definition of a count.
|
41
41
|
#
|
42
|
-
# @option options [
|
43
|
-
#
|
44
|
-
#
|
45
|
-
#
|
46
|
-
#
|
47
|
-
# memcached instance goes away. Can be nil; defaults to using a value
|
48
|
-
# grabbed from the cache or DB at startup.
|
42
|
+
# @option options [Lambda] :default_value_lambda
|
43
|
+
# Uses this value when the cache is empty, while it is being populated
|
44
|
+
#
|
45
|
+
# @option options [Lambda] :value_updater_lambda
|
46
|
+
# Override logic for updating a cache value - e.g. to perform background updates
|
49
47
|
#
|
50
48
|
def caches_count_where(attribute_name, options = {})
|
51
49
|
# Delay actual run to work around circular dependencies
|
@@ -136,14 +134,6 @@ module CachedCounts
|
|
136
134
|
version = options.fetch :version, 1
|
137
135
|
key = scope_count_key(attribute_name, version)
|
138
136
|
|
139
|
-
unless options.has_key?(:race_condition_fallback)
|
140
|
-
options[:race_condition_fallback] = default_race_condition_fallback_proc(
|
141
|
-
key,
|
142
|
-
relation,
|
143
|
-
options
|
144
|
-
)
|
145
|
-
end
|
146
|
-
|
147
137
|
[attribute_name, *Array(options[:alias])].each do |attr_name|
|
148
138
|
add_count_attribute_methods(
|
149
139
|
attr_name,
|
@@ -156,23 +146,6 @@ module CachedCounts
|
|
156
146
|
end
|
157
147
|
end
|
158
148
|
|
159
|
-
def default_race_condition_fallback_proc(key, relation, options)
|
160
|
-
fallback = Rails.cache.read(key)
|
161
|
-
fallback = fallback.value if fallback.is_a?(ActiveSupport::Cache::Entry)
|
162
|
-
|
163
|
-
if fallback.nil?
|
164
|
-
begin
|
165
|
-
fallback = relation.count
|
166
|
-
rescue ActiveRecord::StatementInvalid => e
|
167
|
-
fallback = 0
|
168
|
-
end
|
169
|
-
|
170
|
-
Rails.cache.write key, fallback, expires_in: options.fetch(:expires_in, 1.week), raw: true
|
171
|
-
end
|
172
|
-
|
173
|
-
-> { fallback }
|
174
|
-
end
|
175
|
-
|
176
149
|
def define_association_count_attribute(attribute_name, association, options)
|
177
150
|
options = options.dup
|
178
151
|
|
@@ -225,7 +198,12 @@ module CachedCounts
|
|
225
198
|
|
226
199
|
def add_count_attribute_methods(attribute_name, key_getter, relation_getter, define_with, counted_class, options)
|
227
200
|
expires_in = options.fetch :expires_in, 1.week
|
228
|
-
|
201
|
+
default_value_lambda = options.fetch :default_value_lambda, -> {0}
|
202
|
+
value_updater_lambda = options.fetch :value_updater_lambda, default_value_updater_lambda
|
203
|
+
# TODO: need a good strategy to figure this value out
|
204
|
+
# As a fallback value is used, we immediately fire the calculation for the real value
|
205
|
+
# Is it reasonable to wait as long as it takes to calcualte it? Is there an alternative?
|
206
|
+
fallback_expiry_seconds = expires_in
|
229
207
|
|
230
208
|
key_method = "#{attribute_name}_count_key"
|
231
209
|
|
@@ -235,34 +213,23 @@ module CachedCounts
|
|
235
213
|
val = Rails.cache.fetch(
|
236
214
|
send(key_method),
|
237
215
|
expires_in: expires_in,
|
238
|
-
race_condition_ttl:
|
216
|
+
race_condition_ttl: fallback_expiry_seconds,
|
239
217
|
raw: true # Necessary for incrementing to work correctly
|
240
218
|
) do
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
raw: true
|
251
|
-
)
|
252
|
-
end
|
253
|
-
|
219
|
+
# Write down the default value first so subsequent reads don't repeat calculations
|
220
|
+
# TODO: multiple requests can potentially get to this point and start multiple update operations? Is that a problem?
|
221
|
+
default_value = instance_exec &default_value_lambda
|
222
|
+
Rails.cache.write(
|
223
|
+
send(key_method),
|
224
|
+
default_value,
|
225
|
+
expires_in: fallback_expiry_seconds,
|
226
|
+
raw: true
|
227
|
+
)
|
254
228
|
relation = instance_exec(&relation_getter)
|
255
229
|
relation = relation.reorder('')
|
256
230
|
relation.select_values = ['count(*)']
|
257
|
-
|
258
|
-
conn = CachedCounts.connection_for(counted_class)
|
259
|
-
if Rails.version < '4.2'.freeze
|
260
|
-
conn.select_value(relation.to_sql, nil, relation.values[:bind] || []).to_i
|
261
|
-
else
|
262
|
-
conn.select_value(relation.to_sql).to_i
|
263
|
-
end
|
231
|
+
value_updater_lambda.call(counted_class, relation.to_sql, send(key_method), expires_in) || default_value
|
264
232
|
end
|
265
|
-
|
266
233
|
if val.is_a?(ActiveSupport::Cache::Entry)
|
267
234
|
val.value.to_i
|
268
235
|
else
|
@@ -284,6 +251,20 @@ module CachedCounts
|
|
284
251
|
end
|
285
252
|
end
|
286
253
|
|
254
|
+
def default_value_updater_lambda()
|
255
|
+
lambda { |counted_class, relation_sql, cache_key, expires_in|
|
256
|
+
conn = CachedCounts.connection_for(counted_class)
|
257
|
+
value = conn.select_value(relation_sql).to_i
|
258
|
+
Rails.cache.write(
|
259
|
+
cache_key,
|
260
|
+
value,
|
261
|
+
expires_in: expires_in,
|
262
|
+
raw: true
|
263
|
+
)
|
264
|
+
return value
|
265
|
+
}
|
266
|
+
end
|
267
|
+
|
287
268
|
def add_counting_hooks(attribute_name, key_getter, counted_class, options)
|
288
269
|
increment_hook = "increment_#{attribute_name}_count"
|
289
270
|
counted_class.send :define_method, increment_hook do
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: incrdecr_cached_counts
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 0.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- David Judd
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-06
|
11
|
+
date: 2016-12-06 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|
@@ -175,18 +175,9 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
175
175
|
version: '0'
|
176
176
|
requirements: []
|
177
177
|
rubyforge_project:
|
178
|
-
rubygems_version: 2.
|
178
|
+
rubygems_version: 2.5.1
|
179
179
|
signing_key:
|
180
180
|
specification_version: 4
|
181
181
|
summary: A replacement for Rails' counter caches using memcached (via Dalli)
|
182
|
-
test_files:
|
183
|
-
- spec/caches_count_of_spec.rb
|
184
|
-
- spec/caches_count_where_spec.rb
|
185
|
-
- spec/database.yml
|
186
|
-
- spec/fixtures.rb
|
187
|
-
- spec/fixtures/department.rb
|
188
|
-
- spec/fixtures/following.rb
|
189
|
-
- spec/fixtures/university.rb
|
190
|
-
- spec/fixtures/user.rb
|
191
|
-
- spec/spec_helper.rb
|
182
|
+
test_files: []
|
192
183
|
has_rdoc:
|