Current section

Files

Jump to
nebulex lib nebulex object version.ex
Raw

lib/nebulex/object/version.ex

defmodule Nebulex.Object.Version do
@moduledoc """
Version handler for cached objects.
The purpose of this module is to allow users implement custom
version generator and validator. This interface is used by the
adapters to generate and/or validate object versions.
## Example
defmodule MyApp.Versioner do
@behaviour Nebulex.Object.Version
def generate(nil), do: 0
def generate(%Object{version: version}) do
version + 1
end
end
If you are going to rely on this feature, it is recommended to use a good
version generation algorithm (e.g.: **Vector Clocks**).
"""
@doc """
Generates a new version for the current cached object.
"""
@callback generate(cached_object :: Nebulex.Object.t()) :: any
alias Nebulex.Object
@doc """
Validates version conflicts for the given cached `object_or_key`.
This function is used by cache's adapters which implement
optimistic locking via object's version.
For more information, check out adapters implementation.
"""
@spec validate(
object_or_key :: object | Nebulex.Cache.key(),
cache :: Nebulex.Cache.t(),
opts :: Nebulex.Cache.opts()
) :: {:override | :nothing, object | nil}
when object: Nebulex.Object.t()
def validate(nil, _cache, _opts), do: {:override, nil}
def validate(object_or_key, cache, opts) do
opts
|> Keyword.get(:version)
|> validate(object_or_key, cache, opts)
end
## Helpers
defp validate(nil, %Object{} = object, _cache, _opts) do
{:override, object}
end
defp validate(nil, key, _cache, _opts) do
{:override, %Object{key: key}}
end
defp validate(vsn, object_or_key, cache, opts) do
cache
|> maybe_get(object_or_key)
|> on_conflict(vsn, Keyword.get(opts, :on_conflict, :raise))
end
defp maybe_get(_cache, %Object{} = object),
do: object
defp maybe_get(cache, key),
do: cache.__adapter__.get(cache, key, [])
defp on_conflict(%Object{version: version} = cached, version, _on_conflict),
do: {:override, cached}
defp on_conflict(cached, _version, on_conflict) when on_conflict in [:override, :nothing],
do: {on_conflict, cached}
defp on_conflict(cached, version, :raise),
do: raise(Nebulex.VersionConflictError, cached: cached, version: version)
defp on_conflict(_, _, other),
do: raise(ArgumentError, "unknown value for :on_conflict, got: #{inspect(other)}")
end