RedisHashStore
RedisHashStore
extends ActiveSupport's RedisCacheStore
to provide the ability to easily use Redis hashes for caching.
We decided to create this gem because:
- We were previously using
#delete_matched
which can have many performance issues at scale (See this similar issue at GitLab). -
#deleted_matched
doesn't delete values from all the nodes in a Redis cluster.
Rails
Supported Rails versions are listed in Appraisals
.
Installing
Install it yourself as:
$ gem install redis_hash_store
Or add it to your Gemfile
:
gem 'redis_hash_store'
and then execute:
$ bundle
Configuration
All you need to do is add:
# config/production(development|test|staging|preview).rb
config.cache_store = :redis_hash_store, redis_cache_store_options
Usage
This gem simply adds new functionality to RedisCacheStore
, so all existing logic in that class is not affected.
Here is a list of available methods:
#write_hash_value
#read_hash_value
#read_hash
#delete_hash_value
#delete_hash
#fetch_hash_value
Examples
Let's imagine we need to store amount of Services for City.
#write_hash_value
city = "Riyadh"
coffee_shop_type = "coffee_shop"
restaurants_type = "restaurant"
coffee_shops_count = Service.where(type: coffee_shop_type, city: city).count
=> 250
restaurants_count = Service.where(type: restaurants_type, city: city).count
=> 340
Rails.cache.write_hash_value("#{city} counters", coffee_shop_type, coffee_shops_count)
=> 1
Rails.cache.write_hash_value("#{city} counters", restaurants_type, restaurants_count)
=> 1
#read_hash_value
Now it's accessible by:
Rails.cache.read_hash_value("#{city} counters", coffee_shop_type)
=> 250
Rails.cache.read_hash_value("#{city} counters", restaurants_type)
=> 340
#read_hash
Looks pretty easy, right? Maybe you're thinking: "What the difference?"
- You can access all records under the
"#{city} counters"
hash
Rails.cache.read_hash("#{city} counters")
=> { "coffee_shop"=>250, "restaurant"=>340 }
#delete_hash_value
- You can easily remove every value under
"#{city} counters"
Rails.cache.delete_hash_value("#{city} counters", coffee_shop_type)
=> 1
#delete_hash
- You can also delete the entire
"#{city} counters"
hash
Rails.cache.delete_hash("#{city} counters")
=> 1
#fetch_hash_value
- You can fetch needed value under
"#{city} counters"
Rails.cache.fetch_hash_value("#{city} counters", coffee_shop_type) do
Service.where(type: coffee_shop_type, city: city).count
end
=> 250
Rails.cache.fetch_hash_value("#{city} counters", coffee_shop_type, force: true) do
Service.where(type: coffee_shop_type, city: city).count * 2
end
=> 500
Benchmarks
indexes = 1..1_000_000
indexes.each do |index|
Rails.cache.write("some_data_#{index}", index)
Rails.cache.write_hash_value("some_data", index, index)
end
Benchmark.bm do |x|
x.report("delete_matched") { Rails.cache.delete_matched("some_data_*") }
x.report("delete_hash") { Rails.cache.delete_hash("some_data") }
end
user system total real
delete_matched 0.571040 0.244962 0.816002 (3.791056)
delete_hash 0.000000 0.000225 0.000225 (0.677891)
Contributing
Please see CONTRIBUTING.md.