2

在 Elixir 1.7.4 上使用 Commanded 0.17.2 构建的应用程序经常耗尽内存。一项调查发现,内存泄漏似乎是由越来越多的聚合实例引起的,这些实例从未停止过。

有问题的聚合接收由外部系统触发的命令。在某些情况下,该execute函数会返回一个事件,而在其他一些情况下,该命令应该被忽略并因此返回nil(如docs所述)。

def execute(%RemoteThing{}, %ImportRemoteThing{deleted: true}), do: nil

似乎每次nil返回而不是事件,聚合实例都无限期地保持活动状态。即使附加了超时和寿命,也会发生这种情况,这明确意味着其他事情:

defmodule RemoteThing.Lifespan do
  @behaviour Commanded.Aggregates.AggregateLifespan

  def after_event(_event), do: :stop
  def after_command(_command), do: :stop
end

dispatch(
  ImportRemoteThing,
  to: RemoteThing,
  lifespan: RemoteThing.Lifespan,
  timeout: 15_000
)

我怀疑这是Commanded中的错误:

defp aggregate_lifespan_timeout(_context, []), do: :infinity

避免内存泄漏的一种方法是产生一个事件,即使没有人需要它。这将导致污染的持久事件存储而不是易失性内存,因此从长远来看可能会导致更大的问题。

我现在正在寻找一种方法来停止聚合实例,以防execute函数返回nil. 解决方法的每一个想法将不胜感激。

4

1 回答 1

2

这已经修复,将在Commanded 的下一个版本中提供

拉取请求#210扩展聚合生命周期行为以包含after_error/1after_command/1回调。

以前您只需要定义一个after_event/1回调函数来实现该Commanded.Aggregates.AggregateLifespan行为:

defmodule BankAccountLifespan do
  @behaviour Commanded.Aggregates.AggregateLifespan

  def after_event(%BankAccountClosed{}), do: :stop
  def after_event(_event), do: :infinity
end

现在您还必须定义after_command/1after_error/1回调函数:

defmodule BankAccountLifespan do
  @behaviour Commanded.Aggregates.AggregateLifespan

  def after_event(%BankAccountClosed{}), do: :stop
  def after_event(_event), do: :infinity

  def after_command(%CloseAccount{}), do: :stop
  def after_command(_command), do: :infinity

  def after_error(:invalid_initial_balance), do: :stop
  def after_error(_error), do: :stop
end

回调将after_command/1允许您在没有事件产生时停止聚合。

于 2018-12-11T15:40:00.537 回答