Oh, mi dbt (herramienta de creación de datos)
Mi experiencia y un par de notas sobre el uso de esta magnífica herramienta durante un mes.
Tomas Peluritis
12 de julio·10 min de lectura
Introducción
Toda mi vida estuve trabajando con datos. De alguna manera suena dramático cuando lo digo así. Básicamente, he hecho algunos análisis y trabajo básico con SQL como analista de negocios, pero nada donde necesitaría plantillas. La llamada carrera de BI comencé en 2013. Como consultor y trabajando principalmente con MSSQL en múltiples proyectos similares, hubiera sido una bendición tener algo como dbt (o al menos saber sobre Jinja en ese momento…); descartámoslo como falta de experiencia.
Gracioso que yo tried dbt solo ahora. Si soy honesto contigo, lo he estado usando durante ~ mes, así que ten en cuenta que no soy un profesional, solo estoy difundiendo el conocimiento y compartiendo lo que he encontrado. Puede encontrar muchos otros artículos medianos sobre algunos aspectos específicos o ir directamente a la fuente de dbt.
Prerrequisitos
En primer lugar, para que esto funcione, necesitaría Docker. Si no está familiarizado con la ventana acoplable, promocionaré la publicación de mi blog anterior que escribí hace algún tiempo al respecto. Cuando trabajo en entornos creados por Docker, prefiero usar VSCode con su opción de contenedor de desarrollo, donde básicamente crea un entorno aislado con todas mis configuraciones, montajes, etc. Si realiza algún cambio en la imagen de Docker existente, puede elegir el opción reconstruir imagen, la compondrá y la abrirá con todos los cambios. Muy útil si está desarrollando cosas para que pueda omitir manualmente la ventana acoplable-componer.
En mi imagen de la ventana acoplable, he creado un archivo de composición de ventana acoplable específico con dos componentes: postgres simple: 13-alpine y python 3.8. Al elegir Python 3.8.11 en lugar de 3.9: hubo algunos problemas al intentar instalar dbt debido a problemas de compatibilidad. También estoy usando la opción de montaje en mi archivo docker-compose para pasar el archivo profiles.yml adecuado para este proyecto específico.
Postgres Dockerfile:
FROM postgres:13-alpine
ENV POSTGRES_PASSWORD=nopswd
ENV POSTGRES_DB db
COPY init.sql /docker-entrypoint-initdb.d/
En el init.sql archivo, acabo de crear una base de datos llamada db.
Python Dockerfile:
FROM python:3.8
COPY requirements.txt requirements.txt
RUN pip install -r requirements.txt
Nada sofisticado en requisitos, solo la biblioteca dbt.
Si ya tiene un entorno de producción con dbt y está configurando uno local, utilice siempre la misma versión de dbt que tiene en producción. Tuve problemas con la ejecución de dbt, pero mis colegas no. Causa raíz: todo el mundo estaba usando 0.19.0, e instalé la última versión en ese momento 0.19.2 y se produjeron algunos problemas de compatibilidad para dbt deps que teníamos en el archivo packages.yml.
Docker-compose, como mencioné, tiene algunas cosas más, pero nada sofisticado:
Quizás se pregunte por qué estoy abriendo el puerto 8001: es necesario para alguna función dbt que verá más adelante.
Empezando con dbt
Ok, ¿qué es este dbt? Quizás se esté preguntando. Básicamente, es una herramienta increíble para facilitar su parte de transformación en su flujo de ELT que le brinda linaje de datos, documentación y control total sobre las actualizaciones de datos si algunos datos subyacentes cambian en uno de los modelos en algún lugar intermedio. Realmente no quiero (y generalmente no me gusta) ir a los detalles del producto, ya que soy una persona más técnica, no un producto.
Ok, entonces hay un par de archivos importantes en dbt.
- profiles.yml: archivo donde configura todas las conexiones y cómo las va a usar
- dbt-project.yml: configuración específica para un proyecto dbt específico en el que tiene este archivo.
Repasemos el archivo profiles.yml:
Tenemos que tener un perfil predeterminado; aquí será donde se ejecutará todo si no se especifica nada más. Los diferentes perfiles le permitirán probar fácilmente las canalizaciones en diferentes entornos (es decir, prueba y producción):
# Running on default:
dbt run# Running on prod:
dbt run --profile prod# Running on default with specified profile:
dbt run --profile default
Después de jugar en VSCode abriendo mi carpeta en el contenedor de desarrollo, es interesante ver si todo funciona según lo previsto.
Como puede ver, tenemos un error en dbt_project.yml. Arreglemoslo.
Para simplificar y mantener la estructura dbt original, podemos inicializarlo. Para hacer esto, ejecutemos este comando:
dbt init MY_DBT_PROJECT_NAME
Ahora podemos ver cuál es la estructura que dbt nos espera y cómo funciona:
Comprobemos si todo lo demás funciona desde esta carpeta y los perfiles creados correctamente .yml
¡Gran éxito! Nuestro entorno es completamente funcional y está listo para que podamos comprobarlo todo.
Intentemos ejecutar dbt en el perfil predeterminado:
Vemos que tenemos dos modelos (que corresponden a dos archivos llamados my_first_dbt_model.sql y my_second_dbt_model.sql), pero ¿qué son estas pruebas? ¿De dónde vienen? Profundicemos en la carpeta del modelo.
Podemos ver que tenemos el archivo schema.yml con contenido
Podemos ver que tenemos dos columnas descritas más pruebas: una columna tiene que ser única y no nula.
Descubrí que mis colegas están creando un archivo yml para cada modelo. En mi opinión, esta es una mejor opción:
- visualmente se ve más claro
- no hay conflictos de fusión porque, lo más probable, ¡habrá un desarrollador por modelo!
Si miramos las consultas, son sencillas. Crea una tabla con 1 y nulo, crea una vista de la primera tabla donde id = 1. Pero espere, nuestras pruebas no dijeron que fallamos. ¡Tenemos un valor nulo! Eso es porque no tiene ningún dato para probar. Entonces, después de ejecutar nuestro modelo, debemos probarlo.
Para ejecutar pruebas:
dbt test --model example
La salida en la consola se verá así:
Claramente, podemos ver que hay algunos problemas de nuestra parte y tenemos que solucionarlos.
La solución es sencilla. Cambiemos de nulo a algún número y probemos de nuevo. Seguiríamos viendo el mismo estado si ejecutáramos directamente «dbt test» después de la corrección. No ejecutamos el modelo, por lo que los datos subyacentes no cambiaron. Necesitamos ejecutarlo y probarlo.
¡Hurra, acabamos de arreglar y ejecutar nuestros modelos con éxito!
Si ejecutamos dbt run tanto en dev / default como en prod, veríamos en DB todo esto
detalles de dbt
Carpeta de destino
Tras nuestro ejecutar dbt, teníamos esta carpeta creada. Su contenido:
Para mí, los archivos interesantes están en el compilado / ejecutar directorio. Si bajamos por la madriguera del conejo, podemos encontrar nuestras consultas SQL analizadas.
También podríamos compilar nuestros archivos ejecutando:
dbt compile
Ejecutar crearía o actualizaría archivos en compilado y correr carpetas. También tendrá pruebas compiladas SQL, para que pueda comprender qué se estaba ejecutando en sus pruebas especificadas.
Registros
Si ocurre algún problema y no está del todo claro de qué se trata, consulte logs / dbt.log. es decir, en el trabajo, recibí el mensaje «Error de base de datos: permiso denegado para la base de datos X». No tengo ni idea de qué permisos me faltaban. Obtuve un enlace a la página de depuración de dbt y mi colega dijo que revisara los registros. A partir de ahí, encontré los permisos que me faltaban.
Modelo incremental
Imaginemos que tenemos una situación en la que nuestros datos que residen en la base de datos son grandes y queremos agregar una carga incremental. Genéricamente, haríamos un script si existe una tabla; créelo desde cero, de lo contrario, insértelo y (o) actualícelo. Básicamente, tenemos partes repetitivas de código y tenemos que mantenerlo en dos lugares. No cumple con DRY (Don’t Repeat Yourself). Afortunadamente, dbt tiene una característica sorprendente como una carga incremental. Para ello, crearemos una tabla fuente adicional usando Mockaroo. Ejecuté 01_mock_users_data.sql en mi base de datos local de Postgres. También hice un pequeño cambio y convertí la columna created_at para que fuera una columna de marca de tiempo en lugar de una fecha.
Creó un modelo simple para usar la macro is_incremental:
Si lo ejecutamos ahora y verificamos target / run:
create table "db"."dbt_dev"."mock_users"
as (
select * from "db"."operational_db"."mock_users_data"
);
Corramos 02_more_mock_users_data.sql y vuelva a ejecutar dbt. ¡En target / run, podemos ver diferentes salidas!
select * from "db"."operational_db"."mock_users_data"
-- this filter will only be applied on an incremental run
where created_at >= (select max(created_at) from "db"."dbt_dev"."mock_users")
Aunque hay un matiz aquí, se ejecutará exactamente mediante los filtros que especificó. La primera ejecución será para TODO el historial; la próxima ejecución será solo para filas nuevas. Es posible que la consulta inicial ni siquiera finalice o encuentre otros problemas en el camino (tiempo de espera, algunos límites estrictos en el tiempo de ejecución de la consulta, etc.). Por lo tanto, podría crear un filtro de límite superior en el que solo tomaría un par de días / semanas / mes y actualizarlo fácilmente de esta manera en varios lotes. Aunque es tedioso y tendrías que ejecutarlo manualmente para recuperarlo.
Macros + insert_by_period
Descargo de responsabilidad: insert_by_period funciona solo con Redshift, dbt-vault created vault_insert_by_period funciona en Snowflake. Básicamente, solo estoy explicando mi viaje, lo que probé y comprobé a lo largo del camino.
Mencioné en la carga incremental «Macros», tal vez se pregunte qué es. Es un código personalizado, que se ejecuta para agregar alguna funcionalidad faltante o lógica más compleja. Es decir, antes mencionado una carga incremental tediosa. En nuestro caso, es una inserción condicional simple que cargaría nuestros datos iniciales en múltiples lotes. Puede consultarlo en la discusión original sobre esta macro aquí. Con todo, está incluido en el paquete dbt-utils. Podemos importar especificándolo en el archivo packages.yml. La versión 0.7.0 no era compatible con mi versión dbt de 0.19.2 (solicité 0.20, que es solo un candidato de lanzamiento en el momento en que se estaba escribiendo esta publicación de blog), así que usé 0.6.4.
y podemos instalar dependencias con
dbt deps
Si seguimos toda la información de la versión para nuestro caso de uso de Postgres, no funcionará, ya que, como está escrito en los comentarios, ¡solo es adecuado para el desplazamiento al rojo! Después de esto, entré en la madriguera del conejo, verifiqué dbt-vault, hice algunos ajustes y creé mi propia macro usando comentarios en GitHub. Pero supongo que soy demasiado nuevo en macros, un tema avanzado, y no pude hacerlo funcionar. Tendré que profundizar en esto más tarde.
Modelo de instantánea
El nombre no explica realmente lo que hace. Al menos para mí, una instantánea significa el estado actual de los datos. Aunque en el caso de dbt, si creamos un modelo de instantánea (sugieren ponerlo en la carpeta «instantáneas»), tendremos SCD tipo 2 (por cierto, escribí un artículo sobre SCD2 en Spark hace algún tiempo, que cubre qué es un SCD).
Así que usemos los mismos datos de usuarios simulados para este ejemplo. Agreguemos la columna updated_at y hagamos que coincida con la columna created_at (03_update_at.sql). Sigamos el ejemplo básico de dbt docs y ejecutemos instantánea de dbt. Podemos ver cómo se ve la instantánea (solo interesado en columnas recién agregadas):
Podemos ver que tenemos dbt_scd_id y dbt_valid_from y dbt_valid_to, correspondiente a los cambios. Ejecutemos 04_change_some_names.sql y ejecutemos instantánea de dbt.
Bien, básicamente, configuramos lo que es único y dbt se encarga del resto. Eso habría sido útil muchas veces para mí. Mirando en la carpeta target / run / snapshots, podemos ver que nuestro código de instantánea también se generó para nosotros.
Básicamente, podemos ver que creó una tabla temporal y luego hizo todas las comparaciones por nosotros.
Generar documentos
Linaje de datos y …