ar-resque-counter-cache (formerly ar-async-counter-cache)
This gem allows you to update ActiveRecord (2.3.x) counter cache columns asynchronously using Resque (http://github.com/defunkt/resque). You may want to do this in situations where you want really speedy inserts and have models that "belong_to" many different parents; that is, you want the request making the INSERT to return before waiting for the many UPDATE...SET counter cache SQL queries to finish. You may also want to use this gem to avoid "Mysql::Error: Lock wait timeout exceeded" issues: if you have a lot of children being created at a time for a single parent row, MySQL can run into lock timeouts while waiting for parent row to update its counter cache over and over. A while ago, I remember seeing that Thoughtbot was having a similar issue in its Hoptoad service...
How does ar-resque-counter-cache address these issues? It uses Redis as a temporary counter cache and Resque to actually update the counter cache column sometime in the future. For example, let's say a single Post gets 1000 comments very quickly. This will set a key in Redis indicating that there is a delta of +1000 for that Post's comments_count column. Previously (in versions 3.0.2 and below), it would also queue 1000 Resque jobs. This is where resque-lock-timeout came in. Only one of those jobs will be allowed to run at a time. Once a job acquires the lock it removes all other instances of that job from the queue (see IncrementCountersWorker.around_perform_lock1) using Redis's lrem command. Unfortunately we ran into some giant Redis cpu overload with lrems during traffic spikes and decided to switch to the simpler resque-loner gem. This gem instead uses a key to determine whether or not to enqueue a given job in the first place.
You use it like such:
class User < ActiveRecord::Base has_many :comments has_many :posts end class Post < ActiveRecord::Base belongs_to :user, :async_counter_cache => true has_many :comments end class Comment < ActiveRecord::Base belongs_to :user, :async_counter_cache => true belongs_to :post, :async_counter_cache => "count_of_comments" end
Notice, you may specify the name of the counter cache column just as you can
with the normal belongs_to
:counter_cache option. You also may not use both
:counter_cache options in the same belongs_to
All you should need to do is require this gem in your project that uses ActiveRecord and you should be good to go;
e.g. In your Gemfile:
gem 'ar-resque-counter-cache', 'x.x.x'
and then in RAILS_ROOT/config/environment.rb somewhere:
By default, the Resque job is placed on the
@queue = :counter_caches
However, you can change this:
in RAILS_ROOT/config/environment.rb somewhere:
ArAsyncCounterCache.resque_job_queue = :low_priority
ArAsyncCounterCache::IncrementCountersWorker.cache_and_enqueue can also be
used to increment/decrement arbitrary counter cache columns (outside of
ArAsyncCounterCache::IncrementCountersWorker.cache_and_enqueue(klass, id, column, direction)
klassis the Class of the ActiveRecord object
idis the id of the object
columnis the counter cache column