Ecto.Model.OptimisticLock

Parsed documentation:
View on GitHub
Facilities for using the optimistic-locking technique.

[Optimistic
locking](http://en.wikipedia.org/wiki/Optimistic_concurrency_control) (or
*optimistic concurrency control*) is a technique that allows concurrent edits
on a single record. While pessimistic locking works by locking a resource for
an entire transaction, optimistic locking only checks if the resource changed
before updating it.

This is done by regularly fetching the record from the database, then checking
whether another process has made changes to the record *only when updating the
record*. This behaviour is ideal in situations where the chances of concurrent
updates to the same record are low; if they're not, pessimistic locking or
other concurrency patterns may be more suited.

## Usage

Optimistic locking works by keeping a "version" counter for each record; this
counter gets incremented each time a modification is made to a record. Hence,
in order to use optimistic locking, a column must be added to a given model's
table and a field must be added to that model's schema.

## Examples

Assuming we have a `Post` model (stored in the `posts` table), the first step
is to add a version column to the `posts` table:

    alter table(:posts) do
      add :lock_version, :integer, default: 1
    end

The column name is arbitrary and doesn't need to be `:lock_version`. However,
it **needs to be an integer**.

Now a field must be added to the schema and the `optimistic_lock/1` macro has
to be used in order to specify which column in the schema will be used as
the "version" column.

    defmodule Post do
      use Ecto.Model

      schema "posts" do
        field :title, :string
        field :lock_version, :integer, default: 1
      end

      optimistic_lock :lock_version
    end

Note that the `optimistic_lock/1` macro is defined in this module, which is
imported when `Ecto.Model` is used. To use the `optimistic_lock/1` macro
without using `Ecto.Model`, just use `Ecto.Model.OptimisticLock` but be sure
to use `Ecto.Model.Callbacks` as well since it's used by
`Ecto.Model.OptimisticLock` under the hood.

When a conflict happens (a record which has been previously fetched is being
updated, but that same record has been modified since it was fetched), an
`Ecto.StaleModelError` exception is raised.

    iex> post = Repo.insert!(%Post{title: "foo"})
    %Post{id: 1, title: "foo", lock_version: 1}
    iex> valid_change = cast(%{title: "bar"}, post, ~w(title), ~w())
    iex> stale_change = cast(%{title: "baz"}, post, ~w(title), ~w())
    iex> Repo.update!(valid_change)
    %Post{id: 1, title: "bar", lock_version: 2}
    iex> Repo.update!(stale_change)
    ** (Ecto.StaleModelError) attempted to update a stale model:

    %Post{id: 1, title: "baz", lock_version: 1}

Optimistic locking also works with delete operations: when trying to delete a
stale model, an `Ecto.StaleModelError` exception is raised as well.
No suggestions.
Please help! Open an issue on GitHub if this assessment is incorrect.