Using Redis Hashes for Caching in Ruby on Rails
This article discusses improving Redis caching performance in Ruby on Rails by replacing #delete_matched with Redis Hashes.
The Problem
We found that Railsβs #delete_matched function became slow at scale and didnβt work properly across Redis clusters, only operating on single nodes.
The Solution
We implemented Redis Hashes as a faster alternative. Deleting a hash is much faster and there is no risk of leaving out undeleted data across multiple nodes.
Technical Implementation
Hereβs a RedisHashStore module with basic hash operations:
module RedisHashStore
class Entry
attr_accessor :value, :expires_at
def initialize(value, expires_at: nil)
@value = value
@expires_at = expires_at
end
def expired?
expires_at && Time.current > expires_at
end
def dump
Marshal.dump(self)
end
def self.load(data)
return nil if data.nil?
Marshal.load(data)
end
end
def write_hash(hash_name, key, value, expires_in: nil)
expires_at = expires_in ? Time.current + expires_in : nil
entry = Entry.new(value, expires_at: expires_at)
redis.hset(hash_name, key, entry.dump)
end
def read_hash(hash_name, key)
data = redis.hget(hash_name, key)
entry = Entry.load(data)
return nil if entry.nil? || entry.expired?
entry.value
end
def delete_hash(hash_name)
redis.del(hash_name)
end
def delete_hash_key(hash_name, key)
redis.hdel(hash_name, key)
end
private
def redis
Redis.current
end
end
The key insight is that since Redis Hashes donβt natively support TTL on individual fields, we manage expiration ourselves using the Entry class with Marshal.dump and Marshal.load for serialization.
Benchmark Results
Testing with 1 million entries showed dramatic improvements:
| Method | Time |
|---|---|
delete_matched | 3.79 seconds |
delete_hash | 0.68 seconds |
Thatβs approximately a 5.5x improvement in deletion speed.
Ruby Gem
We created a Ruby gem implementing these patterns that you can use in your projects:
Conclusion
If youβre using delete_matched in a Redis cluster environment and experiencing performance issues, consider migrating to Redis Hashes. The performance gains are significant, and you avoid the pitfalls of pattern matching across cluster nodes.
Co-authored by Alex Golubenko. Originally published on engineering.mrsool.co