Executar um notebook Databricks a partir de outro notebook

Importante

Para a orquestração de notebooks, use Databricks Jobs. Para cenários de modularização de código, use os arquivos workspace. O senhor só deve usar as técnicas descritas neste artigo quando o seu caso de uso não puder ser implementado com o uso de um Databricks Job, como, por exemplo, para repetir o Notebook em um conjunto dinâmico de parâmetros, ou se não tiver acesso aos arquivosworkspace . Para obter mais informações, consulte programar e orquestrar fluxo de trabalho e compartilhar código.

Comparação de %run e dbutils.notebook.run()

O comando %run permite incluir outro Notebook em um Notebook. Você pode usar %run para modularizar seu código, por exemplo, colocando funções de suporte em um Notebook separado. Você também pode usá-lo para concatenar Notebook que implementam os passos em uma análise. Quando você usa %run, o Notebook chamado é executado imediatamente e as funções e variáveis definidas nele ficam disponíveis no Notebook chamador.

A API do dbutils.notebook é um complemento do %run porque permite a você transmitir parâmetros e retornar valores de um notebook. Isso permite que você crie fluxos de trabalho e pipelines complexos com dependências. Por exemplo, você pode obter uma lista de arquivos em um diretório e passar os nomes para outro notebook, que não é possível com %run. Também é possível criar fluxos de trabalho de if-then-else com base em valores de retorno ou chamar outros notebooks usando caminhos relativos.

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

Esses métodos, como todas as APIs dbutils, estão disponíveis apenas em Python e Scala. No entanto, você pode usar dbutils.notebook.run() para invocar um notebook R.

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 os dois notebooks estão no mesmo diretório no 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. Você pode organizar notebooks 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 você usa %run para executar um Notebook que contém widgets, por default a execução Notebook especificada com os valores default do widget. Você também pode passar valores para widgets; consulte Usar widgets do Databricks com %run.

dbutils.notebook API

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

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

O parâmetro timeout_seconds controla o tempo limite da execução (0 significa sem tempo limite): a chamada para run lança uma exceção se não for concluída dentro do tempo especificado. Se o Databricks estiver 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", ...))

run Exemplo

Suponha que você tenha um bloco de anotações chamado workflows com um widget chamado foo que imprima o valor do widget:

dbutils.widgets.text("foo", "fooDefault", "fooEmptyLabel")
print(dbutils.widgets.get("foo"))

A execução dbutils.notebook.run("workflows", 60, {"foo": "bar"}) produz o seguinte resultado:

Notebook com widget

O widget tinha o valor que você passou usando dbutils.notebook.run(), "bar", em vez do padrão.

exit(value: String): void Saia de um notebook com um valor. Se você chamar um notebook utilizando o método run, este é o valor retornado.

dbutils.notebook.exit("returnValue")

Chamar dbutils.notebook.exit em um trabalho faz com que o notebook seja concluído com sucesso. Se você quiser causar falha no trabalho, lance uma exceção.

Exemplo

No exemplo a seguir, você passa argumentos para DataImportNotebook e executa notebooks diferentes (DataCleaningNotebook ou ErrorHandlingNotebook) com base no resultado de DataImportNotebook.

exemplo if-else

Quando o código é executado, aparece uma tabela com um link para o site Notebook:

Link para o notebook em execução

Para view os detalhes da execução, clique no link começar time na tabela. Se a execução estiver concluída, o senhor também pode view os detalhes da execução clicando no link End time.

Resultado da execução efêmera do bloco de anotações

Passe dados estruturados

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, you can 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 4 notebooks a seguir. Os notebooks são gravados 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