处理并发任务

Handling concurrent tasks

我有多个并发任务,它们都试图检查记录是否存在,如果不存在,将插入一个。

不幸的是,我最终将记录重复写入数据库,因为似乎所有任务都决定同时不存在该记录,然后所有任务都进行插入。

期望的行为是我只得到一次插入,然后其他任务就会识别出刚刚插入的记录的存在。

这是我的尝试:

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

感谢任何帮助!

您应该在数据库中为 mobile_number 字段添加一个 UNIQUE INDEX。它会更有效率(您只需要对数据库执行一个查询)并且您可以保证数据库永远不会在 table.

中的该字段具有重复值

您需要做三件事:

  1. 使用迁移将 unique_index 添加到 table。

  2. 在您的变更集函数中添加对 unique_constraint 的调用。

  3. 在您的控制器中,只需执行 Repo.insert(changeset)。如果该字段重复,您将得到 {:error, changeset} 返回并在 changeset.errors.

  4. 中显示错误消息

接受的答案很好。

但是当您不能强制执行唯一索引时,您有两种选择。首先是使用锁,以防止进程同时查询数据库。有关此示例,请参阅 elixir_locker

另一种方法是序列化请求。这意味着,您将有一个进程,最好是 GenServer,它将执行 SELECT+INSERT。然后让您的进程向其发送消息,而不是自己查询数据库。这使得请求 运行 一个接一个,只有第一个请求会插入,其他请求会读取。当然,如果你有很多请求,这个过程本身可能会成为瓶颈。