Criar dependências de tarefas para executar tarefas que dependem de outras tarefas
Com as dependências de tarefas em lote, você cria tarefas agendadas para execução em nós de computação após a conclusão de uma ou mais tarefas pai. Por exemplo, você pode criar um trabalho que renderiza cada quadro de um filme 3D com tarefas separadas e paralelas. A tarefa final mescla os quadros renderizados no filme completo somente depois que todos os quadros tiverem sido renderizados com êxito. Em outras palavras, a tarefa final depende das tarefas pai anteriores.
Alguns cenários em que as dependências de tarefas são úteis incluem:
- Cargas de trabalho no estilo MapReduce na nuvem.
- Trabalhos cujas tarefas de processamento de dados podem ser expressas como um gráfico acíclico direcionado (DAG).
- Processos de pré-renderização e pós-renderização, onde cada tarefa deve ser concluída antes que a próxima tarefa possa começar.
- Qualquer outro trabalho em que as tarefas a jusante dependam da saída de tarefas a montante.
Por padrão, as tarefas dependentes são agendadas para execução somente depois que a tarefa pai for concluída com êxito. Opcionalmente, você pode especificar uma ação de dependência para substituir o comportamento padrão e executar a tarefa dependente, mesmo que a tarefa pai falhe.
Neste artigo, discutimos como configurar dependências de tarefas usando a biblioteca .NET em lote. Primeiro, mostramos como habilitar a dependência de tarefas em seus trabalhos e, em seguida, demonstramos como configurar uma tarefa com dependências. Também descrevemos como especificar uma ação de dependência para executar tarefas dependentes se o pai falhar. Finalmente, discutimos os cenários de dependência suportados pelo Batch.
Habilitar dependências de tarefas
Para usar dependências de tarefas em seu aplicativo em lotes, você deve primeiro configurar o trabalho para usar dependências de tarefas. No Batch .NET, habilite-o em seu CloudJob definindo sua propriedade UsesTaskDependencies como true
:
CloudJob unboundJob = batchClient.JobOperations.CreateJob( "job001",
new PoolInformation { PoolId = "pool001" });
// IMPORTANT: This is REQUIRED for using task dependencies.
unboundJob.UsesTaskDependencies = true;
No trecho de código anterior, "batchClient" é uma instância da classe BatchClient .
Criar tarefas dependentes
Para criar uma tarefa que depende da conclusão de uma ou mais tarefas pai, você pode especificar que a tarefa "depende" das outras tarefas. No Batch .NET, configure a propriedade CloudTask.DependsOn com uma instância da classe TaskDependencies :
// Task 'Flowers' depends on completion of both 'Rain' and 'Sun'
// before it is run.
new CloudTask("Flowers", "cmd.exe /c echo Flowers")
{
DependsOn = TaskDependencies.OnIds("Rain", "Sun")
},
Este trecho de código cria uma tarefa dependente com a ID da tarefa "Flores". A tarefa "Flores" depende das tarefas "Chuva" e "Sol". A tarefa "Flores" será agendada para ser executada em um nó de computação somente depois que as tarefas "Chuva" e "Sol" forem concluídas com êxito.
Nota
Por padrão, uma tarefa é considerada concluída com êxito quando está no estado concluído e seu código de saída é 0
. No Batch .NET, isso significa que um valor de propriedade CloudTask.State é Completed
e o valor da propriedade TaskExecutionInformation.ExitCode do CloudTask é 0
. Para saber como alterar isso, consulte a seção Ações de dependência.
Cenários de dependência
Há três cenários básicos de dependência de tarefas que você pode usar no Lote do Azure: dependência um-para-um, um-para-muitos e dependência de intervalo de ID de tarefa. Estes três cenários podem ser combinados para fornecer um quarto cenário: muitos-para-muitos.
Cenário | Exemplo | Ilustração |
---|---|---|
Um para um | taskB depende da tarefaUma tarefaB não será agendada para execução até que a taskA tenha sido concluída com êxito |
|
Um-para-muitos | taskC depende da taskA e taskB taskC não será agendada para execução até que a taskA e a taskB tenham sido concluídas com êxito |
|
Intervalo de ID da tarefa | taskD depende de um intervalo de tarefas taskD não será agendado para execução até que as tarefas com IDs 1 a 10 tenham sido concluídas com êxito |
Gorjeta
Você pode criar relações muitos-para-muitos , como onde as tarefas C, D, E e F dependem das tarefas A e B. Isso é útil, por exemplo, em cenários de pré-processamento paralelo em que suas tarefas downstream dependem da saída de várias tarefas upstream.
Nos exemplos desta seção, uma tarefa dependente é executada somente depois que as tarefas pai são concluídas com êxito. Esse comportamento é o comportamento padrão para uma tarefa dependente. Você pode executar uma tarefa dependente depois que uma tarefa pai falhar especificando uma ação de dependência para substituir o comportamento padrão.
Um para um
Em uma relação um-para-um, uma tarefa depende da conclusão bem-sucedida de uma tarefa pai. Para criar a dependência, forneça um único ID de tarefa para o método estático TaskDependencies.OnId ao preencher a propriedade CloudTask.DependsOn .
// Task 'taskA' doesn't depend on any other tasks
new CloudTask("taskA", "cmd.exe /c echo taskA"),
// Task 'taskB' depends on completion of task 'taskA'
new CloudTask("taskB", "cmd.exe /c echo taskB")
{
DependsOn = TaskDependencies.OnId("taskA")
},
Um-para-muitos
Em uma relação um-para-muitos, uma tarefa depende da conclusão de várias tarefas pai. Para criar a dependência, forneça uma coleção de IDs de tarefa específicas para o método estático TaskDependencies.OnIds quando você preencher a propriedade CloudTask.DependsOn .
// 'Rain' and 'Sun' don't depend on any other tasks
new CloudTask("Rain", "cmd.exe /c echo Rain"),
new CloudTask("Sun", "cmd.exe /c echo Sun"),
// Task 'Flowers' depends on completion of both 'Rain' and 'Sun'
// before it is run.
new CloudTask("Flowers", "cmd.exe /c echo Flowers")
{
DependsOn = TaskDependencies.OnIds("Rain", "Sun")
},
Importante
A criação da tarefa dependente falhará se o comprimento combinado dos IDs da tarefa pai for maior que 64000 caracteres. Para especificar um grande número de tarefas pai, considere usar um intervalo de ID de Tarefa.
Intervalo de ID da tarefa
Em uma dependência de um intervalo de tarefas pai, uma tarefa depende da conclusão de tarefas cujas IDs estão dentro de um intervalo que você especificar.
Para criar a dependência, forneça os IDs da primeira e da última tarefa no intervalo para o método estático TaskDependencies.OnIdRange quando você preencher a propriedade CloudTask.DependsOn .
Importante
Quando você usa intervalos de ID de tarefas para suas dependências, somente as tarefas com IDs que representam valores inteiros serão selecionadas pelo intervalo. Por exemplo, o intervalo 1..10
selecionará tarefas 3
e 7
, mas não 5flamingoes
.
Os zeros à esquerda não são significativos ao avaliar as dependências do intervalo, portanto, as tarefas com identificadores de cadeia de 4
caracteres , 04
e 004
estarão todas dentro do intervalo, uma vez que todas elas serão tratadas como tarefa 4
, a primeira a ser concluída satisfará a dependência.
Para que a tarefa dependente seja executada, cada tarefa no intervalo deve satisfazer a dependência, seja concluindo com êxito ou concluindo com uma falha mapeada para uma ação de dependência definida como Satisfazer.
// Tasks 1, 2, and 3 don't depend on any other tasks. Because
// we will be using them for a task range dependency, we must
// specify string representations of integers as their ids.
new CloudTask("1", "cmd.exe /c echo 1"),
new CloudTask("2", "cmd.exe /c echo 2"),
new CloudTask("3", "cmd.exe /c echo 3"),
// Task 4 depends on a range of tasks, 1 through 3
new CloudTask("4", "cmd.exe /c echo 4")
{
// To use a range of tasks, their ids must be integer values.
// Note that we pass integers as parameters to TaskIdRange,
// but their ids (above) are string representations of the ids.
DependsOn = TaskDependencies.OnIdRange(1, 3)
},
Ações de dependência
Por padrão, uma tarefa dependente ou um conjunto de tarefas é executado somente depois que uma tarefa pai é concluída com êxito. Em alguns cenários, talvez você queira executar tarefas dependentes mesmo se a tarefa pai falhar. Você pode substituir o comportamento padrão especificando uma ação de dependência que indica se uma tarefa dependente está qualificada para execução.
Por exemplo, suponha que uma tarefa dependente esteja aguardando dados da conclusão da tarefa upstream. Se a tarefa upstream falhar, a tarefa dependente ainda poderá ser executada usando dados mais antigos. Nesse caso, uma ação de dependência pode especificar que a tarefa dependente está qualificada para ser executada apesar da falha da tarefa pai.
Uma ação de dependência é baseada em uma condição de saída para a tarefa pai. Você pode especificar uma ação de dependência para qualquer uma das seguintes condições de saída:
- Quando ocorre um erro de pré-processamento.
- Quando ocorre um erro de carregamento de ficheiro. Se a tarefa sair com um código de saída especificado por meio de exitCodes ou exitCodeRanges e, em seguida, encontrar um erro de carregamento de arquivo, a ação especificada pelo código de saída terá precedência.
- Quando a tarefa é encerrada com um código de saída definido pela propriedade ExitCodes .
- Quando a tarefa é encerrada com um código de saída que está dentro de um intervalo especificado pela propriedade ExitCodeRanges .
- O caso padrão, se a tarefa sair com um código de saída não definido por ExitCodes ou ExitCodeRanges, ou se a tarefa sair com um erro de pré-processamento e a propriedade PreProcessingError não estiver definida, ou se a tarefa falhar com um erro de carregamento de arquivo e a propriedade FileUploadError não estiver definida.
Para .NET, essas condições são definidas como propriedades da classe ExitConditions .
Para especificar uma ação de dependência, defina a propriedade ExitOptions.DependencyAction para a condição de saída como uma das seguintes:
- Satisfazer: Indica que as tarefas dependentes estão qualificadas para execução se a tarefa pai for encerrada com um erro especificado.
- Bloquear: indica que as tarefas dependentes não estão qualificadas para execução.
A configuração padrão para a propriedade DependencyAction é Satisfazer para o código de saída 0 e Block para todas as outras condições de saída.
O trecho de código a seguir define a propriedade DependencyAction para uma tarefa pai. Se a tarefa pai for encerrada com um erro de pré-processamento ou com os códigos de erro especificados, a tarefa dependente será bloqueada. Se a tarefa pai for encerrada com qualquer outro erro diferente de zero, a tarefa dependente estará qualificada para execução.
// Task A is the parent task.
new CloudTask("A", "cmd.exe /c echo A")
{
// Specify exit conditions for task A and their dependency actions.
ExitConditions = new ExitConditions
{
// If task A exits with a pre-processing error, block any downstream tasks (in this example, task B).
PreProcessingError = new ExitOptions
{
DependencyAction = DependencyAction.Block
},
// If task A exits with the specified error codes, block any downstream tasks (in this example, task B).
ExitCodes = new List<ExitCodeMapping>
{
new ExitCodeMapping(10, new ExitOptions() { DependencyAction = DependencyAction.Block }),
new ExitCodeMapping(20, new ExitOptions() { DependencyAction = DependencyAction.Block })
},
// If task A succeeds or fails with any other error, any downstream tasks become eligible to run
// (in this example, task B).
Default = new ExitOptions
{
DependencyAction = DependencyAction.Satisfy
}
}
},
// Task B depends on task A. Whether it becomes eligible to run depends on how task A exits.
new CloudTask("B", "cmd.exe /c echo B")
{
DependsOn = TaskDependencies.OnId("A")
},
Exemplo de código
O projeto de exemplo TaskDependencies no GitHub demonstra:
- Como habilitar a dependência de tarefas em um trabalho.
- Como criar tarefas que dependem de outras tarefas.
- Como executar essas tarefas em um pool de nós de computação.
Próximos passos
- Saiba mais sobre o recurso de pacotes de aplicativos do Batch, que fornece uma maneira fácil de implantar e fazer a versão dos aplicativos que suas tarefas executam em nós de computação.
- Saiba mais sobre a verificação de erros para trabalhos e tarefas.