我有多个并发任务,他们都试图检查记录存在,然后,如果不存在,将插入一个。
不幸的是,我最终将重复写入记录到DB,因为似乎所有任务都决定记录不同时存在,然后所有记录都插入。
期望的行为是,我只插入一次,然后,其他任务会识别刚插入的记录的存在。
这是我的尝试:
alias MyApp.Parent, as: Parent
alias MyApp.Repo, as: Repo
changeset = Parent.changeset(%Parent{}, model)
case Repo.all(from p in Parent, where: p.mobile_number == ^model.mobile_number) do
[] ->
#does not exist
case Repo.insert_or_update(changeset) do
{:ok, %MyApp.Parent{ id: parent_id }} -> parent_id
error_message -> nil
end
[parent_get_by|t] ->
#already exist
%MyApp.Parent{ id: parent_id }= parent_get_by
parent_id
end
感谢任何帮助!
答案 0 :(得分:2)
您应该在数据库中为UNIQUE INDEX
字段添加mobile_number
。它会更高效(您只需要对数据库进行一次查询),并且可以保证数据库永远不会在表中具有该字段的重复值。
你需要做三件事:
使用迁移向表中添加unique_index
。
在变更集功能中添加对unique_constraint
的调用。
在您的控制器中,只需执行Repo.insert(changeset)
。如果字段重复,您将{:error, changeset}
返回changeset.errors
中的错误消息。
答案 1 :(得分:0)
接受的答案很好。
但是当您无法执行唯一索引时,您有两种选择。首先是使用锁,以防止进程同时查询db。有关此示例,请参阅elixir_locker。
另一种方法是序列化请求。这意味着,您将拥有一个可以执行GenServer
的流程,最好是SELECT+INSERT
。然后你有进程发送消息给这个,而不是自己查询数据库。这使得请求在彼此之后运行,并且只有第一个请求将被插入,其他请求将被读取。当然,如果您有很多请求,这个过程本身可能会成为瓶颈。