Current section
Files
Jump to
Current section
Files
lib/cache.ex
# lib/cache.ex - Add enabled check
defmodule Carve.Cache do
@moduledoc """
Request-scoped cache for Carve to prevent redundant data fetching within a single render.
Each render operation gets its own isolated cache keyed by a unique ref.
TTL defaults to 500ms. Increase if requests involve slow queries.
The cache is cleared at end of each render regardless of TTL.
Configuration:
config :carve, Carve.Config,
enable_cache: false,
cache_ttl: 100
"""
@doc """
Creates a new cache context for a render operation.
Returns a cache key that should be passed through the render pipeline.
"""
def new_context do
if Carve.Config.caching_enabled?() do
cache_key = make_ref()
Cachex.put(:carve_cache, cache_key, %{}, ttl: Carve.Config.cache_ttl())
cache_key
else
nil
end
end
@doc """
Fetches a value from cache or executes the function if not cached.
If caching is disabled, always executes the function.
"""
def fetch(nil, _key, fun) when is_function(fun, 0), do: fun.()
def fetch(cache_key, key, fun) when is_function(fun, 0) do
if Carve.Config.caching_enabled?() do
# Map.fetch, not Map.get: a cached nil (e.g. a missing entity or a
# cache_many id absent from the batch result) is a hit, not a miss.
case Map.fetch(read(cache_key), key) do
{:ok, cached_value} ->
cached_value
:error ->
value = fun.()
put(cache_key, key, value)
value
end
else
fun.()
end
end
@doc """
Looks up a cached value without computing it.
Returns `{:ok, value}` (value may be nil) or `:error` when the key is
absent or caching is disabled.
"""
def peek(nil, _key), do: :error
def peek(cache_key, key) do
if Carve.Config.caching_enabled?() do
Map.fetch(read(cache_key), key)
else
:error
end
end
@doc """
Stores a value in the request cache. No-op when caching is disabled.
"""
def put(nil, _key, _value), do: :ok
def put(cache_key, key, value) do
if Carve.Config.caching_enabled?() do
# Re-read before writing to pick up writes from nested fetch calls
updated = Map.put(read(cache_key), key, value)
Cachex.put(:carve_cache, cache_key, updated, ttl: Carve.Config.cache_ttl())
end
:ok
end
defp read(cache_key) do
case Cachex.get(:carve_cache, cache_key) do
{:ok, %{} = data} -> data
_ -> %{}
end
end
@doc """
Gets the current cache key from the process dictionary, or creates a new one.
Returns nil if caching is disabled.
"""
def get_or_create_context do
if Carve.Config.caching_enabled?() do
case Process.get(:carve_cache_key) do
nil ->
cache_key = new_context()
Process.put(:carve_cache_key, cache_key)
cache_key
cache_key ->
cache_key
end
else
nil
end
end
@doc """
Clears the cache context from the process dictionary.
"""
def clear_context do
if Carve.Config.caching_enabled?() do
Process.delete(:carve_cache_key)
end
end
end