Orquestradores singleton na Tarefa Durável

Para trabalhos em segundo plano, frequentemente é necessário garantir que apenas uma instância de um orquestrador específico seja executada de cada vez, evitando a execução simultânea de orquestrações duplicadas. Você pode implementar esse padrão singleton em Durable Functions ou nos SDKs de Tarefa Durable Task atribuindo uma ID de instância específica a um orquestrador ao criá-la e verificando se uma instância com essa ID já está em execução antes de iniciar uma nova.

Este artigo mostra como implementar orquestradores singleton com exemplos de código para cada idioma com suporte.

Pré-requisitos

Observação

Há uma condição de corrida potencial no padrão singleton. Se dois clientes executarem a lógica de verificação e início simultaneamente, ambas as chamadas poderão relatar êxito, mas apenas uma instância de orquestração realmente será iniciada. Dependendo de seus requisitos, isso pode ter efeitos colaterais indesejáveis. Se forem necessárias garantias estritas de instância única, considere a adição de mecanismos de bloqueio adicionais.

Importante

Atualmente, o SDK da Tarefa Durável do PowerShell não está disponível.

Exemplo de orquestrador singleton

O exemplo a seguir mostra uma função de gatilho HTTP que cria uma orquestração singleton de trabalho em segundo plano. O código tenta garantir que apenas uma instância ativa exista para uma ID de instância especificada.

[Function("HttpStartSingle")]
public static async Task<HttpResponseData> RunSingle(
    [HttpTrigger(AuthorizationLevel.Function, "post", Route = "orchestrators/{functionName}/{instanceId}")] HttpRequestData req,
    [DurableClient] DurableTaskClient starter,
    string functionName,
    string instanceId,
    FunctionContext executionContext)
{
    ILogger logger = executionContext.GetLogger("HttpStartSingle");

    // Check if an instance with the specified ID already exists or an existing one stopped running(completed/failed/terminated).
    OrchestrationMetadata? existingInstance = await starter.GetInstanceAsync(instanceId, getInputsAndOutputs: false);
    if (existingInstance == null 
    || existingInstance.RuntimeStatus == OrchestrationRuntimeStatus.Completed 
    || existingInstance.RuntimeStatus == OrchestrationRuntimeStatus.Failed 
    || existingInstance.RuntimeStatus == OrchestrationRuntimeStatus.Terminated)
    {
        // An instance with the specified ID doesn't exist or an existing one stopped running, create one.
        string requestBody = await new StreamReader(req.Body).ReadToEndAsync();
        await starter.ScheduleNewOrchestrationInstanceAsync(functionName, requestBody, new StartOrchestrationOptions { InstanceId = instanceId });
        logger.LogInformation($"Started orchestration with ID = '{instanceId}'.");
        return await starter.CreateCheckStatusResponseAsync(req, instanceId);
    }
    else
    {
        // An instance with the specified ID exists or an existing one still running, don't create one.
        var response = req.CreateResponse(HttpStatusCode.Conflict);
        await response.WriteStringAsync($"An instance with ID '{instanceId}' already exists.");
        return response;
    }
}

Observação

O código C# anterior é para o modelo de trabalho isolado, que é o modelo recomendado para aplicativos .NET. Para saber mais sobre as distinções entre modelos de trabalho em processo e isolados, confira o artigo Versões do Durable Functions.

O exemplo a seguir mostra como criar uma orquestração singleton usando os SDKs do Durable Task. O código tenta garantir que apenas uma instância ativa exista para uma ID de instância especificada.

using Microsoft.DurableTask.Client;

// Check if an instance with the specified ID already exists
string instanceId = "singleton-job";
OrchestrationMetadata? existingInstance = await client.GetInstanceAsync(instanceId, getInputsAndOutputs: false);

if (existingInstance == null ||
    existingInstance.RuntimeStatus == OrchestrationRuntimeStatus.Completed ||
    existingInstance.RuntimeStatus == OrchestrationRuntimeStatus.Failed ||
    existingInstance.RuntimeStatus == OrchestrationRuntimeStatus.Terminated)
{
    // An instance with the specified ID doesn't exist or an existing one stopped running, create one.
    await client.ScheduleNewOrchestrationInstanceAsync("MyOrchestration", input, new StartOrchestrationOptions(instanceId));
    Console.WriteLine($"Started orchestration with ID = '{instanceId}'.");
}
else
{
    // An instance with the specified ID exists or an existing one still running.
    Console.WriteLine($"An instance with ID '{instanceId}' already exists.");
}

Como o padrão singleton funciona

Como as IDs de instância são exclusivas em um hub de tarefas, agendar uma orquestração com uma ID fixa conhecida e verificar seu status primeiro impede execuções simultâneas duplicadas. Por padrão, as IDs de instância são GUIDs geradas aleatoriamente. Nos exemplos anteriores, no entanto, uma ID de instância específica é passada. Em seguida, o código busca os metadados da instância de orquestração para verificar se uma instância com essa ID já está em execução. Se nenhuma instância desse tipo estiver em execução, uma nova instância será criada com essa ID.

A função de orquestrador em si pode usar qualquer padrão – uma função padrão que inicia e é concluída ou uma Orquestração Eterna que é executada continuamente. O padrão singleton controla apenas quantas instâncias são executadas simultaneamente.

Próximas Etapas