【问题标题】:AsyncResult and handling rollbackAsyncResult 和处理回滚
【发布时间】:2019-12-08 13:50:12
【问题描述】:

(警告:这也在https://forums.fsharp.org/t/asyncresult-and-handling-rollback/928上发布)

尝试在 F# 中实现 2PC Transaction Like 工作流(请参阅 http://learnmongodbthehardway.com/article/transactions/)并遇到计算表达式(例如 asyncResult)和回滚问题。

如果你有以下伪代码:

let rollbackWorkflow parX parY =
… here calling rollbackService1 and rollbackService2

let executeWorkflow par1 par2 par3 =
asyncResult {
let! result1 = callService1 x y z
let! result2 = callService2 x2 y2 z2
}

如果 result1 和/或 result2 为错误,我如何检查 executeWorkflow,然后调用 rollbackWorkflow 函数?我应该更改 callService1 和 callService2 以引发异常,而不是在预期的错误情况下(资金不足、超出限制)也返回结果,还是应该使用诸如 teeError 之类的函数?任何建议都非常感谢!

附:这是我最终想要实现的:

function executeTransaction(from, to, amount) {
var transactionId = ObjectId();

transactions.insert({
_id: transactionId,
source: from,
destination: to,
amount: amount,
state: “initial”
});

var result = transactions.updateOne(
{ _id: transactionId },
{ $set: { state: “pending” } }
);

if (result.modifiedCount == 0) {
cancel(transactionId);
throw Error(“Failed to move transaction " + transactionId + " to pending”);
}

// Set up pending debit
result = accounts.updateOne({
name: from,
pendingTransactions: { $ne: transactionId },
balance: { $gte: amount }
}, {
$inc: { balance: -amount },
$push: { pendingTransactions: transactionId }
});

if (result.modifiedCount == 0) {
rollback(from, to, amount, transactionId);
throw Error(“Failed to debit " + from + " account”);
}

// Setup pending credit
result = accounts.updateOne({
name: to,
pendingTransactions: { $ne: transactionId }
}, {
$inc: { balance: amount },
$push: { pendingTransactions: transactionId }
});

if (result.modifiedCount == 0) {
rollback(from, to, amount, transactionId);
throw Error(“Failed to credit " + to + " account”);
}

// Update transaction to committed
result = transactions.updateOne(
{ _id: transactionId },
{ $set: { state: “committed” } }
);

if (result.modifiedCount == 0) {
rollback(from, to, amount, transactionId);
throw Error(“Failed to move transaction " + transactionId + " to committed”);
}

// Attempt cleanup
cleanup(from, to, transactionId);
}

executeTransaction(“Joe Moneylender”, “Peter Bum”, 100);

【问题讨论】:

标签: f# expression computation


【解决方案1】:

只需在工作流程之外进行一些错误处理,如下所示:

type TransactionError =
    | NoFunds
    | Other

let rollbackWorkflow parX parY = async.Return ( printfn "here calling rollbackService1 and rollbackService2"; Ok -1 )
let callService1 parX parY = async.Return ( printfn "callService1"; if parX + parY > 0 then Ok 1 else Error NoFunds )
let callService2 parX parY = async.Return ( printfn "callService2"; if parX + parY > 0 then Ok 2 else Error Other )

let executeWorkflow par1 par2 par3 =
    asyncResult {
        let! result1 = callService1 par1 par2
        let! result2 = callService2 result1 par3
        return result2
        } |> AsyncResult.bindError (fun x -> if x = NoFunds then rollbackWorkflow 0 1 else rollbackWorkflow 1 0)

我使用您链接的代码中的 AsyncResult 编写了该示例。加上bindError 应该是这样的:

/// Apply a monadic function to an AsyncResult error  
let bindError (f: 'a -> AsyncResult<'b,'c>) (xAsyncResult : AsyncResult<_, _>) :AsyncResult<_,_> = async {
    let! xResult = xAsyncResult 
    match xResult with
    | Ok x -> return Ok x
    | Error err -> return! f err
    }

如果你想一想,bindError 就像一个纯版本的 catch 函数,例如 this code fragment ,使用另一个库。

【讨论】:

  • 哇,感谢您的样品!如果我添加以下内容: let r : Result>> = executeWorkflow -1 -2 3 |> Async.RunSynchronously 那么 r 的类型为 val r : Result>> = Error Microsoft.FSharp.Control.FSharpAsync1[Microsoft.FSharp.Core.FSharpResult2[System.Int32,FSI_0003+TransactionError]] 我可以在主 Result 的错误部分而不是 Async>?
  • 我可以完成回滚并从 Async 解包,因此 executeWorkflow 函数的总体结果仍然是 Async&lt;Result&lt;int,TransactionError&gt;&gt; 而不是 Async&lt;Result&lt;int,Async&lt;Result&lt;int,TransactionError&gt;&gt;&gt;&gt;
  • 对不起。看着那些签名,我意识到我看错了,实际上mapError 并不是我想的那样。我找不到一个以纯粹的方式捕获错误的函数,所以我只是写了它。请参阅我的更新答案。
  • 非常感谢@Gus,这确实有效。我们甚至创建了 bindExn - 请参阅 stackoverflow.com/questions/57922915/…
猜你喜欢
  • 2014-09-20
  • 1970-01-01
  • 2012-07-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多