回滚数据库在持久功能中更改

问题描述 投票:0回答:1

假设我有以下编排:

[FunctionName("Orchestration")]
public static async Task Orchestration_Start([OrchestrationTrigger]  DurableOrchestrationContext ctx)
{
    await ctx.CallActivityAsync("Foo");
    await ctx.CallActivityAsync("Bar");
    await Task.WhenAll(ctx.CallActivityAsync("Baz"), ctx.CallActivityAsync("Baz"));
}

我的所有活动都使用Azure SQL数据库,如果任何调用失败,我想撤消之前活动所做的所有更改 - 例如,如果第二次调用Baz引发异常,我想要撤消所有由FooBar,如果第一个Baz已经完成,我也想撤消其修改。

在非函数应用程序中,我可以将业务流程的整个主体包装在using scope = new TransactionScope()块中。

这是否适用于潜在的分布式业务流程,如果没有,Azure功能框架中是否有任何类似的机制?或者我是否需要为每个活动编写回滚实现,并在完成每个活动后将更改提交到数据库?

c# azure azure-functions azure-durable-functions
1个回答
0
投票

持久功能实现了最终一致性的机制。这是一个完全不同于其他类型的一致性(例如强大)的概念,因为它保证了交易最终会完成。那是什么意思?

通过使用TransactionScope,您可以确保,如果事务中出现任何问题,将自动执行回滚。在Durable Function中并非如此 - 您没有自动化功能,它为您提供了这样的功能 - 实际上,如果示例中的第二个活动失败,您将最终得到存储在数据库中的不一致数据。

要在这种情况下实现事务,您必须尝试/捕获可能的问题并执行逻辑,这将允许您缓解错误:

[FunctionName("Orchestration")]
public static async Task Orchestration_Start([OrchestrationTrigger]  DurableOrchestrationContext ctx)
{
    try 
    {
        await ctx.CallActivityAsync("Foo");
        await ctx.CallActivityAsync("Bar");
        await Task.WhenAll(ctx.CallActivityAsync("Baz"), ctx.CallActivityAsync("Baz"));
    }
    catch(Exception)
    {
        // Do something...
    }  
}

还可以实施重试策略以避免瞬时错误:

public static async Task Run(DurableOrchestrationContext context)
{
    var retryOptions = new RetryOptions(
        firstRetryInterval: TimeSpan.FromSeconds(5),
        maxNumberOfAttempts: 3);

    await ctx.CallActivityWithRetryAsync("FlakyFunction", retryOptions, null);

    // ...
}

但是,重要的是要了解Durable Functions的运行时如何在出现问题时真正管理情况。我们假设以下代码失败:

[FunctionName("Orchestration")]
public static async Task Orchestration_Start([OrchestrationTrigger]  DurableOrchestrationContext ctx)
{
    await ctx.CallActivityAsync("Foo");
    await ctx.CallActivityAsync("Bar"); // THROWS!
    await Task.WhenAll(ctx.CallActivityAsync("Baz"), ctx.CallActivityAsync("Baz"));
}

如果重放整个业务流程,第一个活动(通过“Foo”的活动)将不会再次执行 - 其状态将存储在存储中,因此结果将立即可用。运行时在每个活动之后执行检查点,因此状态将被保留,并且它知道先前完成的位置。

现在要正确处理一个情况,你必须实现以下算法:

  • 捕获异常时执行手动回滚
  • 如果失败,请将消息推送到例如队列,然后由了解该过程如何工作的人手动处理

虽然最初,它可能看起来像一个很大的缺陷,事实上,它是一个非常好的解决方案 - 错误确实发生所以避免瞬态(使用重试)总是一个好主意,但如果回滚失败,这清楚地表明存在你的系统有问题。

您可以选择 - 您是否具有很强的一致性并且必须处理可伸缩性问题,或者使用更宽松的模型来提供更好的可伸缩性,但更难以使用。

© www.soinside.com 2019 - 2024. All rights reserved.