# `Nebulex.Adapters.Cachex`
[🔗](https://github.com/elixir-nebulex/nebulex_adapters_cachex/blob/v3.1.0/lib/nebulex/adapters/cachex.ex#L1)

Nebulex adapter for [Cachex][cachex].

This adapter provides a Nebulex interface on top of Cachex, a powerful
in-memory caching library for Elixir. It combines Nebulex's unified caching
API with Cachex's rich feature set including transactions, hooks, expiration,
and statistics. Use this adapter when you need a feature-rich local cache or
as a building block for distributed caching topologies.

[cachex]: https://hexdocs.pm/cachex/Cachex.html

## Options

This adapter supports all Cachex configuration options. The options are passed
directly to [Cachex.start_link/2][cachex_start_link], allowing you to leverage
Cachex's full feature set including expiration, hooks, limits, and warmers.

[cachex_start_link]: https://hexdocs.pm/cachex/Cachex.html#start_link/2

## Example

You can define a cache using Cachex as follows:

    defmodule MyApp.Cache do
      use Nebulex.Cache,
        otp_app: :my_app,
        adapter: Nebulex.Adapters.Cachex
    end

Where the configuration for the cache must be in your application
environment, usually defined in your `config/config.exs`:

    config :my_app, MyApp.Cache,
      stats: true,
      ...

If your application was generated with a supervisor (by passing `--sup`
to `mix new`) you will have a `lib/my_app/application.ex` file containing
the application start callback that defines and starts your supervisor.
You just need to edit the `start/2` function to start the cache as a
supervisor on your application's supervisor:

    def start(_type, _args) do
      children = [
        {MyApp.Cache, []},
      ]

      ...
    end

Since Cachex uses macros for some configuration options, you could also
pass the options in runtime when the cache is started, either by calling
`MyApp.Cache.start_link/1` directly, or in your app supervision tree:

    def start(_type, _args) do
      children = [
        {MyApp.Cache, cachex_opts()},
      ]

      ...
    end

    defp cachex_opts do
      import Cachex.Spec

      [
        expiration: expiration(
          # how often cleanup should occur
          interval: :timer.seconds(30),

          # default record expiration
          default: :timer.seconds(60),

          # whether to enable lazy checking
          lazy: true
        ),

        # hooks
        hooks: [
          hook(module: MyHook, name: :my_hook, args: { })
        ],

        ...
      ]
    end

> See [Cachex.start_link/2][cachex_start_link] for more information.

## Distributed caching topologies

Using the distributed adapters with `Cachex` as a primary storage is possible.
For example, let's define a multi-level cache (near cache topology), where
the L1 is a local cache using Cachex and the L2 is a partitioned cache.

    defmodule MyApp.NearCache do
      use Nebulex.Cache,
        otp_app: :nebulex,
        adapter: Nebulex.Adapters.Multilevel

      defmodule L1 do
        use Nebulex.Cache,
          otp_app: :nebulex,
          adapter: Nebulex.Adapters.Cachex
      end

      defmodule L2 do
        use Nebulex.Cache,
          otp_app: :nebulex,
          adapter: Nebulex.Adapters.Partitioned,
          adapter_opts: [primary_storage_adapter: Nebulex.Adapters.Cachex]
      end
    end

And the configuration may look like:

    config :my_app, MyApp.NearCache,
      model: :inclusive,
      levels: [
        {MyApp.NearCache.L1, []},
        {MyApp.NearCache.L2, primary: [transactions: true]}
      ]

> **NOTE:** You could also use [Nebulex.Adapters.Redis][nbx_redis_adapter] for
  L2, it would be a matter of changing the adapter for the L2 and the
  configuration to set up the Redis adapter.

[nbx_redis_adapter]: https://github.com/elixir-nebulex/nebulex_redis_adapter

See [Nebulex examples](https://github.com/elixir-nebulex/nebulex_examples).
You will find examples for all different topologies, even using other adapters
like Redis; for all examples using the `Nebulex.Adapters.Local` adapter,
you can replace it by `Nebulex.Adapters.Cachex`.

## Query API

The adapter supports querying cached entries using Cachex's query syntax or
explicit key lists.

### Pattern-based queries

Use Cachex query syntax for pattern matching:

    # Get all entries
    MyApp.Cache.get_all!()

    # Count all entries
    MyApp.Cache.count_all!()

    # Delete all entries
    MyApp.Cache.delete_all!()

    # Delete expired entries
    MyApp.Cache.delete_all!(:expired)

    # Stream entries for large datasets
    MyApp.Cache.stream!() |> Enum.take(100)

### Explicit key queries

Query specific keys using the `in: keys` syntax:

    # Get multiple keys
    MyApp.Cache.get_all!(in: ["key1", "key2", "key3"])

    # Count specific keys
    MyApp.Cache.count_all!(in: ["key1", "key2"])

    # Delete specific keys
    MyApp.Cache.delete_all!(in: ["key1", "key2"])

## Transactions

The adapter provides full transaction support through Cachex's locking
mechanism, ensuring atomic operations across multiple keys.

    MyApp.Cache.transaction(
      fn ->
        value = MyApp.Cache.get!("counter")
        MyApp.Cache.put!("counter", value + 1)
        MyApp.Cache.put!("last_updated", DateTime.utc_now())
      end,
      keys: ["counter", "last_updated"]
    )

Transactions automatically handle locking and isolation for the specified keys.

## Stats and Monitoring

Enable statistics collection by setting `stats: true` in your configuration
(enabled by default):

    config :my_app, MyApp.Cache,
      stats: true

Retrieve statistics:

    # Get all stats
    iex> MyApp.Cache.info!(:stats)
    %{
      calls: %{get: 10, put: 5, delete: 2},
      evictions: 0,
      expirations: 1,
      hit_rate: 80.0,
      hits: 8,
      misses: 2,
      ...
    }

See `Cachex.Stats` for detailed statistics information.

---

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