Orquestradores singleton em Tarefas Duráveis

Para trabalhos em segundo plano, muitas vezes é necessário garantir que apenas uma instância de um orquestrador específico corra de cada vez, evitando que orquestrações duplicadas sejam executadas simultaneamente. Pode implementar este padrão singleton em Durable Functions ou nos SDKs de Tarefas Durable atribuindo um ID de instância específico a um orquestrador ao criá-lo, e depois verificando se uma instância com esse ID já está a correr antes de iniciar uma nova.

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

Pré-requisitos

Observação

Existe uma condição potencial de corrida no padrão singleton. Se dois clientes executarem a lógica de verificação e início em simultâneo, ambas as chamadas podem reportar sucesso, mas apenas uma instância de orquestração começa efetivamente. Dependendo das suas necessidades, isto pode ter efeitos secundários indesejáveis. Se forem necessárias garantias estritas de instância única, considere adicionar mecanismos adicionais de bloqueio.

Importante

Atualmente, o PowerShell Durable Task SDK não está disponível.

Exemplo de orquestrador singleton

O exemplo seguinte mostra uma função acionada por HTTP que cria uma orquestração de tarefa em segundo plano singleton. O código tenta garantir que apenas exista uma instância ativa para um ID de instância especificado.

[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 trabalhador isolado, que é o modelo recomendado para aplicações .NET. Para mais informações sobre as diferenças entre os modelos de trabalhador em processo e os modelos isolados, consulte o artigo Durable Functions versões.

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

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 funciona o padrão singleton

Como os IDs de instância são únicos dentro de um hub de tarefas, agendar uma orquestração com um ID fixo conhecido e verificar primeiro o seu estado previne execuções simultâneas duplicadas. Por defeito, os IDs de instância são GUIDs gerados aleatoriamente. Nos exemplos anteriores, no entanto, é passado um ID de instância específico. O código então recolhe os metadados da instância de orquestração para verificar se uma instância com esse ID já está a correr. Se nenhuma instância desse tipo estiver a correr, é criada uma nova instância com esse ID.

A própria função do orquestrador pode usar qualquer padrão — uma função padrão que inicia e completa, ou uma Orquestração Eterna que se executa continuamente. O padrão singleton controla apenas quantas instâncias são executadas simultaneamente.

Passos seguintes