Criar uma sequência de tarefas com um gráfico de tarefas

No Snowflake, você pode gerenciar várias tarefas com um gráfico de tarefas, também conhecido como gráfico acíclico direcionado, ou DAG. Um gráfico de tarefas é composto de uma tarefa raiz e de tarefas filho dependentes. As dependências devem ser executadas em uma direção do início ao fim, sem loops. Uma tarefa final opcional (finalizadora) pode realizar operações de limpeza depois que todas as outras tarefas forem concluídas.

Crie gráficos de tarefas com comportamento dinâmico, especificando operações baseadas em lógica no corpo da tarefa usando valores de tempo de execução, configuração no nível do gráfico e valores de retorno das tarefas pai.

Você pode criar tarefas e gráficos de tarefas usando linguagens e ferramentas compatíveis como SQL, JavaScript, Python, Java, Scala ou Snowflake Scripting. Esse tópico fornece exemplos em SQL. Para obter exemplos em Python, consulte Gerenciamento de tarefas e gráficos de tarefas do Snowflake com Python.

Crie um gráfico de tarefa

Crie uma tarefa raiz usando CREATE TASK e, em seguida, crie tarefas filho usando CREATE TASK … AFTER para selecionar as tarefas pai.

A tarefa raiz define quando o gráfico de tarefas executa. As tarefas filho são executadas na ordem definida pelo gráfico de tarefas.

Quando várias tarefas filho têm a mesma tarefa pai, as tarefas filho são executadas em paralelo.

Quando uma tarefa tem vários tarefas pai, ela espera que todas as tarefas anteriores sejam concluídas com sucesso antes de iniciar. (A tarefa também pode ser executada quando algumas tarefas pai são ignoradas. Para obter mais informações, consulte Ignorar ou suspender uma tarefa filho).

O exemplo a seguir cria um gráfico de tarefas sem servidor que começa com uma tarefa raiz programada para ser executada a cada minuto. A tarefa raiz tem duas tarefas filho que são executadas em paralelo. (O diagrama mostra um exemplo em que uma dessas tarefas é executada por mais tempo do que a outra) Após a conclusão dessas duas tarefas, uma terceira tarefa filho é executada. A tarefa finalizadora é executada depois que todas as outras tarefas são concluídas ou apresentam falha na conclusão:

Um diagrama de uma sequência de tarefas.
CREATE TASK task_root
  SCHEDULE = '1 MINUTE'
  AS SELECT 1;

CREATE TASK task_a
  AFTER task_root
  AS SELECT 1;

CREATE TASK task_b
  AFTER task_root
  AS SELECT 1;

CREATE TASK task_c
  AFTER task_a, task_b
  AS SELECT 1;
Copy

Considerações:

  • O gráfico de tarefa é limitado a um máximo de 1.000 tarefas.

  • Uma única tarefa pode ter um máximo de 100 tarefas pai e 100 tarefas filho.

  • Quando as tarefas são executadas em paralelo no mesmo warehouse gerenciado pelo usuário, os recursos de computação devem ser dimensionados para lidar com as execuções de tarefas simultâneas.

Tarefa finalizadora

Você pode adicionar uma tarefa finalizadora opcional para ser executada depois que todas as outras tarefas no gráfico de tarefas forem concluídas (ou apresentarem falha na conclusão). Use isso para fazer o seguinte:

  • Realizar operações de limpeza, por exemplo, limpar os dados intermediários que não são mais necessários.

  • Enviar notificações sobre o sucesso ou falha da tarefa.

Uma sequência de tarefas mostra uma tarefa raiz que aponta para duas tarefas filho, que, por sua vez, apontam para outra tarefa. Uma tarefa finalizadora é mostrada na parte inferior, sendo executada depois que todas as outras tarefas são concluídas ou apresentam falha na conclusão.

Para criar uma tarefa finalizadora, use CREATE TASK … FINALIZE … na tarefa raiz. Exemplo:

CREATE TASK task_finalizer
  FINALIZE = task_root
  AS SELECT 1;
Copy

Considerações:

  • Uma tarefa finalizadora está sempre associada a uma tarefa raiz. Cada tarefa raiz pode ter apenas uma tarefa finalizadora, e uma tarefa finalizadora pode ser associada a apenas uma tarefa raiz.

  • Quando a tarefa raiz de um gráfico de tarefas é ignorada (por exemplo, por causa da sobreposição de execuções do gráfico de tarefas), a tarefa finalizadora não será iniciada.

  • Uma tarefa finalizadora não pode ter tarefas secundárias.

  • Uma tarefa finalizadora é programada somente quando nenhuma outra tarefa estiver em execução ou enfileirada na execução do gráfico de tarefas atual.

Para obter mais exemplos, consulte Exemplo de tarefa finalizadora: Enviar notificação por e-mail e Exemplo de tarefa finalizadora: Correção de erros.

Gerenciamento da propriedade do gráfico de tarefa

Todas as tarefas em um gráfico de tarefa devem ter o mesmo proprietário e ser armazenadas no mesmo banco de dados e esquema.

Você pode transferir a propriedade de todas as tarefas em um gráfico de tarefa usando uma das seguintes ações:

  • Descarte o proprietário de todas as tarefas no gráfico de tarefa usando DROP ROLE. O Snowflake transfere a propriedade para a função que executa o comando DROP ROLE.

  • Transfira a propriedade de todas as tarefas no gráfico de tarefa usando GRANT OWNERSHIP em todas as tarefas em um esquema.

Quando você transfere a propriedade de tarefa em um gráfico de tarefa usando esses métodos, as tarefas no gráfico de tarefa mantêm seus relacionamentos entre si.

Transferir a propriedade de uma única tarefa remove a dependência entre a tarefa e quaisquer tarefas pai e filho. Para obter mais informações, consulte Desvinculação de tarefas pai e filho (neste tópico).

Nota

A replicação de banco de dados não funciona para gráficos de tarefa se o gráfico for de propriedade de uma função diferente da que executa a replicação.

Executar ou programar tarefas em um gráfico de tarefas

Executar um gráfico de tarefas manualmente

Você pode executar uma única instância de um gráfico de tarefas. Isso é útil para testar gráficos de tarefas novos ou modificados antes de ativar o gráfico de tarefas na produção ou para execuções únicas, conforme necessário.

Antes de iniciar o gráfico de tarefas, use ALTER TASK … RESUME em cada tarefa filho (incluindo a tarefa finalizadora opcional) que você deseja incluir na execução.

Para executar uma única instância de um gráfico de tarefas, use EXECUTE TASK na tarefa raiz. Quando você executa a tarefa raiz, todas as tarefas filho retomadas no gráfico de tarefas são executadas na ordem definida pelo gráfico de tarefas.

Executar uma tarefa em um cronograma ou como uma tarefa acionada

Na tarefa raiz, defina quando o gráfico de tarefas será executado. Os gráficos de tarefas podem ser executados em uma programação recorrente ou podem ser acionados por um evento. Para obter mais informações, consulte os seguintes tópicos:

Para iniciar o gráfico de tarefas, você pode fazer uma das seguintes opções:

  • Retome cada tarefa filho individual (incluindo a finalizadora) que você deseja incluir na execução e, em seguida, retome a tarefa raiz, usando ALTER TASK … RESUME.

  • Retome todas as tarefas em um gráfico de tarefas de uma só vez usando SYSTEM$TASK_DEPENDENTS_ENABLE ( <root_task_name> ) na tarefa raiz.

Visualização de tarefas dependentes em um gráfico de tarefa

Para visualizar as tarefas filho de uma tarefa raiz, chame a TASK_DEPENDENTS função de tabela. Para recuperar todas as tarefas em um gráfico de tarefa, insira a tarefa raiz ao chamar a função.

Você também pode usar o Snowsight para gerenciar e visualizar seus gráficos de tarefa. Para obter mais informações, consulte Visualização de tarefas e gráficos de tarefa no Snowsight.

Modificar, suspender ou tentar novamente as tarefas

Modificar uma tarefa em um gráfico de tarefas

Para modificar uma tarefa em um gráfico de tarefas agendadas, suspenda a tarefa raiz usando ALTER TASK … SUSPEND. Se uma execução do gráfico de tarefas estiver em andamento, ele concluirá a execução atual. Todas as futuras execuções programadas da tarefa raiz são canceladas.

Quando a tarefa raiz é suspensa, as tarefas filho, inclusive a tarefa finalizadora, mantêm seu estado (suspenso, em execução ou concluído). As tarefas filho não precisam ser suspensas individualmente.

Depois de suspender a tarefa raiz, você pode modificar qualquer tarefa no gráfico de tarefas.

Para retomar o gráfico de tarefas, você pode fazer uma das seguintes opções:

  • Retomar a tarefa raiz usando ALTER TASK … RESUME. As tarefas filho individuais que estavam sendo executadas anteriormente não precisam ser retomadas.

  • Retomar todas as tarefas em um gráfico de tarefas de uma só vez chamando SYSTEM$TASK_DEPENDENTS_ENABLE e passando o nome da tarefa raiz.

Ignorar ou suspender uma tarefa filho

Para ignorar uma tarefa filho em um gráfico de tarefas, suspenda a tarefa filho usando ALTER TASK … SUSPEND.

Quando você suspende uma tarefa filho, o gráfico de tarefas continua a ser executado como se a tarefa filho tivesse sido bem-sucedida. Uma tarefa filha com múltiplos predecessores é executada desde que pelo menos um dos predecessores esteja em um estado de retomada, e todos os predecessores retomados sejam executados com sucesso até a conclusão.

Um diagrama mostra um gráfico de tarefas que inclui uma tarefa filho suspensa. A tarefa filho suspensa é ignorada, e o gráfico de tarefas é concluído.

Tentar novamente uma tarefa que falhou

Use EXECUTE TASK … RETRY LAST para tentar executar o gráfico de tarefas a partir da última tarefa que falhou. Se a tarefa for bem-sucedida, todas as tarefas filho continuarão a ser executadas à medida que as tarefas anteriores forem concluídas.

Novas tentativas automáticas

Por padrão, se uma tarefa filho falhar, todo o gráfico de tarefas será considerado como tendo falhado.

Em vez de esperar até a próxima execução programada do gráfico de tarefas, você pode instruir o gráfico de tarefas a tentar novamente de imediato, definindo o parâmetro TASK_AUTO_RETRY_ATTEMPTS na tarefa raiz. Quando uma tarefa filho falha, todo o gráfico de tarefas é imediatamente repetido, até o número de vezes especificado. Se o gráfico de tarefas ainda assim não for concluído, considera-se que o gráfico de tarefas falhou.

Suspender gráficos de tarefas após execuções de gráficos de tarefas com falha

Por padrão, um gráfico de tarefas é suspenso após 10 falhas consecutivas. Você pode alterar esse valor definindo SUSPEND_TASK_AFTER_NUM_FAILURES na tarefa raiz.

No exemplo a seguir, sempre que uma tarefa filho falha, o gráfico de tarefas imediatamente faz duas novas tentativas antes que todo o gráfico de tarefas seja considerado falho. Se o gráfico de tarefas falhar três vezes seguidas, o gráfico de tarefas será suspenso.

CREATE OR REPLACE TASK task_root
  SCHEDULE = '1 MINUTE'
  TASK_AUTO_RETRY_ATTEMPTS = 2   --  Failed task graph retries up to 2 times
  SUSPEND_TASK_AFTER_NUM_FAILURES = 3   --  Task graph suspends after 3 consecutive failures
  AS SELECT 1;
Copy

Sobreposição de execuções de gráficos da tarefa

Por padrão, o Snowflake assegura que apenas uma instância de um determinado gráfico da tarefa seja autorizada a funcionar de cada vez. A próxima execução de uma tarefa raiz só é programada depois que todas as tarefas no gráfico da tarefa tiverem terminado. Isto significa que se o tempo acumulado necessário para executar todas as tarefas no gráfico da tarefa exceder o tempo explicitamente programado determinado na definição da tarefa raiz, pelo menos uma execução do gráfico de tarefas é ignorada.

Para permitir que as tarefas filho se sobreponham, use CREATE TASK ou ALTER TASK na tarefa raiz e defina ALLOW_OVERLAPPING_EXECUTION como TRUE. (As tarefas raiz nunca se sobrepõem)

Sobreposição de execuções de gráficos da tarefa

As execuções sobrepostas podem ser toleradas (ou mesmo desejáveis) quando operações SQL de leitura/escrita realizadas por execuções sobrepostas de um gráfico da tarefa não produzem dados incorretos ou duplicados. Entretanto, para outros gráficos da tarefa, os proprietários das tarefas (a função com o privilégio OWNERSHIP em todas as tarefas no gráfico da tarefa) devem definir um cronograma apropriado na tarefa raiz e escolher um tamanho de warehouse apropriado (ou usar recursos de computação sem servidor) para garantir que uma instância do gráfico da tarefa termine a conclusão antes que a tarefa raiz seja agendada.

Para alinhar melhor um gráfico da tarefa com o cronograma definido na tarefa raiz:

  1. Se possível, aumente o tempo de agendamento entre as execuções da tarefa raiz.

  2. Considere modificar tarefas de computação pesadas para usar recursos de computação sem servidor. Se a tarefa depender de recursos de computação gerenciados pelo usuário, aumente o tamanho do warehouse que executa instruções SQL grandes ou complexas ou procedimentos armazenados no gráfico da tarefa.

  3. Analise as instruções SQL ou procedimentos armazenados executados por cada tarefa. Determine se o código pode ser reescrito para aproveitar o processamento paralelo.

Se nenhuma das soluções acima ajudar, considere se é necessário permitir execuções simultâneas do gráfico da tarefa definindo ALLOW_OVERLAPPING_EXECUTION = TRUE na tarefa raiz. Este parâmetro pode ser definido ao criar uma tarefa (usando CREATE TASK) ou mais tarde (usando ALTER TASK ou no Snowsight).

Controle de versão

Quando a tarefa raiz em um gráfico de tarefa é retomada ou executada manualmente, o Snowflake define uma versão de todo o gráfico de tarefa, incluindo todas as propriedades de todas as tarefas no gráfico de tarefa. Após uma tarefa ser suspensa e modificada, o Snowflake define uma nova versão quando a tarefa raiz é retomada ou executada manualmente.

Para modificar ou recriar qualquer tarefa em um gráfico de tarefa, a tarefa raiz deve primeiro ser suspensa. Quando a tarefa raiz é suspensa, todas as futuras execuções agendadas da tarefa raiz são canceladas; entretanto, se alguma tarefa estiver sendo executada, essas tarefas e quaisquer tarefas descendentes continuam a ser executadas usando a versão atual.

Nota

Se a definição de um procedimento armazenado chamado por uma tarefa mudar enquanto o gráfico da tarefa estiver sendo executado, a nova programação poderá ser executada quando o procedimento armazenado for chamado pela tarefa na execução atual.

Por exemplo, suponha que a tarefa raiz em um gráfico da tarefa esteja suspensa, mas uma execução programada desta tarefa já tenha começado. O proprietário de todas as tarefas no gráfico da tarefa modifica o código SQL chamado por uma tarefa filha enquanto a tarefa raiz ainda está em execução. A tarefa filha executa o código SQL em sua definição usando a versão do gráfico da tarefa que era atual quando a tarefa raiz iniciou sua execução. Quando a tarefa raiz é retomada ou é executada manualmente, uma nova versão do gráfico da tarefa é definida. Esta nova versão inclui as modificações da tarefa filha.

Para recuperar o histórico das versões de tarefas, consulte TASK_VERSIONS Exibição do Account Usage (no banco de dados SNOWFLAKE compartilhado).

Duração do gráfico de tarefas

A duração do gráfico de tarefas inclui o tempo desde o início da tarefa raiz até a conclusão da última tarefa filho. Para calcular a duração de um gráfico de tarefas, consulte Exibição COMPLETE_TASK_GRAPHS e compare SCHEDULED_TIME com COMPLETED_TIME.

Por exemplo, o diagrama a seguir mostra um gráfico de tarefas programado para ser executado a cada minuto. A tarefa raiz e suas duas tarefas filho ficam na fila por 5 segundos e são executadas por 10 segundos, exigindo um total de 45 segundos para serem concluídas.

Um diagrama de um gráfico de tarefas, incluindo três tarefas com dependências. Cada tarefa fica na fila por 5 segundos e é executada por 10 segundos, totalizando 45 segundos de execução.

Tempo limite do gráfico de tarefas

Quando USER_TASK_TIMEOUT_MS é definido na tarefa raiz, o tempo limite se aplica a todo o gráfico da tarefa.

Quando USER_TASK_TIMEOUT_MS é definido em uma tarefa filho ou tarefa finalizadora, o tempo limite se aplica somente a essa tarefa.

Quando USER_TASK_TIMEOUT_MS é definido tanto na tarefa raiz quanto em uma tarefa filho, o tempo limite da tarefa filho substitui o tempo limite da tarefa raiz para essa tarefa filho.

Considerações

  • Para tarefas sem servidor, o Snowflake dimensiona automaticamente os recursos para garantir que as tarefas sejam concluídas dentro de um intervalo de conclusão desejado, incluindo o tempo de fila.

  • Para tarefas gerenciadas pelo usuário, é comum haver períodos mais longos de fila quando as tarefas são programadas para serem executadas em um warehouse compartilhado ou ocupado.

  • Para gráficos de tarefas, o tempo total pode incluir tempo adicional de fila para tarefas filho que aguardam a conclusão de suas predecessoras.

Criar um gráfico de tarefas com lógica (informações de tempo de execução, configuração e valores de retorno)

As tarefas em um gráfico de tarefas podem usar os valores de retorno das tarefas pai para realizar operações baseadas em lógica no corpo da função.

Considerações:

  • Alguns comandos baseados em lógica, como SYSTEM$GET_PREDECESSOR_RETURN_VALUE, diferenciam maiúsculas de minúsculas. No entanto, as tarefas criadas usando CREATE TASK sem aspas são armazenadas e resolvidas em letras maiúsculas. Para gerenciar isso, você pode fazer o seguinte:

    • Criar nomes de tarefas usando apenas letras maiúsculas.

    • Usar aspas ao nomear e chamar tarefas.

    • Para nomes de tarefas definidos com caracteres minúsculos, chame a tarefa usando caracteres maiúsculos. Por exemplo: uma tarefa definida por «CREATE TASK task_c…» pode ser chamada como SELECT SYSTEM$GET_PREDECESSOR_RETURN_VALUE(“TASK_C”).

Passar informações de configuração para o gráfico de tarefas

Você pode passar informações de configuração usando um objeto JSON que pode ser lido por outras tarefas em um gráfico de tarefas. Use a sintaxe CREATE/ALTER TASK … CONFIG para configurar, cancelar a configuração ou modificar as informações de configuração na tarefa raiz. Use a função SYSTEM$GET_TASK_GRAPH_CONFIG para recuperá-la. Exemplo:

CREATE OR REPLACE TASK "task_root"
  SCHEDULE = '1 MINUTE'
  USER_TASK_TIMEOUT_MS = 60000
  CONFIG='{"environment": "production", "path": "/prod_directory/"}'
  AS SELECT 1;

CREATE OR REPLACE TASK "task_a"
  USER_TASK_TIMEOUT_MS = 600000
  AFTER "task_root"
  AS
    BEGIN
      LET VALUE := (SELECT SYSTEM$GET_TASK_GRAPH_CONFIG('path'));
      CREATE TABLE IF NOT EXISTS demo_table(NAME VARCHAR, VALUE VARCHAR);
      INSERT INTO demo_table VALUES('task c path',:value);
    END;
Copy

Passar valores de retorno entre tarefas

Você pode passar valores de retorno entre tarefas em um gráfico de tarefas. Use a função SYSTEM$SET_RETURN_VALUE para adicionar um valor de retorno de uma tarefa e use a função SYSTEM$GET_PREDECESSOR_RETURN_VALUE para recuperá-lo.

Quando uma tarefa tem vários predecessores, você deve especificar qual tarefa tem o valor de retorno que deseja. No exemplo a seguir, criamos uma tarefa raiz em um gráfico de tarefas que adiciona informações de configuração.

CREATE OR REPLACE TASK "task_c"
  SCHEDULE = '1 MINUTE'
  USER_TASK_TIMEOUT_MS = 60000
  AS
    BEGIN
      CALL SYSTEM$SET_RETURN_VALUE('task_c successful');
    END;

CREATE OR REPLACE TASK "task_d"
  USER_TASK_TIMEOUT_MS = 60000
  AFTER "task_c"
  AS
    BEGIN
      LET VALUE := (SELECT SYSTEM$GET_PREDECESSOR_RETURN_VALUE('task_c'));
      CREATE TABLE IF NOT EXISTS demo_table(NAME VARCHAR, VALUE VARCHAR);
      INSERT INTO demo_table VALUES('Value from predecessor task_c', :value);
    END;
Copy

Obter e usar informações de tempo de execução

Use a função SYSTEM$TASK_RUNTIME_INFO para relatar informações sobre a execução da tarefa atual. Essa função tem várias opções específicas para gráficos de tarefas. Por exemplo, use CURRENT_ROOT_TASK_NAME para obter o nome da tarefa raiz no gráfico de tarefas atual. Os exemplos a seguir mostram como adicionar um carimbo de data a uma tabela com base em quando a tarefa raiz do gráfico de tarefas foi iniciada.

-- Updates the date/time table after the root task completes.
CREATE OR REPLACE TASK "task_date_time_table"
  USER_TASK_TIMEOUT_MS = 60000
  AFTER "task_root"
  AS
    BEGIN
      LET VALUE := (SELECT SYSTEM$TASK_RUNTIME_INFO('CURRENT_TASK_GRAPH_ORIGINAL_SCHEDULED_TIMESTAMP'));
      INSERT INTO date_time_table VALUES('order_date',:value);
    END;
Copy

Exemplos

Exemplo: como iniciar várias tarefas e relatar o status

No exemplo a seguir, a tarefa raiz inicia tarefas para atualizar três tabelas diferentes. Depois que essas três tabelas são atualizadas, uma tarefa combina as informações das outras três tabelas em uma tabela de vendas agregada.

O fluxograma mostra uma tarefa raiz que inicia três tarefas filho, cada uma das quais atualiza uma tabela. Todas essas três tarefas precedem outra tarefa filho, que combina as alterações anteriores em outra tabela.
-- Create a notebook in the public schema
-- USE DATABASE <database name>;
-- USE SCHEMA <schema name>;

-- task_a: Root task. Starts the task graph and sets basic configurations.
CREATE OR REPLACE TASK task_a
  SCHEDULE = '1 MINUTE'
  TASK_AUTO_RETRY_ATTEMPTS = 2
  SUSPEND_TASK_AFTER_NUM_FAILURES = 3
  USER_TASK_TIMEOUT_MS = 60000
  CONFIG='{"environment": "production", "path": "/prod_directory/"}'
  AS
    BEGIN
      CALL SYSTEM$SET_RETURN_VALUE('task_a successful');
    END;
;

-- task_customer_table: Updates the customer table.
--   Runs after the root task completes.
CREATE OR REPLACE TASK task_customer_table
  USER_TASK_TIMEOUT_MS = 60000
  AFTER task_a
  AS
    BEGIN
      LET VALUE := (SELECT customer_id FROM ref_cust_table
        WHERE cust_name = "Jane Doe";);
      INSERT INTO customer_table VALUES('customer_id',:value);
    END;
;

-- task_product_table: Updates the product table.
--   Runs after the root task completes.
CREATE OR REPLACE TASK task_product_table
  USER_TASK_TIMEOUT_MS = 60000
  AFTER task_a
  AS
    BEGIN
      LET VALUE := (SELECT product_id FROM ref_item_table
        WHERE PRODUCT_NAME = "widget";);
      INSERT INTO product_table VALUES('product_id',:value);
    END;
;

-- task_date_time_table: Updates the date/time table.
--   Runs after the root task completes.
CREATE OR REPLACE TASK task_date_time_table
  USER_TASK_TIMEOUT_MS = 60000
  AFTER task_a
  AS
    BEGIN
      LET VALUE := (SELECT SYSTEM$TASK_RUNTIME_INFO('CURRENT_TASK_GRAPH_ORIGINAL_SCHEDULED_TIMESTAMP'));
      INSERT INTO "date_time_table" VALUES('order_date',:value);
    END;
;

-- task_sales_table: Aggregates changes from other tables.
--   Runs only after updates are complete to all three other tables.
CREATE OR REPLACE TASK task_sales_table
  USER_TASK_TIMEOUT_MS = 60000
  AFTER task_customer_table, task_product_table, task_date_time_table
  AS
    BEGIN
      LET VALUE := (SELECT sales_order_id FROM ORDERS);
      JOIN CUSTOMER_TABLE ON orders.customer_id=customer_table.customer_id;
      INSERT INTO sales_table VALUES('sales_order_id',:value);
    END;
;
Copy

Exemplo de tarefa finalizadora: Enviar notificação por e-mail

Esse exemplo mostra como uma tarefa finalizadora pode enviar um e-mail que resume o desempenho do gráfico da tarefa. A tarefa chama duas funções externas: uma agrega informações sobre o status de conclusão da tarefa e a outra usa as informações para compor um e-mail que pode ser enviado por meio de um serviço de mensagens remoto.

Uma sequência de tarefas mostra uma tarefa raiz que aponta para duas tarefas filho, que, por sua vez, apontam para outra tarefa. Uma tarefa finalizadora é mostrada na parte inferior, executada depois que todas as outras tarefas são concluídas ou apresentam falha ao concluir.
CREATE OR REPLACE TASK notify_finalizer
  USER_TASK_TIMEOUT_MS = 60000
  FINALIZE = task_root
AS
  DECLARE
    my_root_task_id STRING;
    my_start_time TIMESTAMP_LTZ;
    summary_json STRING;
    summary_html STRING;
  BEGIN
    --- Get root task ID
    my_root_task_id := (SELECT SYSTEM$TASK_RUNTIME_INFO('CURRENT_ROOT_TASK_UUID'));
    --- Get root task scheduled time
    my_start_time := (SELECT SYSTEM$TASK_RUNTIME_INFO('CURRENT_TASK_GRAPH_ORIGINAL_SCHEDULED_TIMESTAMP')::timestamp_ltz);
    --- Combine all task run info into one JSON string
    summary_json := (SELECT get_task_graph_run_summary(:my_root_task_id, :my_start_time));
    --- Convert JSON into HTML table
    summary_html := (SELECT HTML_FROM_JSON_TASK_RUNS(:summary_json));

    --- Send HTML to email
    CALL SYSTEM$SEND_EMAIL(
        'email_notification',
        'admin@snowflake.com',
        'notification task run summary',
        :summary_html,
        'text/html');
    --- Set return value for finalizer
    CALL SYSTEM$SET_RETURN_VALUE('✅ Graph run summary sent.');
  END

CREATE OR REPLACE FUNCTION get_task_graph_run_summary(my_root_task_id STRING, my_start_time TIMESTAMP_LTZ)
  RETURNS STRING
AS
$$
  (SELECT
    ARRAY_AGG(OBJECT_CONSTRUCT(
      'task_name', name,
      'run_status', state,
      'return_value', return_value,
      'started', query_start_time,
      'duration', duration,
      'error_message', error_message
      )
    ) AS GRAPH_RUN_SUMMARY
  FROM
    (SELECT
      NAME,
      CASE
        WHEN STATE = 'SUCCEED' then '🟢 Succeeded'
        WHEN STATE = 'FAILED' then '🔴 Failed'
        WHEN STATE = 'SKIPPED' then '🔵 Skipped'
        WHEN STATE = 'CANCELLED' then '🔘 Cancelled'
      END AS STATE,
      RETURN_VALUE,
      TO_VARCHAR(QUERY_START_TIME, 'YYYY-MM-DD HH24:MI:SS') AS QUERY_START_TIME,
      CONCAT(TIMESTAMPDIFF('seconds', query_start_time, completed_time),
        ' s') AS DURATION,
      ERROR_MESSAGE
    FROM
      TABLE(my-database.information_schema.task_history(
        ROOT_TASK_ID => my_root_task_id ::STRING,
        SCHEDULED_TIME_RANGE_START => my_start_time,
        SCHEDULED_TIME_RANGE_END => current_timestamp()
      ))
    ORDER BY
      SCHEDULED_TIME)
  )::STRING
$$
;

CREATE OR REPLACE FUNCTION HTML_FROM_JSON_TASK_RUNS(JSON_DATA STRING)
  RETURNS STRING
  LANGUAGE PYTHON
  RUNTIME_VERSION = '3.8'
  HANDLER = 'GENERATE_HTML_TABLE'
AS
$$
  IMPORT JSON

  def GENERATE_HTML_TABLE(JSON_DATA):
    column_widths = ["320px", "120px", "400px", "160px", "80px", "480px"]

  DATA = json.loads(JSON_DATA)
  HTML = f"""
    <img src="https://example.com/logo.jpg"
      alt="Company logo" height="72">
    <p><strong>Task Graph Run Summary</strong>
      <br>Sign in to Snowsight to see more details.</p>
    <table border="1" style="border-color:#DEE3EA"
      cellpadding="5" cellspacing="0">
      <thead>
        <tr>
        """
        headers = ["Task name", "Run status", "Return value", "Started", "Duration", "Error message"]
        for i, header in enumerate(headers):
            HTML += f'<th scope="col" style="text-align:left;
            width: {column_widths[i]}">{header.capitalize()}</th>'

        HTML +="""
        </tr>
      </thead>
      <tbody>
        """
        for ROW_DATA in DATA:
          HTML += "<tr>"
          for header in headers:
            key = header.replace(" ", "_").upper()
            CELL_DATA = ROW_DATA.get(key, "")
            HTML += f'<td style="text-align:left;
            width: {column_widths[headers.index(header)]}">{CELL_DATA}</td>'
          HTML += "</tr>"
        HTML +="""
      </tbody>
    </table>
    """
  return HTML
$$
;
Copy

Exemplo de tarefa finalizadora: Correção de erros

Esse exemplo demonstra como uma tarefa finalizadora pode corrigir erros.

Para fins de demonstração, as tarefas foram projetadas para falhar na primeira execução. As tarefas finalizadoras corrigem o problema e reiniciam as tarefas, que são bem-sucedidas nas execuções seguintes:

Diagrama mostrando uma série de tarefas. A tarefa A é mostrada no canto superior esquerdo. Uma seta aponta para a direita da tarefa A para a tarefa B, que aponta para a tarefa C, que aponta para a tarefa D. Abaixo da tarefa A, uma seta aponta para a tarefa finalizadora, a tarefa F.
-- Configuration
-- By default, the notebook creates the objects in the public schema.
-- USE DATABASE <database name>;
-- USE SCHEMA <schema name>;

-- 1. Set the default configurations.
--    Creates a root task ("task_a"), and sets the default configurations
--    used throughout the task graph.
--    Configurations include:
--    * Each task runs after one minute, with a 60-second timeout.
--    * If a task fails, retry it twice. if it fails twice,
--      the entire task graph is considered as failed.
--    * If the task graph fails consecutively three times, suspend the task.
--    * Other environment values are set.

CREATE OR REPLACE TASK task_a
  SCHEDULE = '1 MINUTE'
  USER_TASK_TIMEOUT_MS = 60000
  TASK_AUTO_RETRY_ATTEMPTS = 2
  SUSPEND_TASK_AFTER_NUM_FAILURES = 3
  AS
    BEGIN
      CALL SYSTEM$SET_RETURN_VALUE('task a successful');
    END;
;

-- 2. Use a runtime reflection variable.
--    Creates a child task ("task_b").
--    By design, this example fails the first time it runs, because
--    it writes to a table ("demo_table") that doesn’t exist.
CREATE OR REPLACE TASK task_b
  USER_TASK_TIMEOUT_MS = 60000
  AFTER task_a
  AS
    BEGIN
      LET VALUE := (SELECT SYSTEM$TASK_RUNTIME_INFO('current_task_name'));
      INSERT INTO demo_table VALUES('task b name',:VALUE);
    END;
;

-- 3. Get a task graph configuration value.
--    Creates the child task ("task_c").
--    By design, this example fails the first time it runs, because
--    the predecessor task ("task_b") fails.
CREATE OR REPLACE TASK task_c
  USER_TASK_TIMEOUT_MS = 60000
  AFTER task_b
  AS
    BEGIN
      CALL SYSTEM$GET_TASK_GRAPH_CONFIG('path');
      LET VALUE := (SELECT SYSTEM$GET_TASK_GRAPH_CONFIG('path'));
      INSERT INTO demo_table VALUES('task c path',:value);
    END;
;

-- 4. Get a value from a predecessor.
--    Creates the child task ("task_d").
--    By design, this example fails the first time it runs, because
--    the predecessor task ("task_c") fails.
CREATE OR REPLACE TASK task_d
  USER_TASK_TIMEOUT_MS = 60000
  AFTER task_c
  AS
    BEGIN
      LET VALUE := (SELECT SYSTEM$GET_PREDECESSOR_RETURN_VALUE('TASK_A'));
      INSERT INTO demo_table VALUES('task d: predecessor return value', :value);
    END;
;

-- 5. Create the finalizer task ("task_f"), which creates the missing demo table.
--    After the finalizer completes, the task should automatically retry
--    (see task_a: task_auto_retry_attempts).
--    On retry, task_b, task_c, and task_d should complete successfully.
CREATE OR REPLACE TASK task_f
  USER_TASK_TIMEOUT_MS = 60000
  FINALIZE = task_a
  AS
    BEGIN
      CREATE TABLE IF NOT EXISTS demo_table(NAME VARCHAR, VALUE VARCHAR);
    END;
;

-- 6. Resume the finalizer. Upon creation, tasks start in a suspended state.
--    Use this command to resume the finalizer.
ALTER TASK task_f RESUME;
SELECT SYSTEM$TASK_DEPENDENTS_ENABLE('task_a');

-- 7. Query the task history
SELECT
    name, state, attempt_number, scheduled_from
  FROM
    TABLE(information_schema.task_history(task_name=> 'task_b'))
  LIMIT 5;
;

-- 8. Suspend the task graph to stop incurring costs
--    Note: To stop the task graph, you only need to suspend the root task
--    (task_a). Child tasks don’t run unless the root task is run.
--    If any child tasks are running, they have a limited duration
--    and will end soon.
ALTER TASK task_a SUSPEND;
DROP TABLE demo_table;

-- 9. Check tasks during execution (optional)
--    Run this command to query the demo table during execution
--    to check which tasks have run.
SELECT * FROM demo_table;

-- 10. Demo reset (optional)
--     Run this command to remove the demo table.
--     This causes task_b to fail during its first run.
--     After the task graph retries, task_b will succeed.
DROP TABLE demo_table;
Copy