class ActiveSupport::Cache::Store

def fetch(name, options = nil, &block)


end
token
options.expires_at = token.expires_at
token = authenticate_to_service
cache.fetch("authentication-token:#{user.id}") do |key, options|

instance is passed as the second argument to the block. For example:
on the cached value. To support this, an ActiveSupport::Cache::WriteOptions
In some cases it may be necessary to dynamically compute options based

==== Dynamic Options

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:
cache miss. Thus, +fetch+ supports the same options as #read and #write.
Internally, +fetch+ calls +read_entry+, and calls +write_entry+ on a

==== 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
    unless options[:force]
      instrument(:read, name, options) do |payload|
        cached_entry = read_entry(key, **options, event: payload)
        entry = handle_expired_entry(cached_entry, key, options)
        if entry
          if entry.mismatched?(normalize_version(name, options))
            entry = nil
          else
            begin
              entry.value
            rescue DeserializationError
              entry = nil
            end
          end
        end
        payload[:super_operation] = :fetch if payload
        payload[:hit] = !!entry if payload
      end
    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