Orquestrar o Notebook e modularizar o código no Notebook

Saiba como orquestrar o Notebook e modularizar o código no Notebook. Veja exemplos e entenda quando usar métodos alternativos para a orquestração do Notebook.

Métodos de orquestração e modularização de código

A tabela a seguir compara os métodos disponíveis para orquestrar o Notebook e modularizar o código no Notebook.

Método

Caso de uso

Notas

Jobs do Databricks

Notebook orquestração (recomendado)

Método recomendado para orquestrar o Notebook.

Suporta fluxos de trabalho complexos com dependências de tarefas, programar e acionadores. Fornece uma abordagem robusta e escalável para cargas de trabalho de produção, mas requer instalação e configuração.

dbutils.Notebook.execução()

Notebook orquestração

Use o site dbutils.notebook.run() se o Jobs não for compatível com o seu caso de uso, como o looping do Notebook em um conjunto dinâmico de parâmetros.

começar um novo Job efêmero para cada chamada, o que pode aumentar a sobrecarga e carece de recurso avançado de programação.

arquivos do espaço de trabalho

Modularização de código (recomendada)

Método recomendado para modularizar o código.

Modularize o código em arquivos de código reutilizáveis armazenados no site workspace. Oferece suporte ao controle de versão com repositórios e integração com IDEs para melhorar a depuração e os testes de unidade. Requer configuração adicional para gerenciar caminhos de arquivos e dependências.

%run

Modularização de código

Use %run se o senhor não puder acessar os arquivos workspace.

Basta importar funções ou variáveis de outro Notebook, executando-as em linha. Útil para prototipagem, mas pode levar a um código fortemente acoplado que é mais difícil de manter. Não suporta passagem de parâmetros ou controle de versão.

%run versus dbutils.notebook.run()

O comando %run permite que o senhor inclua outro Notebook dentro de um Notebook. O senhor pode usar o site %run para modularizar seu código, colocando as funções de suporte em um Notebook separado. O senhor também pode usá-lo para concatenar o Notebook que implementa as etapas de uma análise. Quando o senhor usa %run, o Notebook chamado é imediatamente executado e as funções e variáveis definidas nele ficam disponíveis no Notebook de chamada.

O dbutils.notebook API complementa o %run porque permite que o senhor passe parâmetros e retorne valores de um Notebook. Isso permite que o senhor crie fluxos de trabalho complexos e pipelines com dependências. Por exemplo, o senhor pode obter uma lista de arquivos em um diretório e passar os nomes para outro Notebook, o que é impossível com %run. O senhor também pode criar fluxos de trabalho if-then-else com base em valores de retorno.

Ao contrário do %run, o método dbutils.notebook.run() inicia um novo trabalho para executar o notebook.

Como todas as APIs do site dbutils, esses métodos estão disponíveis somente em Python e Scala. No entanto, o senhor pode usar dbutils.notebook.run() para chamar um R Notebook.

Usar %run para importar um notebooks

Neste exemplo, o primeiro notebook define uma função, reverse, que fica disponível no segundo notebook depois que você usa mágica %run para executar shared-code-notebook.

Notebook de código compartilhado
Exemplo de importação de notebook

Como ambos os Notebooks estão no mesmo diretório em workspace, use o prefixo ./ em ./shared-code-notebook para indicar que o caminho deve ser resolvido em relação ao Notebook em execução no momento. O senhor pode organizar o Notebook em diretórios, como %run ./dir/notebook, ou usar um caminho absoluto, como %run /Users/username@organization.com/directory/notebook.

Observação

  • %run deve estar em uma célula própria, pois executa todo o notebook em linha.

  • Você não pode usar %run para executar um arquivo Python e import as entidades definidas nesse arquivo em um Notebook. Para importar de um arquivo Python, consulte Modularizar seu código usando arquivos. Ou, empacote o arquivo em uma biblioteca Python, crie uma biblioteca Databricks dessa biblioteca Python e instale a biblioteca nos clusters que você usa para executar seu Notebook.

  • Quando o senhor usa %run para executar um Notebook que contém widgets, default a execução do Notebook especificado com os valores default do widget. O senhor também pode passar valores para widgets; consulte Usar widgets do Databricks com %run.

Use dbutils.notebook.run para começar um novo trabalho

Executa um notebook e retorna seu valor de saída. O método inicia um trabalho curto que é executado imediatamente.

Os métodos disponíveis na API do dbutils.notebook são run e exit. Ambos os parâmetros e valores de retorno devem ser strings.

run(path: String,  timeout_seconds: int, arguments: Map): String

O parâmetro timeout_seconds controla o tempo limite da execução (0 significa que não há tempo limite). A chamada para run lançará uma exceção se não for concluída dentro do tempo especificado. Se o site Databricks ficar inativo por mais de 10 minutos, a execução do Notebook falhará independentemente de timeout_seconds.

O parâmetro arguments define valores de widget do notebook de destino. Especificamente, se o notebook que você está executando tiver um widget chamado A e você passar um par key-value ("A": "B") como parte do parâmetro de argumentos para a chamada run(), a recuperação do valor do widget A será retornar "B". Você pode encontrar as instruções para criar e trabalhar com widgets no artigo Widgets do Databricks .

Observação

  • O parâmetro arguments aceita apenas caracteres latinos (conjunto de caracteres ASCII). Usar caracteres não ASCII retorna um erro.

  • Os trabalhos criados usando a API dbutils.notebook devem ser concluídos em 30 dias ou menos.

run Uso

dbutils.notebook.run("notebook-name", 60, {"argument": "data", "argument2": "data2", ...})
dbutils.notebook.run("notebook-name", 60, Map("argument" -> "data", "argument2" -> "data2", ...))

Passar dados estruturados entre o Notebook

Esta seção ilustra como passar dados estruturados entre notebooks.

# Example 1 - returning data through temporary views.
# You can only return one string using dbutils.notebook.exit(), but since called notebooks reside in the same JVM, you can
# return a name referencing data stored in a temporary view.

## In callee notebook
spark.range(5).toDF("value").createOrReplaceGlobalTempView("my_data")
dbutils.notebook.exit("my_data")

## In caller notebook
returned_table = dbutils.notebook.run("LOCATION_OF_CALLEE_NOTEBOOK", 60)
global_temp_db = spark.conf.get("spark.sql.globalTempDatabase")
display(table(global_temp_db + "." + returned_table))

# Example 2 - returning data through DBFS.
# For larger datasets, you can write the results to DBFS and then return the DBFS path of the stored data.

## In callee notebook
dbutils.fs.rm("/tmp/results/my_data", recurse=True)
spark.range(5).toDF("value").write.format("parquet").save("dbfs:/tmp/results/my_data")
dbutils.notebook.exit("dbfs:/tmp/results/my_data")

## In caller notebook
returned_table = dbutils.notebook.run("LOCATION_OF_CALLEE_NOTEBOOK", 60)
display(spark.read.format("parquet").load(returned_table))

# Example 3 - returning JSON data.
# To return multiple values, you can use standard JSON libraries to serialize and deserialize results.

## In callee notebook
import json
dbutils.notebook.exit(json.dumps({
  "status": "OK",
  "table": "my_data"
}))

## In caller notebook
import json

result = dbutils.notebook.run("LOCATION_OF_CALLEE_NOTEBOOK", 60)
print(json.loads(result))
// Example 1 - returning data through temporary views.
// You can only return one string using dbutils.notebook.exit(), but since called notebooks reside in the same JVM, you can
// return a name referencing data stored in a temporary view.

/** In callee notebook */
sc.parallelize(1 to 5).toDF().createOrReplaceGlobalTempView("my_data")
dbutils.notebook.exit("my_data")

/** In caller notebook */
val returned_table = dbutils.notebook.run("LOCATION_OF_CALLEE_NOTEBOOK", 60)
val global_temp_db = spark.conf.get("spark.sql.globalTempDatabase")
display(table(global_temp_db + "." + returned_table))

// Example 2 - returning data through DBFS.
// For larger datasets, you can write the results to DBFS and then return the DBFS path of the stored data.

/** In callee notebook */
dbutils.fs.rm("/tmp/results/my_data", recurse=true)
sc.parallelize(1 to 5).toDF().write.format("parquet").save("dbfs:/tmp/results/my_data")
dbutils.notebook.exit("dbfs:/tmp/results/my_data")

/** In caller notebook */
val returned_table = dbutils.notebook.run("LOCATION_OF_CALLEE_NOTEBOOK", 60)
display(sqlContext.read.format("parquet").load(returned_table))

// Example 3 - returning JSON data.
// To return multiple values, use standard JSON libraries to serialize and deserialize results.

/** In callee notebook */

// Import jackson json libraries
import com.fasterxml.jackson.module.scala.DefaultScalaModule
import com.fasterxml.jackson.module.scala.experimental.ScalaObjectMapper
import com.fasterxml.jackson.databind.ObjectMapper

// Create a json serializer
val jsonMapper = new ObjectMapper with ScalaObjectMapper
jsonMapper.registerModule(DefaultScalaModule)

// Exit with json
dbutils.notebook.exit(jsonMapper.writeValueAsString(Map("status" -> "OK", "table" -> "my_data")))

/** In caller notebook */

// Import jackson json libraries
import com.fasterxml.jackson.module.scala.DefaultScalaModule
import com.fasterxml.jackson.module.scala.experimental.ScalaObjectMapper
import com.fasterxml.jackson.databind.ObjectMapper

// Create a json serializer
val jsonMapper = new ObjectMapper with ScalaObjectMapper
jsonMapper.registerModule(DefaultScalaModule)

val result = dbutils.notebook.run("LOCATION_OF_CALLEE_NOTEBOOK", 60)
println(jsonMapper.readValue[Map[String, String]](result))

Trabalhando com erros

Esta seção ilustra como lidar com erros.

# Errors throw a WorkflowException.

def run_with_retry(notebook, timeout, args = {}, max_retries = 3):
  num_retries = 0
  while True:
    try:
      return dbutils.notebook.run(notebook, timeout, args)
    except Exception as e:
      if num_retries > max_retries:
        raise e
      else:
        print("Retrying error", e)
        num_retries += 1

run_with_retry("LOCATION_OF_CALLEE_NOTEBOOK", 60, max_retries = 5)
// Errors throw a WorkflowException.

import com.databricks.WorkflowException

// Since dbutils.notebook.run() is just a function call, you can retry failures using standard Scala try-catch
// control flow. Here, we show an example of retrying a notebook a number of times.
def runRetry(notebook: String, timeout: Int, args: Map[String, String] = Map.empty, maxTries: Int = 3): String = {
  var numTries = 0
  while (true) {
    try {
      return dbutils.notebook.run(notebook, timeout, args)
    } catch {
      case e: WorkflowException if numTries < maxTries =>
        println("Error, retrying: " + e)
    }
    numTries += 1
  }
  "" // not reached
}

runRetry("LOCATION_OF_CALLEE_NOTEBOOK", timeout = 60, maxTries = 5)

Execute vários notebooks simultaneamente

Você pode executar vários Notebook ao mesmo tempo usando construções padrão Scala e Python, como Threads (Scala, Python) e Futures (Scala, Python). O exemplo Notebook demonstra como usar essas construções.

  1. Faça o download dos quatro Notebooks a seguir. O Notebook está escrito em Scala.

  2. Importe os cadernos em uma única pasta no workspace.

  3. Execute o notebook Executar simultaneamente.

Notebook executar simultaneamente

Abra o bloco de anotações em outra guia

Execute em um notebook paralelo

Abra o bloco de anotações em outra guia

Teste de notebook

Abra o bloco de anotações em outra guia

Teste 2 de notebook

Abra o bloco de anotações em outra guia