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