ets_lock v0.2.0 EtsLock View Source

EtsLock is a library for acquiring exclusive locks on data in ETS tables.

Using with_ets_lock/4, you can process all {key, value} tuples for a given key while being sure other processes using with_ets_lock/4 are not mutating the data stored for this key.

Processing is performed in a separate process so that an execution timeout (:exec_timeout) can be enforced.

For high concurrent performance, all locks are stored in a separate ETS table, and no GenServers or other processes are used to coordinate access. This is in contrast to the erlang-ets-lock library, which uses a single GenServer to serialize access to ETS.

Link to this section Summary

Functions

Locks a key in ETS and invokes fun on the {key, value} tuples for that key. Returns {:ok, fun.(tuples)} on success.

Link to this section Types

Link to this type

error_reason()

View Source
error_reason() :: :wait_timeout | :exec_timeout | any()
Link to this type

option()

View Source
option() ::
  {:wait_timeout, non_neg_integer() | :infinity}
  | {:exec_timeout, non_neg_integer() | :infinity}
  | {:fail_timeout, non_neg_integer() | :infinity}
  | {:spin_delay, non_neg_integer()}
  | {:lock_table, :ets.tab()}

Link to this section Functions

Link to this function

with_ets_lock(table, key, fun, opts \\ [])

View Source
with_ets_lock(:ets.tab(), key(), ([{key(), value()}] -> any()), opts()) ::
  {:ok, any()} | {:error, error_reason()}

Locks a key in ETS and invokes fun on the {key, value} tuples for that key. Returns {:ok, fun.(tuples)} on success.

If the key is already locked, this function spins until the lock is released or the :wait_timeout is reached.

Example:

iex> table = :ets.new(:whatever, [:set, :public])
iex> spawn(fn ->
...>   ## Wait 50ms, try to acquire lock, then insert
...>   Process.sleep(50)
...>   EtsLock.with_ets_lock(table, :key, fn _ ->
...>     :ets.insert(table, {:key, :yup})
...>   end)
...> end)
iex> spawn(fn ->
...>   ## Acquire lock immediately, hold it for 100ms, then insert
...>   EtsLock.with_ets_lock(table, :key, fn _ ->
...>     Process.sleep(100)
...>     :ets.insert(table, {:key, :nope})
...>   end)
...> end)
iex> Process.sleep(200)
iex> :ets.lookup(table, :key)
[{:key, :yup}]

Options:

  • :wait_timeout - Milliseconds to wait when acquiring a lock. Default 5000. Set to :infinity to try forever to acquire a lock.

  • :exec_timeout - Milliseconds to allow fun to hold the lock before its execution is cancelled and the lock is released. Default 5000. Set to :infinity to hold the lock indefinitely until fun has finished.

  • :fail_timeout - Milliseconds to wait before forcibly deleting a lock after it should have been released, but wasn't. Default 1000. Provides protection against permanently hanging locks in the case that both the caller and the spawned task crash. Set to :infinity to disable this protection (not recommended).

  • :spin_delay - Milliseconds to wait between every attempt to acquire a lock. Default 2.

  • :lock_table - ETS table in which to store locks. Default EtsLock.Locks.