Databricks Tips #3: Unity Catalog — el modelo de gobernanza que nadie implementa bien

Databricks Tips
Data Engineering
Unity Catalog
Namespace de 3 niveles, GRANTS heredados, linaje automático, row/column security y errores comunes de gobernanza.
Autor
Publicado

10 de marzo de 2026

Tercera entrega de Databricks Tips. Unity Catalog parece simple (catalog.schema.table), pero la mayoría de las implementaciones que vi tienen problemas de gobernanza desde el día uno.

El modelo de 3 niveles: más que un namespace

Listado 1: Namespace de 3 niveles en Unity Catalog
metastore
  └── catalog          (env o dominio)
       └── schema      (área funcional)
            └── table  (dato)

La primera decisión crítica: cómo organizar los catalogs. Hay dos escuelas:

Opción A: catalogs por ambiente

Listado 2: Opcion A: catalogs organizados por ambiente (dev, staging, prod)
CREATE CATALOG dev;
CREATE CATALOG staging;
CREATE CATALOG prod;

-- Mismos schemas en cada catalog
CREATE SCHEMA dev.sales;
CREATE SCHEMA staging.sales;
CREATE SCHEMA prod.sales;

Ventaja: el código cambia solo el catalog (${environment}.sales.orders), los schemas y tablas son idénticos.

Desventaja: necesitás 3x los permisos. Fácil cometer errores cruzando ambientes.

Opción B: catalogs por dominio

Listado 3: Opcion B: catalogs organizados por dominio de negocio
CREATE CATALOG sales;
CREATE CATALOG finance;
CREATE CATALOG marketing;

-- Schemas por etapa
CREATE SCHEMA sales.bronze;
CREATE SCHEMA sales.silver;
CREATE SCHEMA sales.gold;

Ventaja: alineado con Data Mesh. Cada dominio tiene ownership claro.

Desventaja: el deploy es más complejo (no podés simplemente cambiar un prefijo).

Mi recomendación: arrancá con la Opción A si tenés un equipo chico. Migrá a Opción B cuando tengas ownership claro por dominio.

GRANTS heredados: el error más común

Los permisos en Unity Catalog son heredables hacia abajo. Esto es poderoso pero peligroso:

Listado 4: GRANTS heredados: error comun vs. patron correcto a nivel schema
-- Este GRANT da acceso a TODAS las tablas presentes y FUTURAS del catalog
GRANT USE CATALOG ON CATALOG prod TO `data-analysts`;
GRANT USE SCHEMA ON CATALOG prod TO `data-analysts`;
GRANT SELECT ON CATALOG prod TO `data-analysts`;

-- Mejor: dar acceso solo al schema gold
GRANT USE CATALOG ON CATALOG prod TO `data-analysts`;
GRANT USE SCHEMA ON SCHEMA prod.gold TO `data-analysts`;
GRANT SELECT ON SCHEMA prod.gold TO `data-analysts`;

Regla de oro: nunca des SELECT ON CATALOG. Siempre bajá al nivel de schema o tabla.

Row & Column Level Security

Esto casi nadie lo usa pero es muy potente para cumplir con regulaciones (GDPR, PCI):

Listado 5: Row y column level security: masking de PII y filtros por region
-- Column masking: ocultar PII
CREATE FUNCTION prod.security.mask_email(email STRING)
RETURNS STRING
RETURN CASE
  WHEN is_member('pii-authorized') THEN email
  ELSE regexp_replace(email, '(.).*@', '$1***@')
END;

ALTER TABLE prod.gold.customers
ALTER COLUMN email SET MASK prod.security.mask_email;

-- Row filtering: cada equipo ve solo sus datos
CREATE FUNCTION prod.security.region_filter(region STRING)
RETURNS BOOLEAN
RETURN CASE
  WHEN is_member('global-access') THEN true
  WHEN is_member('latam-team') THEN region IN ('LATAM', 'BR', 'UY', 'AR')
  WHEN is_member('eu-team') THEN region IN ('EU', 'UK', 'DE', 'FR')
  ELSE false
END;

ALTER TABLE prod.gold.customers
SET ROW FILTER prod.security.region_filter ON (region);

Ahora un analista de LATAM hace SELECT * FROM prod.gold.customers y solo ve clientes de su región, con emails enmascarados. Sin cambiar una línea de código.

Linaje automático

Unity Catalog trackea linaje automáticamente para cualquier operación que pase por Spark. Para aprovecharlo:

Listado 6: Linaje automatico: buenas practicas para trazabilidad completa
-- Ver linaje de una tabla (qué tablas la alimentan)
-- Esto se ve en el UI, pero también vía API:
-- GET /api/2.1/unity-catalog/lineage/table-lineage

-- Para que el linaje sea completo:
-- 1. Usar tablas de Unity Catalog (no paths directos)
-- 2. Evitar df.write.parquet() - usar df.write.saveAsTable()
-- 3. En DLT, el linaje es automático al 100%

Tip: si tenés un pipeline que lee de un path S3 directo, Unity Catalog no puede trackear el linaje. Registrá el path como External Location y External Table:

Listado 7: External Location y tabla externa para habilitar linaje en S3
CREATE EXTERNAL LOCATION raw_s3
URL 's3://my-bucket/raw/'
WITH (STORAGE CREDENTIAL my_credential);

CREATE TABLE prod.bronze.events
USING DELTA
LOCATION 's3://my-bucket/raw/events/';

Volumes: archivos no tabulares

Desde DBR 13.3, Unity Catalog gobierna también archivos (CSV, JSON, modelos, artefactos):

Listado 8: Volumes: gestionar archivos no tabulares con gobernanza UC
-- Volume managed: Databricks gestiona el storage
CREATE VOLUME prod.ml.model_artifacts;

-- Volume external: vos controlás el storage
CREATE EXTERNAL VOLUME prod.raw.landing_zone
LOCATION 's3://my-bucket/landing/';

-- Subir archivos
-- PUT /api/2.0/fs/files/Volumes/prod/ml/model_artifacts/model.pkl

-- Usar en código
df = spark.read.csv("/Volumes/prod/raw/landing_zone/customers.csv")

Los Volumes tienen los mismos GRANTS que tablas (READ VOLUME, WRITE VOLUME), así que tenés gobernanza uniforme.

Checklist de gobernanza


Próxima semana: Structured Streaming — watermarks, triggers y cómo no perder datos en micro-batch.