Привет, Хабр! Мы в Dodo Pizza Engineering очень любим данные (а кто их сейчас не любит?). Сейчас будет история о том, как накопить все данные мира Dodo Pizza и дать любому сотруднику компании удобный доступ к этому массиву данных. Задача под звёздочкой: сохранить нервы команды Data Engineering.



Как настоящие Плюшкины, мы копим всевозможную инфу о работе наших пиццерий:


  • помним все заказы пользователей;
  • знаем, сколько времени ушло на изготовление самой первой пиццы в Сыктывкаре;
  • видим, сколько времени пицца остывает на тепловой полке в Воронеже прямо сейчас;
  • храним данные по списанию продуктов;
  • и многое-многое другое.

За работу с данными в Dodo Pizza сейчас отвечает несколько команд, одна из них – команда Data Engineering. Сейчас перед ними (то есть нами) стоит задача: дать любому сотруднику компании удобный доступ к этому массиву данных.


Когда мы стали думать, как это сделать и начали обсуждать задачу, мы нашли очень интересный подход к управлению данными – Data Mesh (по ссылке вы найдёте огромную шикарную статью). Её идеи очень хорошо легли на наше представление о том, как мы хотим построить нашу систему. Дальше в статье будет наше переосмысление подхода и то, как мы видим его внедрение в Dodo Pizza Engineering.


Что мы имеем в виду под «данными»


Для начала давайте определимся с тем, что мы подразумеваем под данными в Dodo Pizza Engineering:


  • События, которые шлют сервисы (у нас есть общая шина, построенная с помощью RabbitMQ);
  • Записи внутри базы данных (для нас это MySQL и CosmosDB);
  • Clickstream с мобильного приложения и сайта.

Чтобы бизнес Dodo Pizza мог использовать эти данные и полагаться на них, важно чтобы выполнялись следующие условия:


  • Они должна быть целостными. Мы должны быть уверены, что не изменяем данные в процессе их обработки, хранения и отображения. Если бизнес не сможет доверять нашим данным, то в них не будет никакой пользы.
  • Они должны иметь метку времени и не затираться. Это значит, что мы в любой момент времени хотим иметь возможность откатиться назад и посмотреть на данные того отрезка времени. Например, узнать, сколько пицц продали 8 июля 2018 года.
  • Они должны быть надёжными. В процессе сбора и хранения данных мы должны не терять не только целостность, но и надежность. Мы не можем терять данные, слайсы времени, потому что вместе с ними мы теряем доверие наших клиентов (как внешних, так и внутренних).
  • Они должны быть со стабильной схемой – мы пишем запросы на эти данные. Нам бы очень не хотелось, чтобы с изменением кода приложения, с рефакторингом, они поменялась настолько, что наши запросы перестали работать. Тот, кто пишет запросы никогда не узнает, что вы сделали рефакторинг, пока всё не сломается нафиг. Не хотелось бы узнать об этом от клиентов.

Учитывая все эти требования, мы пришли к выводу, что данные в Dodo – это продукт. Такой же, как публичное API сервиса. Соответственно, владеть данными должна та же команда, которая владеет сервисом. Также изменения схемы данных должны быть всегда обратно-совместимыми.


Традиционный подход – Data Lake


Для решения задач надёжного хранения и обработки больших данных существует традиционный подход, принятый во многих компаниях, которые работают с таким пулом информации – Data Lake. В рамках этого подхода инженеры по работе с данными собирают информацию со всех компонентов системы и складывают их в одно большое хранилище (это может быть, например, Hadoop, Azure Kusto, Apache Cassandra или даже MySQL реплика, если данные в неё влезают).


Дальше эти же самые инженеры пишут запросы к такому хранилищу. Реализация этого подхода в Dodo Pizza Engineering подразумевает, что команда Data Engineering будет владеть схемой данных в аналитическом хранилище.


При этом варианте развития событий, команда становится очень грустными котиками и вот почему:


  • Она должна следить за изменениями во ВСЕХ сервисах внутри компании. А их много и изменений очень много (в среднем мы мерджим ~100 pull requests в неделю, при этом многие сервисы не делают pull requests совсем).
  • При изменении схемы данных продуктоунер и команда, изменяющая схему данных, должны ждать пока Data Engineering допишет код, необходимый чтобы изменения поддерживались. При этом у нас уже давно фичатимы и ситуация, когда одна команда ждёт другую – очень редкая. И мы не хотим, чтобы это стало «нормальной» частью процесса разработки.
  • Она должна быть погружена в ВЕСЬ бизнес компании. Сеть пиццерий выглядит простым бизнесом, но это только кажется. Очень сложно собрать в одной команде достаточно компетенций для построения адекватной модели данных для всей компании.
  • Она является единой точкой отказа. Каждый раз, когда надо изменить данные, которые возвращает сервис или написать запрос, – все эти задачи падают к команде Data Engineering. В итоге у команды перегруженный бэклог.

Получается, что команда находится в точке пересечения огромного количества потребностей и вряд ли сможет удовлетворить их. При этом будет находиться в постоянном цейтноте и стрессе. Нам такого очень не хочется. Поэтому приходится думать, как решить эти проблемы и при этом получить возможность анализировать данные.

Перетекая от Data Lake к Data Mesh


К счастью, этот вопрос задавали себе не только мы. На самом деле, подобная проблема уже была решена в индустрии (аллилуйя!). Только в другой области: развертывание приложений. Да, я говорю про DevOps подход, где команда определяет, как нужно разворачивать продукт, который они создают.


Похожий подход к решению проблем Data Lake предложила Zhamak Dehghani, консультант ThoughtWorks. Наблюдая за тем, как подобные задачи решают Netflix и Spotify, она написала потрясающую статью How to Move Beyond a Monolithic Data Lake to a Distributed Data Mesh(ссылка на нее была в начале статьи). Основные идеи, которые мы для себя из неё вынесли:


  • Поделить большой Data Lake на домены данных, которые очень похожи на domain-driven design домены. Каждый домен – маленький bounded context.
  • Feature Team, которые отвечают за DDD домены, отвечают и за соответствующие домены данных. Они хранят схему, вносят в неё изменения, загружают в неё данные. При этом сами всё знают: как изменить загрузку данных и ничего не сломать, когда приложение меняется. Знания никуда не уходят. Чтобы открыть данные, им не надо никуда идти. Сама команда ведёт полный цикл разработки от изменения оперативных данных до предоставления аналитических данных третьим лицам. Одна команда владеет всем связанным с доменом (как бизнес-доменом, так и доменом данных).
  • Data Engineer – роль внутри Feature Team. Это не обязательно должен быть отдельный человек, но обязательно, чтобы команда обладала данной компетенцией.

А в это время команда Data Engineering...


Если представить, что всё это реализуется по щелчку пальцев, то останется ответить на два вопроса:


Чем теперь будет заниматься команда Data Engineering? В Dodo Pizza Engineering уже есть команда платформы/SRE. Её задача – дать разработчикам инструменты для простого развертывания сервисов. Команда Data Engineering будет выполнять такую же роль только для данных.


Превращение операционных данных в аналитические – сложный процесс. Сделать аналитические данные доступными для всей компании – ещё сложнее. Именно решением этих проблем и будет заниматься команда Data Engineering.


Мы собираемся предоставить Feature Team удобный набор инструментов и практик, с помощью которых они смогут публиковать данные из своего сервиса для остальной компании. Также мы будем отвечать за общие инфраструктурные части data pipeline (очереди, надежное хранилище, кластера для выполнения преобразований над данными).


Как навыки Data Engineer появятся внутри Feature Team? С Feature Team всё сложнее. Конечно, мы могли бы попробовать нанять по одному Data Engineer в каждую из наших команд. Но это очень сложно. Найти человека с хорошим бэкграундом в обработке данных и убедить его работать внутри продуктовый команды тяжело.


Большим плюсом Dodo является то, что мы любим внутреннее обучение. Так что сейчас наш план такой: команда Data Engineering начинает публиковать данные некоторых сервисов, плачет, колется, но продолжает кушать кактус. Как только мы поймем, что у нас есть готовый процесс для публикации, мы начинаем рассказывать о нём в Feature Team.


У нас есть несколько способов, как это сделать:


  1. DevForum, на котором мы расскажем, как выглядит процесс, который мы создали, какие есть инструменты и как их наиболее эффективно использовать.
  2. Выступление на DevForum поможет нам собрать обратную связь от продуктовых разработчиков. После этого мы сможем присоединяться к продуктовым командам и помогать им решать задачи с публикацией данных, организовывать тренинги для команд.

Потребление данных


Сейчас я много рассказал о публикации данных. Но есть ещё и потребление. Что по этому вопросу?


У нас есть замечательная команда BI, которая пишет очень сложные отчеты для управляющей компании. Внутри Dodo IS есть много отчетов для наших партнеров, которые помогают им управлять пиццериями. В нашей новой модели мы думаем о них, как о потребителях данных, у которых есть свои домены данных. И именно потребители будут отвечать за свои собственные домены. Иногда домен потребителя может быть описан одним запросом в аналитическое хранилище – и это хорошо. Но мы понимаем, что это не всегда будет работать. Именно поэтому мы хотим, чтобы та платформа, которую мы создадим для продуктовых команд, могла также быть использована и потребителями данных (ведь в случае с отчетами внутри Dodo IS – это будут одни команды).


Вот так мы видим работу с данными в Dodo Pizza Engineering. С удовольствием прочитаем ваши мысли по этому поводу в комментариях.

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


  1. bvasilyev
    14.11.2019 07:58
    +1

    Интересная статья, спасибо. Но есть ощущение, что не полностью раскрыт рабочий процесс при использовании нового подхода с data mesh.

    Из того, что сразу приходит на ум: к примеру, одна продуктовая команда — например, CRM, становится ответственной за «домен» CRM внутри data lake. При подходе data mesh эта команда самостоятельно вносит изменения в структуры данных и логику загрузки этих данных в data lake — то есть полностью управляет данными своей области. Но в большинстве случаев потребителям данных (скажем, для аналитических отчетов), нужны не только данные из CRM, но и из других областей тоже (к примеру, из биллинговой системы — чтобы посчитать выручку по каждой пиццерии). В таком случае, потребитель отчета уже оказывается «завязан» на две продуктовые команды. И если любая из этих команд внесет изменения в данные в своем домене, не учтя все возможные последствия этого изменения, то отчет скорее всего работать перестанет. А в любой крупной компании таких зависимостей между данными разных систем будет очень и очень много, соответственно и разделить ответственность между продуктовыми командами будет непросто — все равно нужен «надсмотрщик», который будет разруливать зависимости и контролировать изменения. Что в некоторой степени возвращает к «классической» модели с единой командой, отвественной за весь data lake.

    Сталкивались ли с подобным? Или как планирует решать?


    1. onikiychuka Автор
      14.11.2019 09:30

      Но есть ощущение, что не полностью раскрыт рабочий процесс при использовании нового подхода с data mesh.

      Ну поскольку мы только начинаем его строить — раскрыть полностью процесс очень сложно в одной статье. Очень сложно раскрыть то, что полностью пока не сформировалось :) Сейчас мы итеративно выстраиваем процесс работы с данными внутри команды Data Engeneering, что-бы потом переносить его (естественно адаптирую) на всю компанию.
      А в любой крупной компании таких зависимостей между данными разных систем будет очень и очень много, соответственно и разделить ответственность между продуктовыми командами будет непросто — все равно нужен «надсмотрщик», который будет разруливать зависимости и контролировать изменения.

      На мой взгляд тут ситуация похожа с API микросервисов. Если API опубликован — в нем нельзя делать breaking changes. То же самое с опубликованными схемами данных. Как это форсировать и контролировать? Мы сейчас думаем про автоматические тесты, которые не дадут накатить миграцию если в ней есть breaking changes.


      1. bvasilyev
        14.11.2019 17:26

        На мой взгляд тут ситуация похожа с API микросервисов. Если API опубликован — в нем нельзя делать breaking changes. То же самое с опубликованными схемами данных. Как это форсировать и контролировать? Мы сейчас думаем про автоматические тесты, которые не дадут накатить миграцию если в ней есть breaking changes.

        Мне кажется, тут будет довольно сложно определить, что означает breaking change. Очевидно, изменения в структуре данных, такие как удаление колонки в таблице или изменение типа данных в колонке — это breaking change.

        Но как быть с изменениями в логике данных? Простой пример: раньше в столбце «сумма чека» хранилась сумма без НДС, а потом в операционной системе что-то поменяли и стала храниться сумма уже с включенным НДС. С точки зрения продуктовой команды — это необходимое изменение, но при этом все потребители, которые опирались на этот показатель в data lake, сразу прибегут и скажут что у них сломались расчеты. Для написания тестов, которые будут проверять все возможные подобные случаи, надо будет потратить кучу времени — для каждой метрики или атрибута продумать возможные варианты использования, написать тесты, убедиться что они корректно работают при всех возможных раскладах и т.п.

        Я искренне желаю чтобы у вас все получилось :) но из моего опыта часто при разделении ответственности за данные между разными командами начинается хаос. В основном потому, что каждой команде проще встать в позицию «моя хата с краю» и не думать про последствия изменений, которые они вносят. Также начинается дублирование данных в data lake, так как часто отдельной команде срочно необходимы какие-то данные и им проще рассчитать их заново, нежели искать, не сделал ли кто-то это уже ранее.


        1. onikiychuka Автор
          14.11.2019 18:23
          +1

          Но как быть с изменениями в логике данных? Простой пример: раньше в столбце «сумма чека» хранилась сумма без НДС, а потом в операционной системе что-то поменяли и стала храниться сумма уже с включенным НДС. С точки зрения продуктовой команды — это необходимое изменение, но при этом все потребители, которые опирались на этот показатель в data lake, сразу прибегут и скажут что у них сломались расчеты.

          Так они у нас и текущим DataMonolith прибегают регулярно. Плюс мы сами чиним переливки в текущее аналитическое хранилище тк фича команда может удалить столбец без объявления войны :) Вообщем с монолитом еще хуже :)


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

          Ну тут наверное подход будет похожим на TDD — сначала пишем тесты на требования, при нахождении багов — тесты на баги. Так хотя-бы не будем чинить одно и то-же дважды.


          Мы понимаем что нет серебряной пули, но нам кажется что эти инструменты упростят нам жизнь.


          Я искренне желаю чтобы у вас все получилось :) но из моего опыта часто при разделении ответственности за данные между разными командами начинается хаос. В основном потому, что каждой команде проще встать в позицию «моя хата с краю» и не думать про последствия изменений, которые они вносят.

          Спасибо за теплые пожелания :) Мы приложим все силы что-бы получилось. Ну а хаос будет всегда — в наших силах только попытаться минимизировать ущерб от него через инструментарий и процесс :)


          1. bvasilyev
            14.11.2019 18:28
            +1

            Ждем следующую статью по результатам внедрения!


  1. epishman
    14.11.2019 09:11
    +1

    Похоже и НСИ придется разделять между доменами, а значит — частично дублировать их, синхронизировать и тд.


    1. onikiychuka Автор
      14.11.2019 09:34

      Возможно вы правы. Но честно говоря я не вижу сильных проблем в дублировании, если оно не несет за собой проблемы другого рода (рассинхрон данных например). Если будет нести — придется наверное выделить НСИ в отдельный домен. В принципе за НСИ в операционной системе сейчас отвечает отдельный микросервис. Так что выделение их в отдельный домен вполне возможно.


      1. epishman
        14.11.2019 11:06

        Для статичных НСИ все хорошо, а для достаточно динамичных (например производственные спецификации) рано или поздно встанет вопрос о распределенной транзакции, чтобы в процессе корректировки справочника никто не провел цепочку документов. Мне кажется проще не выделять НСИ и допустить рассинхрон между доменами — это более соответствует подходу. А что делать с рассинхроном — думать уже в отчетах, есть же свертка по нестрогому соответствию, или что-то вроде того.


        1. onikiychuka Автор
          14.11.2019 17:49

          Распределенные транзакции — это все-таки про операционную деятельность. В аналитической мы стараемся иметь дело с набором случившихся фактов. Так что пока мы не видим необходимости в транзакциях. Хотя факты конечно могут (и будут) разезжаться если сопоставлять их по времени. Так что мы сейчас думаем как сопоставлять факты по foregin key.


  1. xFFFF
    14.11.2019 11:35

    Не пойму, зачем пиццерии Big Data и нейросети? Или пиццерия это только прикрытие?


    1. zverolyub
      14.11.2019 18:20

      Когда у тебя одна пиццерия, то можно и без Big Data обойтись. Но когда у тебя 545 пиццерий в 13 странах мира (связанных единой системой управления), каждую минуту обрабатывается 120-180 заказов, тысячи отслеживаемых метрик и т.д. и т.д., то тут уж без неё никак.


  1. Phoen
    14.11.2019 13:07

    Спасибо за статью, возникло несколько вопросов:
    1. Как боретесь с дублированием данных в разных доменах? Как организовано их переиспользование? На первый взгляд создавать одинаковые датасеты для разных команд в режиме изоляции не выглядит оптимальным.
    2. Датасеты BI команды строятся на данных из доменов продуктовых команд?
    3. Расскажите больше о том, как команды управляются внутри своих доменов. Используют airflow для etl задач? Кто пишет джобы? Как делят ресурсы?
    4. Почему бы просто не раздать/обучить для каждой команды по data инженеру используя классический data lake подход? Насколько я понял, по сути, к этому все и пришло.


    1. onikiychuka Автор
      14.11.2019 18:37

      Почитав ваши вопросы, мне показалось, что вам кажется что все что описано — внедрено внутри компании. Это пока не так :) Сейчас мы на примере одной команды разрабатываем процесс и инструменты для внедрения Data Mesh в Додо. Так что мои ответы будут основываться на том, как мы видим конечный результат внедрения сейчас, а не на рабочем процессе.


      1 Как боретесь с дублированием данных в разных доменах? Как организовано их переиспользование? На первый взгляд создавать одинаковые датасеты для разных команд в режиме изоляции не выглядит оптимальным.

      Никак. У нас нет такой задачи. Если это будет неоптимальным в будущем — будем думать. Как вариант — выносить в отдельный домен данных которым будет владеть команда Data Engeneering.


      2 Датасеты BI команды строятся на данных из доменов продуктовых команд?

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


      3 Расскажите больше о том, как команды управляются внутри своих доменов. Используют airflow для etl задач? Кто пишет джобы? Как делят ресурсы?

      Технически мы используем Azure Kusto и его Kql для ETL. Он очень быстрый и нам хватает его возможностей. Данные в Kusto попадают через публикацию сообщений в Event Hub.


      4 Почему бы просто не раздать/обучить для каждой команды по data инженеру используя классический data lake подход? Насколько я понял, по сути, к этому все и пришло.

      Пока не пришло :) Еще идем. Классический Data Lake подразумевает что есть команда, которая отвечает за всю аналитическую схему данных в компании. Даже если все в итоге будет длится в 1 Mysql (который будет Data Lake) но ответственность за схему будет делится между фичакомандами — это уже Data mesh. Подход не про технологии, а про организацию работы людей.


      1. Phoen
        14.11.2019 18:58

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


        Нет опасений что таким образом у вас получится просто гигантское кол-во доменов с крайне разрозненными (в плохом смысле слова) данными? Как следствие может появится несколько велосипедов которые каждый по своему изощренно дергают источник и дают на выходе весьма похожие датасеты. Какова вообще логика напилки доменов? Per team, per user, per db object?

        Подход не про технологии, а про организацию работы людей.


        Ну вот поэтому мне и вдвойне интересно :) Сейчас наблюдаем схожую с описанной в посте проблему и хорошего + быстрого и не ресурсоемкого решения пока на горизонте обнаружено не было.


  1. amarao
    14.11.2019 15:45
    +1

    Данные должны быть целостными, доступными и не должно быть партиционирования.


    /Закопали CAP.


    1. Phoen
      14.11.2019 15:58

      Заметил что каждый раз когда выкапывают CAP — где-то что-то тормозит, не дистрибьютит или не может обеспечить заявленного. По крайней мере таков личный опыт :)

      Что в целом не отменяет вашей правоты.


      1. amarao
        14.11.2019 16:39

        У бухгалтеров такое часто бывает. Куда-то делось 100500 денег, приходится законы арифметики выкапывать.


    1. onikiychuka Автор
      14.11.2019 18:15

      Нее в терминах CAP мы хотим построить CP систему. A в терминах CAP мы не гарантируем A но хотим стремится к минимальным простоям по доступности.


  1. dborovikov
    14.11.2019 16:51

    Не очень понятно как при таком подходе будет обеспечивтаться сквозная аналитика всего хранилища. Получается, что все данные будут жить в своих silos и аналитика будет происходить на уровне каждого silos независимо.

    В одной из моих предыдущих компаний был такой подход: данные хранились и обрабатывались централизованно, однако у каждого data set был свой владелец (фича-команда), которые отвечал за наполнение этого сета свежими данными, а так же за публикации актуальной спецификации структуры данных. Спецификация хранилась в центральном репозитории в формате Protobuf.


    1. onikiychuka Автор
      14.11.2019 18:41

      Не очень понятно как при таком подходе будет обеспечивтаться сквозная аналитика всего хранилища. Получается, что все данные будут жить в своих silos и аналитика будет происходить на уровне каждого silos независимо.

      Мы пока думаем как это сделать. И надо ли это делать вообще. Ведь вполне можно создать домен для ad-hook аналитики, с помощью которого можно решить 80% вопросов. А если задача не попадает под 80% стандартных — ну что-ж надо создавать узкий домен.


      В одной из моих предыдущих компаний был такой подход: данные хранились и обрабатывались централизованно, однако у каждого data set был свой владелец (фича-команда), которые отвечал за наполнение этого сета свежими данными, а так же за публикации актуальной спецификации структуры данных. Спецификация хранилась в центральном репозитории в формате Protobuf.

      Это тоже Data Mesh в нашем понимании. Основная идея для нас — чтобы не было господа-бога-данных, без которого нельзя решить ни один вопрос, касающийся данных.


  1. Elemir
    14.11.2019 18:43

    Да, я говорю про DevOps подход, где команда определяет, как нужно разворачивать продукт, который они создают.
    Великий здравый смысл, с чего вы решили, что DevOps практики это про зоопарк велосипедов?


    1. onikiychuka Автор
      14.11.2019 18:46

      Мы понимаем DevOps как набор практик, с помощью которых фичакоманда может доставить свой продукт до конечного потребителя. Если кто-то делает это через зоопарк велосипедов (и это работает) — кто мы такие что-бы запрещать ковыряться в носу :)
      У нас самих зоопарка если что нет :) k8s — наше все :)