简体   繁体   English

Azure Durable 函数 - CallActivityAsync 时出现 InvalidOperationException

[英]Azure Durable function - InvalidOperationException when CallActivityAsync

I'm playing around with the Azure Durable functions .我正在玩Azure Durable 功能 Currently I'm getting InvalidOperationException within Orchestration function after I call an activity.目前,我在调用活动后在 Orchestration 函数中收到InvalidOperationException It complains that Multithreaded execution was detected.它抱怨检测到多线程执行。 This can happen if the orchestrator function previously resumed from an unsupported async callback .如果协调器函数之前从不受支持的异步回调中恢复,则可能会发生这种情况

Have any one experienced such an issue?有没有人遇到过这样的问题? What I'm doing wrong?我做错了什么? Complete code can be found on GitHub完整代码可以在GitHub找到

Here is the line from the orchestration function:这是编排功能中的一行:

var res = await ctx.CallActivityAsync<int>("LengthCheck", "inputData");

The LengthCheck activitiy function is: LengthCheck活动功能是:

[FunctionName("LengthCheck")]
public static Task<int> Calc([ActivityTrigger] string input)
{
    var task = Task.Delay(TimeSpan.FromSeconds(5));
    task.Wait();
    return Task.FromResult(input.Length);
}

The stack trace is:堆栈跟踪是:

ac6fd5cdd07a4dc9b2577657d65c4f27: Function 'InpaintOrchestration (Orchestrator)', version '' failed with an error. ac6fd5cdd07a4dc9b2577657d65c4f27:函数“InpaintOrchestration(Orchestrator)”,版本“”因错误而失败。 Reason: System.InvalidOperationException: Multithreaded execution was detected.原因:System.InvalidOperationException:检测到多线程执行。 This can happen if the orchestrator function previously resumed from an unsupported async callback.如果协调器函数之前从不受支持的异步回调中恢复,则可能会发生这种情况。

at Microsoft.Azure.WebJobs.DurableOrchestrationContext.ThrowIfInvalidAccess()在 Microsoft.Azure.WebJobs.DurableOrchestrationContext.ThrowIfInvalidAccess()

at Microsoft.Azure.WebJobs.DurableOrchestrationContext.d__47`1.MoveNext()在 Microsoft.Azure.WebJobs.DurableOrchestrationContext.d__47`1.MoveNext()

End of stack trace from previous location where exception was thrown从先前抛出异常的位置结束堆栈跟踪

at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)在 System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(任务任务)

at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)在 System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(任务任务)

at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()在 System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()

This exception happens whenever an orchestrator function does async work in an unsupported way. 只要协调器函数以不支持的方式执行异步工作,就会发生此异常。 "Unsupported" in this context effectively means that await was used on a non-durable task (and "non-durable" means that it was a task that came from some API other than DurableOrchestrationContext ). 在此上下文中“不支持”实际上意味着await用于非持久性任务(“非持久性”意味着它是来自除DurableOrchestrationContext之外的某些API的任务)。

You can find more information on the code constraints for orchestrator functions here: https://docs.microsoft.com/en-us/azure/azure-functions/durable/durable-functions-checkpointing-and-replay#orchestrator-code-constraints . 您可以在此处找到有关orchestrator函数的代码约束的更多信息: https//docs.microsoft.com/en-us/azure/azure-functions/durable/durable-functions-checkpointing-and-replay#orchestrator-code-约束

Here are the rules that were broken in your code when I quickly scanned it: 以下是我快速扫描时代码中已破坏的规则:

  • Orchestrator code should be non-blocking . Orchestrator代码应该是非阻塞的 For example, that means no I/O and no calls to Thread.Sleep or equivalent APIs. 例如,这意味着没有I / O,也没有调用Thread.Sleep或等效的API。 If an orchestrator needs to delay, it can use the CreateTimer API. 如果一个orchestrator需要延迟,它可以使用CreateTimer API。

  • Orchestrator code must never initiate any async operation except by using the DurableOrchestrationContext API. 除了使用DurableOrchestrationContext API之外,Orchestrator代码绝不能启动任何异步操作 For example, no Task.Run, Task.Delay or HttpClient.SendAsync. 例如,没有Task.Run,​​Task.Delay或HttpClient.SendAsync。 The Durable Task Framework executes orchestrator code on a single thread and cannot interact with any other threads that could be scheduled by other async APIs. Durable Task Framework在单个线程上执行orchestrator代码,不能与其他异步API调度的其他线程交互。

This exception specifically occurs when we detect that an unsupported async call is made. 当我们检测到发生了不支持的异步调用时,会发生此异常。 I noticed that is happening in this code: 我注意到这个代码中发生了这种情况:

    private static async Task SaveImageLabToBlob(ZsImage imageLab, CloudBlobContainer container, string fileName)
    {
        var argbImage = imageLab
            .Clone()
            .FromLabToRgb()
            .FromRgbToArgb(Area2D.Create(0, 0, imageLab.Width, imageLab.Height));

        using (var bitmap = argbImage.FromArgbToBitmap())
        using (var outputStream = new MemoryStream())
        {
            // modify image
            bitmap.Save(outputStream, ImageFormat.Png);

            // save the result back
            outputStream.Position = 0;
            var resultImageBlob = container.GetBlockBlobReference(fileName);
            await resultImageBlob.UploadFromStreamAsync(outputStream);
        }
    }

The proper way to make async or blocking calls is to wrap them in activity functions, which don't have any of these constraints. 进行异步或阻塞调用的正确方法是将它们包装在没有任何这些约束的活动函数中。

In more recent versions of this extension (v1.3.2 and greater), we've included a link to the documentation describing code-constraints in the exception message. 在此扩展的更新版本(v1.3.2和更高版本)中,我们包含了描述异常消息中代码约束的文档的链接。

This was happening for my durable orchestrator function too.这也发生在我的持久协调器功能上。 I had to get rid of all the .ConfigureAwait(false) endings for the activity function invocations.我必须摆脱活动函数调用的所有 .ConfigureAwait(false) 结尾。

 //invoking First activity function
        var id = await context.CallActivityAsync<Guid>(Function1, requestModel);
        

        //invoking second activity function that uses data from the first activity function without ConfigureAwait(false)
        var readModel = await context.CallActivityAsync<ReadModel>(Function2, id);
       

        //invoking third activity function that uses data from the second activity function without ConfigureAwait(false)
        await context.CallActivityAsync(Function3, cartReadModel);
        

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM