← Назад к вопросам

С какими трудностями сталкивалась при разворачивании всего стэка на проекте

1.0 Junior🔥 121 комментариев
#Архитектура и проектирование#Опыт и soft skills

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI21 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

# Трудности при разворачивании Data Stack: От опыта

Контекст

На своём последнем проекте (PayFlow) я отвечал за архитектуру всего data stack с нуля. От выбора железа до production pipeline — вся ответственность. Встретился со многими сложностями.

1. Выбор облака и миграция инфраструктуры

Проблема

Компания начинала с AWS (привычка) и PostgreSQL на EC2. По мере роста:

  • Warehouse query'ки становились медленнее
  • PostgreSQL не масштабировался горизонтально
  • Затраты на компьютинг росли, но мы упирались в пределы вертикального масштабирования

Решение (с трудностями)

Вариант 1: Перейти на Redshift

  • Плюсы: AWS native, JDBC/ODBC, знакомый SQL
  • Минусы: дорого, нужна предварительная денормализация

Вариант 2: BigQuery (Google Cloud)

  • Плюсы: serverless, супер быстрый для big queries, автоматическое масштабирование
  • Минусы: выход из AWS экосистемы, миграция 2TB данных, новая кривая обучения

Вариант 3: Snowflake

  • Плюсы: cross-cloud, лучше всех работает с разными источниками
  • Минусы: дорого на масштабе, новое

Что выбрали: BigQuery

Трудности при миграции

  1. Data Transfer: 2TB данных = 4 дня полной передачи. Попытались:

    • Первая попытка: gsutil cp — ~24 часа, потом timeout
    • Вторая попытка: AWS DataTransfer + Google Cloud partner network — дорого
    • Успешная попытка: S3 → GCS (через Google Storage Transfer Service) → BigQuery, параллельная загрузка
  2. Schema differences:

    -- PostgreSQL
    CREATE TABLE events (
        id SERIAL PRIMARY KEY,
        created_at TIMESTAMP DEFAULT NOW()
    );
    
    -- BigQuery (нет типа SERIAL)
    CREATE TABLE events (
        id INT64,
        created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP()
    );
    -- Пришлось переписать загрузку и переделать primary key логику
    
  3. Query rewrites: Много запросов написано под PostgreSQL:

    -- PostgreSQL (нет)
    SELECT * FROM events LIMIT 10 OFFSET 100;
    
    -- BigQuery (нужно переписать)
    SELECT * FROM events LIMIT 10 OFFSET 100;
    -- На самом деле это работает, но оно неэффективно в BQ
    -- Нужно использовать:
    SELECT * FROM (
        SELECT *, ROW_NUMBER() OVER (ORDER BY id) as rn 
        FROM events
    ) WHERE rn > 100 LIMIT 10;
    
  4. Стоимость: BigQuery дешевле для запросов (платим за отсканированные данные), но дороже для streaming ingestion ($6.25 за TB вместо $3.75 за TB в классическом).

2. Kafka Stability

Проблема

Желали real-time события (пользователь кликнул → сразу обновляется аналитика). Развернули Kafka.

Трудности

  1. Broker стабильность:

    • На 2-й день production'а один брокер отстал (network issue)
    • Consumer group перестал читать (ISR issues)
    • Пришлось ручками rebalance'ить, потеряли несколько часов данных
    • Решение: настроить proper replication factor (3) и monitoring на ISR
  2. Consumer lag:

    Утро: 0 lag
    Полдень: 5млн messages lag (очередь растёт)
    Вечер: спотыкается
    

    Оказалось, что Spark Streaming job'ы медленнее чем rate ingestion.

    Решение:

    • Partitioned Kafka topic'и по user_id (параллельная обработка)
    • Scaling Spark executors (2 → 8)
    • Оптимизация transform кода (убрали лишние shuffle)
  3. Message ordering: Хотели гарантию: если пользователь A отправил 100 событий, они придут в том же порядке.

    Проблема: с multiple партициями порядок не гарантирован.

    Решение: partition_key = user_id, тогда все события одного user'а идут в одну partition.

3. Spark Job Reliability

Проблема

Spark job'ы крашились случайно.

Трудности

  1. Out of Memory errors:

    # Плохо: загружаем всё в память
    df = spark.read.parquet("s3://data/*")
    df.join(other_df, "user_id").collect()  # OOM!
    
    # Хорошо: используем Broadcast для маленьких датасетов
    from pyspark.sql.functions import broadcast
    df.join(broadcast(small_df), "user_id")
    

    Решение: профилирование Spark jobs с spark-submit --verbose

  2. Shuffle deadlocks: Когда shuffle'им 10TB данных, иногда один executor зависал и весь job валился.

    Решение: увеличить spark.sql.shuffle.partitions (100 → 500) и добавить timeout'ы

  3. State management in Streaming:

    # Проблема: состояние теряется при crash
    query = df.groupBy("user_id") \
        .agg(count("*").alias("event_count")) \
        .writeStream.start()
    
    # Решение: использовать checkpoint directory
    query = df.groupBy("user_id") \
        .agg(count("*").alias("event_count")) \
        .writeStream \
        .option("checkpointLocation", "s3://checkpoints/events") \
        .start()
    

4. Airflow DAG Complexity

Проблема

Начали с простых DAG'ов. По мере роста стало 50+ DAG'ов с зависимостями.

Трудности

  1. Dependencies nightmare:

    load_events → transform_events → aggregate_metrics
                ↓
             validate_data → build_warehouse
    

    Если transform упал, validate тоже не запускается, но warehouse зависит от validate?

    Решение: явно определить dependencies через downstream_list и upstream_list

  2. Variable scope issues:

    # Плохо: DAG использует переменные
    default_args = {
        'retries': RETRIES,  # Где это определено? В global scope?
    }
    
    # Хорошо: явное определение
    RETRIES = Variable.get("default_retries", 3)
    default_args = {'retries': int(RETRIES)}
    
  3. SLA monitoring: DAG должен закончиться за 4 часа. Но что если задержка на 2 часа по upstream?

    Решение: SLA на конкретные tasks, не на DAG в целом

5. Data Quality Framework

Проблема

Часто в warehouse попадали неправильные данные. Аналитики жаловались: "Это число не совпадает с production!".

Трудности

  1. When to fail?

    Column X имеет 1% NULL'ов.
    Это нормально? Или нужно падать?
    

    Решение: начать с warning'ов, потом перейти к hard fails после сбора baseline метрик

  2. Great Expectations overhead:

    # Great Expectations добавляет ~10 минут на каждый check
    # для датасета в 1TB
    
    expectation_suite = context.create_expectation_suite(...)
    # Sample 1% данных вместо 100%
    validator = context.get_validator(
        expectation_suite=expectation_suite,
        data_context=context,
        sampling_method="random_sample",  # Важно!
        sampling_kwargs={"n": 100000}
    )
    
  3. Reconciliation with source: Warehouse говорит: 1M пользователей. Но Production DB: 1.1M.

    Решение: добавить reconciliation task который сравнивает counts

6. Cost Management

Проблема

Расходы на BigQuery выросли с $2K на $15K/месяц за 6 месяцев.

Трудности

  1. Unoptimized queries:

    -- Плохо: сканирует весь датасет (100TB)
    SELECT * FROM events WHERE DATE(timestamp) >= '2024-01-01';
    
    -- Хорошо: используем partition (сканирует 1 день = 500GB)
    SELECT * FROM events 
    WHERE _PARTITIONTIME >= '2024-03-01';
    

    Решение: дать аналитикам доступ к BigQuery logs и показать им стоимость на query

  2. Streaming inserts = дорого:

    Batch insert: $3.75 за TB
    Streaming insert: $6.25 за TB
    

    Переходили на батч каждый час вместо real-time — сэкономили 40%

  3. Unused tables: Были таблицы которые никто не использовал, но копились год.

    Решение: TTL на все tables (180 дней) + monthly cleanup скрипт

7. Schema Evolution

Проблема

Требование: добавить новую колонку currency в таблицу orders.

Трудности

  1. Backward compatibility:

    # Со старыми данными где нет currency
    df_new = df_old.withColumn('currency', lit('USD'))
    
    # Но что если в старых данных currency был null?
    # Нужна миграция всех старых rows
    
  2. Version control: Как отследить что изменилось в schema?

    Решение: dbt с версионированием

  3. Cascading changes: Изменил колонку в silver layer → нужно переписать 5 downstream моделей

    Решение: сначала написать тесты, потом менять

8. Authentication и Security

Проблема

Developer'ы нужен доступ к данным, но нельзя дать root permissions.

Трудности

  1. IAM complexity:

    Нужно: read-only доступ к production таблицам, read-write к dev таблицам
    
    Google Cloud имеет 50+ roles. Какую выбрать?
    

    Решение: создать custom roles для типичных use cases

  2. PII masking: Должны логировать какие developer'ы доступили какие данные

    Решение: Cloud Audit Logs + автоматические alerts

Главные learnings

✅ Что помогло

  1. Incremental migration: Не переходил вся сразу, а тестировал 1 месяц данных параллельно
  2. Документирование: Каждое решение записывал (почему Kafka, почему BigQuery) — спасло при onboarding
  3. Automation: Скрипты для мониторинга, alerting, reconciliation
  4. Early involvement: Просил feedback от аналитиков и ML engineer'ов до финального деплоя

❌ Что бы переделал

  1. Planning: Не планировал заранее масштабы (как будет выглядеть через год)
  2. Testing: Первые 3 месяца тестов было мало — много bagов попало в production
  3. Consultation: Выбрал BigQuery без обсуждения с командой, хотя кто-то предпочитал Snowflake

Итого

Разворачивание data stack — это 20% технология, 80% опыт и trial-and-error. Главное: правильно выбрать инструменты для масштаба, зафиксировать решения и быстро адаптироваться при смене требований.