ash-project / ash

A declarative, extensible framework for building Elixir applications.
https://www.ash-hq.org
MIT License
1.63k stars 220 forks source link

Ash.bulk_update called from before_action not producing correct sql in ash 3.4.29 and newer #1526

Closed zimakki closed 1 month ago

zimakki commented 1 month ago

Describe the bug The below test is failing only after upgrading to ash 3.4.29. In previous versions it was working fine. I am able to reproduce this every time by setting a specific version of ash.

test "when put into standby mode - pods should be locked" do
      locker = locker_fixture()
      pod_1 = pod_fixture(%{locker_id: locker.id, lock_status: :unlocked})
      pod_2 = pod_fixture(%{locker_id: locker.id, lock_status: :locked})
      pod_3 = pod_fixture(%{locker_id: locker.id, lock_status: :unlocked})

      assert %Locker{status: :standby} = Lockers.put_in_standby!(locker)
      assert %Pod{lock_status: :locked} = Ash.get!(Pod, pod_1.id)
      assert %Pod{lock_status: :locked} = Ash.get!(Pod, pod_2.id)
      assert %Pod{lock_status: :locked} = Ash.get!(Pod, pod_3.id)
    end

The specific assert that is failing is the 2nd assert that is checking the lock_status attribute: assert %Pod{lock_status: :locked} = Ash.get!(Pod, pod_1.id)

This is the put_in_standby action:

update :put_in_standby do
      accept []

      require_atomic? false

      change Changes.LockAllPods

      change set_attribute(:status, :standby)
    end

this is the LockAllPods change:

def change(changeset, _options, _context) do
    Changeset.before_action(changeset, &lock_all_pods/1)
  end

  defp lock_all_pods(changeset) do
    id = Changeset.get_data(changeset, :id)

    result =
      Pod
      |> Ash.Query.filter(locker_id == ^id)
      |> Ash.bulk_update(:lock, %{}, notify?: true, authorize?: false)

    case result do
      %Ash.BulkResult{errors: nil} -> changeset
      %Ash.BulkResult{errors: []} -> changeset
      %Ash.BulkResult{errors: errors} -> Changeset.add_error(changeset, errors)
    end
  end

this is the lock action that is called from the change:

update :lock do
      accept []

      change set_attribute(:lock_status, :locked)
    end

Below is the sql debug from an earlier version of ash vs 3.4.29. NOTE: the 3.4.29 version does not have the set "lock_status" = ...

 UPDATE "pods" AS p0 
SET 
"lock_status" = $1::varchar::varchar, 
"updated_at" = $2::timestamp::timestamp 

WHERE (p0."locker_id"::uuid = $3::uuid) 
  RETURNING p0."id", 
  p0."size", p0."cell_ref", 
  p0."door_status", 
  p0."lock_status", 
  p0."inserted_at", 
  p0."updated_at", 
  p0."locker_id" [:locked, ~U[2024-10-16 16:05:07.367995Z], "857d2293-f955-4737-8716-a54c8f67e7b3"]

vs

  UPDATE "pods" AS p0 
SET 
"updated_at" = $1::timestamp::timestamp 
WHERE (p0."locker_id"::uuid = $2::uuid) 
RETURNING 
p0."id", 
p0."size", 
p0."cell_ref", 
p0."door_status", 
p0."lock_status",
p0."inserted_at", 
p0."updated_at", 
p0."locker_id" [~U[2024-10-16 16:01:41.422699Z], "63295e09-2bc2-4990-8871-e994c80186a2"]

Expected behavior All pods should have there lock_status attribute set to locked.

Runtime

Additional context Add any other context about the problem here.