处理并发任务

时间:2017-07-31 11:49:56

标签: elixir phoenix-framework ecto

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

不幸的是,我最终将重复写入记录到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

感谢任何帮助!

2 个答案:

答案 0 :(得分:2)

您应该在数据库中为UNIQUE INDEX字段添加mobile_number。它会更高效(您只需要对数据库进行一次查询),并且可以保证数据库永远不会在表中具有该字段的重复值。

你需要做三件事:

  1. 使用迁移向表中添加unique_index

  2. 在变更集功能中添加对unique_constraint的调用。

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

答案 1 :(得分:0)

接受的答案很好。

但是当您无法执行唯一索引时,您有两种选择。首先是使用锁,以防止进程同时查询db。有关此示例,请参阅elixir_locker

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