README.md

# NebulexLocalMultilevelAdapter

A variation of
[Multilevel](https://hexdocs.pm/nebulex/Nebulex.Adapters.Multilevel.html)
adapter that assumes Level 1 being `Local` cache running in a distributed
cluster.

## Installation

Add `nebulex_local_multilevel_adapter` to your list of dependencies in `mix.exs`:

```elixir
def deps do
  [
    {:nebulex_local_multilevel_adapter, "~> 0.2.0"}
  ]
end
```

## Usage

<!-- MDOC -->

`NebulexLocalMultilevelAdapter` setup resembles `Nebulex.Adapters.Multilevel` with two exceptions: `model` option is always
`:inclusive` and the first level is autocreated:

```elixir
defmodule MyApp.Cache do
  use Nebulex.Cache,
    otp_app: :slab,
    adapter: NebulexLocalMultilevelAdapter
end

# This can be shared cache, e.g. Partitioned, Replicated, Memcached
defmodule MyApp.Cache.Redis do
  use Nebulex.Cache,
    otp_app: :slab,
    adapter: NebulexRedisAdapter
end
```

Then configure `MyApp.Cache` levels just like normal `Multilevel`:

```elixir
config :my_app, MyApp.Cache,
  local_opts: [],
  levels: [
    {MyApp.Cache.Redis, []},
  ]
```

The adapter will automatically create `MyApp.Cache.Local` L1 cache using options
provided in `local_opts`.

## How it works

`LocalMultilevelAdapter` is different from `Nebulex.Adapters.Multilevel` in a couple of ways:

1. L1 is created automatically and uses `Nebulex.Adapters.Local` adapter
2. Other levels must be global for nodes, meaning they behave like a shared
  storage. The simplest example is `NebulexRedisAdapter`, but `Replicated` and
  `Partitioned` should work too.
3. All write operations are asynchronously broadcasted to other nodes which
  invalidate affected keys in their local L1 caches.

> #### Race conditions {: .warning}
> There are several important things to keep in mind when working with a
> multilevel cache in a clustered environment:
> 1. Always update the underlying storage (e.g. Ecto repo) first and invalidate
>    the cache after to avoid a potential race condition when another client can
>    write a stale value to the cache. The adapter follows this pattern moving
>    from higher to lower levels with deletes.
> 2. Keep in mind that invalidation messages are broadcasted without any
>    confirmation from recipient nodes, so there is always a small chance of
>    reading a stale value from the cache.

<!-- MDOC -->

## Development

`NebulexLocalMultilevelAdapter` relies on shared test code from `Nebulex` repository, so you'll need to fetch it first

```console
export NEBULEX_PATH=nebulex
mix nbx.setup
```

make sure `epmd` is running:

```console
epmd -daemon
```

From this it should be business as usual:

```console
mix deps.get
mix test
```