No release in over a year
Enhanced application performance with faster reads, data redundancy, and reduced backpressure on the outer cache store.
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
 Dependencies
 Project Readme

CompositeCacheStore 🚀

Lines of Code GEM Version GEM Downloads Ruby Style Tests Sponsors
Ruby.Social Follow Twitter Follow

Boost application speed and maximize user satisfaction with layered caching

Table of Contents

  • Sponsors
  • Why a composite cache?
  • Eventual consistentency
  • Dependencies
  • Installation
  • Setup
  • Usage
  • License

Sponsors

Proudly sponsored by

Why a composite cache?

Layered caching allows you to stack multiple caches with different scopes, lifetimes, and levels of reliability. A technique that yields several benefits.

  • Improved performance
  • Higher throughput
  • Reduced load
  • Enhanced capacity/scalability

Inner cache layer(s) provide the fastest reads as they're close to the application, typically in-memory within the same process. Outer layers are slower (still fast) but are shared by multiple processes and servers.

You can configure each layer with different expiration times, eviction policies, and storage mechanisms. You're in control of balancing the trade-offs between performance and data freshness.

Inner layers are supersonic while outer layers are speedy.

The difference between a cache hit on a local in-memory store versus a cache hit on a remote store is similar to making a grocery run in a Bugatti Chiron Super Sport 300+ compared to making the same trip on a bicyle, but all cache layers will be much faster than the underlying operations. For example, a complete cache miss (that triggers database queries and view rendering) would be equivalent to making this trip riding a sloth.

Eventual consistentency

Layered caching techniques exhibit some of the same traits as distributed systems because inner layers may hold onto stale data until their entries expire. Be sure to configure inner layers appropriately with shorter lifetimes.

This behavior is similar to the race_condition_ttl option in ActiveSupport::Cache::Store which helps to avoid race conditions whenever multiple threads/processes try to write to the same cache entry simultaneously.

Be mindful of the potential gotchas.

  • Data consistency - it's possible to end up with inconsistent or stale data
  • Over-caching - caching too much can lead to increased memory usage and even slower performance
  • Bugs/Testing - difficult bugs can be introduced with sophisticated caching techniques

Dependencies

Installation

bundle add "composite_cache_store"

Setup

Here's an example of how you might set up layered caching in a Rails application.

# config/initializers/composite_cache_store.rb
def Rails.composite_cache
  @composite_cache ||= CompositeCacheStore.new(
    layers: [
      # Layer 1 cache (fastest)
      # Most beneficial for high traffic volume
      # Isolated to the process running an application instance
      ActiveSupport::Cache::MemoryStore.new(
        expires_in: 15.minutes,
        size: 32.megabytes
      ),

      # Layer 2 cache (faster)
      # Most beneficial for moderate traffic volume
      # Isolated to the machine running N-number of application instances,
      # and shared by all application processes on the machine
      ActiveSupport::Cache::RedisCacheStore.new(
        url: "redis://localhost:6379/0",
        expires_in: 2.hours
      ),

      # Layer 3 cache (fast)
      # Global cache shared by all application processes on all machines
      ActiveSupport::Cache::RedisCacheStore.new(
        url: "redis://remote.example.com:6379/0",
        expires_in: 7.days
      ),

      # additional layers are optional
    ]
  )
end

Usage

A composite cache is ideal for mitigating hot spot latency in frequently invoked areas of the codebase.

# method that's invoked frequently by multiple processes/machines
def hotspot
  Rails.composite_cache.fetch("example", expires_in: 12.hours) do
    # reserve for high frequency access of slow operations
    #
    # examples:
    # - api invocations
    # - database queries
    # - template renders
    # - etc.

    frequently_accessed_slow_operation
  end
end

License

The gem is available as open source under the terms of the MIT License.