`Ash.Query.lock`
zachdaniel:
I’ve added
Ash.Query.lock
, which will attach a lock to the query. This currently is only supported by the postgres data layer. There are two kinds of locks, builtin locks and data layer locks. The only builtin lock currently is
:for_update
. The rest will be passed to the data layer.
For example:
query
|> Ash.Query.lock(:for_update)
or
query
|> Ash.Query.lock( "FOR SHARE NOWAIT")
There is also a builtin change for refetching and locking a record before updating it. This can help with ensuring that all update actions to a given record are serialized. For example:
changes do
change get_and_lock_for_update(), on: [:update]
end
or in individual actions
update :update do
change get_and_lock_for_update()
end
jharton:
this is awesome work my dude!
lejoko_annatel:
Hey! Isn’t that exactly what I needed in my “update of related resource” post ? That’s great!
zachdaniel:
Yep! This should help with your thing 🙂
hanrelan:
Am I right that this can be used in place of atomics (until those are ready) for incrementing for example?
zachdaniel:
Yes, the
get_and_lock_for_update
change can be used to achieve the same result
hanrelan:
Been struggling with this for a while but it doesn’t seem to work:
update :increment_completed_sections do
accept []
change get_and_lock_for_update()
change fn changeset, _context ->
Ash.Changeset.before_action(changeset, fn changeset ->
old_completed_sections = Ash.Changeset.get_attribute(changeset, :completed_sections)
Ash.Changeset.change_attribute(
changeset,
:completed_sections,
old_completed_sections + 1
)
end)
end
The update call just doesn’t update
completed_sections
(only updates
updated_at
)
hanrelan:
If I comment out the
change get_and_lock_for_update()
then it does the right thing
hanrelan:
Ok I think I figured it out -
get_and_lock_for_update() need to come after my action because
before_action
hooks are executed in reverse order.
This is a pretty subtle issue so it might be worth calling out explicitly in the docs
zachdaniel:
Yeah, good point. I’ve been thinking of potentially adding
another
phase, called
setup_action
that runs before any
before_action
hooks and happen in the order that they are added.