azure 耐用功能不安全?

dm7nw8vv  于 2022-11-25  发布在  其他
关注(0)|答案(1)|浏览(126)

我正在一个基于无服务器计时器的持久函数中工作,但一直得到错误:

[2022-10-11T03:42:06.874Z] ServerlessTimers.Application: Exception of type 'System.Exception' was thrown.
[2022-10-11T03:42:06.883Z] 0396b0bd-6a87-4490-a2fe-b0b9121a9504: Function 'OrchestrateTimerFunction (Orchestrator)' failed with an error. Reason: System.InvalidOperationException: Multithreaded execution was detected. This can happen if the orchestrator function code awaits on a task that was not created by a DurableOrchestrationContext method. More details can be found in this article https://docs.microsoft.com/en-us/azure/azure-functions/durable-functions-checkpointing-and-replay#orchestrator-code-constraints.
[2022-10-11T03:42:06.886Z]    at Microsoft.Azure.WebJobs.Extensions.DurableTask.DurableOrchestrationContext.ThrowIfInvalidAccess() in D:\a\_work\1\s\src\WebJobs.Extensions.DurableTask\ContextImplementations\DurableOrchestrationContext.cs:line 1163
[2022-10-11T03:42:06.887Z]    at Microsoft.Azure.WebJobs.Extensions.DurableTask.TaskOrchestrationShim.InvokeUserCodeAndHandleResults(RegisteredFunctionInfo orchestratorInfo, OrchestrationContext innerContext) in D:\a\_work\1\s\src\WebJobs.Extensions.DurableTask\Listener\TaskOrchestrationShim.cs:line 150. IsReplay: False. State: Failed. HubName: TestHubName. AppName: . SlotName: . ExtensionVersion: 2.7.1. SequenceNumber: 4. TaskEventId: -1

从我的Angular 来看,我看不出该编排器函数有什么问题。有趣的是,当我在该编排器函数中设置断点并调用该函数时,错误消失了,日志中看不到任何错误。这是否意味着它可能是关于调用该编排器的http-triggerd函数的争用条件?似乎极不可能,但如果我错了,请纠正我。
这里是配器功能。这个“计时器”和你手机上的一样,只是在云端。

namespace ServerlessTimers.Application.Functions.Durables;

using System;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.DurableTask;
using Microsoft.Extensions.Logging;
using ServerlessTimers.Application.Exceptions;
using ServerlessTimers.Application.Models.DurableEvents;
using ServerlessTimers.Application.Models.Durables;
using ServerlessTimers.Application.Services.Durables;
using ServerlessTimers.Domain.Aggregators.Timers;
using ServerlessTimers.Domain.Services;

public class OrchestrateTimerFunction
{

    private readonly ILogger logger;
    private readonly IDurableFacade durableFacade;
    private readonly ITimerRepository timerRepository;
    private readonly ITimerCalculatorFactory calculatorFactory;
    private readonly CancellationTokenSource cts;

    public OrchestrateTimerFunction(
        IDurableFacade durableFacade,
        ITimerRepository timerRepository,
        ITimerCalculatorFactory calculatorFactory,
        ILogger<OrchestrateTimerFunction> logger)
    {
        this.logger = logger;
        this.durableFacade = durableFacade;
        this.timerRepository = timerRepository;
        this.calculatorFactory = calculatorFactory;
        cts = new CancellationTokenSource();
    }

    [FunctionName(nameof(OrchestrateTimerFunction))]
    public async Task RunOrchestrator(
        [OrchestrationTrigger] 
        IDurableOrchestrationContext context)
    {
        try
        {

            // Get timer
            var input = context.GetInput<TimerOrchestratorInput>();
            var timer = await timerRepository.FindByIdAsync(input.TimerId) ??
                throw new TimerNotFoundException(input.TimerId);

            // Do not run orchestration if timer's shouldn't be running
            if(!timer.State.EqualRunningState())
            {
                logger.LogError($"Timer {timer.Id}: " +
                    $"Tried to be orchestrated but has {timer.State} state");
                throw new Exception();
            }

            // Calculate the completion date of the timer
            var calculator = calculatorFactory.GetCalculator(timer);
            var remainingTime = calculator.CalculateRemainingTime();
            logger.LogInformation($"Timer {timer.Id}: " +
                $"To complete in {remainingTime}");
            if (remainingTime <= TimeSpan.Zero)
            {
                logger.LogError($"Timer {timer.Id}: " +
                    $"Remaining time is negative");
                throw new Exception();
            }

            // Set external events
            var timerPausedEventTask = context.WaitForExternalEvent<DurableEvent>(
                name: nameof(TimerPausedDurableEvent),
                defaultValue: new TimerCompletedDurableEvent(),
                timeout: remainingTime,
                cancelToken: cts.Token);
            var timerStoppedEventTask = context.WaitForExternalEvent<DurableEvent>(
                name: nameof(TimerStoppedDurableEvent),
                defaultValue: new TimerCompletedDurableEvent(),
                timeout: remainingTime,
                cancelToken: cts.Token);
            
            // Await timer
            var durableEvent = await Task.WhenAny<DurableEvent>(
                timerPausedEventTask, timerStoppedEventTask);
            cts.Cancel();
            
            // Handle events
            if(durableEvent.Result is TimerCompletedDurableEvent)
            {
                logger.LogInformation($"Timer {timer.Id}: Completed");
            }
            else if (durableEvent.Result is TimerStoppedDurableEvent)
            {
                logger.LogInformation($"Timer {timer.Id}: Stopped");
            }
            else if (durableEvent.Result is TimerPausedDurableEvent pausedEvent)
            {
                logger.LogInformation($"Timer {timer.Id}: Paused ({pausedEvent.Reason})");
            }

        }
        catch(Exception ex)
        {
            logger.LogError(ex, ex.Message);
        }
    }

}
2vuwiymt

2vuwiymt1#

您的代码可能超出了编排器工作所需的code constraints。您应该将此类代码移到活动函数中以使其工作。
在你的例子中,我会说下面几行是原因,因为在重放时,它们可能会产生不同的结果。

1、

var timer = await timerRepository.FindByIdAsync(input.TimerId) ??
            throw new TimerNotFoundException(input.TimerId);

二、

var calculator = calculatorFactory.GetCalculator(timer);

三、

var remainingTime = calculator.CalculateRemainingTime();

相关问题