Откат изменений базы данных в долговременной функции - PullRequest
0 голосов
/ 05 сентября 2018

Допустим, у меня есть следующая оркестровка:

[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"));
}

Во всех моих действиях используется база данных SQL Azure, и в случае сбоя любого из вызовов я хочу отменить все изменения, сделанные предыдущими действиями - например, если второй вызов Baz выдает исключение, я хочу чтобы отменить все, что было сделано Foo, Bar, и если первый Baz завершен, я хочу отменить и его модификации.

В приложении без функций я смогу просто обернуть все тело оркестровки в блок using scope = new TransactionScope().

Будет ли это работать для потенциально распределенной оркестровки, и если нет, есть ли аналогичный механизм в структуре функций Azure? Или я должен написать реализацию отката для каждого из действий и зафиксировать изменения в базе данных после выполнения каждого из них?

1 Ответ

0 голосов
/ 06 сентября 2018

Durable Functions реализуют механизм возможной согласованности . Это совершенно иное понятие, чем другие виды согласованности (например, сильные), поскольку оно гарантирует, что транзакция будет завершена в конечном итоге . Что это значит?

Используя 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») не будет выполнено еще раз - его состояние будет сохранено в хранилище, поэтому результат будет сразу же доступен. Среда выполнения выполняет контрольную точку после каждого действия, поэтому состояние сохраняется и оно знает, где оно завершилось ранее.

Теперь, чтобы правильно обработать ситуацию, вы должны реализовать следующий алгоритм:

  • выполнить откат вручную при обнаружении исключения
  • Если это не удалось, отправьте сообщение, например, на очередь, которая затем обрабатывается вручную кем-то, кто понимает, как работает процесс

Хотя изначально это может выглядеть как большой недостаток, на самом деле это совершенно прекрасное решение - ошибки действительно случаются, поэтому всегда полезно избегать переходных (с помощью повторных попыток), но если откат не удался, это явно означает, что в вашей системе что-то не так.

Выбор за вами - имеете ли вы сильную согласованность и вам приходится иметь дело с проблемами с масштабируемостью, или вы используете более свободную модель, которая обеспечивает лучшую масштабируемость, но с которой труднее работать.

...