Visualizaciones innovadoras en Power BI
2 diciembre, 2020
Optimización de rendimiento en Power BI
9 diciembre, 2020

El volumen de datos a los que actualmente tenemos acceso es impresionante, pero gracias a la tecnología Big Data somos capaces de capturarlos, almacenarlos y analizarlos, independientemente del formato que tengan. Nada que no sepamos ya. Sin embargo, la necesidad de servir el dato confiable, limpio y orientado a negocio, tal como hacen los Data Warehouse, sigue siendo plenamente actual porque básicamente, el concepto sigue siendo válido aunque haya (afortunadamente) evolucionado la tecnología.

Por lo tanto, la necesidad de trasformar el dato desde su estado más puro (raw data) hasta aquél donde el usuario de negocio puede ser capaz de consultar para extraer la información que le interesa (processed data) sigue formando parte de prácticamente cualquier arquitectura de datos. Son las famosas herramientas ETL las encargadas de realizar esta crítica tarea, aunque hoy en día lo podemos encontrar casi con cualquier combinación de estas letras: ELT, EL, EL-T… e incluso solo la T. Y como buen tema tecnológico, también en estas herramientas hay un buen debate: ¿son mejor aquellas en las que combinas elementos propios (filtros, ordenamientos, agrupaciones, etc.) o las que simplemente actúan de orquestadores y delegan la transformación de los datos en instrucciones que ejecuta el motor de base de datos? Puntos a favor y en contra hay en ambos bandos, como debe ser:

  • En el caso de las primeras, permite visualizar el flujo de transformación directamente, lo que las hace más legibles respecto las segundas.
  • Sin embargo, estas segundas permiten aprovechar la potencia de los motores (y aquí estamos hablando de Spark, Synapse, Big Query, Redshift, Snowflake…) y encima aplicarlo allá donde está el dato.

A esto se puede replicar que es posible ejecutar código SQL dentro de las herramientas del primer tipo, pero entonces perdemos la legibilidad que teníamos al principio.

Dificultad a la hora de integrar DevOps en procesos ETL

Pero más allá de este debate, hay otro casi incluso más importante. ¿Podemos conseguir integrar de verdad este tipo de procesos ETL / ELT en las prácticas de DevOps que tan buenos resultados están dando en otros escenarios? Porque hacerlo no significa añadir el proyecto a un repositorio de código fuente, implica más cosas. Esta es una de las razones por las que se empiezan a hacer este tipo de procesos usando librerías de Python (desde el clásico Pandas a otros más específicos como petl o Bonobo), pero sigue requiriendo cierto es fuerzo el introducirlas dentro del proceso de CI/CD, además de la dependencia de la evolución de una librería externa y, por descontado, con la necesidad de que dentro de tu equipo haya gente que se sienta cómodo con Python.

Y es que, aunque hoy en día parece que todos tenemos que ser expertos en cualquier tecnología que aparezca en el horizonte, no todo el mundo se siente cómodo en un entorno de desarrollo programando con un lenguaje como Java, Scala o Python (por nombrar los más comunes en estos tiempos de Big Data). Más si tu experiencia está más por el lado de las bases de datos relacionales y multidimensionales, donde la lengua franca suele ser SQL. En ese caso, lo normal es encontrarse cómodo con herramientas ETL como SQL Server Integration Services, Talend, Fivetran, Matillion, Informatica, etc. Y también lo normal es que quien tenga más experiencia a la hora de implementar estos procesos sean justamente profesionales con este perfil, con lo que el problema sigue estando ahí.

Este es un panorama que se suele dar en muchas empresas porque realmente no había muchas alternativas. Hasta que en Kabel descubrimos una herramienta que unía ambos mundos, el de desarrollos ágiles siguiendo la práctica de DevOps y el mundo del ETL con su SQL.

Data Building Tool – DBT

Si nos vamos a la página del producto donde aparece la definición oficial podemos leer lo siguiente:

Your entire analytics engineering workflow

Analytics engineering is the data transformation work that happens between loading data into your warehouse and analyzing it. dbt allows anyone comfortable with SQL to own that workflow.

Siendo sinceros, no es algo que atraiga especialmente la atención, más allá que deja claro que está pensado para los que se sienten cómodos con SQL. Bueno, vamos a darle otra oportunidad. Si avanzamos un poco en la página, podemos leer lo siguiente:

“dbt is a development environment that speaks the preferred language of data analysts everywhere—SQL. With dbt, analysts take ownership of the entire analytics engineering workflow, from writing data transformation code to deployment and documentation.”

Esto resulta más interesante, porque además de confirmarnos que se trabaja con SQL, nos indica que nos permite tomar el control del flujo completo, desde el desarrollo propio de las transformaciones que hay que implementar hasta su despliegue y documentación. Nos vamos acercando a DevOps o, más concretamente, a DataOps. ¿A que va mereciendo más la pena el dedicarle un tiempo?

¿Pero qué es, exactamente, DBT?

Primero, especificar que estamos hablando de une herramienta escrita en Python y que es completamente open source. En esencia es una línea de comandos que se centra en la “T” del proceso ETL. Es decir, una vez que los datos están en el repositorio (BigQuery, Snowflake, Redshift…) permite escribir las transformaciones en puro SQL como si fueran módulos siguiendo por tanto uno de los principios básicos de software engineering: modularidad. Pero no es el único, también podemos crear test de calidad de datos, crear entornos y, al ser básicamente un proyecto de desarrollo cuyo lenguaje es SQL, implementar todo el ciclo de CI/CD como haríamos en cualquier otro proyecto.

Parafraseando lo que los propios creadores escribieron en su blog en sus inicios, “DBT es una herramienta que te ayuda a escribir y ejecutar los trabajos de trasformación de datos que son lanzados en tu propio warehouse. La única función de DBT es coger ese código, compilarlo a SQL y ejecutarlo contra tu base de datos”. Y es que, cuando habla de “compilar” a SQL significa que estamos escribiendo código SQL estándar mezclado con pequeños snippets de Jinja y luego él se encarga de transformarlo a las particularidades del motor especificado en nuestro proyecto. Los que se soportan en el momento de escribir este artículo son:

  • Postgres
  • Redshift
  • BigQuery
  • Snowflake
  • Presto
  • Spark

Pero ya la comunidad está ampliando este número para incluir:

  • SQL Server
  • Azure Synapse
  • Exasol
  • Oracle
  • Dremio

Pero si aun así no os ha llamado suficientemente la atención, ¿qué os parecería si os dijera que permite generar la documentación completa y que luce tan chula como esto?

Primeros pasos en DBT

Sí, es cierto que hemos estado hablando que se trabaja con SQL aunque mezclado con Jinja, y además que DBT es Python. ¿Significa esto que tengo que ser experto en este lenguaje? Bueno, si bien es cierto que hoy en día sin duda es casi imprescindible sentirte cómodo con él, puedo asegurar que para trabajar con DBT se requieren nociones muy básicas. Tan básicas como tener instalado Python en el equipo de trabajo y un IDE como pueda ser Visual Studio Code. Y ya. No es necesario nada más para empezar a realizar tus transformaciones en DBT, como veremos a continuación.

Los archivos con los que vamos a trabajar son comunes en un proyecto de desarrollo, lo cual permite acercarnos al mundo de software engineering aunque vengamos de otra parte. Estos archivos son:

  • Archivos .md que usan el lenguaje de marcado markdown y que permiten introducir documentación adicional a la que realiza DBT por sí solo
  • Archivos .yml que usa el formato YAML para seguir el concepto de Configuration-As-Code
  • Archivos .sql que permiten definir nuestros conjuntos de datos a través de instrucciones SQL

Abrimos VSCode y nos situamos en la carpeta donde queremos crear el proyecto. En una ventana de terminal, instalamos DBT simplemente con pip install tal como indica la propia documentación:

pip install dbt

Como cualquier otro proyecto de desarrollo, DBT requiere de una serie de archivos de configuración y de carpetas para su gestión. Lo podríamos hacer a mano, pero mejor sacar provecho de una de las funcionalidades y dejarle a él que lo inicialice. ¿Cómo? Muy sencillo, con

dbt init kabeldemo

Nos mostrará una salida similar a la siguiente:

kabeldemo es el nombre del proyecto que acabamos de crear, y la herramienta ya nos recomienda hacer una serie de acciones, la primera de ella abrir el archivo profiles.yml que se encuentra en la ruta indicada para ver cómo se configuran los entornos donde estableceremos la conexión a la base de datos:

La configuración de la conexión depende del motor pero, y este es otro de los aspectos destacables de DBT, para esto tenemos la documentación a nuestro rescate. Primero para saber qué es exactamente el papel que juega el archivo profiles.yml y luego para saber por ejemplo cómo hay que parametrizar la conexión a nuestro, por ejemplo SQL Server, Snowflake o lo que tengamos.

Todas estas carpetas que nos crea son la recomendación que DBT nos propone, pero es algo que podemos configurar a nuestro antojo a través de la configuración establecida en el archivo dbt_proyect.yml

De este archivo, lo que deberíamos cambiar en un proyecto real sería:

  • name, para darle un nombre al proyecto más descriptivo. No tiene por qué llamarse igual que la carpeta donde están los archivos del proyecto.
  • profile, para que en base al archivo yml que se indique en la ejecución (el predeterminado o uno específico del proyecto), hacer referencia al nombre del perfil que se ha creado (de forma predeterminada, default). Como se puede ver, es muy fácil especificar los diferentes aspectos que cambian de un entorno a otro simplemente cambiando de perfil al que apunta nuestro proyecto, el cual a su vez está descrito en este archivo de configuración.

La estructuración de un proyecto DBT es muy flexible y depende mucho de la política que se quiera seguir, pero no está de más leerse la forma en la que los creadores del producto lo usan dentro de sus propios proyectos y que dejaron descrita en https://discourse.getdbt.com/t/how-we-structure-our-dbt-projects/355.

La importancia del modelo

De todas estas carpetas, la más importante es models porque es con la que más vamos a trabajar al ser donde definimos los modelos. Estos no son más que archivos donde definimos nuestros conjuntos de datos a través de código SQL. Podemos también enriquecerlas a través de Jinja para hacer el código más reutilizable o simplificar la instrucción, pero no es obligatorio, son cosas que podemos dejar para cuando nos sintamos más cómodos con DBT. De hecho, para hacernos más suave esa curva de aprendizaje la herramienta nos proporciona un ejemplo dentro de la carpeta models\example:

Este ejemplo simula un modelo denominado my_first_dbt_model que, en este caso, construye un dataset llamado source_data con datos inventados. En un proyecto real, lo normal sería hacer referencia a un conjunto de datos en bruto que tuviéramos en nuestro sistema. Pero más allá de eso, vemos que nuestro modelo es código SQL puro. La línea 10 es un pequeño código en Jinja en la que se define el modo en que queremos que se materialice (materializations) esa instrucción en nuestro repositorio, las cuales pueden ser view, table, incremental o ephemeral.

Si abrimos el otro modelo es donde empezamos a ver la magia de DBT:

Vayamos por partes:

  • Vemos que aquí no se define el modo de materializar esta instrucción, intuyendo por lo tanto que está cogiendo un valor predeterminado de algún sitio. En este caso lo cogerá del valor que hemos establecido dentro del archivo dbt_project.yml, justo en las últimas líneas confirmamos que lo que hará DBT será construir una vista
  • Lo segundo es que estamos haciendo referencia al modelo que hemos creado en el paso anterior a través del código {{ ref(‘my_first_dbt_model’) }}. Esto le permite a DBT identificar las dependencias entre los diferentes procesos e ir montando el DAG de ejecución que luego, como ya vamos aprendiendo, ejecutará en nuestro motor. Y es justamente la misma razón por la que es posible tener la documentación con las dependencias generada automáticamente, ese compilador que interpreta esas referencias y que, en definitiva, nos va a permitir tener una trazabilidad de las trasformaciones que se aplican a nuestros datos en todo el pipeline de ejecución.

En este ejemplo hemos visto cómo se construye un primer dataset de datos simulados, pero lo normal es que partamos de datos que existen en nuestro repositorio. Estos datos estarán en bruto, y para referenciarlos podemos definirlos dentro de nuestra carpeta models dentro de un archivo de configuración que contenga el apartado sources. Algo como lo que muestra la siguiente imagen:

De este modo estamos documentando, en el mismo proyecto de desarrollo, el esquema de las tablas que contienen los datos en bruto. Algo que también podemos hacer con nuestros modelos, ya que si nos fijamos un poco, podemos observar que dentro de la carpeta example/models hay un fichero denominado schema.yml que contiene (como no podía ser de otro modo) la definición del esquema de nuestros modelos:

Pasemos a la acción

Quedan aún unas cuantas cosas que comentar, pero con lo que tenemos hasta ahora podemos empezar a probar algunos comandos específicos. Aunque no está soportado (aún) oficialmente, vamos a hacer las pruebas con una base de datos Azure SQLDB para sentirnos en un ambiente familiar. Para ello realizamos los siguientes pasos:

  • primero ejecutamos el comando ps1 que tenemos en nuestro repositorio, cambiando los valores de las variables para adecuarlo a nuestro entorno (principalmente subscriptionId)
  • nos instalamos el driver ODBC para SQL Server, disponible aquí
  • luego, desde el entorno de Python nos instalamos la librería necesaria ejecutando pip install dbt-sqlserver
  • y por último, nos vamos a crear un archivo yml en el directorio raíz del proyecto (a la altura de dbt_project.yml) con la siguiente configuración:
dbt-sqlserver:
  target: dev
  outputs:
    dev:
      type: sqlserver
      driver: 'ODBC Driver 17 for SQL Server'
      server: url de la conexión #copiar la url de conexión a la base de datos
      port: 1433
      user: admin user #sustituir por el usuario que hayamos puesto
      password: pwd #sustituir por la contraseña que hayamos puesto
      database: AdventureWorksLT
      schema: DBT

Bien, lo primero que vamos a comprobar es si nuestro proyecto está bien configurado y nuestra conexión a la base de datos es correcta. Para ello ejecutamos el siguiente comando

dbt compile --project-dir <ruta donde se encuentra el archivo dbt_project.yml>  --profiles-dir <ruta donde se encuentra nuestro archivo profiles.yml>

Si todo está configurado de forma predeterminada, este comando generará una carpeta target/compiled/<modelo> con las instrucciones SQL que lanzará en nuestro motor de base de datos.

Ahora vamos a crear un pequeño modelo como muestra representativa de las capacidades de DBT, creando una pequeña tabla final en base a unas tablas de origen de la base de datos AdventureWorksLT que hemos creado en Azure. En el repositorio de código fuente que enlaza este artículo tenemos el detalle, del que aquí solo resumimos lo importante:

  • La carpeta models/adventureworks nos permite crear un nuevo modelo. Por hacer un paralelismo, sería un el DataFlow de un paquete de SQL Server Integration Services
  • Al haber creado este nuevo modelo, tenemos que incluirlo dentro del proyecto. Hemos editado el archivo dbt_project.yml para añadirlo adecuadamente:

  • También hemos especificado que el perfil que vamos a usar de forma predeterminada es el que habíamos creado en un paso anterior:

 

  • Hemos creado un archivo denominado adventureworks.yml donde incluimos un apartado sources para definir las tablas sobre las que queremos hacer referencia en nuestro modelo como los datos en bruto de los que partir

  • Hemos creado un archivo denominado schema.yml en el que definimos el esquema de la tabla que vamos a generar. En este caso una pequeña tabla de hechos con el total de las ventas por pedido

  • También hemos creado un archivo FactSales.sql donde definimos la instrucción que genera los datos para esta entidad, un simple SELECT con un GROUP BY combinando las tablas de cabecera y detalle pedido

Si nos fijamos en el snippet de Jinja que está al principio de este script, podemos observar tres propiedades interesantes:

  • materialized = ‘table’ nos permite sobreescribir el comportamiento predeterminado del modelo, que tal como hemos especificado en yml habíamos establecido a “view”
  • as_columnstore = false nos permite especificar que no queremos crear la tabla como columnstore, ya que hemos creado la base de datos como S0 (no incluye esta funcionalidad)

Listo. Ya tenemos configurado nuestro (pequeño) modelo. Primero, vamos a ver lo que hemos creado con la documentación que nos genera el producto automáticamente. Lanzamos el siguiente comando

dbt docs generate –-profiles-dir <ruta donde está el archivo profiles.yml>

Una vez generados la documentación, podemos verla en nuestro navegador lanzando el siguiente comando:

dbt docs serve –-profiles-dir <ruta donde está el archivo profiles.yml>

Y la verdad es que luce espectacular, tal como se puede ver en la siguiente imagen:

Directamente en el código tenemos todo lo que permite generar la documentación: orígenes, modelos, dependencias, tests, código SQL “original” y “compilado”, e incluso un gráfico para ver visualmente las dependencias de nuestro modelo. Y repito, todo incluido en nuestro proyecto, dentro de un repositorio de código fuente para lo que necesitemos. Simplemente genial.

¿Y para ejecutarlo? Pues  tan sencillo como ejecutar la siguiente instrucción

dbt run --models adventureworks --project-dir <ruta del archivo dbt_project.yml>  --profiles-dir <ruta del archivo profiles.yml>

Al lanzarlo, DBT nos devuelve la siguiente salida:

Que, en la práctica, lo que ha hecho ha sido construir la tabla FactSales a partir de los datos que le hemos indicado

Done it!!

Conclusiones

Nos han faltado muchas cosas por ver, y seguro que también quedan muchas dudas: ¿cómo serían modelos incrementales?¿qué pasa si mis transformaciones son más complejas? ¿cómo se integra realmente con DevOps? ¿cómo migro mis actuales procesos? ¿cómo organizo mi proyecto?

Pero estoy seguro que una de las mayores dudas es ¿cómo ejecuto estos modelos? Ya la propia documentación da algunas ideas, ya que puede integrarse perfectamente con los orquestadores más comunes como Airflow, Prefect o Dagster. Pero si no tienes experiencia con ellos, no hay problema porque DBT también es una librería de Python en sí misma, de modo que puede ser ejecutado desde una Azure Function (o cualquier otro servicio similar disponible en los otros cloud providers)

En cualquier caso, este era un artículo introductorio para presentar la herramienta, la cual creemos que tiene mucho futuro. La lista de empresas que están entre las que la usan es cada vez mayor, así como la comunidad que lo soporta y, en general, todo lo que hay alrededor de ella: desde la propia comunidad hasta los fondos (Sequoia Capital) que han conseguido recientemente, hasta el evento que han organizado y que tan buena pinta tiene.

En Kabel seguiremos publicando artículos sobre estas tecnologías emergentes que creemos pueden cambiar la forma de hacer las cosas, así que… stay tuned!!

 

 

 

Si queréis poneros en contacto con nosotros, podéis hacerlo en info@kabel.es
También podéis seguirnos en Twitter, LinkedIn, Facebook

 

Licencia de Creative Commons

Este obra está bajo una licencia de Creative Commons Reconocimiento-NoComercial 4.0 Internación 

Compártelo: Share on FacebookTweet about this on TwitterShare on LinkedInPin on Pinterest

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

NEWSLETTER