我的代碼導致罕見的雙重或三重插入到數據庫中,我對此感到不知所措。這是非常困難的重現,但我可以看看時間戳看到創建時間基本上是相同的,當它發生。我相信只有當CardMeta
尚未找到時纔會發生。Ecto insert_or_update創建多個插入?
我想我需要添加一個唯一的密鑰或將其包裝在一個事務中。
def get_or_create_meta(user, card) do
case Repo.all(from c in CardMeta, where: c.user_id == ^user.id,
where: c.card_id == ^card.id) do
[] ->
%CardMeta{}
metas ->
hd metas
end
end
def bury(user, card) do
get_or_create_meta(user, card)
|> Repo.preload([:card, :user])
|> CardMeta.changeset(%{last_seen: DateTime.utc_now(), user_id: user.id, card_id: card.id,
learning: false, known: false, prev_interval: 0})
|> Repo.insert_or_update
end
編輯:將變更源
def changeset(struct, params \\ %{}) do
struct
|> cast(params, [:last_seen, :difficulty, :prev_interval, :due, :known, :learning,
:user_id, :card_id])
|> assoc_constraint(:user)
|> assoc_constraint(:card)
end
調用來自控制器
def update(conn, %{"currentCardId" => card_id, "command" => command}) do
# perform some update on card
card = Repo.get!(Card,card_id)
user = Guardian.Plug.current_resource(conn)
case command do
"fail" ->
SpacedRepetition.fail(user, card)
"learn" ->
SpacedRepetition.learn(user, card)
_ ->
SpacedRepetition.bury(user, card)
end
sendNextCard(conn, user)
end
編輯掩埋:
我注意到last_seen字段微秒重複的行之間不同,而create_at字段沒有該分辨率。因此我懷疑insert_or_update調用是否正常,但控制器在數據庫更新之前發射了兩次。這可能是客戶端,我不想考慮的東西。所以我只是要添加一個唯一的密鑰。
你可以張貼'CardMeta.changeset'源和代碼,你是從調用'bury'? – Dogbert
嘗試樂觀鎖定,看看是否有幫助。 https://hexdocs.pm/ecto/0.9.0/Ecto.Model.OptimisticLock.html –
您可能會遇到控制器更新操作的併發問題。你可以嘗試的一件事是將埋葬代碼放入GenServer中並觸發它。這樣你就可以確定沒有其他請求進來了。 –