class FastCache::Cache
@author {github.com/ssimeonov Simeon Simeonov}, {swoop.com Swoop, Inc.}
In-process cache with least-recently used (LRU) and time-to-live (TTL) expiration semantics.
This implementation is not thread-safe. It does not use a thread to clean up expired values. Instead, an expiration check is performed:
-
Every time you retrieve a value, against that value. If the value has expired, it will be removed and ‘nil` will be returned.
-
Every ‘expire_interval` operations as the cache is used to remove all expired values up to that point.
For manual expiration call {#expire!}.
@example
# Create cache with one million elements no older than 1 hour cache = FastCache::Cache.new(1_000_000, 60 * 60) cached_value = cache.fetch('cached_value_key') do # Expensive computation that returns the value goes here end
Public Class Methods
Initializes the cache.
@param [Integer] max_size Maximum number of elements in the cache. @param [Numeric] ttl Maximum time, in seconds, for a value to stay in
the cache.
@param [Integer] expire_interval Number of cache operations between
calls to {#expire!}.
# File lib/fast_cache/cache.rb, line 35 def initialize(max_size, ttl, expire_interval = 100) @max_size = max_size @ttl = ttl.to_f @expire_interval = expire_interval @op_count = 0 @data = {} @expires_at = {} end
Public Instance Methods
Retrieves a value from the cache.
@param key [Object] the key to look up @return [Object, nil] the value at the key, when present, or ‘nil`
# File lib/fast_cache/cache.rb, line 64 def [](key) _, value = get(key) value end
Stores a value in the cache.
@param key [Object] the key to store at @param val [Object] the value to store @return [Object] the value
# File lib/fast_cache/cache.rb, line 74 def []=(key, val) expire! store(key, val) end
Clears the cache.
@return [self]
# File lib/fast_cache/cache.rb, line 105 def clear @data.clear @expires_at.clear self end
Returns the number of elements in the cache.
@note calls to {#empty?} do not count against ‘expire_interval`.
Therefore, the number of elements is that prior to any expiration.
@return [Integer] number of elements in the cache.
# File lib/fast_cache/cache.rb, line 117 def count @data.count end
Removes a value from the cache.
@param key [Object] the key to remove at @return [Object, nil] the value at the key, when present, or ‘nil`
# File lib/fast_cache/cache.rb, line 83 def delete(key) entry = @data.delete(key) if entry @expires_at.delete(entry) entry.value else nil end end
Allows iteration over the items in the cache.
Enumeration is stable: it is not affected by changes to the cache, including value expiration. Expired values are removed first.
@note The returned values could have expired by the time the client
code gets to accessing them.
@note Because of its stability, this operation is very expensive.
Use with caution.
@return [Enumerator, Array<key, value>] an Enumerator, when a block is
not provided, or an array of key/value pairs.
@yield [Array<key, value>] key/value pairs, when a block is provided.
# File lib/fast_cache/cache.rb, line 137 def each(&block) expire! @data.map { |key, entry| [key, entry.value] }.each(&block) end
Checks whether the cache is empty.
@note calls to {#empty?} do not count against ‘expire_interval`.
@return [Boolean]
# File lib/fast_cache/cache.rb, line 98 def empty? count == 0 end
Removes expired values from the cache.
@return [self]
# File lib/fast_cache/cache.rb, line 145 def expire! check_expired(Time.now.to_f) self end
Retrieves a value from the cache, if available and not expired, or yields to a block that calculates the value to be stored in the cache.
@param key [Object] the key to look up or store at @return [Object] the value at the key @yield yields when the value is not present @yieldreturn [Object] the value to store in the cache.
# File lib/fast_cache/cache.rb, line 51 def fetch(key) found, value = get(key) if found value else store(key, yield) end end
Returns information about the number of objects in the cache, its maximum size and TTL.
@return [String]
# File lib/fast_cache/cache.rb, line 154 def inspect "<#{self.class.name} count=#{count} max_size=#{@max_size} ttl=#{@ttl}>" end
Private Instance Methods
# File lib/fast_cache/cache.rb, line 210 def check_expired(t) if (@op_count += 1) % @expire_interval == 0 while (key_value_pair = @expires_at.first) && (entry = key_value_pair.first).expires_at <= t key = @expires_at.delete(entry) @data.delete(key) end end end
# File lib/fast_cache/cache.rb, line 171 def get(key) t = Time.now.to_f check_expired(t) found = true entry = @data.delete(key) { found = false } if found if entry.expires_at <= t @expires_at.delete(entry) return false, nil else @data[key] = entry return true, entry.value end else return false, nil end end
# File lib/fast_cache/cache.rb, line 203 def shrink_if_needed if @data.length > @max_size entry = delete(@data.shift) @expires_at.delete(entry) end end
# File lib/fast_cache/cache.rb, line 189 def store(key, val) expires_at = Time.now.to_f + @ttl entry = Entry.new(val, expires_at) store_entry(key, entry) val end
# File lib/fast_cache/cache.rb, line 196 def store_entry(key, entry) @data.delete(key) @data[key] = entry @expires_at[entry] = key shrink_if_needed end