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