在Elixir中重试失败的工作

时间:2018-04-04 11:33:55

标签: elixir phoenix-framework

请问我有问题,我正在用Elixir取代Predictive dialer。到目前为止,它已超出所有预期。但是,我正面临着一个问题。假设所有第三方依赖项都按预期工作,这段代码很有效,

def perform(phonebook_contacts, ...) do
  alias FSModEvent.Connection, as: C

  for x <-Enum.chunk(phonebook_contacts, 100, 100, []), y <- x  do 
    unless Telephony.user_balance(account_number) <= 0 do
      Task.start_link(fn -> 
        # some background job to dailout phone number y
      end)
    # :timer.sleep(1000);
  end

  ...
end

phonebook_contacts可以是包含多达200K数字的列表

我正在使用exq。如果外部事件导致作业失败,当它被重试时,它从列表的开头开始,是否有一种方法可以从作业失败的最后一次联系重试?

假设[12,34,56,78,90…]

如果作业在56处失败,它会再次从12重新开始,有一种方法可以从78(某种失败的地方)继续,......?或者更好的方法来处理这个用例

有建议可能将工作状态存储在redis中并从那里重试,但我不知道该怎么做。

2 个答案:

答案 0 :(得分:1)

这里的简单方法是启动另一个流程,您可以使用该流程存储&#34;成功状态&#34;每个人的工作。然后,在任何类型的重新启动期间,您可以检查作业是否已经运行/完成。

Agent是一个好的,易于使用的流程,用于跟踪您不需要执行更复杂交互的小部分状态。

例如:

def perform(phonebook_contacts, ...) do
  completed_agent = Agent.new(fn -> MapSet.new() end)
  # the Enum code...
    Task.start_link(fn ->
      completed? = Agent.get(completed_agent, &(MapSet.member?(&1, y)))
      unless completed? do
        # the dialout code...

        if dialout_code_succeeded do
          Agent.update(completed_agent, &MapSet.put(&1, y))
        end
      end
  end)
end

现在,这可能是最天真的方式,并且可能不是特别高效。重要的是要注意Agent.get/3Agent.update/3中提供的匿名功能有效地阻止来自任何其他进程的代理,因此您需要这些功能运行速度非常快,否则您可能会因尝试使用代理的其他进程造成超时。

答案 1 :(得分:0)

您可以使用起始索引作为参数创建递归作业。

如果作业失败,你可以拯救它并用当前索引加一个来自称。

在你的工作中你不再使用phonebook_contacts进行迭代,但是这个函数

  defp enum_from(phonebook_contacts, index) do
    Enum.slice(phonebook_contacts, index, Enum.count(phonebook_contacts))
  end

当你第一次给你打电话时,你可以在索引0处调用它。

通过在

之后的索引处递归调用作业来捕获异常并跳过有问题的数字