Class: SafeMemoize::Stores::Redis
- Defined in:
- lib/safe_memoize/stores/redis.rb
Overview
Cache store adapter backed by Redis.
Not auto-required. Add to your application: require "safe_memoize/stores/redis"
Requires a Redis-compatible client that responds to +#get+, +#set+, +#del+, and +#scan_each+. Compatible with the +redis+ gem (v4+) and any drop-in replacement.
Values and keys are serialized with +Marshal+ (Base64-encoded via +Array#pack("m0")+) so that any Ruby object, including +nil+ and +false+, can be stored and retrieved faithfully. TTL is forwarded to Redis as the +PX+ option (milliseconds, rounded up to the nearest millisecond; minimum 1 ms) to preserve sub-second precision.
Constant Summary
Constants inherited from Base
Instance Method Summary collapse
-
#clear ⇒ void
Removes all entries written by this adapter (scoped to the namespace).
- #delete(key) ⇒ void
-
#initialize(client, namespace: "safe_memoize") ⇒ Redis
constructor
A new instance of Redis.
-
#keys ⇒ Array<Object>
Returns all live keys in the namespace, deserialized back to their original Ruby form.
-
#read(key) ⇒ Object
The stored value, or Base::MISS if absent.
- #write(key, value, expires_in: nil) ⇒ void
Methods inherited from Base
Constructor Details
#initialize(client, namespace: "safe_memoize") ⇒ Redis
Returns a new instance of Redis.
41 42 43 44 |
# File 'lib/safe_memoize/stores/redis.rb', line 41 def initialize(client, namespace: "safe_memoize") @client = client @namespace = namespace end |
Instance Method Details
#clear ⇒ void
This method returns an undefined value.
Removes all entries written by this adapter (scoped to the namespace). Uses +SCAN+ internally to avoid blocking Redis.
80 81 82 83 84 |
# File 'lib/safe_memoize/stores/redis.rb', line 80 def clear to_delete = [] @client.scan_each(match: "#{@namespace}:*") { |k| to_delete << k } @client.del(*to_delete) unless to_delete.empty? end |
#delete(key) ⇒ void
This method returns an undefined value.
73 74 75 |
# File 'lib/safe_memoize/stores/redis.rb', line 73 def delete(key) @client.del(redis_key(key)) end |
#keys ⇒ Array<Object>
Returns all live keys in the namespace, deserialized back to their original Ruby form. Entries that cannot be deserialized are silently skipped. Because Redis handles TTL natively, every key returned by +SCAN+ is live.
92 93 94 95 96 97 98 99 100 101 102 |
# File 'lib/safe_memoize/stores/redis.rb', line 92 def keys prefix = "#{@namespace}:" result = [] @client.scan_each(match: "#{@namespace}:*") do |rk| encoded = rk.delete_prefix(prefix) result << Marshal.load(encoded.unpack1("m0")) # rubocop:disable Security/MarshalLoad rescue ArgumentError, TypeError # skip keys that cannot be deserialized (e.g. written by another serializer) end result end |
#read(key) ⇒ Object
Returns the stored value, or Base::MISS if absent.
48 49 50 51 52 53 54 55 |
# File 'lib/safe_memoize/stores/redis.rb', line 48 def read(key) raw = @client.get(redis_key(key)) return MISS if raw.nil? Marshal.load(raw) # rubocop:disable Security/MarshalLoad rescue TypeError, ArgumentError MISS end |
#write(key, value, expires_in: nil) ⇒ void
This method returns an undefined value.
62 63 64 65 66 67 68 69 |
# File 'lib/safe_memoize/stores/redis.rb', line 62 def write(key, value, expires_in: nil) raw = Marshal.dump(value) if expires_in @client.set(redis_key(key), raw, px: [(expires_in * 1000).ceil, 1].max) else @client.set(redis_key(key), raw) end end |