Class: SupportTableCache::MemoryCache
- Inherits:
-
Object
- Object
- SupportTableCache::MemoryCache
- Defined in:
- lib/support_table_cache/memory_cache.rb
Overview
An optimized cache implementation that can be used when all records can easily fit in memory and are never changed. It is intended for use with small, static support tables only.
This cache will not store nil values. This is to prevent the cache from filling up with cache misses because there is no purging mechanism.
Instance Method Summary collapse
-
#clear ⇒ void
Clear all values from the cache.
-
#delete(key) ⇒ void
Delete a value from the cache.
-
#fetch(key, expires_in: nil) { ... } ⇒ Object?
Fetch a value from the cache.
-
#initialize ⇒ SupportTableCache::MemoryCache
constructor
Create a new memory cache.
-
#read(key) ⇒ Object?
Read a value from the cache.
-
#write(key, value, expires_in: nil) ⇒ void
Write a value to the cache.
Constructor Details
#initialize ⇒ SupportTableCache::MemoryCache
Create a new memory cache.
14 15 16 17 18 19 20 21 |
# File 'lib/support_table_cache/memory_cache.rb', line 14 def initialize @cache = {} @mutex = Mutex.new # Maps a cache key to the tokens of the fetches currently generating a value for it. # Invalidating a key drops its tokens so that those fetches will not store the stale # value they generated. Only keys with a fetch in flight are tracked. @pending = {} end |
Instance Method Details
#clear ⇒ void
This method returns an undefined value.
Clear all values from the cache.
124 125 126 127 128 129 130 |
# File 'lib/support_table_cache/memory_cache.rb', line 124 def clear @mutex.synchronize do @pending.clear @cache.clear end nil end |
#delete(key) ⇒ void
This method returns an undefined value.
Delete a value from the cache.
113 114 115 116 117 118 119 |
# File 'lib/support_table_cache/memory_cache.rb', line 113 def delete(key) @mutex.synchronize do @pending.delete(key) @cache.delete(key) end nil end |
#fetch(key, expires_in: nil) { ... } ⇒ Object?
Fetch a value from the cache. If the key is not found or has expired, yields to get a new value.
29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 |
# File 'lib/support_table_cache/memory_cache.rb', line 29 def fetch(key, expires_in: nil) serialized_value = nil token = nil @mutex.synchronize do cached_value, cached_expire_at = @cache[key] if cached_expire_at && cached_expire_at < Process.clock_gettime(Process::CLOCK_MONOTONIC) @cache.delete(key) else serialized_value = cached_value end if serialized_value.nil? && block_given? token = Object.new (@pending[key] ||= []) << token end end if serialized_value.nil? begin value = yield if block_given? return nil if value.nil? serialized_value = Marshal.dump(value) # The expiration is always recalculated from the expires_in argument so that replacing # an expired entry without an expiration does not carry over the old expiration time. expire_at = (Process.clock_gettime(Process::CLOCK_MONOTONIC) + expires_in if expires_in) @mutex.synchronize do # Only store the value if this key was not invalidated while the value was being # generated. Otherwise a record deleted or overwritten by a concurrent update could # be resurrected in the cache with stale data. @cache[key] = [serialized_value, expire_at] if @pending[key]&.include?(token) end ensure @mutex.synchronize do tokens = @pending[key] if tokens tokens.delete(token) @pending.delete(key) if tokens.empty? end end end end Marshal.load(serialized_value) end |
#read(key) ⇒ Object?
Read a value from the cache.
80 81 82 |
# File 'lib/support_table_cache/memory_cache.rb', line 80 def read(key) fetch(key) end |
#write(key, value, expires_in: nil) ⇒ void
This method returns an undefined value.
Write a value to the cache.
90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 |
# File 'lib/support_table_cache/memory_cache.rb', line 90 def write(key, value, expires_in: nil) return if value.nil? if expires_in expire_at = Process.clock_gettime(Process::CLOCK_MONOTONIC) + expires_in end serialized_value = Marshal.dump(value) @mutex.synchronize do # Discard any fetch generating a value for this key so that it cannot overwrite the # newer value being written here. @pending.delete(key) @cache[key] = [serialized_value, expire_at] end nil end |