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

Мы сотрудничаем с глобальным сообществом Salesfore.org, которое помогает в решении крупнейших гуманитарных, образовательных проблем, а также проблем окружающей среды. Точно также, как и коммерческие клиенты Salesfore, наши глобальные партнеры ожидают, что предлагаемые нами продукты смогут масштабироваться до десятков миллионов записей. Поэтому перед выпуском нам необходимо тестировать их на реалистичных обширных наборах данных.

Команда Salesforce разработала открытый инструмент под названием Snowfakery, который подобен фабрике по производству крупномасштабных фиктивных данных, содержащих связи между сущностями. Особенность Snowfakery в генерации уникального содержимого датасетов. Каждая строка представляет фиктивные данные, но при этом они уникальны и случайны, наподобие снежинок.

Архитекторы могут использовать этот инструмент для создания CSV, SQL, bulk insert или JSON-файлов, которые затем можно загружать с помощью различных средств, но при этом Snowfakery уже интегрирован с инфраструктурой объемной загрузки CumulusCI и работает с ней очень слаженно.

Что такое Snowfakery?

В связи с доступностью многих систем генерации синтетических данных поначалу мы сомневались, а стоит ли создавать еще одну. Но, несмотря на свое удобство и понятность в использовании, всем этим системам недостает емкости для создания глубоко связанных данных: где Contacts соотносятся с Accounts, Opportunities с обеими этими категориями, Custom Objects связываются в сложных графах и т.д.

Нас интересовала система со следующими характеристиками:

  • масштабируемость до сотен миллионов строк;
  • реалистичные данные;
  • автоматическое управление связями между объектами (Master/Detail, Lookup или Junction);
  • интуитивный и понятный синтаксис для “инструкций”;
  • возможность по умолчанию использовать его в сочетании с Salesforce;
  • открытый исходный код и бесплатность (как и у других наших инструментов: CumulusCI, MetsCI, Metecho и MetaDeploy).

Найти подобный инструмент мы не смогли, поэтому и решили создать его сами.

Snowfakery для простых данных

Инструкции Snowfakery определяются в YAML, который является относительно простым и понятным для человека форматом, знакомым многим разработчикам и проектировщикам. Далее принцип работы инструкций будет расписан подробнее.

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

- object: Contact
  fields:
    name: Buster Bluth
    phone: 555-582-4553    # Comment: same as 555-LUC-ILLE

Через Snowflakery мы прогоняем этот пример так:

$ snowfakery docs/examples/simple_static.yml

В результате генерируется:

Contact(id=1, name=Buster Bluth, phone=555-582-4553)

Как видите, была создана одна запись контакта с тремя полями:

| | Id | Name | Phone |
| ----------- | ----------- | ----------- | ----------- |
| 1 | 1 | Buster Bluth | 555-582-4553 |

Два поля содержат данные из инструкции YAML, а ID генерируется отдельно. Эти данные также можно использовать в CSV, SQL, bulk insert или JSON-формате, а также в качестве диаграммы. Кроме того, после некоторых настроек, Snowfakery может делать запись напрямую в различные базы данных.

Но я обещал вам уникальные данные, подобные прекрасным неповторимым снежинкам. Так что продолжим:

- object: Account
  count: 8
  fields:
    FirstName:
      fake: first_name
    LastName:
      fake: last_name
    PhoneNumber:
      fake: phone_number

С помощью этого инструкции Snowfakery сгенерирует восемь Account, представляющих ценные и уникальные личности, которые никогда не существовали (точно не с этими номерами телефонов):

Account(id=1, FirstName=Dana, LastName=Ewing, PhoneNumber=7636043937)
Account(id=2, FirstName=Jennifer, LastName=Brown, PhoneNumber=036.920.1773x9467)
Account(id=3, FirstName=Nathan, LastName=Porter, PhoneNumber=(351)905-4880x24018)
Account(id=4, FirstName=Makayla, LastName=Warner, PhoneNumber=981-314-3601)
Account(id=5, FirstName=Marc, LastName=Benioff, PhoneNumber=+1-211-499-3398x412)
Account(id=6, FirstName=Rebecca, LastName=Chavez, PhoneNumber=001-595-256-8369x4195)
Account(id=7, FirstName=Monica, LastName=Jones, PhoneNumber=001-451-980-9489x155)
Account(id=8, FirstName=Justin, LastName=Clark, PhoneNumber=001-526-356-3339)

Есть старая шутка, что ученые по данным считают так: 0, 1, бесконечность. Конечно же, мы можем создать больше, чем эти восемь записей. На деле без изменения инструкции можно попросить Snowfakery выполнить его многократно вплоть до генерации даже миллиона строк.

Следующая инструкция даст Snowfakery команду выполнять инструкцию, пока не будет сгенерирован 1 000 000 аккаунтов, для чего придется выполнить ее 125 000 раз:

$ snowfakery examples/bluth.yml--target-number 1000000 Account--output-format csv

Этот инструмент не потребляет много памяти, поэтому для генерации 1 000 000 строк ее потребуется столько же, сколько для 10. Память из-под более ненужных результатов будет освобождаться сразу после их записи в базу данных или файловую систему.

На моем двухлетнем ноутбуке при выполнении этой простой инструкции Snowfakery генерирует более 3 000 строк в секунду.

Связывание объектов

Я упоминал, что особенным свойством нашей разработки является управление связями. Далее мы поговорим об Accounts (учетные записи), Opportunities (возможности) и Contacts (контакты). Вы уже знаете, что связываются эти объекты между собой так:

Создадим все это в Snowfakery, начав с Contact. Рассматривая этот раздел, мы попутно затронем еще одно новое понятие: взвешенный случайный выбор.

- object: Contact
  fields:
    FirstName:
      fake: first_name
    LastName:
      fake: last_name
    Salutation:
      random_choice:
        Mr.: 40%
        Ms.: 40%
        Dr.: 10%
        Prof.: 10%

Далее мы перейдем к Contact и Opportunity. Opportunity связывается с Contact, поэтому самым простым способом сделать это будет вложить Contact в Opportunity. Удалим Salutation, чтобы сократить пример.

- object: Opportunity
  fields:
    ContactId:
      - object: Contact
        fields:
          FirstName:
            fake: first_name
          LastName:
            fake: last_name

    Name: The ${{ContactId.LastName}} Opportunity

Здесь у нас три новых принципа:

  1. Contact можно вложить в Opportunity.
  2. Opportunity можно связать с Contact через одно из его полей.
  3. Opportunity можно назвать по имени поля из Contact, используя язык формул Snowfakery.

Единственная сложность при этом заключается в том, что вам зачастую понадобится привязывать к Contact множество Opportunity. Для этого нужно обратить вложение и поместить Count в Opportunity. Проблема в том, что Contact не связываются непосредственно с Opportunity ссылкой. Так куда же их вкладывать?

В этом нам помогает еще одна возможность Snowfakery, а именно friends. Объявление friends позволяет связывать объекты в файле при отсутствии их прямой связи в Salesforce или схеме базы данных.

Теперь развернем инструкцию:

- object: Contact
  fields:
    FirstName:
      fake: first_name
    LastName:
      fake: last_name
  friends:
    - object: Opportunity
      count:
        random_number:
          min: 5
          max: 20
      fields:
        StageName:
          random_choice:
            Prospecting: 50%
            Qualification: 50%
        CloseDate: 2022-01-01
        ContactId:
            reference: Contact
        Name: The ${{Contact.LastName}} Opportunity

Да, я тайком протащил сюда еще одну фишку: random_number

Эта команда генерирует для каждого Contact случайное количество Opportunity, именуя их согласно этому контакту. Обратите внимание, что мы обращаемся из Opportunity к содержащему ее Contact с помощью reference.

Snowfakery может даже генерировать картинки небольших датасетов. Например:

Продолжим проделывать тот же трюк с вложением, чтобы объединить Account и OpportunityContactRole:

- object: Account
  fields:
    name:
      fake: company
  friends:
    - object: Contact
      count: 2
      fields:
        .... elided for space ....
      friends:
        - object: Opportunity
          count:
            random_number:
              min: 1
              max: 3
          fields:
            StageName:
              random_choice:
                Prospecting: 50%
                Qualification: 50%
            CloseDate: 2022-01-01
            ContactId:
              reference: Contact
            AccountId:
              reference: Account
            Name: The ${{Contact.LastName}} Opportunity
          friends:
            - object: OpportunityContactRole
              fields:
                OpportunityId:
                  reference: Opportunity
                ContactId:
                  reference: Contact
                Role:
                  random_choice:
                    Business User: 20%
                    Decision Maker: 20%
                    Economic Buyer: 20%
                    Economic Decision Maker: 20%
                    Evaluator: 20%

Можно даже вложить еще одну Opportunity во вторую OpportunityContactRole, но так наш пример станет уже слишком длинным.

Вот наглядное представление OpportunityContactRole:

Импорт в Salesforce

Несмотря на то, что Snowfakery можно установить и использовать отдельно от CumulusCI, его также можно установить как часть этого инструмента. В таком случае он просто окажется еще одной из множества его возможностей.

Я могу загрузить приблизительно 10 000 Opportunity вместе со всеми связанными с ними записями так:

$ cci task run generate_and_load_from_yaml --num_records 10000 --num_records_tablename Opportunity --generator_yaml AccountContactOpportunity.recipe.yml --org qa

Эта команда выполнит инструкцию необходимое количество раз, сгенерировав не менее 10 000 возможностей, и автоматически создаст подходящее число соответствующих Account (~2 500), Contact (~5 000) и OpportunityContactRoles(~10 000).

Эта же команда автоматически загружает данные в организацию, подключенную к CumulusCI с псевдонимом “qa”.

Выглядит это так:

В самой организации отобразятся эти данные так:

Использование хранилища данных Salesforce

Дополнительные возможности

В Snowfakery есть и многие другие возможности, включая:

  • Удобный механизм встраивания для написанных на Python плагинов.
  • Сотни видов фиктивных данных среди десятков языков и регионов, например корпоративные слоганы, адреса в десятках стран, имена людей из многих культур, компьютерные коды, включая hex-цвета, IP-адреса и другие.
  • Макрос для поддержки повторного использования кода и реорганизации файлов инструкций.
  • Инструкции, включающие другие инструкции в качестве модулей.
  • Богатый язык формул с функциями для управления строками и математическими операциями.
  • Инструменты для объединения данных CSV и Salesforce.

CumulusCI, в свою очередь, обладает еще более обширным списком возможностей, чем Snowfakery, что делает его чрезвычайно эффективным. 

Опенсорс в Salesforce.org

В рамках Salesforce.org мы используем Snowfakery и CumulusCI для тестирования собственных продуктов в масштабе. Открытость этих инструментов также делает их доступными для наших клиентов и партнеров. Помимо этого, мы делаем доступным исходный код наших моделей данных и некоторых продуктов. Вокруг Snowfakery сформировалось открытое сообщество, и некоторые из его участников уже создали красивые Web-интерфейсы для редактирования файлов Snowfakery. Будьте в теме!

Также приглашаем вас в группу Trailblazer для обсуждения любых возникающих вопросов. 

Читайте также:

Читайте нас в Telegram, VK и Яндекс.Дзен


Перевод статьи Paul Prescod: Generate Realistic Datasets with Snowfakery

Предыдущая статьяКак разработчику стать архитектором ПО?
Следующая статьяКраткая история инструментов веб-дизайна