# Interaction between optimistic logging and changeset errors: \`lock\_version\` is updated in error-return changeset

**URL:** <https://forum.elixirforum.com/t/interaction-between-optimistic-logging-and-changeset-errors-lock-version-is-updated-in-error-return-changeset/26147>\
**Category:** Questions\
**Tags:** ecto, constraint, optimistic\_lock\
**Created:** [October 17, 2019, 9:07pm UTC](https://forum.elixirforum.com/t/interaction-between-optimistic-logging-and-changeset-errors-lock-version-is-updated-in-error-return-changeset/26147 "2019-10-17T21:07:37Z")\
**Posts on this page:** 1\
**Page:** 1

<div class="post-metadata">

**Author:** ![marick](https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/marick/32/1774_2.png) [@marick](https://forum.elixirforum.com/u/marick)\
**Post date:** [October 17, 2019, 9:07pm UTC](https://forum.elixirforum.com/t/interaction-between-optimistic-logging-and-changeset-errors-lock-version-is-updated-in-error-return-changeset/26147/1 "2019-10-17T21:07:37Z")

</div>

_Later: I have realized this applies to validation errors, not constraints, as I’d originally thought. See footnote [3]_

I have a changeset with both a constraint and optimistic locking:

```elixir
    %Animal{id: id}
    |> cast(attrs, [:name, :lock_version])
    |> unique_constraint(:name, name: "unique_available_names")
    |> optimistic_lock(:lock_version)

```

IF the `unique_constraint` is violated, I get an `{:error, changeset}` back, but the changeset contains an _incremented_ version of the `:lock_version`. Since the update didn’t happen, the version in the changeset will be used to populate the edit form, which uses a `hidden` input to send back the lock version:

```elixir
<%= form_for @changeset, ...
  <%= text_input(f, :name) %> ...
  <%= hidden_input(f, :lock_version) %> <<<<<<<<<<<

```

A corrected update attempt will now fail with a `StaleEntryError`.[1]

Am I misunderstanding the right way to use optimistic locking?[2] Details below.

[1] I assume the error check uses equality rather than `>=`, but I only traced the code to [Ecto.Adapters.Sql.schema](https://github.com/elixir-ecto/ecto_sql/blob/0464becc640dcb9b830b56cca5affa060d893704/lib/ecto/adapters/sql.ex#L674), which is not far enough.

[2] This seems a different problem than [25570](https://forum.elixirforum.com/t/how-to-use-ecto-optimistic-lock-with-phoenix-generated-update-action/25570).

[3] I’ve confirmed that this also happens with a `validate_length` error. That is, the `:lock_version` is set to 2 in the error changeset, even though the `:lock_version` in the on-disk copy is 1.

```elixir
full changeset: #Ecto.Changeset<
  action: :update,
  changes: %{lock_version: 2, name: "preexisting"}, <<<<<<<<<<<<<<
  errors: [
    name: {"should be %{count} character(s)",
     [count: 3838, validation: :length, kind: :is, type: :string]}
  ],
  data: #Crit.Usables.Animal<>,
  valid?: false
>

```

* * *

Start with two animals:

```elixir
%Crit.Usables.Animal{
  available: true,
  id: 48789,
  lock_version: 1,
  name: "Original Bossie",
}

%Crit.Usables.Animal{
  id: 48790,
  lock_version: 1,
  name: "preexisting",
}

```

The form will send back these params:

```elixir
%{"lock_version" => "1", "name" => "preexisting"}

```

However, the changeset pipeline shown above transforms the lock version before it hits `Repo.update`:

```elixir
update: #Ecto.Changeset<
  action: nil,
  changes: %{lock_version: 2, name: "preexisting"},
  errors: [],
  data: #Crit.Usables.Animal<>,
  valid?: true
>

```

… and the resulting error changeset contains the updated `:lock_version`:

```elixir
#Ecto.Changeset<
  action: :update,
  changes: %{lock_version: 2, name: "preexisting"}, <<<<<<<<<<<<<<
  errors: [
    name: {"has already been taken",
     [constraint: :unique, constraint_name: "unique_available_names"]}
  ],
  data: #Crit.Usables.Animal<>,
  valid?: false
>

```

Might be worth noting that the original `Animal` (`changeset.data`) has not been changed.
