Faraday Http Cache

repository·master·Indexed 43 days ago

https://github.com/sourcelevel/faraday-http-cache

A middleware for the Faraday HTTP client library that implements HTTP caching logic. It respects standard HTTP cache headers and supports features like stale-while-revalidate, custom caching strategies (ByUrl and ByVary), and instrumentation via ActiveSupport::Notifications. It requires a store responding to the ActiveSupport::Cache API.

Tokens
4.1K
Snippets
15
Records
20
Agent score
55%

What's inside Faraday Http Cache

  1. Choose a caching strategy

    master

    You can specify a :strategy to change how cache keys are generated.

    • Faraday::HttpCache::Strategies::ByUrl (Default): Uses URL + HTTP method. Stores an array of request + response for each key.
    • Faraday::HttpCache::Strategies::ByVary: Uses headers from the Vary header to generate keys. This is ideal for caching private responses with the same URL but different results for different users (e.g., /user).

    To implement a custom strategy, subclass Faraday::HttpCache::Strategies::BaseStrategy and implement #write, #read, and #delete.

    client = Faraday.new do |builder|
      builder.use :http_cache, store: Rails.cache, strategy: Faraday::HttpCache::Strategies::ByVary
      builder.adapter Faraday.default_adapter
    end
  2. Use stale-while-revalidate with background refresh

    master

    The middleware supports stale-while-revalidate directives. When a response is stale but within the window, the middleware serves the stale response immediately. You can use the :on_stale callback to trigger asynchronous background refreshes.

    client = Faraday.new do |builder|
      builder.use :http_cache,
        store: Rails.cache,
        on_stale: lambda { |request:, env:, cached_response:|
          RefreshApiCacheJob.perform_later(request.url.to_s)
        }
      builder.adapter Faraday.default_adapter
    end
  3. Configure Faraday Http Cache middleware

    master

    To use the middleware, add it to your Faraday builder. You can use the symbol :http_cache or the class Faraday::HttpCache. You must provide a store that responds to write, read, and delete (following the ActiveSupport::Cache API).

    client = Faraday.new do |builder|
      builder.use :http_cache, store: Rails.cache
      # or
      builder.use Faraday::HttpCache, store: Rails.cache
    
      builder.adapter Faraday.default_adapter
    end
  4. Configure shared vs non-shared cache behavior

    master

    By default, the middleware acts as a 'shared cache' (RFC 9111), meaning it does not cache Cache-Control: private responses. To allow caching of private resources, set :shared_cache to false.

    client = Faraday.new do |builder|
      builder.use :http_cache, shared_cache: false
      builder.adapter Faraday.default_adapter
    end
  5. Configure the cache store and serializer

    master

    The middleware requires a store for recording API responses. While a default in-memory store is provided, it is not suitable for production as it is not persisted across processes. For full Unicode support or handling binary data like images, you should specify a serializer such as Marshal or a library like oj or yajl-ruby.

    # Using a specific Memcache store
    store = ActiveSupport::Cache.lookup_store(:mem_cache_store, ['localhost:11211'])
    
    client = Faraday.new do |builder|
      builder.use :http_cache, store: store
      builder.adapter Faraday.default_adapter
    end
    
    # Using Marshal for full unicode/binary support
    client = Faraday.new do |builder|
      builder.use :http_cache, store: Rails.cache, serializer: Marshal
      builder.adapter Faraday.default_adapter
    end
  6. Use the ByVary caching strategy

    master

    The ByVary strategy generates cache keys based on the Vary response header. It is specifically designed for caching private responses that share the same URL but differ based on request headers (e.g., https://api.github.com/user).

    Key Characteristics:

    • Header-based indexing: It uses an index that maps the request URL and Vary headers to specific cache entries.
    • Limitation on Cache Invalidation: This strategy does not support the #delete method for clearing the cache via unsafe HTTP methods (like DELETE), because it cannot reliably find the specific cache keys using only the URL.
    • Serialization: If you encounter Encoding::UndefinedConversionError during serialization, you may need to use Marshal for your serialization logic.
  7. Enable logging for cache operations

    master

    Pass a :logger option to receive debug information about cache hits, misses, and stores.

    client = Faraday.new do |builder|
      builder.use :http_cache, store: Rails.cache, logger: Rails.logger
      builder.adapter Faraday.default_adapter
    end
    
    # Example log output:
    # HTTP Cache: [GET users] miss, store
  8. Use the ByUrl caching strategy

    master

    The Faraday::HttpCache::Strategies::ByUrl strategy is the default caching mechanism. It generates cache keys using a combination of the request URL and the HTTP method.

    When using this strategy, the cache stores multiple entries for the same URL to accommodate different HTTP methods or different Vary header requirements. It supports an optional :max_entries option to limit the number of cached responses stored per URL.

    Key Behaviors

    • Cache Key Generation: Uses a SHA1 hash of the URL (and an internal salt).
    • Vary Header Support: It respects the Vary header. A cached response matches an incoming request if the Vary header is empty or if all headers specified in the Vary header match the incoming request's headers. If Vary: * is present, the cache is considered a mismatch.
    • Serialization: If you encounter an Encoding::UndefinedConversionError during a write operation, it indicates the response could not be serialized. You may need to use a different serialization method (like Marshal) for your cache store.
    # Example of initializing the strategy with a max entries limit
    strategy = Faraday::HttpCache::Strategies::ByUrl.new(max_entries: 10)
  9. Integrate Faraday::HttpCache middleware

    master

    To enable HTTP caching in your Faraday connection, add the :http_cache middleware to your builder stack.

    Important: This middleware should be the last handler attached to your stack (closest to the inner adapter) to avoid interference from other middlewares.

    client = Faraday.new do |builder|
      builder.use :http_cache, store: my_store_backend
      builder.adapter Faraday.default_adapter
    end
  10. Instrument cache events

    master

    Pass an :instrumenter (e.g., ActiveSupport::Notifications) to subscribe to the http_cache.faraday event. The event payload contains :env and :cache_status.

    ActiveSupport::Notifications.subscribe "http_cache.faraday" do |*args|
      event = ActiveSupport::Notifications::Event.new(*args)
      cache_status = event.payload[:cache_status]
    
      case cache_status
      when :fresh, :valid, :stale
        # Handle cache hit
      when :invalid, :miss
        # Handle cache miss
      when :unacceptable
        # Handle cache bypass
      end
    end
  11. Configure Faraday::HttpCache options

    master

    When initializing Faraday::HttpCache, you can provide several configuration options to control storage, serialization, and behavior:

    OptionDescription
    :storeA cache store object that responds to read, write, and delete.
    :serializerAn object that responds to dump and load (e.g., Marshal).
    :shared_cacheBoolean flag. If true (default), the middleware acts as a shared cache according to RFC 2616.
    :instrumenterAn object that responds to instrument (e.g., ActiveSupport::Notifications).
    :instrument_nameThe String name of the instrument being reported on (defaults to 'http_cache.faraday').
    :loggerA logger object for debugging cache hits/misses.
    :on_staleA Proc or lambda called with request:, env:, and cached_response: when a stale response is served during a stale-while-revalidate window.
    :strategyThe caching strategy class to use (defaults to Faraday::HttpCache::Strategies::ByUrl).
    :max_entriesMaximum number of entries per cache key (only used with ByUrl strategy).
    # Example: Using a Rails cache with Marshal serialization and a custom stale callback
    client = Faraday.new do |builder|
      builder.use :http_cache, 
        store: Rails.cache, 
        serializer: Marshal, 
        on_stale: ->(request:, env:, cached_response:) { puts "Serving stale: #{request.url}" }
      builder.adapter Faraday.default_adapter
    end