# `Redis.Cache`
[🔗](https://github.com/joshrotenberg/redis_ex/blob/v0.7.1/lib/redis/cache/cache.ex#L1)

Client-side caching using RESP3 server-assisted invalidation.

Wraps a `Redis.Connection` and caches read command results locally.
The Redis server tracks which keys this client has read and pushes
invalidation messages when those keys are modified by any client.

## How It Works

1. On start, sends `CLIENT TRACKING ON` to enable server-assisted tracking
2. Read commands (GET, MGET, HGETALL, etc.) check the local cache first
3. Cache misses go to Redis — response is cached before returning
4. When any client modifies a cached key, Redis pushes an `invalidate` message
5. The push arrives as `{:redis_push, :invalidate, keys}` from Connection
6. Cache evicts those keys locally
7. Next read goes to Redis again

## Usage

    {:ok, cache} = Redis.Cache.start_link(port: 6379)

    # First call: cache miss -> hits Redis
    {:ok, "bar"} = Redis.Cache.get(cache, "foo")

    # Second call: cache hit -> served locally
    {:ok, "bar"} = Redis.Cache.get(cache, "foo")

    # Another client does SET foo newval -> invalidation push -> evicted
    # Next call: cache miss again
    {:ok, "newval"} = Redis.Cache.get(cache, "foo")

    # Generic cached command -- any allowlisted command works
    {:ok, 3} = Redis.Cache.cached_command(cache, ["LLEN", "mylist"])
    {:ok, ["a", "b"]} = Redis.Cache.cached_command(cache, ["LRANGE", "mylist", "0", "1"])

    # Stats
    Redis.Cache.stats(cache)
    #=> %{hits: 1, misses: 2, evictions: 1, ...}

## Options

  * All `Redis.Connection` options (host, port, password, etc.)
  * `:ttl` - local TTL in ms for cached entries (default: nil, rely on server invalidation)
  * `:optin` - if true, only cache commands prefixed with CACHING YES (default: false)
  * `:max_entries` - maximum number of cached entries (default: 10,000, 0 for unlimited)
  * `:eviction_policy` - `:lru` or `:fifo` (default: `:lru`)
  * `:sweep_interval` - ms between expired-entry sweeps (default: 60,000, nil to disable)
  * `:cacheable` - controls which commands are cached (default: `:default`).
    Accepts `:default` (built-in allowlist), a list of command entries
    (e.g. `["GET", {"LRANGE", ttl: 5_000}]`), or a function
    `([String.t()] -> boolean | {:ok, ttl})`.
  * `:backend` - module implementing `Redis.Cache.Backend` (default: `Redis.Cache.Store`)
  * `:backend_opts` - additional keyword list passed to `backend.init/1`
  * `:name` - GenServer name

# `cached_command`

```elixir
@spec cached_command(GenServer.server(), [String.t()]) ::
  {:ok, term()} | {:error, term()}
```

Executes a command with caching if it is in the allowlist.

For allowlisted commands, results are cached locally and served from
cache on subsequent calls with the same arguments. The cache key is
derived from the full command arguments, and invalidation is tracked
by the Redis key (first argument after the command name).

Non-allowlisted commands are passed through to Redis without caching.

    {:ok, 3} = Redis.Cache.cached_command(cache, ["LLEN", "mylist"])
    {:ok, ["a", "b"]} = Redis.Cache.cached_command(cache, ["LRANGE", "mylist", "0", "1"])

# `child_spec`

Returns a specification to start this module under a supervisor.

See `Supervisor`.

# `command`

```elixir
@spec command(GenServer.server(), [String.t()]) :: {:ok, term()} | {:error, term()}
```

Sends a command through the underlying connection (not cached).

# `flush`

```elixir
@spec flush(GenServer.server()) :: :ok
```

Flushes the local cache.

# `get`

```elixir
@spec get(GenServer.server(), String.t()) :: {:ok, term()} | {:error, term()}
```

GET with caching. Returns `{:ok, value}` or `{:ok, nil}`.

# `hgetall`

```elixir
@spec hgetall(GenServer.server(), String.t()) :: {:ok, term()} | {:error, term()}
```

HGETALL with caching.

# `mget`

```elixir
@spec mget(GenServer.server(), [String.t()]) :: {:ok, [term()]} | {:error, term()}
```

MGET with caching.

# `start_link`

```elixir
@spec start_link(keyword()) :: GenServer.on_start()
```

# `stats`

```elixir
@spec stats(GenServer.server()) :: map()
```

Returns cache statistics.

# `stop`

```elixir
@spec stop(GenServer.server()) :: :ok
```

Disables tracking and stops.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
