ar-resque-counter-cache 3.0.2 → 3.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.
data/README.md
CHANGED
@@ -18,11 +18,15 @@ How does ar-resque-counter-cache address these issues? It uses Redis as a
|
|
18
18
|
temporary counter cache and Resque to actually update the counter cache column
|
19
19
|
sometime in the future. For example, let's say a single Post gets 1000 comments
|
20
20
|
very quickly. This will set a key in Redis indicating that there is a delta of
|
21
|
-
+1000 for that Post's comments_count column.
|
22
|
-
jobs. This is where resque-lock-timeout
|
23
|
-
be allowed to run at a time. Once a job
|
24
|
-
instances of that job from the queue
|
25
|
-
IncrementCountersWorker.around\_perform\_lock1)
|
21
|
+
+1000 for that Post's comments_count column. Previously (in versions 3.0.2 and
|
22
|
+
below), it would also queue 1000 Resque jobs. This is where resque-lock-timeout
|
23
|
+
came in. Only one of those jobs will be allowed to run at a time. Once a job
|
24
|
+
acquires the lock it removes all other instances of that job from the queue
|
25
|
+
(see IncrementCountersWorker.around\_perform\_lock1) using Redis's lrem
|
26
|
+
command. Unfortunately we ran into some giant Redis cpu overload with lrems
|
27
|
+
during traffic spikes and decided to switch to the simpler resque-loner gem.
|
28
|
+
This gem instead uses a key to determine whether or not to enqueue a given job
|
29
|
+
in the first place.
|
26
30
|
|
27
31
|
You use it like such:
|
28
32
|
|
@@ -9,8 +9,8 @@ Gem::Specification.new do |s|
|
|
9
9
|
s.authors = ["Aaron Gibralter"]
|
10
10
|
s.email = ["aaron.gibralter@gmail.com"]
|
11
11
|
s.homepage = "http://github.com/agibralter/ar-resque-counter-cache"
|
12
|
-
s.summary = %q{Increment ActiveRecord's counter cache column asynchronously using Resque (and resque-
|
13
|
-
s.description = %q{Increment ActiveRecord's counter cache column asynchronously using Resque (and resque-
|
12
|
+
s.summary = %q{Increment ActiveRecord's counter cache column asynchronously using Resque (and resque-loner).}
|
13
|
+
s.description = %q{Increment ActiveRecord's counter cache column asynchronously using Resque (and resque-loner).}
|
14
14
|
|
15
15
|
s.rubyforge_project = "ar-resque-counter-cache"
|
16
16
|
|
@@ -21,7 +21,7 @@ Gem::Specification.new do |s|
|
|
21
21
|
|
22
22
|
s.add_dependency "activerecord", "~> 3.2"
|
23
23
|
s.add_dependency "resque", "~> 1.0"
|
24
|
-
s.add_dependency "resque-
|
24
|
+
s.add_dependency "resque-loner", "~> 1.2"
|
25
25
|
|
26
26
|
s.add_development_dependency "rspec", "~> 2.4.0"
|
27
27
|
s.add_development_dependency "sqlite3-ruby", "~> 1.3.3"
|
@@ -1,5 +1,5 @@
|
|
1
1
|
require 'resque'
|
2
|
-
require 'resque-
|
2
|
+
require 'resque-loner'
|
3
3
|
|
4
4
|
module ArResqueCounterCache
|
5
5
|
|
@@ -10,13 +10,6 @@ module ArResqueCounterCache
|
|
10
10
|
end
|
11
11
|
end
|
12
12
|
|
13
|
-
# The default lock_timeout is 60 seconds.
|
14
|
-
def self.resque_lock_timeout=(lock_timeout)
|
15
|
-
IncrementCountersWorker.class_eval do
|
16
|
-
@lock_timeout = lock_timeout
|
17
|
-
end
|
18
|
-
end
|
19
|
-
|
20
13
|
# If you don't want to use Resque's Redis connection to store the temporary
|
21
14
|
# counter caches, you can set a different connection here.
|
22
15
|
def self.redis=(redis)
|
@@ -26,14 +19,13 @@ module ArResqueCounterCache
|
|
26
19
|
end
|
27
20
|
|
28
21
|
# ArResqueCounterCache will very quickly increment a counter cache in Redis,
|
29
|
-
# which will then later be updated by a Resque job. Using
|
30
|
-
#
|
31
|
-
# at a time.
|
22
|
+
# which will then later be updated by a Resque job. Using require-loner, we
|
23
|
+
# can ensure that only one job per payload is enqueued at a time.
|
32
24
|
class IncrementCountersWorker
|
33
25
|
|
34
|
-
|
26
|
+
include Resque::Plugins::UniqueJob
|
35
27
|
@queue = :counter_caches
|
36
|
-
@
|
28
|
+
@loner_ttl = 3600 # Don't hold lock for more than 1 hour...
|
37
29
|
|
38
30
|
def self.cache_and_enqueue(parent_class, id, column, direction)
|
39
31
|
parent_class = parent_class.to_s
|
@@ -57,35 +49,11 @@ module ArResqueCounterCache
|
|
57
49
|
args.join('-')
|
58
50
|
end
|
59
51
|
|
60
|
-
# Try again later if lock is in use.
|
61
|
-
def self.lock_failed(*args)
|
62
|
-
::Resque.enqueue(self, *args)
|
63
|
-
end
|
64
|
-
|
65
52
|
# args: (parent_class, id, column)
|
66
53
|
def self.cache_key(*args)
|
67
54
|
"ar-resque-counter-cache:#{identifier(*args)}"
|
68
55
|
end
|
69
56
|
|
70
|
-
# The name of this method ensures that it runs within around_perform_lock.
|
71
|
-
#
|
72
|
-
# We've leveraged resque-lock-timeout to ensure that only one job is
|
73
|
-
# running at a time. Now, this around filter essentially ensures that only
|
74
|
-
# one job per parent-column can sit on the queue at once. Since the
|
75
|
-
# cache_key entry in redis stores the most up-to-date delta for the
|
76
|
-
# parent's counter cache, we don't have to actually perform the
|
77
|
-
# Klass.update_counters for every increment/decrement. We can batch
|
78
|
-
# process!
|
79
|
-
def self.around_perform_lock1(*args)
|
80
|
-
# Remove all other instances of this job (with the same args) from the
|
81
|
-
# queue. Uses LREM (http://code.google.com/p/redis/wiki/LremCommand) which
|
82
|
-
# takes the form: "LREM key count value" and if count == 0 removes all
|
83
|
-
# instances of value from the list.
|
84
|
-
redis_job_value = ::Resque.encode(:class => self.to_s, :args => args)
|
85
|
-
::Resque.redis.lrem("queue:#{@queue}", 0, redis_job_value)
|
86
|
-
yield
|
87
|
-
end
|
88
|
-
|
89
57
|
def self.perform(parent_class, id, column)
|
90
58
|
key = cache_key(parent_class, id, column)
|
91
59
|
if (delta = redis.getset(key, 0).to_i) != 0
|
data/spec/integration_spec.rb
CHANGED
@@ -24,9 +24,9 @@ describe "integration" do
|
|
24
24
|
@post2.count_of_comments.should == 0
|
25
25
|
|
26
26
|
# 2 for posts incrementing users' posts counts
|
27
|
-
#
|
28
|
-
#
|
29
|
-
Resque.size(:testing).should ==
|
27
|
+
# 1 for comments incrementing users' comments counts
|
28
|
+
# 2 for comments incrementing posts' comments counts
|
29
|
+
Resque.size(:testing).should == 5
|
30
30
|
|
31
31
|
# [ArResqueCounterCache::IncrementCountersWorker, "User", 1, "posts_count"]
|
32
32
|
perform_next_job
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ar-resque-counter-cache
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 3.0
|
4
|
+
version: 3.1.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date:
|
12
|
+
date: 2013-01-09 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: activerecord
|
@@ -44,13 +44,13 @@ dependencies:
|
|
44
44
|
- !ruby/object:Gem::Version
|
45
45
|
version: '1.0'
|
46
46
|
- !ruby/object:Gem::Dependency
|
47
|
-
name: resque-
|
47
|
+
name: resque-loner
|
48
48
|
requirement: !ruby/object:Gem::Requirement
|
49
49
|
none: false
|
50
50
|
requirements:
|
51
51
|
- - ~>
|
52
52
|
- !ruby/object:Gem::Version
|
53
|
-
version: '
|
53
|
+
version: '1.2'
|
54
54
|
type: :runtime
|
55
55
|
prerelease: false
|
56
56
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -58,7 +58,7 @@ dependencies:
|
|
58
58
|
requirements:
|
59
59
|
- - ~>
|
60
60
|
- !ruby/object:Gem::Version
|
61
|
-
version: '
|
61
|
+
version: '1.2'
|
62
62
|
- !ruby/object:Gem::Dependency
|
63
63
|
name: rspec
|
64
64
|
requirement: !ruby/object:Gem::Requirement
|
@@ -92,7 +92,7 @@ dependencies:
|
|
92
92
|
- !ruby/object:Gem::Version
|
93
93
|
version: 1.3.3
|
94
94
|
description: Increment ActiveRecord's counter cache column asynchronously using Resque
|
95
|
-
(and resque-
|
95
|
+
(and resque-loner).
|
96
96
|
email:
|
97
97
|
- aaron.gibralter@gmail.com
|
98
98
|
executables: []
|
@@ -138,7 +138,7 @@ rubygems_version: 1.8.24
|
|
138
138
|
signing_key:
|
139
139
|
specification_version: 3
|
140
140
|
summary: Increment ActiveRecord's counter cache column asynchronously using Resque
|
141
|
-
(and resque-
|
141
|
+
(and resque-loner).
|
142
142
|
test_files:
|
143
143
|
- spec/ar_resque_counter_cache/active_record_spec.rb
|
144
144
|
- spec/integration_spec.rb
|