From 84e954a627b44362f5ebd72f6b7c4b97ba3f7d3a Mon Sep 17 00:00:00 2001 From: Zack Siri Date: Sat, 11 Nov 2023 12:37:30 +0700 Subject: [PATCH] Resolve race condition for deployment Signed-off-by: Zack Siri --- lib/uplink/packages/deployment.ex | 3 +++ lib/uplink/packages/deployment/manager.ex | 33 ++++++++++++++++++++--- 2 files changed, 32 insertions(+), 4 deletions(-) diff --git a/lib/uplink/packages/deployment.ex b/lib/uplink/packages/deployment.ex index 8b2ff9dd..a41b2857 100644 --- a/lib/uplink/packages/deployment.ex +++ b/lib/uplink/packages/deployment.ex @@ -49,6 +49,9 @@ defmodule Uplink.Packages.Deployment do deployment |> cast(params, @valid_attrs) |> validate_required(@required_attrs) + |> unique_constraint(:hash, + name: :deployments_app_id_hash_index + ) end def update_changeset(deployment, params) do diff --git a/lib/uplink/packages/deployment/manager.ex b/lib/uplink/packages/deployment/manager.ex index 78d6a96b..b838090f 100644 --- a/lib/uplink/packages/deployment/manager.ex +++ b/lib/uplink/packages/deployment/manager.ex @@ -36,22 +36,47 @@ defmodule Uplink.Packages.Deployment.Manager do end @spec get_or_create(%App{}, map) :: {:ok, %Deployment{}} - def get_or_create(%App{id: app_id}, params) do + def get_or_create(%App{id: app_id} = app, params) do hash = Map.get(params, :hash) || Map.get(params, "hash") Deployment |> Repo.get_by(app_id: app_id, hash: hash) |> case do nil -> - %Deployment{app_id: app_id} - |> Deployment.changeset(params) - |> Repo.insert() + create(app, params) %Deployment{} = deployment -> {:ok, deployment} end end + def create(%App{id: app_id}, params) do + %Deployment{app_id: app_id} + |> Deployment.changeset(params) + |> Repo.insert() + |> case do + {:ok, deployment} -> + {:ok, deployment} + + {:error, + %Ecto.Changeset{ + changes: %{hash: hash}, + errors: [ + hash: + {_, + [ + constraint: :unique, + constraint_name: "deployments_app_id_hash_index" + ]} + ] + }} -> + {:ok, Repo.get_by!(Deployment, app_id: app_id, hash: hash)} + + error -> + error + end + end + def update(%Deployment{} = deployment, params) do deployment |> Deployment.update_changeset(params)