larshei
Ash - does not implement `atomic/3`
After doing the Ash Livebook Getting started for several times over the last 2? years,
I finally have a small project that I dare to tackle with Ash.
Loving it so far.
However, sometimes I am not quite sure what to do when receiving an error.
Basic Idea / Context
- The system is meant to collect “Proposals”.
- Proposals can be handed in, updated, accepted and rejected during a given timespan.
- Each given timespan is called a Proposal Round.
- All Proposals are related to a Proposal Round. They can only be interacted with while the Proposal Round is active.
- The Proposal Round being active is checked in a custom validation
The custom validation
defmodule Validations.OpenForProposals do
use Ash.Resource.Validation
@impl true
# is this how we would properly check the round_id?
def validate(%{attributes: %{round_id: id}}, _opts, _context) when not is_nil(id) do
validate_open(id)
end
def validate(%{data: %{round_id: id}}, _opts, _context) when not is_nil(id) do
validate_open(id)
end
def validate(_changeset, _opts, _context) do
{:error, field: :round_id, message: "No proposal round was set."}
end
def validate_open(round_id) do
with {:ok, round} <- Ash.get(Proposal, round_id),
true <- round.is_open == true do
IO.puts("OPEN FOR PROPOSALS") # ---- THIS IS PRINTED :) --------
:ok
else
false -> {:error, field: :round_id, message: "Not currently open for proposals."}
e -> e
end
end
end
Domain Module
defmodule Proposals do
use Ash.Domain
resources do
resource Proposal do
define :propose,
args: [:employee_id, :round_id, :text],
action: :propose
define :accept,
args: [:comment],
action: :accept
define :reject,
args: [:comment],
action: :reject
end
end
end
Resource Module
defmodule Proposal do
use Ash.Resource, domain: Proposals, data_layer: AshPostgres.DataLayer
actions do
defaults [:read, :destroy, update: :*]
create :propose do
accept [:employee_id, :round_id, :text]
validate {Validations.OpenForProposals, []}
end
update :accept do
accept [:comment]
validate {Validations.OpenForProposals, []}
change set_attribute(:status, :accepted)
change set_attribute(:comment, arg(:comment), set_when_nil?: false)
end
update :reject do
accept [:comment]
validate {Validations.OpenForProposals, []}
change set_attribute(:status, :rejected)
change set_attribute(:comment, arg(:comment), set_when_nil?: false)
end
end
attributes do
# ...
end
relationships do
# ...
end
postgres do
# ..
end
end
The error
The current error is the following:
iex> {:ok, proposal} = Proposals.propose(me.id, 1, "Add 5 more chairs to the break room.")
{:ok,
#Proposal<
...
>}
iex > Proposals.accept(proposal, "Good idea. Matthew will take care of it.")
%Ash.Error.framework{
errors: [
%Ash.Error.Framework.MustBeAtomic{
resource: Proposal,
action: :accept,
reason: "Validations.OpenForProposals does not implement `atomic/3`",
splode: Ash.Error,
bread_crumbs: [],
vars: [],
path: [],
stacktrace: #Splode.Stacktrace<>,
class: :framework
}]}
So the validation works on :propose, but not on :accept.
I have read Basic bulk actions, atomics, new stream options, `error/2` expression and Update Actions — ash v3.29.3, but I do not understand what to do or what the error actually means (and why it occurs on the validation?!) ![]()
What am I not seeing / understanding and where I could I learn about it?
Marked As Solved
03juan
As far as I can tell, in the info block for Fully Atomic Updates it states
An atomic update is one that can be done in a single operation in the data layer. This ensures that there are no issues with concurrent access to the record being updated, and that it is as performant as possible.
Since you’re fetching the record from the data layer and asserting on is_open in validate_open, there could be a race condition between application processes where one reads and acts on what it sees is a valid state in the data layer but before the write happens another process also changed the data so that the first process’s data is actually not valid any more.
So Ash is expecting you to tell it how the update could be done atomically using the atomic/3 callback.
See Validations without an atomic callback a little further down.
I think the first case works because it’s a create action, not an update
Also Liked
zachdaniel
zachdaniel
Exactly correct ![]()
If this can’t be done atomically, you can add require_atomic? false to the action definition.
Last Post!
03juan
Popular in Questions
Other popular topics
Categories:
Sub Categories:
Forums
Popular Tags
- #ecto
- #liveview
- #troubleshooting
- #learning-elixir
- #deployment
- #library
- #erlang
- #testing
- #genserver
- #mix
- #absinthe
- #remote-other
- #otp
- #plug
- #how-to-question
- #macros
- #postgres
- #channels
- #elixirconf
- #exunit
- #discussion
- #code-sync
- #javascript
- #podcasts
- #onsite
- #dialyzer
- #docker
- #authentication
- #umbrella
- #full-time-contract
- #podcasts-by-brainlid
- #ecto-query
- #elixir-ls
- #phoenix_html
- #iex
- #blog-post
- #graphql
- #genstage
- #ai
- #websockets
- #supervisor
- #elixirconf-us
- #advent-of-code
- #distillery
- #processes
- #api
- #forms
- #metaprogramming
- #security
- #hex









