私のコードは、データベースへのまれな二重または三重の挿入をもたらしており、なぜ私は紛失してしまったのですか?再現することは非常に難しいですが、タイムスタンプを見ると、作成された時刻が基本的に同じ時刻になることがわかります。私はそれが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 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
editから埋め込む呼び出す
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
:
Iが、一方、last_seenフィールドは重複行の間の異なるマイクロ秒である気づいcreate_atフィールドにはその解決策がありません。したがって、insert_or_update呼び出しは問題ないと思われますが、コントローラはDBを更新する前に2回起動しています。これは、私が考えたくないクライアント側のものかもしれません。だから私はちょうどユニークなキーを追加するつもりです。
あなたは 'CardMeta.changeset'とあなたから' bury'を呼び出しているコードのソースを投稿することができますか? – Dogbert
楽観的なロックを試してみてください。 https://hexdocs.pm/ecto/0.9.0/Ecto.Model.OptimisticLock.html –
コントローラーの更新操作で並行性の問題が発生する可能性があります。あなたが試すことができる1つのことは、埋葬コードをGenServerに入れて呼び出しをトリガーすることです。こうすることで、他のリクエストが来ていないことを確認することができます。 –