Last active
April 22, 2016 22:00
-
-
Save estum/6db8cbd09e846adf52da2dde5b6293d5 to your computer and use it in GitHub Desktop.
CachedCount: the ActiveRecord::Relation extension to speed up query records
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require "redis" | |
require "cityhash" | |
module CachedCount | |
mattr_accessor(:ttl, instance_accessors: false) { 60 } | |
mattr_accessor(:redis, instance_accessors: false) { Redis.new } | |
class << self | |
def flush! | |
keys = redis.scan_each(match: "cached_count/*").to_a | |
keys.present? && Kefir.redis.del(*keys) | |
end | |
def fetch(key) | |
if from_cache = redis.get(key) | |
from_cache.to_i | |
else | |
yield.tap { |value| redis.set(key, value, nx: true, ex: ttl) } | |
end | |
end | |
end | |
def count(column = nil, options = nil) | |
column = primary_key if column.nil? || column == :all | |
if limited_scope? | |
records_count | |
else | |
sanitized_for_count.cached_count(column) | |
end | |
end | |
def cached_count(column, &block) | |
@cached_count ||= CachedCount.fetch(cache_key(column)) { calculate(:count, column, nil) } | |
end | |
def records_count | |
@records_count ||= 0 | |
end | |
def limited_scope? | |
limit_value.present? | |
end | |
protected | |
def sanitized_for_count? | |
!@sanitized_for_count.nil? | |
end | |
def sanitized_for_count | |
@sanitized_for_count ||= spawn.sanitized_for_count! | |
end | |
def sanitized_for_count! | |
joins_was = joins_values.size | |
self.joins_values = joins_values.grep_v(/^LEFT( OUTER)? JOIN/i) | |
unscope!(:group) if (joins_was - joins_values.size) > 0 | |
unscope!(:select) | |
self | |
end | |
def cache_key(*args) | |
@cache_key ||= begin | |
hashed_sql = CityHash.hash64(to_sql.squish!) | |
conditions = [hashed_sql, *args] | |
conditions.delete(primary_key) | |
[table_key, conditions.join(":")].join('/') | |
end | |
end | |
private | |
def exec_queries | |
super.tap { @records_count = size } | |
end | |
def table_key | |
@table_key ||= "cached_count/#{table_name}".freeze | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
CachedCount
ActiveRecord::Relation extension to speed up query records counts with Redis.
It's helpful if you use it with shameless paginator gems, like Kaminari,
especially with ActiveAdmin.
Install:
It requires
redis
andcityhash
gems, ensure you have them in theGemfile
.Just put the
cached_count.rb
to your app's directory in the$REQUIRE_PATH
,for example to the
lib/cached_count.rb
, and require it somewhere, for example,in the
config/initializers/cached_count.rb
initializer.Also, if you using ActiveAdmin and Rails 4, please, add the hook after AA initialized:
Usage: