class ActiveSupport::Cache::Store
def fetch(name, options = nil, &block)
val_2 # => "original value"
val_1 # => "new value 1"
cache.fetch('foo') # => "new value 1"
sleep 10 # First thread extended the life of cache by another 10 seconds
cache.fetch('foo') # => "original value"
end
end
'new value 2'
val_2 = cache.fetch('foo', race_condition_ttl: 10.seconds) do
Thread.new do
end
end
'new value 1'
sleep 1
val_1 = cache.fetch('foo', race_condition_ttl: 10.seconds) do
Thread.new do
sleep 60
val_2 = nil
val_1 = nil
cache.write('foo', 'original value')
cache = ActiveSupport::Cache::MemoryStore.new(expires_in: 1.minute)
# Set all values to expire after one minute.
has elapsed.
process can try to generate a new value after the extended time window
If the first process errors out while generating a new value, another
process writes the new value, other processes will then use it.
other processes will continue to use the old value. After the first
this extended time window, while the process generates a new value,
+:race_condition_ttl+ seconds before generating a new value. During
+:race_condition_ttl+ seconds ago, it will bump the expiration time by
When a process encounters a cache entry that has expired less than
same entry (also known as the dog pile effect).
by preventing multiple processes from simultaneously regenerating the
This can be used to prevent race conditions when cache entries expire,
an expired value can be reused while a new value is being generated.
* +:race_condition_ttl+ - Specifies the number of seconds during which
cache.exist?('bar') # => false
cache.exist?('foo') # => true
cache.fetch('bar', skip_nil: true) { nil }
cache.fetch('foo') { nil }
* skip_nil: true - Prevents caching a nil result:
just call +write+.
ask whether you should force a cache write. Otherwise, it's clearer to
The +:force+ option is useful when you're calling some other method to
cache.fetch('today', force: true) # => ArgumentError
cache.fetch('today', force: true) { 'Tuesday' } # => 'Tuesday'
cache.write('today', 'Monday')
required when +force+ is true so this always results in a cache write.
cache value as missing even if it's present. Passing a block is
* force: true - Forces a cache "miss," meaning we treat the
Additionally, +fetch+ supports the following options:
miss. Thus, +fetch+ supports the same options as #read and #write.
Internally, +fetch+ calls #read_entry, and calls #write_entry on a cache
==== Options
cache.fetch('city') # => "Duckburgh"
end
'Duckburgh'
cache.fetch('city') do
cache.fetch('city') # => nil
cache.fetch('today') # => "Monday"
cache.write('today', 'Monday')
return value will be returned.
block will be written to the cache under the given cache key, and that
the key and executed in the event of a cache miss. The return value of the
returned. However, if a block has been passed, that block will be passed
If there is no such data in the cache (a cache miss), then +nil+ will be
the cache with the given key, then that data is returned.
Fetches data from the cache, using the given key. If there is data in
def fetch(name, options = nil, &block) if block_given? options = merged_options(options) key = normalize_key(name, options) entry = nil instrument(:read, name, options) do |payload| cached_entry = read_entry(key, **options, event: payload) unless options[:force] entry = handle_expired_entry(cached_entry, key, options) entry = nil if entry && entry.mismatched?(normalize_version(name, options)) payload[:super_operation] = :fetch if payload payload[:hit] = !!entry if payload end if entry get_entry_value(entry, name, options) else save_block_result_to_cache(name, options, &block) end elsif options && options[:force] raise ArgumentError, "Missing block: Calling `Cache#fetch` with `force: true` requires a block." else read(name, options) end end