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

Код-ревью: PySpark-задача отчёта по заказам

Вопрос

Это ежедневная PySpark-задача. Найдите проблемы производительности и корректности.

orders = spark.read.parquet("s3://dwh/orders/")
users = spark.read.parquet("s3://dwh/users/")
countries = spark.read.parquet("s3://dwh/countries/")

@F.udf("string")
def norm_country(code):
    return code.strip().upper()

df = orders.join(users, "user_id").join(countries, "country_code")
df = df.withColumn("country", norm_country(F.col("country_code")))
df = df.filter(F.col("order_date") == run_date)

print("rows:", df.count())
top = df.groupBy("country").agg(F.sum("amount").alias("revenue"))
rows = df.collect()
top.coalesce(1).write.mode("append").parquet("s3://marts/revenue_by_country/")

Коротко

Главное: фильтр до джойна и идемпотентная запись. Дальше встроенные функции вместо UDF, broadcast справочника, отбор колонок, без лишних действий и без collect. coalesce(1) для маленького агрегата допустим, но для больших результатов он сводит работу к одной задаче.

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

  1. Производительностьстрока 11
    Фильтр по дате стоит после джойнов: соединяются все заказы за всю историю. Фильтровать заказы по дате (и партиции) до джойна, тогда Spark прочитает только нужную партицию.
  2. Производительностьстрока 6
    Python UDF для strip().upper(): есть встроенные F.upper(F.trim(...)), UDF медленнее и скрывает логику от оптимизатора.
  3. Ошибкастрока 7
    UDF упадёт на NULL в country_code: у None нет метода strip.
  4. Производительностьстрока 9
    Справочник стран крошечный: стоит явно указать F.broadcast(countries), а из users выбрать только нужные колонки до джойна.
  5. Производительностьстрока 13
    count() перед агрегацией запускает весь план второй раз; при нескольких действиях нужен cache() или отказ от лишнего действия.
  6. Ошибкастрока 15
    collect() всех строк на driver: при реальном объёме driver упадёт по памяти, а результат нигде не используется.
  7. Ошибкастрока 16
    mode("append") неидемпотентен: перезапуск за тот же день задвоит выручку. Нужна перезапись партиции дня (dynamic partition overwrite) и колонка даты в результате.

Разбор

Главное: фильтр до джойна и идемпотентная запись. Дальше встроенные функции вместо UDF, broadcast справочника, отбор колонок, без лишних действий и без collect. coalesce(1) для маленького агрегата допустим, но для больших результатов он сводит работу к одной задаче.

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

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