1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 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 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194
|
# frozen_string_literal: true
require "active_support/core_ext/object/duplicable"
require "active_support/core_ext/string/inflections"
require "active_support/per_thread_registry"
module ActiveSupport
module Cache
module Strategy
# Caches that implement LocalCache will be backed by an in-memory cache for the
# duration of a block. Repeated calls to the cache for the same key will hit the
# in-memory cache for faster access.
module LocalCache
autoload :Middleware, "active_support/cache/strategy/local_cache_middleware"
# Class for storing and registering the local caches.
class LocalCacheRegistry # :nodoc:
extend ActiveSupport::PerThreadRegistry
def initialize
@registry = {}
end
def cache_for(local_cache_key)
@registry[local_cache_key]
end
def set_cache_for(local_cache_key, value)
@registry[local_cache_key] = value
end
def self.set_cache_for(l, v); instance.set_cache_for l, v; end
def self.cache_for(l); instance.cache_for l; end
end
# Simple memory backed cache. This cache is not thread safe and is intended only
# for serving as a temporary memory cache for a single thread.
class LocalStore < Store
def initialize
super
@data = {}
end
# Don't allow synchronizing since it isn't thread safe.
def synchronize # :nodoc:
yield
end
def clear(options = nil)
@data.clear
end
def read_entry(key, options)
@data[key]
end
def read_multi_entries(keys, options)
values = {}
keys.each do |name|
entry = read_entry(name, options)
values[name] = entry.value if entry
end
values
end
def write_entry(key, value, options)
@data[key] = value
true
end
def delete_entry(key, options)
!!@data.delete(key)
end
def fetch_entry(key, options = nil) # :nodoc:
@data.fetch(key) { @data[key] = yield }
end
end
# Use a local cache for the duration of block.
def with_local_cache
use_temporary_local_cache(LocalStore.new) { yield }
end
# Middleware class can be inserted as a Rack handler to be local cache for the
# duration of request.
def middleware
@middleware ||= Middleware.new(
"ActiveSupport::Cache::Strategy::LocalCache",
local_cache_key)
end
def clear(options = nil) # :nodoc:
return super unless cache = local_cache
cache.clear(options)
super
end
def cleanup(options = nil) # :nodoc:
return super unless cache = local_cache
cache.clear
super
end
def increment(name, amount = 1, options = nil) # :nodoc:
return super unless local_cache
value = bypass_local_cache { super }
write_cache_value(name, value, options)
value
end
def decrement(name, amount = 1, options = nil) # :nodoc:
return super unless local_cache
value = bypass_local_cache { super }
write_cache_value(name, value, options)
value
end
private
def read_entry(key, options)
if cache = local_cache
cache.fetch_entry(key) { super }
else
super
end
end
def read_multi_entries(keys, options)
return super unless local_cache
local_entries = local_cache.read_multi_entries(keys, options)
missed_keys = keys - local_entries.keys
if missed_keys.any?
local_entries.merge!(super(missed_keys, options))
else
local_entries
end
end
def write_entry(key, entry, options)
if options[:unless_exist]
local_cache.delete_entry(key, options) if local_cache
else
local_cache.write_entry(key, entry, options) if local_cache
end
super
end
def delete_entry(key, options)
local_cache.delete_entry(key, options) if local_cache
super
end
def write_cache_value(name, value, options)
name = normalize_key(name, options)
cache = local_cache
cache.mute do
if value
cache.write(name, value, options)
else
cache.delete(name, options)
end
end
end
def local_cache_key
@local_cache_key ||= "#{self.class.name.underscore}_local_cache_#{object_id}".gsub(/[\/-]/, "_").to_sym
end
def local_cache
LocalCacheRegistry.cache_for(local_cache_key)
end
def bypass_local_cache
use_temporary_local_cache(nil) { yield }
end
def use_temporary_local_cache(temporary_cache)
save_cache = LocalCacheRegistry.cache_for(local_cache_key)
begin
LocalCacheRegistry.set_cache_for(local_cache_key, temporary_cache)
yield
ensure
LocalCacheRegistry.set_cache_for(local_cache_key, save_cache)
end
end
end
end
end
end
|