Hey there,
I have the relatively simple Multi:
def set_user_team_is_default(%UserTeam{user_id: user_id, team_id: team_id}) do
Multi.new()
|> Multi.update_all(
:set_other_teams_to_non_default,
from(ut in UserTeam, where: ut.user_id == ^user_id and ut.is_default == true),
set: [is_default: false]
)
|> Multi.update(
:set_to_default,
# getting the user_team here or not getting and using the received one results in the same
UserTeam.changeset(get_user_team(user_id, team_id), %{is_default: true})
)
|> Repo.transaction()
end
It is used to set every user_team(that is default) is_default to false and then set one as default.
Now the following unexpected thing happened:
-
User opened the page in browser A and then opened the same page in browser B at the same time.
-
User selects team A as default in browser A - works as expected
-
User selects same team A as default in browser B - user ends up without a default team.
Why does this happen?
The result of the multi when this occurs is this:
{:ok,
%{
set_other_teams_to_non_default: {1, nil},
set_to_default: %XXX.UsersTeams.UserTeam{
__meta__: #Ecto.Schema.Metadata<:loaded, "users_teams">,
id: 3,
inserted_at: ~N[2022-03-01 09:28:23],
is_default: true,
role: :admin,
status: :active,
team: #Ecto.Association.NotLoaded<association :team is not loaded>,
team_id: 3,
updated_at: ~N[2022-04-07 05:54:59],
user: #Ecto.Association.NotLoaded<association :user is not loaded>,
user_id: 2
}
}}
As you see in the result of the multi is_default is true for the user_team.
But!
If I fetch the user_team right after the multi it shows this:
%XXX.UsersTeams.UserTeam{
__meta__: #Ecto.Schema.Metadata<:loaded, "users_teams">,
id: 3,
inserted_at: ~N[2022-03-01 09:28:23],
is_default: false,
role: :admin,
status: :active,
team: #Ecto.Association.NotLoaded<association :team is not loaded>,
team_id: 3,
updated_at: ~N[2022-04-07 05:54:59],
user: #Ecto.Association.NotLoaded<association :user is not loaded>,
user_id: 2
}
While the multi returns that is_default is true, in the database it is false!
According to the multi docs: Ecto.Multi — Ecto v3.11.1
If a multi is valid (i.e. all the changesets in it are valid), all operations will be executed in the order they were added.
Can someone explain as to why does this happen?
Fixing it is easy(patter match on the incoming user_team, if is_default: true
ignore it), I am not asking for that, I want to understand why does it happen.