incrdecr_cached_counts 0.0.6 → 0.1.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/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:
|