Postgresql EXTO insert\u或\u update创建多个插入?
我的代码导致数据库中罕见的两次或三次插入,我不知道为什么。这是很难复制,但我可以看看时间戳,看到创建的时间基本上是相同的,当它发生。我相信只有当CardMeta尚未找到时才会发生 我想我需要添加一个唯一的密钥或将其包装到事务中Postgresql EXTO insert\u或\u update创建多个插入?,postgresql,elixir,phoenix-framework,ecto,Postgresql,Elixir,Phoenix Framework,Ecto,我的代码导致数据库中罕见的两次或三次插入,我不知道为什么。这是很难复制,但我可以看看时间戳,看到创建的时间基本上是相同的,当它发生。我相信只有当CardMeta尚未找到时才会发生 我想我需要添加一个唯一的密钥或将其包装到事务中 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 == ^c
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
从控制器调用bury
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
编辑:
我注意到,在重复的行之间,最后一次看到的字段相差微秒,而create_at字段没有这样的分辨率。因此,我怀疑insert_或_update调用没有问题,但控制器在DB更新之前触发了两次。这可能是客户端的问题,我不想去想。所以我只想添加一个唯一的密钥。我相信您可以通过在用户id和卡id上添加一个复合主键来解决这个问题
如果这不能解决您的问题,请在此处添加您的数据模型 如果您不想更改CardMeta上的主键,可以通过迁移在数据库中设置唯一索引约束,以替代@aliCna的回答:
defmodule YourApp.Repo.Migrations.AddCardMetaUniqueIndex do
use Ecto.Migration
def change do
create unique_index(
:card_meta,
[:card_id, :user_id],
name: :card_meta_unique_index)
end
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)
|> unique_constraint(:user_id, name: :card_meta_unique_index)
end
你能发布CardMeta.changeset的源代码和调用bury的代码吗?试试乐观锁定,看看这是否有帮助。控制器的更新操作可能存在并发问题。您可以尝试的一件事是将bury代码放在GenServer中,并触发一个调用。通过这种方式,您将确保没有其他请求进入。我发现另一件奇怪的事情是在卸载的架构上运行Repo.preload。我必须测试它才能相信它有效。
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)
|> unique_constraint(:user_id, name: :card_meta_unique_index)
end