在编写工作程序时学到的东西

88 阅读2分钟

我在做Elixir应用程序时学到的东西

最近,我正在为我们的一个客户使用Elixir编写一个后台工作者。这篇文章是关于我在编写工作程序时学到的东西。

添加十进制数字

我试着为小数类型做加法,就像我们做整数的方法一样:

screenshot1

screenshot1

它引发了一个算术错误,因为十进制有一个基础结构:%Decimal{coef: coefficient(), exp: exponent(), sign: sign()}。

我检查了十进制库,发现它有一个加法函数,可以将十进制或整数值相加。因此,加法可以按以下方式进行。

screenshot1

比较十进制值

整数的比较可以用== 算子来完成。

screenshot1

那么小数呢?

screenshot1

由于== 操作符的RHS(右侧)上的值不是小数,所以它返回错误。

我尝试使用Decimal.new,如下所示:

screenshot1

根据警告信息,我尝试使用Decimal.from_float和Decimal.cast,如下所示:

screenshot1

但这也会返回错误。

然后我试着向Decimal.new传递一个字符串,如下所示:

screenshot1

并且成功了。

使用find_in_batches的方法

在处理一个业务用例时,获取待处理订单并处理它们,代码如下:

  defp orders_query() do
    from(order in Order,
      where: order.status == ^@pending
    )
  end

  def perform do
    orders_query()
    |> Repo.all()
    |> Enum.each(fn order ->
       update_order(order)
    end)
  end

上述代码将在内存中一次性加载所有的记录。我决定在Elixir中搜索类似于Rails中find_in_batches的东西,我发现了这个讨论

更新后的代码如下:

  def perform do
      Repo.transaction(fn ->
        orders_query()
        |> Repo.stream()
        |> Enum.each(fn order ->
           update_order(order)
        end)
      end)
  end

我使用了Repo.stream,它默认以500条为一个批次获取记录,并且需要用一个事务来包装。

递归

在上面的方法中,我面临一个问题。当更新记录的时间超过超时时,Ecto会产生一个超时错误,我在这里描述过。

我通过使用递归来解决了这个问题,具体如下:

  @batch_size 500

  def perform do
    remaining_records_count()
    |> iterate_multiple_times()
  end

  defp remaining_records_count do
    orders_query()
    |> Repo.aggregate(:count)
  end

  defp iterate_multiple_times(count) when count <= @batch_size,
    do: make_account_balance_available()

  defp iterate_multiple_times(_count) do
    make_account_balance_available()

    remaining_records_count()
    |> iterate_multiple_times()
  end

在上面的代码中,iterate_multiple_times/1 是一个递归函数,它调用自己直到没有任何剩余的记录。

我希望你在使用Elixir构建任何应用程序/库时,会发现这些学习内容对你有帮助。