Nebulex v3.1 adds the Nebulex.Adapter.CompositeKV behaviour and changes what get_and_update/3 does with nil. If you only use the cache, update the adapters and check the two changes below. If you maintain an adapter, see the last section.


Update the adapters

All adapters must implement Nebulex.Adapter.CompositeKV, and use Nebulex.Cache checks it at compile time. An adapter built for v3.0 does not compile against v3.1; the error tells you which behaviour is missing. Update the adapters along with core:

defp deps do
  [
    {:nebulex, "~> 3.1"},
    {:nebulex_local, "~> 3.1"},
    ...
  ]
end

get_and_update/3 stores nil

In v3, nil is a valid cache value, but get_and_update/3 still followed the v2 rule where a nil new value meant "do not write". Now, when the function returns {current, nil}, the cache stores nil:

# v3.0
iex> MyCache.get_and_update!(:key, fn current -> {current, nil} end)
{6, 6}
iex> MyCache.get!(:key)
6

# v3.1
iex> MyCache.get_and_update!(:key, fn current -> {current, nil} end)
{6, nil}
iex> MyCache.get!(:key)
nil

To remove the entry, return :pop. It now also removes an entry whose value is nil. Before, it treated a cached nil as a missing key.


Update Telemetry handlers that match on :command

Composite operations now emit their own command span, besides the spans of the primitive commands they run. Handlers get four new :command values: :get_and_update, :update, :fetch_or_store, and :get_or_store. If your handler matches :command exhaustively, add a clause for them or a fallback clause. Otherwise it raises and Telemetry detaches it:

  def handle_event(_event, _measurements, %{command: :put}, _config), do: ...
  def handle_event(_event, _measurements, %{command: :fetch}, _config), do: ...
+ def handle_event(_event, _measurements, _metadata, _config), do: :ok

For adapter authors

Implement Nebulex.Adapter.CompositeKV

Add the behaviour to your adapter. use Nebulex.Adapter.CompositeKV gives you the default implementation, which runs the given function on the calling node using the adapter's fetch, put, and delete callbacks:

  defmodule MyAdapter do
    @behaviour Nebulex.Adapter
    @behaviour Nebulex.Adapter.KV
+
+   use Nebulex.Adapter.CompositeKV

To change where the function runs or what atomicity it has, override get_and_update/6, update/7, fetch_or_store/6, or get_or_store/6. See Nebulex.Adapter.CompositeKV.

__before_compile__/1 is required

It was listed as optional, but Nebulex.Cache has always called it. An adapter without it never worked, so nothing changes if yours has it.