Ejecución de un cuaderno de Databricks desde otro cuaderno

Importante

Para la orquestación de cuadernos, use Trabajos de Databricks. Para escenarios de modularización de código, use archivos de área de trabajo. Solo debe usar las técnicas descritas en este artículo cuando el caso de uso no se puede implementar mediante un trabajo de Databricks, como para bucles de cuadernos a través de un conjunto dinámico de parámetros, o si no tiene acceso a archivos del área de trabajo. Para obtener más información, consulte trabajos de Databricks y compartir código.

Comparación de %run y dbutils.notebook.run()

El comando %run permite incluir otro cuaderno dentro de un cuaderno. Puede usar para %run modularizar el código, por ejemplo, colocando funciones auxiliares en un cuaderno independiente. También puede usarlo para concatenar cuadernos que implementen los pasos de un análisis. Cuando se usa %run, el cuaderno al que se llama se ejecuta de inmediato, y las funciones y variables definidas en él están disponibles en el cuaderno que hace la llamada.

La API dbutils.notebook es un complemento a %run, porque permite pasar parámetros a un cuaderno y devolver valores. Esto le permite crear flujos de trabajo y canalizaciones complejos con dependencias. Por ejemplo, puede obtener una lista de archivos de un directorio y pasar los nombres a otro cuaderno, lo que no es posible con %run. También puede crear flujos de trabajo if-then-else basados en valores devueltos o llamar a otros cuadernos mediante rutas de acceso relativas.

A diferencia de %run, el método dbutils.notebook.run() inicia un nuevo trabajo para ejecutar el cuaderno.

Estos métodos, como todas las API de dbutils, solo están disponibles en Python y Scala. Sin embargo, puede usar dbutils.notebook.run() para invocar un cuaderno de R.

Uso de %run para importar un cuaderno

En este ejemplo, el primer cuaderno define una función, reverse, que está disponible en el segundo cuaderno después de usar el comando magic %run para ejecutar shared-code-notebook.

Shared code notebook

Notebook import example

Dado que ambos cuadernos están en el mismo directorio del área de trabajo, use el prefijo ./ en ./shared-code-notebook para indicar que la ruta de acceso debe resolverse en relación con el cuaderno que se está ejecutando actualmente. Puede organizar los cuadernos en directorios, como %run ./dir/notebook, o usar una ruta de acceso absoluta como %run /Users/username@organization.com/directory/notebook.

Nota:

  • %run debe estar en una celda por sí mismo, porque ejecuta todo el cuaderno en línea.
  • No puede usar %run para ejecutar un archivo de Python y usar la opción import en las entidades definidas en ese archivo en un cuaderno. Para importar desde un archivo de Python, consulte Modularizar el código mediante archivos. O bien, empaquete el archivo en una biblioteca de Python, cree una biblioteca de Azure Databricks a partir de esa biblioteca de Python e instale la biblioteca en el clúster que usa para ejecutar el cuaderno.
  • Si usa %run para ejecutar un cuaderno que contiene widgets, de manera predeterminada el cuaderno especificado se ejecuta con los valores predeterminados del widget. También puede pasar valores a widgets; consulte Uso de widgets de Databricks con %run.

dbutils.notebook API

Los métodos disponibles en la API dbutils.notebook son run y exit. Tanto los parámetros como los valores devueltos deben ser cadenas.

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

Ejecute un cuaderno y devuelva su valor de salida. El método inicia un trabajo efímero que se ejecuta de inmediato.

El parámetro timeout_seconds controla el tiempo de espera de la ejecución (0 significa que no hay tiempo de espera): la llamada a run produce una excepción si no finaliza dentro del tiempo especificado. Si Azure Databricks está fuera de servicio durante más de 10 minutos, se produce un error en la ejecución del cuaderno, independientemente de timeout_seconds.

El parámetro arguments establece los valores de widget del cuaderno de destino. En concreto, si el cuaderno que está ejecutando tiene un widget denominado A, y se pasa un par clave-valor ("A": "B") como parte del parámetro de argumentos a la llamada run(), la recuperación del valor del widget A devolverá "B". Puede encontrar las instrucciones para crear y trabajar con widgets en el artículo Widgets de Databricks.

Nota:

  • El parámetro arguments solo acepta caracteres latinos (juego de caracteres ASCII). El uso de caracteres no ASCII produce un error.
  • Los trabajos creados con la API dbutils.notebook se deben completar en 30 días o menos.

run Uso

Python

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

Scala

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

run Ejemplo

Supongamos que tiene un cuaderno denominado workflows con un widget denominado foo que imprime el valor del widget:

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

Ejecutar dbutils.notebook.run("workflows", 60, {"foo": "bar"}) produce el siguiente resultado:

Notebook with widget

El widget tenía el valor que se pasó mediante el uso de dbutils.notebook.run(), "bar", en lugar del valor predeterminado.

exit(value: String): void Sale de un cuaderno con un valor. Si llama a un cuaderno mediante el método run, este es el valor devuelto.

dbutils.notebook.exit("returnValue")

Llamar a dbutils.notebook.exit en un trabajo hace que el cuaderno se complete correctamente. Si quiere provocar un error en el trabajo, genere una excepción.

Ejemplo

En el ejemplo siguiente, se pasan argumentos a DataImportNotebook y se ejecutan cuadernos diferentes (DataCleaningNotebook o ErrorHandlingNotebook) en función del resultado de DataImportNotebook.

if-else example

Cuando se ejecuta el código, aparece un vínculo al cuaderno en ejecución:

Link to running notebook

Haga clic en el vínculo del cuaderno Trabajo de cuaderno n.º xxxx para ver los detalles de la ejecución.

Result of ephemeral notebook run

Pasar datos estructurados

En esta sección se muestra cómo pasar datos estructurados entre cuadernos.

Python

# 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))

Scala

// 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))

errores

En esta sección, se muestra cómo controlar los errores.

Python

# 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)

Scala

// 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)

Ejecución de varios cuadernos simultáneamente

Puede ejecutar varios cuadernos al mismo tiempo mediante construcciones estándar de Scala y Python, como Subprocesos (Scala, Python) y Futuros (Scala, Python). Los cuadernos de ejemplo muestran cómo usar estas construcciones.

  1. Descargue los 4 cuadernos siguientes. Los cuadernos están escritos en Scala.
  2. Importe los cuadernos en una sola carpeta del área de trabajo.
  3. Ejecute el cuaderno Ejecutar simultáneamente.

Ejecutar simultáneamente cuadernos

Obtener el cuaderno

Ejecutar cuadernos paralelos

Obtener el cuaderno

Cuaderno de prueba

Obtener el cuaderno

Cuaderno de prueba-2

Obtener el cuaderno