PhoenixKit.Cache (phoenix_kit v2.28.1)

Copy Markdown View Source

Generic caching system for PhoenixKit with ETS-backed storage.

This module provides a flexible caching foundation that can be used for:

  • Settings caching
  • User roles and permissions
  • Module configurations
  • Any frequently accessed data

Features

  • ETS-backed storage for high-performance lookups
  • Automatic cache warming
  • TTL support for expiring data
  • Statistics tracking
  • Robust fallback mechanisms
  • Multiple cache instances via registry

Usage

# Start a cache instance
{:ok, pid} = PhoenixKit.Cache.start_link(name: :my_cache, warmer: &MyApp.load_data/0)

# Basic operations
PhoenixKit.Cache.put(:my_cache, "key", "value")
PhoenixKit.Cache.get(:my_cache, "key", "default")
PhoenixKit.Cache.invalidate(:my_cache, "key")

# Batch operations
PhoenixKit.Cache.get_multiple(:my_cache, ["key1", "key2"], %{"key1" => "default1"})
PhoenixKit.Cache.invalidate_multiple(:my_cache, ["key1", "key2"])

Configuration

Cache instances can be configured with:

  • :name - Unique name for the cache instance
  • :warmer - Function to warm the cache on startup
  • :ttl - Time-to-live for cache entries (optional)
  • :max_size - Maximum number of entries (optional)

Summary

Functions

Returns a specification to start this module under a supervisor.

Clears all entries from the cache.

Clears all cache entries whose keys start with the given prefix.

Gets a value from the cache.

Gets multiple values from the cache.

get/3, plus the cache's generation at the moment of the read — for a caller that fills a miss from the database. Pass it to put/4 as if_generation:.

Invalidates a key in the cache.

Invalidates multiple keys in the cache.

Invalidates keys and returns only once they are gone.

Puts a value in the cache.

Puts multiple values in the cache. Takes if_generation: like put/4.

Starts a new cache instance.

Gets cache statistics.

Warms the cache using the configured warmer function.

Types

cache_key()

@type cache_key() :: any()

cache_name()

@type cache_name() :: atom()

cache_value()

@type cache_value() :: any()

default_value()

@type default_value() :: any()

options()

@type options() :: [
  name: cache_name(),
  warmer: warmer_fun(),
  ttl: pos_integer() | nil,
  max_size: pos_integer() | nil
]

warmer_fun()

@type warmer_fun() :: (-> map() | nil)

Functions

child_spec(init_arg)

Returns a specification to start this module under a supervisor.

See Supervisor.

clear(cache_name)

@spec clear(cache_name()) :: :ok

Clears all entries from the cache.

Examples

PhoenixKit.Cache.clear(:settings)

clear_by_prefix(cache_name, prefix)

@spec clear_by_prefix(cache_name(), String.t()) ::
  {:ok, non_neg_integer()} | {:error, any()}

Clears all cache entries whose keys start with the given prefix.

Returns the count of entries cleared.

Examples

PhoenixKit.Cache.clear_by_prefix(:publishing_posts, "v1:publishing_post:my-blog:")
# => {:ok, 15}

get(cache_name, key, default \\ nil)

@spec get(cache_name(), cache_key(), default_value()) :: cache_value()

Gets a value from the cache.

Returns the default value if the key is not found or the cache is unavailable.

Examples

PhoenixKit.Cache.get(:settings, "date_format", "Y-m-d")
PhoenixKit.Cache.get(:user_roles, user_uuid, [])

get_multiple(cache_name, keys, defaults \\ %{})

@spec get_multiple(cache_name(), [cache_key()], map()) :: map()

Gets multiple values from the cache.

Returns a map with the requested keys and their values, using defaults for missing keys.

Examples

defaults = %{"date_format" => "Y-m-d", "time_format" => "H:i"}
PhoenixKit.Cache.get_multiple(:settings, ["date_format", "time_format"], defaults)

get_multiple_with_generation(cache_name, keys, defaults \\ %{})

@spec get_multiple_with_generation(cache_name(), [cache_key()], map()) ::
  {map(), non_neg_integer() | nil}

get_multiple/3, plus the generation — see get_with_generation/3.

get_with_generation(cache_name, key, default \\ nil)

@spec get_with_generation(cache_name(), cache_key(), default_value()) ::
  {cache_value(), non_neg_integer() | nil}

get/3, plus the cache's generation at the moment of the read — for a caller that fills a miss from the database. Pass it to put/4 as if_generation:.

A miss-fill races every write: the reader misses, reads the old row, and the writer commits and invalidates before the reader's put arrives. That put would bring the old value back after the invalidation, and it would stay until its TTL. Carrying the generation drops it instead.

The generation is nil when the cache could not answer; a put/4 with if_generation: nil writes nothing.

invalidate(cache_name, key)

@spec invalidate(cache_name(), cache_key()) :: :ok

Invalidates a key in the cache.

Examples

PhoenixKit.Cache.invalidate(:settings, "date_format")

invalidate_multiple(cache_name, keys)

@spec invalidate_multiple(cache_name(), [cache_key()]) :: :ok

Invalidates multiple keys in the cache.

Examples

PhoenixKit.Cache.invalidate_multiple(:settings, ["date_format", "time_format"])

invalidate_now(cache_name, keys)

@spec invalidate_now(cache_name(), [cache_key()]) :: :ok

Invalidates keys and returns only once they are gone.

invalidate/2 and invalidate_multiple/2 are casts: the caller moves on while the cache process still holds the old values. That is fine for a write nobody is watching, and wrong for one that is about to be announced — a subscriber that reacts to "this setting changed" by reading it again would get the value from before the change. Use this when a notification follows.

Never turns a successful write into a failure: a slow, dead or missing cache process is logged and reported as :ok, and the entry expires on its TTL like any other.

put(cache_name, key, value, opts \\ [])

@spec put(cache_name(), cache_key(), cache_value(), keyword()) :: :ok

Puts a value in the cache.

With if_generation: generation (from get_with_generation/3), the value is written only if nothing has been invalidated since that read — the form a miss-fill must use. Without it the write is unconditional.

Examples

PhoenixKit.Cache.put(:settings, "date_format", "m/d/Y")
PhoenixKit.Cache.put(:user_roles, user_uuid, ["admin", "user"])

put_multiple(cache_name, key_values, opts \\ [])

@spec put_multiple(cache_name(), map(), keyword()) :: :ok

Puts multiple values in the cache. Takes if_generation: like put/4.

Examples

PhoenixKit.Cache.put_multiple(:settings, %{"date_format" => "m/d/Y", "time_format" => "h:i A"})

start_link(opts)

@spec start_link(options()) :: GenServer.on_start()

Starts a new cache instance.

Options

  • :name - Required. Unique name for the cache instance
  • :warmer - Optional. Function to warm the cache on startup
  • :ttl - Optional. Time-to-live for cache entries in milliseconds
  • :max_size - Optional. Maximum number of entries before eviction

Examples

{:ok, pid} = PhoenixKit.Cache.start_link(name: :settings)
{:ok, pid} = PhoenixKit.Cache.start_link(name: :user_roles, warmer: &MyApp.load_user_roles/0)

stats(cache_name)

@spec stats(cache_name()) :: map()

Gets cache statistics.

Examples

PhoenixKit.Cache.stats(:settings)
# => %{hits: 150, misses: 5, puts: 20, invalidations: 3, hit_rate: 0.97}

warm(cache_name)

@spec warm(cache_name()) :: :ok

Warms the cache using the configured warmer function.

Examples

PhoenixKit.Cache.warm(:settings)