Код-ревью Middle ETL, ELT и Airflow спрашивают часто обновлено

Код-ревью: DAG ежедневной выгрузки заказов

Вопрос

Это DAG ежедневной загрузки заказов в хранилище. Найдите проблемы.

from datetime import datetime, timedelta
from airflow import DAG
from airflow.operators.python import PythonOperator
import psycopg2, pandas as pd

conn = psycopg2.connect("host=prod-db user=etl password=Qwerty123")
TABLES = pd.read_sql("select table_name from etl_tables", conn)

def load_orders(**ctx):
    day = (datetime.now() - timedelta(days=1)).date()
    df = pd.read_sql(f"select * from orders where created_at::date = '{day}'", conn)
    dwh.execute("insert into dwh.orders select * from df")

with DAG("orders", start_date=datetime.now(), schedule="@daily") as dag:
    PythonOperator(task_id="load", python_callable=load_orders)

Коротко

Почти все проблемы из урока в одном файле: секреты в коде, тяжёлый код на верхнем уровне, период от текущего времени, неидемпотентная вставка, плавающая startdate.

Что должен найти ревьюер

  1. Безопасностьстрока 6
    Пароль от боевой базы в коде DAG. Подключения хранятся в Airflow Connections или во внешнем хранилище секретов.
  2. Производительностьстрока 7
    Запрос к базе на верхнем уровне файла: выполняется при каждом разборе DAG планировщиком. Логику надо унести внутрь задачи.
  3. Ошибкастрока 10
    Период «вчера» от текущего времени: ретрай на следующий день и бэкфилл обработают не тот день. Брать data_interval_start и data_interval_end из контекста.
  4. Ошибкастрока 11
    Фильтр по created_at теряет изменения уже существующих заказов (смену статуса). Нужен инкремент по updated_at или CDC.
  5. Производительностьстрока 11
    created_at::date = ... не использует индекс по created_at; лучше полуинтервал created_at >= start AND created_at < end. Плюс чтение с боевой базы вместо реплики.
  6. Ошибкастрока 12
    Неидемпотентная вставка: при ретрае данные за день задвоятся. Нужна перезапись партиции дня или upsert.
  7. Ошибкастрока 14
    start_date=datetime.now(): динамическая дата старта делает расписание непредсказуемым. Нужна фиксированная дата и явный catchup.
  8. Стильстрока 15
    Нет ретраев, таймаута и проверок качества после загрузки; ошибки будут молча доходить до витрин.

Разбор

Почти все проблемы из урока в одном файле: секреты в коде, тяжёлый код на верхнем уровне, период от текущего времени, неидемпотентная вставка, плавающая start_date. Правильная версия берёт подключение из Connections, читает реплику за data_interval по updated_at, пишет в staging и перезаписывает партицию дня, а после загрузки проверяет объём и ключи.

Ответьте сами, а не прочитайте

В тренажёре вопрос попадёт в повторения и вернётся, когда начнёт забываться. Плюс билеты с зачётом: 20 вопросов, не больше 2 ошибок.