【问题标题】:Erlang: success case and error handling as a transactionErlang:成功案例和错误处理作为事务
【发布时间】:2016-11-19 08:30:31
【问题描述】:

(我想引用另一个问题作为参考:How do I elegantly check many conditions in Erlang?

“成功案例代码与错误处理分离”的通用形式似乎是:

try
    ok = do_x(),
    ...
    ok = do_y()
catch
    error:{badmatch, x_failure} -> do_something1();
    ...
    error:{badmatch, y_failure} -> do_something2();

当 try 子句中的函数做一些有副作用的事情时,例如写入文件、发送网络数据包、向数据库中写入一行等,如何使用这种模式? catch 子句中是否有“回滚”的通用模式?示例:

try
    %How to make this whole block a transaction?
    ok = step_1_write_file(),
    ok = step_2_write_database(),
    ok = step_3_send_packet(),
    ...
catch
    error:{badmatch, database_failure} -> clean_up_step_1() %delete file?
    error:{badmatch, sendpacket_failure} -> clean_up_step_1_and_2() %??

似乎错误处理变得繁重,需要执行的清理取决于 try 块中失败的步骤。

是否有一种通用的编程模式将其视为一个事务,而在失败子句之前的 try 块中的成功步骤是“unwound”?

【问题讨论】:

  • 成功 打字 似乎与这里无关。
  • @AlexeyRomanov 问题标题已编辑

标签: erlang


【解决方案1】:

我个人通过将“验证器”和可选的“终结器”列表传递给一些通用迭代函数来学习编写这样的算法。

因此,您的案例可能会这样编程:

noop() -> ok.

transaction([{Fun, Rollback} | Rest]) ->
    try
        {ok, Result} = Fun(),
        [Result | transaction(Rest)]
    catch Type:Reason ->
        Rollback(),
        erlang:raise(Type, Reason, erlang:get_stacktrace())
    end;
transaction([Fun | Rest]) ->
    % not every action require cleanup on error
    transaction([{Fun, fun noop/0} | Rest]);
transaction([]) -> [].


main() ->
    Actions = [
       {fun write_file/0, fun cleanup_file/0},
       {fun write_database/0, fun cleanup_database/0},
       fun do_safe_thing/0,
       {fun send_packet/0, fun cancel_send_packet/0},
    ],
    transaction(Actions).

如您所见,由于此列表使用主体递归进行评估,因此遍历此列表将形成一个调用堆栈,并且如果在某个步骤中这些函数中的一个会下降,则堆栈将展开并调用每个清理函数以相反的顺序。

例如,如果do_safe_ting/0 将下降,则清理函数noop/0cleanup_database/0cleanup_file/0 将按此顺序调用。

当然,这可以用不同的方式进行编程,不是通过重新抛出异常,而是通过例如返回{ok, Result}{error, Reason}。这只是实现细节。

【讨论】:

  • 就我而言,使用您的示例,如果 send_packet 失败,我想回滚 all 先前的步骤,而不仅仅是当前的先前步骤。我想我可以传递所有回滚函数的列表并继续附加到列表中。
  • @Tommy 实际上,每个之前的回滚函数都会被调用,因为 erlang:raise 会被栈追上
  • 文档中的最新建议是使用try Class:Reason:Stacktrace 来获取堆栈跟踪,而不是现在返回[] 的erlang:get_stacktrace():erlang.org/doc/man/erlang.html#get_stacktrace-0
猜你喜欢
  • 2011-10-15
  • 2017-12-28
  • 2012-02-12
  • 1970-01-01
  • 2020-06-11
  • 1970-01-01
  • 2019-01-08
  • 2019-11-02
  • 1970-01-01
相关资源
最近更新 更多