Перейти к содержимому
TelegramGitHub

Data Quality Fundamentals, глава 6: Как чинить проблемы с качеством данных в масштабе

Главная мысль: Найти проблему в данных мало. Нужен повторяемый процесс: обнаружить инцидент, разобраться, исправить, провести разбор без поиска виноватых и не дать инциденту повториться. Авторы берут этот процесс из DevOps и SRE. Они добавляют роль incident commander и пять шагов поиска первопричины.

Что делать, когда алерт уже сработал? В главе 5 мы строили надёжные пайплайны. Эта глава о том, как справляться с поломками, которые всё равно случаются. Темы главы:

  • чему учит DevOps и что такое жизненный цикл надёжности данных;
  • пять этапов работы с инцидентом: обнаружение, реакция, root cause analysis, исправление, разбор;
  • профилактика: тесты, CI/CD, discovery, observability;
  • роль incident commander, оценка серьёзности, SLO и SLI;
  • кейс PagerDuty.

Жизненный цикл инцидента с данными: Detection, Response, Root cause analysis, Resolution, Blameless postmortem, затем Prevent

Схема показывает путь одного инцидента: пять первых этапов реактивные, а этап Prevent проактивный и замыкает цикл.

Пятница, 17:00. Финансовый директор пишет, что в квартальном отчёте неверные числа. Семь часов уходит на поиск причины: в исходной таблице изменилась схема. Что дальше? Для большинства команд найденная причина — только начало пути к доверию.

Авторы предлагают не изобретать процесс заново. В DevOps есть непрерывный цикл из восьми этапов: Plan, Code, Build, Test, Release, Deploy, Operate, Monitor. Многие приёмы из разработки ПО уже прижились в данных: тесты, SLA, распределённые архитектуры. Но качество данных мы всё ещё чаще чиним реактивно, и это мешает масштабировать аналитику.

Google SRE Handbook напоминает: если реакцию на инцидент не продумать заранее, в реальной аварии принципы быстро забываются. Incident management — это процесс, в котором вы находите проблему, ищете первопричину, исправляете, анализируете и предотвращаете повторение.

Франсиско Альберини, бывший product manager в Segment, вспоминает: когда данные ломались, он писал одному инженеру, который помнил всё. Если инженер был занят, сам он часами вручную проверял тысячи таблиц. Это не масштабируется.

Авторы предлагают data reliability life cycle, вариант цикла DevOps для данных. В нём три группы действий:

  • Detect: собирать данные, мониторить, поднимать алерты.
  • Resolve: оценить ущерб, сообщить о нём, восстановить данные.
  • Prevent: подправить процессы, оценить результат, пересмотреть подход.

В рабочем виде это пять шагов: обнаружение, реакция, root cause analysis (RCA), исправление и blameless postmortem.

Данные нужно тестировать до production. Но плохие данные всё равно просочатся, и тогда нужны мониторинг и алерты. Их задают вручную порогами или получают из решения для поиска аномалий (anomaly detection) либо для data observability: оно сравнивает данные с историей и вашими правилами.

Ключевая часть обнаружения — поиск отклонений в «столпах здоровья» (объём, свежесть, схема и распределение). Он ценнее, когда охватывает всю платформу: хранилище, озеро, ETL, BI. Хороший детектор настраивают так, чтобы было меньше шума и ложных срабатываний (баланс precision и recall). Как строить детекторы, мы разбирали в главе 4.

Авторы предупреждают: команды надеются, что поиск аномалий решит всё. Это не так. Во-первых, инциденты нельзя «решить» навсегда: пайплайны будут ломаться, а null появляться в неудобный момент. Во-вторых, если полагаться только на обнаружение, оно становится единственной точкой отказа. Нужны ещё реакция, исправление и профилактика. Anomaly detection — инструмент, а не серебряная пуля.

Авторы поясняют это сравнением. Водитель говорит механику: «С машиной что-то не так». Механик часами ищет разболтанную свечу. Алерт «что-то сломалось» похож на эту фразу. Руководитель аналитики из крупной компании сказал Барр Мозес: алерт нужен как начало, но важнее знать, что сломалось, кого это задело, почему и где.

Хорошая реакция начинается и заканчивается коммуникацией. Большую часть коммуникации можно подготовить заранее и автоматизировать через PagerDuty и Slack. Команда пишет два вида документов:

  • runbook: инструкции по сервисам и частым проблемам;
  • playbook: пошаговый процесс действий при инциденте.

В них есть ссылки на код и документацию, а главное — распределение ролей при аварии. В SRE для этого есть дежурство (on-call): роли закрепляют по сервисам, обычно на час, день или неделю. Кроме исполнителя (incident responder) есть incident commander. Он раздаёт задачи, собирает информацию и сообщает о проблеме потребителям выше и ниже по потоку.

Метаданные с бизнес-контекстом показывают, какие команды затронуты. Вместе со сквозным lineage (см. главу 7) это даёт карту связей за минуты, а не за часы ручной рисовки.

В теории RCA проста: несколько SQL-запросов по сегментам. На практике инцидент затрагивает много таблиц, иногда сотни. Например, данные устарели: причиной может быть задача в очереди, тайм-аут, партнёр, не доставивший файл, или смена расписания, из-за которой задача пропала из DAG.

По опыту авторов, почти всё сводится к трём источникам:

  • неожиданное изменение во входных данных;
  • изменение логики трансформации (ETL, SQL, Spark);
  • проблема среды: ошибка запуска, права, сбой инфраструктуры, смена расписания.

Причина редко одна. Чаще это сочетание причин, и из него можно извлечь уроки для процессов. Помогает метод «пять почему» (five whys) из Amazon: назовите проблему, спросите, почему она случилась, и запишите ответ. Решите, корень ли это: можно ли было предотвратить или заранее обнаружить причину? Если причина в ошибке человека, почему она была возможна? Повторяйте, считая причину новой проблемой, пока не найдёте корни. Если проверки не поймали проблему заранее, это признак того, что они недостаточны.

Дальше авторы дают пять шагов RCA на примере сломанного дашборда клиентов.

Пять шагов поиска первопричины: lineage, код, данные, среда запуска, коллеги

Под каждым шагом показано, что нужно команде.

Найдите самый верхний по потоку узел, где проблема уже видна: там она и началась. В плохой день это источник за много шагов от дашборда. Тогда придётся идти вверх по DAG, а потом делать backfill. У всех, кто разбирает проблему, должен быть доступ к свежему lineage с BI-отчётами, ML-моделями и reverse ETL. Хорошо, если есть lineage на уровне полей.

Найдите, как эту таблицу создаёт ETL или ELT. Какой код обновлял её последним и когда? Менялась ли логика? Были ли ручные записи (ad hoc writes) или backfill? Нужно знать код таким, каким он был при последнем обновлении.

Неверны все записи или часть? Какой период? Какой сегмент, например только Android или заказы из Франции? Есть ли новые или пропавшие сегменты? Менялась ли схема? Не стали ли доллары центами, а время PST временем EST?

Полезный приём: смотреть, как другие поля связаны с аномалией. Команда Лиора увидела, что в важной таблице users вырос процент null в user_interests. Этот процент разложили по полю source (Twitter, Facebook, Google). Возможны два вывода:

  • выросла доля записей из Twitter, а у него null и так больше. Тогда это сезонность или удачная кампания;
  • доля Twitter прежняя, но null внутри Twitter вырос. Тогда это ошибка обработки данных Twitter, и искать нужно там.

Оба варианта объясняют рост null, но ведут к разным действиям. Статистика помогает, но это лишь часть RCA. Этот пример мы воспроизведём в практике ниже.

Если данные в порядке, посмотрите на среду, где работают ETL/ELT-задачи (например, dbt). Были ли ошибки, задержки старта или долгие запросы? Менялись ли права, сеть, инфраструктура? Не убрали ли задачу из расписания? Для этого нужен доступ к логам и расписанию, например в Airflow.

Прежде чем писать в Slack, спросите себя: были ли похожие случаи и как их решали? Кто владеет набором данных? Кто его использует? Ответы дают метаданные о владельцах и использовании, а также история прошлых инцидентов.

Авторы подводят итог: поломку редко можно свести к одной причине. Пять шагов делают RCA устойчивой практикой, а инженер, который «знает всё», получает передышку.

Когда проблема найдена и ущерб понятен, её исправляют и сообщают участникам следующие шаги. Иногда это происходит ещё до RCA. Простейший вариант: остановить пайплайн или модель и перезапустить. Но данные ломаются по миллиону причин, поэтому часто нужна диагностика.

Часто решений два. «Начальное» — остановить пайплайн через circuit breaker (см. главу 3). «Финальное» — постоянное исправление причины. Статус инцидента публикуйте в выделенном канале Slack, wiki, документе или задаче JIRA. После исправления сообщите пострадавшим о дальнейших шагах и назначьте postmortem через несколько дней.

Бывший SRE из Box и Slack сказал авторам: при любом инциденте виновата система, а не человек, написавший код. Хорошая система устойчива к ошибкам людей. То же верно для данных: пайплайны должны быть отказоустойчивыми и учитывать как known unknowns, так и unknown unknowns.

После исправления и RCA команда проводит кросс-функциональный postmortem: встречу и документ с хронологией, участниками, технологиями и последствиями. Он нужен, чтобы рассказать о результатах и чтобы действовать упреждающе. Рекомендации авторов:

  • Говорите об обучении, а не о вине. Разбор должен быть blameless. Поиск виноватых не укрепляет доверие. Когда цель — обучение и улучшение, проще заранее принять организационные и технические меры.
  • Оцените готовность к следующим инцидентам. Обновите runbook, мониторинг, алерты. В runbook запишите, кто чем владеет и когда запускаются задачи.
  • Документируйте разбор и делитесь им. Без записи знания уходят вместе с инженером.
  • Пересмотрите SLA. SLA, SLI и SLO, разумные полгода назад, сегодня могут не подходить. Команда должна первой это заметить и сообщить потребителям.

Без понимания, как и почему возникает data downtime, устойчивость не улучшить.

Процесс управления инцидентами — только первый шаг. Тесты закрывают known unknowns. А unknown unknowns? По оценке авторов, тесты ловят около 20% таких проблем. Остальные 80% находят недовольные коллеги, которые пишут про «сломанный отчёт».

Detect и Resolve — реактивные этапы цикла. Prevent — проактивный, он сокращает время на «тушение пожаров». Для него авторы советуют data reliability stack: слои тестов, CI/CD, discovery и observability.

Слои надёжности данных на этапах Ingestion, Transformation, Analysis: Testing, CI/CD, Discovery, Observability

Testing стоит ближе к входу и трансформации, а CI/CD и версионирование защищают изменения кода. Discovery показывает владельцев и использование, а observability следит за всем путём. Слои подходят любой архитектуре: централизованной, распределённой или гибридной.

Часто на дата-инженера ложится не только исправление, но и приоритизация, и коммуникация, а на вопрос «кто ведёт этот инцидент?» нет ответа. За SLA отвечает вся команда, но решения должен принимать конкретный человек. Это incident commander. Его задачи:

  • рано и часто сообщать об инциденте команде и заинтересованным лицам;
  • вести список затронутых объектов и аномалий;
  • координировать работу и раздавать задачи;
  • распространять runbook и playbook;
  • оценивать серьёзность и влияние.

Командиров назначают по очереди на неделю или день либо закрепляют за наборами данных. Это в основном вопрос культуры: автоматизация и контроль состояния данных дают многое, а остальное — обучение. Каждый командир делает четыре шага.

Всё зависит от устройства команды данных.

Две схемы: встроенная data-команда в каждом бизнес-юните и центральная data-команда для всех юнитов

  • Встроенная (decentralized) команда. Специалисты работают в бизнес-юнитах: например, маркетинговая команда владеет всеми маркетинговыми пайплайнами. Здесь реакцию делегировать проще, а такие люди подчиняются руководителю юнита или CDO (chief data officer).
  • Центральная (centralized) команда. Она подчиняется CDO и принимает инциденты от многих юнитов. Маршрутизацию алертов надо продумать заранее. Плюсы и минусы обоих вариантов мы увидим в главе 8.

В любом случае заведите отдельные каналы Slack для пайплайнов и пригласите туда заинтересованных людей. Многие команды настраивают также PagerDuty или Opsgenie.

Часть изменений безвредна, например ожидаемая смена схемы. Другие опасны: в важной таблице было 10 000 строк, стало 1 000. Помечайте статус инцидента: fixed, expected, investigating, no action needed, false positive. Метки помогают оценить серьёзность и сообщать обновления в нужные каналы.

А если сломался ненужный или устаревший набор? Авторы называют это phantom data: после разбора выясняется, что данные месяцами никто не использовал, а команда потратила часы. Чтобы понять, что важно, команды смотрят lineage и аналитику использования (operational analytics): какие пайплайны ломаются чаще, сколько стоит хранение. Это дают решения data observability. Если сломались данные для финансовой отчётности, приоритет очень высокий. Если нет, переходите к другим задачам.

Здесь помогает runbook: он закрепляет зоны ответственности и сокращает дублирование работы. Когда «кто что делает» ясно, заведите страницу статуса, чтобы заинтересованные видели обновления в реальном времени. Авторы видели два способа делегирования:

  • Дежурный. Один человек отвечает за все инциденты в период. Иногда это постоянная работа, иногда ротация раз в неделю.
  • Закреплённые таблицы. Самый частый вариант. Каждый отвечает за инциденты по своим таблицам или отчётам наряду с обычной работой.

Подойдёт любой вариант: выбирайте по структуре команды, ресурсам и приоритетам.

Commander не устанавливает SLO, но отвечает за их выполнение. Пример Slack: клиентам тарифа Plus и выше обещают 99,99% доступности за квартал и не больше 10 часов плановой недоступности. Если обещание нарушено, клиенты получают кредиты.

Пояснение: SLA — внешнее обещание клиенту. SLO — внутренняя цель команды. SLI — метрика, по которой это измеряют. В тексте главы эти слова местами смешаны, мы держим такое разделение.

Какие SLI брать, зависит от задачи. Авторы напоминают три метрики из главы 5:

  • N: число инцидентов с конкретным объектом. Вы часто не влияете на него из-за внешних источников, но измерять стоит.
  • Time to detection (TTD): сколько проходит от проблемы до алерта. Без хорошего обнаружения это недели или месяцы, а «тихие ошибки» ведут к дорогим решениям.
  • Time to resolution (TTR): сколько проходит от алерта до исправления.

Снижая TTD и TTR, команда делает системы надёжнее.

В инцидентах время и враг, и союзник. Команды пишут много тестов, а пайплайны всё равно ломаются. Командир с понятными процессами, небольшой автоматизацией и поддержкой организации заметно повышает надёжность.

PagerDuty помогает более чем 16 800 компаниям в 90 странах выполнять SLA по доступности. Авторы поговорили с Ману Раджем, старшим директором по платформе данных и аналитике. Его команда называется DataDuty.

Задача платформы — давать клиентам доверенные данные в любое время. Клиентами команды служат почти все отделы компании. В стеке есть PagerDuty (команда «ест свой собственный корм»), Snowflake, Fivetran, Segment, Mulesoft, AWS, Monte Carlo, Databricks. Недавно добавили data observability на основе ML.

Как у многих SaaS-компаний, данные приходят из облачных приложений (Salesforce, Marketo, Netsuite) и внешних источников, в разных форматах и с разной частотой. Главная проблема: качество должно отвечать ожиданиям пользователей, а потребности бизнеса меняются каждый квартал.

Команда внедрила три практики DevOps.

  1. Охватывайте весь жизненный цикл данных. Управление инцидентами дата-инженеров входит в data operations, расширение DevOps. Оно покрывает проблемы и данных, и пайплайнов: аномалии, свежесть, схему, тренды метрик. Проверки в ETL со временем трудно поддерживать, и они не видят проблем в трендах данных. Поэтому для хранилища нужна observability.
  2. Подавляйте шум. Среди алертов много сигналов об изменении данных, которые не означают новой проблемы. Качество системы алертов оценивают отношением шума к сигналу: чем оно ниже, тем лучше. DataDuty использует PagerDuty, чтобы находить похожие алерты и объединять несколько алертов одного инцидента в один.
  3. Группируйте объекты и инциденты для умной маршрутизации. Data observability, по словам Ману, — первый шаг перед любым управлением инцидентами: «данные не обновились» и «странный тренд» — разные проблемы. Команда сгруппировала проблемы пайплайнов и настроила сортировку и алерты поверх этого. Алерты Airflow тоже идут через PagerDuty. Критичные объекты (отчётность для руководства и финансовая) идут с политикой эскалации и в BI-команду. Метрики BI (число клиентов, отток, число инцидентов с данными) тоже под алертами.
  • Инцидент с данными нужно вести как инцидент в ПО: обнаружить, отреагировать, найти причину, исправить и провести разбор.
  • Цикл надёжности состоит из Detect, Resolve и Prevent. Первые два реактивные, третий проактивный.
  • Поиск аномалий нужен, но один он даёт единственную точку отказа. Нужны ещё реакция, RCA и профилактика.
  • Причины почти всегда в трёх местах: входные данные, логика трансформации, среда запуска. Причин часто несколько.
  • RCA идёт в пять шагов: lineage, код, данные, среда, коллеги. Метод five whys помогает дойти до корня.
  • Разбор должен быть blameless: виновата система, а не человек. Результат записывают и делят с командой.
  • Тесты ловят около 20% unknown unknowns. Остальное нужно ловить через CI/CD, discovery и observability.
  • Incident commander ведёт инцидент и оценивает серьёзность. Прогресс видно по N, TTD и TTR.
  • PagerDuty показывает три практики: охватывать весь жизненный цикл, подавлять шум и группировать алерты для маршрутизации.
ТерминЗначение
data reliability life cycleЦикл Detect, Resolve, Prevent для надёжности данных
incident commanderЧеловек, который ведёт инцидент от начала до конца
runbook, playbookИнструкция по сервисам и пошаговый процесс реакции
поиск первопричины (root cause analysis)Разбор: почему на самом деле сломались данные
five whysМетод: повторять «почему?», пока не найден корень
blameless postmortemРазбор инцидента без поиска виноватых
circuit breakerОстанавливает пайплайн, чтобы плохие данные не пошли дальше
SLA, SLO, SLIОбещание клиентам, внутренняя цель и метрика
TTD, TTRВремя до обнаружения и время до исправления
phantom dataДанные, которые сломались, но бизнесу не нужны
  • Заведите runbook для важных пайплайнов: владелец, расписание, где логи, кого оповестить.
  • Назначьте дежурного по данным на неделю. Пусть его имя будет в канале алертов.
  • Ведите журнал инцидентов с метками: началось, обнаружили, исправили. Считайте TTD и TTR.
  • При разборе аномалии сначала режьте данные по сегментам и периодам.
  • Пишите разбор после серьёзного инцидента: хронология, причина, действия, без имён виноватых.
  • Группируйте алерты одного пайплайна в один инцидент.

Три маленьких примера на цифрах. Данные заданы в коде, сторонние библиотеки не нужны. Запуск: python3 scripts/data-quality-fundamentals-code/<файл>.

Доля null в user_interests выросла с 16,7% до 23–24%. Скрипт ch06_rca_segments.py считает долю каждого источника и долю null внутри него в двух сценариях.

Прошлая неделя : null всего = 16.7%
twitter доля записей 19.9% null внутри 41.0%
facebook доля записей 40.2% null внутри 10.5%
google доля записей 39.9% null внутри 10.8%
Сценарий 1 : null всего = 23.4%
twitter доля записей 45.1% null внутри 39.9%
facebook доля записей 27.1% null внутри 9.9%
google доля записей 27.8% null внутри 9.6%
-> изменилась доля twitter, null внутри источников прежний: сезон или кампания
Сценарий 2 : null всего = 23.9%
twitter доля записей 19.7% null внутри 80.4%
facebook доля записей 40.2% null внутри 10.2%
google доля записей 40.1% null внутри 9.9%
-> доли те же, но null внутри twitter вырос: ошибка в обработке twitter

В сценарии 1 вырос вес Twitter, а null внутри источников прежний: кампания или сезон. В сценарии 2 доли прежние, но null внутри Twitter удвоился: искать нужно в обработке Twitter.

Скрипт ch06_incident_metrics.py берёт журнал инцидентов и считает N, медиану TTD и медиану TTR.

таблица N TTD, ч TTR, ч
orders 2 0.2 2.0
customers 1 47.0 6.0
payments 2 4.0 2.5
всего инцидентов N = 5
медиана TTD = 0.3 ч, медиана TTR = 2.7 ч
худший TTD = 47.0 ч (таблица customers: тихая ошибка жила почти два дня)

Таблицу customers исправили за 6 часов, но нашли только через 47. Улучшать нужно обнаружение, а не скорость исправления.

Скрипт ch06_alert_grouping.py показывает идею из кейса PagerDuty: алерты одного пайплайна в окне 30 минут считаются одним инцидентом, а критичные данные идут с эскалацией.

алертов: 7, инцидентов после группировки: 4
#1 orders_dag с минуты 5, алертов 4 (freshness, volume, schema, volume) -> on-call владельца пайплайна
#2 finance_dag с минуты 40, алертов 1 (freshness) -> эскалация: on-call + BI-команда
#3 orders_dag с минуты 55, алертов 1 (freshness) -> on-call владельца пайплайна
#4 orders_dag с минуты 300, алертов 1 (volume) -> on-call владельца пайплайна

Семь алертов превратились в четыре инцидента. Финансовый пайплайн сразу попал и к дежурному, и в BI-команду.

  1. Почему поиск аномалий нельзя считать полным решением проблемы инцидентов? Какие этапы нужны ещё?
  2. В каком порядке идут шаги RCA и зачем начинать с lineage?
  3. Доля null в поле выросла. Как по сегментам отличить удачную кампанию от ошибки обработки?
  4. Почему разбор инцидента должен быть blameless? Что вы запишете в postmortem, чтобы инцидент не повторился?
  5. Как incident commander оценивает серьёзность инцидента и чем здесь помогает lineage?
  6. Что показывают TTD и TTR? Какую из них улучшать, если ошибки долго остаются незамеченными?

Предыдущая: Глава 5. Архитектура для надёжности данных. Следующая: Глава 7. Сквозной lineage: от источника до дашборда.