AF
Ash Framework•2mo ago
mcoll

get_and_lock_for_update missing data

Hello! I have an action that does a get_and_lock_for_update, then does an anonymous function change. It does this in order to do an array append into an {:array, :map} field.
update :add_story_beat do
change get_and_lock_for_update()

argument :beat_definition, :map do
allow_nil? false
end

change fn changeset, ctx ->
beats =
Ash.Changeset.get_data(changeset, :beats)
|> case do
nil -> [changeset.arguments.beat_definition]
b -> [changeset.arguments.beat_definition | b]
end

changeset
|> Ash.Changeset.force_change_attribute(:beats, beats)
end

require_atomic? false
end
update :add_story_beat do
change get_and_lock_for_update()

argument :beat_definition, :map do
allow_nil? false
end

change fn changeset, ctx ->
beats =
Ash.Changeset.get_data(changeset, :beats)
|> case do
nil -> [changeset.arguments.beat_definition]
b -> [changeset.arguments.beat_definition | b]
end

changeset
|> Ash.Changeset.force_change_attribute(:beats, beats)
end

require_atomic? false
end
3 Replies
mcoll
mcollOP•2mo ago
When running it, I see the queries I would expect, first a SELECT FOR UPDATE, then an update, and finall a commit
begin []
SELECT s0."id", s0."status", s0."title", s0."updated_at", s0."inserted_at", s0."summary", s0."story_outline", s0."beats", s0."learning_plan_id", s0."content_start_at", s0."content_end_at", s0."current_paragraph" FROM "stories" AS s0 WHERE (s0."id"::uuid = $1::uuid) LIMIT $2 FOR UPDATE OF s0
UPDATE "stories" AS s0 SET "updated_at" = $1, "beats" = $2 WHERE (s0."id" = $3) RETURNING s0."id", s0."title", s0."status", s0."summary", s0."beats", s0."current_paragraph", s0."story_outline", s0."content_start_at", s0."content_end_at", s0."inserted_at", s0."updated_at", s0."learning_plan_id"
commit []
begin []
SELECT s0."id", s0."status", s0."title", s0."updated_at", s0."inserted_at", s0."summary", s0."story_outline", s0."beats", s0."learning_plan_id", s0."content_start_at", s0."content_end_at", s0."current_paragraph" FROM "stories" AS s0 WHERE (s0."id"::uuid = $1::uuid) LIMIT $2 FOR UPDATE OF s0
UPDATE "stories" AS s0 SET "updated_at" = $1, "beats" = $2 WHERE (s0."id" = $3) RETURNING s0."id", s0."title", s0."status", s0."summary", s0."beats", s0."current_paragraph", s0."story_outline", s0."content_start_at", s0."content_end_at", s0."inserted_at", s0."updated_at", s0."learning_plan_id"
commit []
When I run this sequentially, only the last inserted element is actually inserted. This is because it seems that the story inside the Changeset is never refreshed and it still has the original items. I'd expect that get_and_lock_for_update would refetch (with the SELECT FOR UPDATE) and then apply the changes. However it seems that that is not what it's doing?
ZachDaniel
ZachDaniel•2mo ago
🤔 I'm not sure if it replaces the data with the fetched data, but it would make sense for it to do that. I'd check the source for that change and if its not feel free to open an issue/PR. To make it not a breaking change, we'd likely need to add get_and_lock_for_update(..., replace?: true)
mcoll
mcollOP•2mo ago
hmm, if I'm reading this right it should be updating data to the latest: https://github.com/ash-project/ash/blob/5d9456196e960b7884384b4fa53f98d8f0f17c5a/lib/ash/resource/change/get_and_lock_for_update.ex#L23-L24 I wonder what's going on, will try to debug later.
GitHub
ash/lib/ash/resource/change/get_and_lock_for_update.ex at 5d9456196...
A declarative, extensible framework for building Elixir applications. - ash-project/ash

Did you find this page helpful?