What dpb generate writes¶
dpb generate writes one file per data product into the output folder. The file is named after the data product folder. What it holds depends on the orchestrator setting:
Orchestrator |
The file |
Described in |
|---|---|---|
|
An Airflow 3 DAG |
This page |
|
A Python module with Dagster definitions |
|
|
A Python script that runs the data product once |
Whatever the orchestrator, the file holds the same four parts: the build of each table, the audit of its quality rules, the SQL that makes the output ports, and a signal that the output ports were updated. The generated files do not import datapatchbay, so the orchestrator needs neither datapatchbay nor its dependencies installed.
For tables built by dbt, dpb generate also writes dbt files into the repo. See the dbt reference. The dpb CLI describes the command itself.
The Airflow DAG¶
There is one DAG per data product, named after the data product folder, with the tag datapatchbay.
- Tasks
Each table gets a build task,
build_<table>, followed by an audit task,audit_<table>, when it has quality rules that are notnone. Task order followsneeds, and a table that needs another waits for that table’s audit. After the last table comesapply_access, which applies the views and grants. The DAG ends withpublish_outputs.- Schedule
A data product with a source input port runs on that port’s schedule. All its source input ports must share one schedule. A data product that reads another data product’s output port is triggered when that port is updated. With both, it runs on the schedule once the ports are updated.
- Output ports
Each output port is an Airflow asset named after the port.
publish_outputsmarks the output ports as updated when the DAG succeeds.- Tables built by dbt
They share one task,
build_dbt, that runsdbt buildon the worker. See dbt on the worker.
Build tasks and runtimes¶
The runtime of a table says where its build function runs. Tables with different runtimes can sit in one DAG.
workerThe task loads the entrypoint file from the data product folder and calls the function with the table’s
params, on the Airflow worker. It finds the folder from the DAG file’s own location, so the output folder and the data product folders must keep their relative places when deployed. Deploying the whole repo does that. See Deploy the generated DAGs.dockerandkubernetesThe task runs the function in a container from the table’s resolved
image, as aDockerOperatororKubernetesPodOperator. DAG files that use them needapache-airflow-providers-dockerorapache-airflow-providers-cncf-kuberneteson Airflow. The container runspython -cwith a short loader, the entrypoint file, the function and theparamsas JSON. So the image needspython, the build code, and the data product folder as its working directory (ADR-0016). Thecontainersetting is added to the task as written, which is where a team supplies environment variables or secrets. See Run build code in a container.
The params must be plain YAML values: a date has to be quoted.
Audit tasks¶
The audit task of a table runs every quality rule as a query that counts the rows that break it. The audit code is part of the generated file.
A broken rule with severity
errorfails the task. The message names the rule, the table and the column, and all brokenerrorrules are named together.A broken rule with severity
warningis written to the task log, and the task succeeds.A rule with severity
noneis skipped. A table whose rules all have severitynonegets no audit task.A bare rule name takes the default severity, which cascades from
datapatchbay.yamldown to the table.
Tables are audited in their data product’s private schema. accepted_values compares values with the column’s type, so write numbers and booleans unquoted for numeric and boolean columns. A sql rule’s SELECT may end in a semicolon.
For a table built by dbt, the rules run as dbt data tests during the dbt build instead, and the table gets no audit task.
Views and grants¶
The apply_access task makes each output port real. Build code writes every output table to the data product’s private schema. An output port holds only generated views over those tables, one view per table, with the columns of the table file. A data product without output ports gets no apply_access task.
- On Postgres
A PII column shows its value to PII readers and NULL to everyone else, and readers are granted access to the views. The statements can be re-applied on every run, and removing a reader removes that reader’s access. The roles must already exist in the warehouse: the SQL grants to them and never creates them (ADR-0017). The views are dropped and created again on every run, so a view in another schema that depends on an output port’s view blocks the apply.
- On DuckDB
DuckDB has no roles and no grants, so readers and PII readers are accepted and applied to nothing, and a PII column shows NULL in the view, to everyone. The schema of each output port is dropped and created again on every apply. DuckDB is meant for local development and tests. The same files work on both warehouses, so a team can develop on DuckDB and deploy on Postgres by changing the
warehousesetting (ADR-0021).
Connections¶
dpb manages no connection. The views, grants and audits run on the orchestrator, through a connection that already exists there. The connection setting only names it, warehouse by default. Build code connects by itself.
The Airflow connection for DuckDB¶
With warehouse: duckdb, install the official apache-airflow-providers-duckdb package on the workers and create a connection of type duckdb with the id that the connection setting names. Its Host field is the path of the DuckDB file, for example /data/shop.duckdb. With no path, the hook opens an in-memory database that is gone when the task ends, so the views would never persist. As an environment variable:
AIRFLOW_CONN_WAREHOUSE='{"conn_type": "duckdb", "host": "/data/shop.duckdb"}'
DuckDB is a file that one process writes at a time. The file must be on a path that every worker running the DAG and the build code can reach, and the build code opens the same file by itself. So let the DAG run one run at a time.